Xpert Agentic App Developer
Overview
Use this skill to build an Xpert Agentic App as a production plugin, not as a loose prompt or a few attached tools. Treat the app as a closed loop: plugin metadata, optional marketplace appConfig, server module, Agent middleware tools, persistence, Workbench or extension view UI, Assistant template, governed installation, and tests.
Do not confuse Agent middleware tools with workflow Agent Tool nodes. In this workflow, "tools" means callable tools returned by Agent middleware to the agent runtime.
The primary UI path for this skill is an Xpert extension view: a Workbench view manifest plus a remote component or platform-rendered view. If a task is primarily about plugin-managed MCP tools or MCP Apps, use the dedicated Xpert plugin development MCP guidance instead; this skill only mentions that path as an optional integration surface.
When designing or refactoring middleware responsibilities, View capability gates, Assistant composition, or the relationship between human and Agent roles, read references/middleware-view-role-boundaries.md before implementing.
Golden Principle: Review Files Over 1,000 Lines
Treat 1,000 lines as an architecture-review threshold for maintained source files. When a code file exceeds 1,000 lines, pause before adding more behavior and assess whether it combines multiple responsibilities. Split coherent responsibilities into focused files when clear boundaries exist, while preserving explicit ownership, stable public contracts, and test coverage. Do not mechanically fragment a cohesive file merely to satisfy the line count.
Development Workflow
- Inspect the target plugin repository and the host app conventions before editing.
- Define the business loop: what the Agent automates, what humans review, and what the system persists. When each plugin business project, case, or similar entity needs an isolated file space shared by the Primary Agent and subagents, read references/assistant-workspace-projects-catalog.md and bind it to the Assistant
projectsWorkspace Catalog. - Determine whether the plugin provides host server capabilities; if it registers entities, controllers, routes, or equivalent process-global infrastructure, declare it as system level and define its stable artifact namespace before implementing artifact identifiers.
- Define domain capability boundaries and map middleware, Views, Agent roles, and human authority according to references/middleware-view-role-boundaries.md.
- When the plugin should appear as a visually presented App in Xpert Explore and support host-governed setup of a dedicated Workspace, optional Knowledge bases, and a published Assistant, read Plugin Application
appConfigand declare a typedappConfiglinked to exactly one same-plugin Assistant template. - Register the server module, entities, services, middleware, and view provider.
- Expose business actions as Agent middleware tools with strict schemas and call order.
- When a deterministic plugin workflow starts specialist subagents through the platform Assistant Task capability, read references/assistant-task-orchestration.md; when durable background work must keep the current Agent conversation turn alive because proactive completion delivery is unavailable, read references/agent-long-running-tasks.md and implement the bounded long-polling bridge.
- Persist reviewable business data with evidence, confidence, status, and failure state.
- Add a Workbench or extension view for human review and operational actions. For Assistant Profile tabs and contextual decisions, read references/assistant-profile-views.md.
- When the app publishes previews or share links, read references/artifact-share-links.md and use the platform Artifacts and Workspace Files capabilities.
- Provide an Assistant template for first-time installation and subsequent in-place upgrades. Before installing, upgrading, or provisioning a versioned role/Orchestrator acceptance suite, read references/assistant-template-lifecycle.md; update an existing instance through
Assistant Settings->Update from Templateinstead of creating a duplicate from the wizard. - Build and register the plugin from an independent plugin repository.
- Validate with unit, integration, manifest, and end-to-end tests.
Architecture Checklist
An Agentic App should usually include:
- Business plugin:
XpertPluginmetadata, system level and artifact namespace when server capabilities are present, config schema, target apps, capabilities, templates, lifecycle. - Agent middleware tools: zod schemas, tool descriptions, ordered tool calls, per-item persistence, failure reporting.
- Services and data models: domain entities, review state, source evidence, confidence, audit-friendly outputs.
- Workbench or extension view: view manifest, actions, data queries, host event subscriptions, optional remote component UI.
- Assistant template: DSL content, required plugins, capabilities, model options, starter prompts.
- Optional marketplace App contract: typed
appConfigpresentation, explicit same-plugin Assistant-template linkage, governed Workspace/Knowledge initialization, model preflight, and installation health when the product needs an Explore App and one-click organization setup. - Optional MCP surface: only when explicitly requested, expose standard MCP tools or MCP Apps through plugin-managed MCP servers; keep detailed MCP implementation guidance outside this skill.
Type Boundary Hygiene
When TypeScript code shows any or unknown around plugin, SDK, React, remote bridge, or domain-library boundaries, inspect the real upstream types before editing. Avoid normalizing patterns such as as any, as unknown as, : any, : unknown, Record<string, any>, broad callback parameters, or untyped mocks. Prefer importing the concrete type, deriving callback/event types with Parameters<> / ReturnType<>, writing narrow type guards, or defining a small boundary DTO such as a JSON payload type. Keep unavoidable compatibility assertions local to the integration boundary through a named helper, and do not let the assertion flow into application logic.
Debug Logging Standard
Every Agentic App with middleware tools, host events, or a remote component should include a switchable debug logger before deep debugging. Do not rely on ad hoc console.info statements. Detailed logs must be off by default in production and easy to enable during local development. For remote components, the host renderer should derive the default debug state from the Cloud environment.production value and pass it in the iframe init message, for example { debug: { enabled: !environment.production, production: environment.production } }; the iframe should consume that value as its default and should not infer development mode from localhost, hostname, API URL, tenant, organization, or token-like values.
Treat every Remote View iframe as a sandboxed opaque-origin document. Never read or write localStorage or sessionStorage from remote-component code; even optional chaining still reads the property and may throw SecurityError when the iframe lacks allow-same-origin. Keep ephemeral state in React state or refs, persist durable state through the platform bridge, and receive runtime configuration through the host init message.
Use a small shared logger per plugin surface:
interface RemoteDebugConfig { enabled: boolean }
let debugEnabled = false
export function configureRemoteDebug(next: RemoteDebugConfig) { debugEnabled = next.enabled }
export function createPluginDebugLogger(namespace: string) {
return {
debug(event: string, data?: object) {
if (debugEnabled) console.debug(`[${namespace}] ${event}`, redactDebugData(data))
},
info(event: string, data?: object) {
if (debugEnabled) console.info(`[${namespace}] ${event}`, redactDebugData(data))
},
warn(event: string, data?: object) {
console.warn(`[${namespace}] ${event}`, redactDebugData(data))
},
error(event: string, data?: object) {
console.error(`[${namespace}] ${event}`, redactDebugData(data))
}
}
}
Keep this logger tiny and typed. Implement redactDebugData beside it to remove secrets and summarize large values before printing. Call configureRemoteDebug(init.debug) when the bridge receives init. For preview or query-string overrides, resolve the switch in the host renderer or preview harness and pass the resulting boolean in init.debug; do not inspect Web Storage inside the iframe. If server-side debug is needed, gate it through plugin config or an explicit environment flag, never through user-provided request data.
Log only useful checkpoints:
- Middleware: tool name, tool call id, compact input summary, target business id, success/failure, duration.
- Host event bus: event id, event type, source, tool name, target business id, subscription key.
- Remote bridge: init, host event received, event normalization result, requestData/action request id, response summary.
- Remote component state: selected business id, dirty state, refresh decision, diff counts, applied/skipped reason.
Never log tokens, credentials, raw file buffers, base64/data URLs, tenant ids, organization ids, full snapshots, full tool outputs, or personally sensitive content. Redact or summarize large payloads before printing. Production builds may keep warn and error, but debug/info must stay gated.
Independent Plugin Repository
Develop production business plugins in an independent plugin repository, commonly with a workspace layout similar to xpert-plugins. The host Xpert app should load, validate, and run the plugin; avoid developing production plugin code directly inside the host application repository.
Each plugin package should declare its package metadata and SDK peer dependency:
{
"name": "@acme/plugin-contract-review",
"version": "0.1.0",
"artifactNamespace": "contract_review",
"main": "dist/index.cjs.js",
"types": "dist/index.d.ts",
"peerDependencies": {
"@xpert-ai/plugin-sdk": "^3.8.0"
}
}
Keep @xpert-ai/plugin-sdk in peerDependencies, not dependencies, so the plugin does not bundle its own SDK copy.
When updating @xpert-ai/contracts or @xpert-ai/plugin-sdk, verify the versions are actually published or available from the active workspace before committing lockfile changes. If a requested peer range is not published yet, do not force a broad pnpm-lock.yaml rewrite or disable peer installation for the whole workspace; record the peer range only when the host is expected to provide it, and keep development-only type packages in devDependencies only when they are needed for local compilation.
System-Level Plugin and Artifact Namespace
An Agentic App that registers TypeORM entities, controllers, server modules, routes, or equivalent host-process capabilities is a system-level plugin. Declare meta.level: 'system' and an explicit, stable meta.artifactNamespace; never rely on the package-name fallback. The namespace may contain only lowercase letters, numbers, and underscores.
Treat artifactNamespace as the root of plugin artifact identity, not as passive metadata:
- Define it once as an exported constant.
- Use
pluginArtifactTableName(namespace, tableKey)for every entity table so the physical name isplugin_<artifactNamespace>_<tableKey>. - Derive controller route prefixes, provider/view/registry keys, Managed Queue identifiers, cache namespaces, persisted artifact keys, and other process-global unique strings from the same constant through small typed helpers.
- Keep runtime meta, top-level package marketplace metadata, and bundle manifest metadata aligned when those surfaces exist.
- Do not double-prefix contracts the platform already namespaces automatically; document and test the final resolved value.
- Never rename a published namespace without an explicit migration for stored tables, references, and registered identifiers.
import { pluginArtifactTableName } from '@xpert-ai/plugin-sdk'
export const PLUGIN_ARTIFACT_NAMESPACE = 'contract_review' as const
export const pluginArtifactKey = (localKey: string) =>
`${PLUGIN_ARTIFACT_NAMESPACE}.${localKey}`
export const CONTRACT_TABLE = pluginArtifactTableName(
PLUGIN_ARTIFACT_NAMESPACE,
'contract'
)
export const REVIEW_VIEW_KEY = pluginArtifactKey('review-view')
export const CONTROLLER_ROUTE = `${PLUGIN_ARTIFACT_NAMESPACE}/contracts`
Plugin Entry Pattern
Define XpertPlugin metadata as the app-facing capability contract. Prefer targetApps and targetAppMeta over ad hoc top-level business metadata. If the plugin is a product-level App that needs host-rendered marketplace presentation and governed first-time initialization, declare PluginMarketplaceContribution.appConfig under targetAppMeta.xpert.marketplace.contents; read Plugin Application appConfig before implementing it.
const plugin: XpertPlugin<z.infer<typeof ConfigSchema>> = {
meta: {
name: '@acme/plugin-contract-review',
version: '0.1.0',
level: 'system',
artifactNamespace: PLUGIN_ARTIFACT_NAMESPACE,
category: 'middleware',
targetApps: ['data-xpert'],
targetAppMeta: {
'data-xpert': {
types: ['workbench-view', 'assistant-tool', 'business-app'],
capabilities: ['contract-review', 'review-workbench']
}
},
displayName: 'Contract Review',
description: 'Parse contracts and expose a review workbench.',
author: 'Acme'
},
config: { schema: ConfigSchema },
templates,
register() {
return { module: ContractReviewPlugin, global: true }
}
}
Use plugin config only for values that administrators or deployments should change: default resource IDs, retrieval modes, external endpoints, feature flags, or credentials handled by the platform config system. Do not put marketplace presentation or declarative App resource initialization in plugin config, and do not confuse appConfig with bundle apps / connectors resources or Workbench View configuration.
Server Module Pattern
Register entities, services, Agent middleware, and view providers in the plugin server module.
@XpertServerPlugin({
imports: [TypeOrmModule.forFeature(ENTITIES)],
entities: ENTITIES,
providers: [
ContractReviewService,
ContractReviewMiddleware,
ContractReviewViewProvider
],
exports: [ContractReviewService]
})
export class ContractReviewPlugin {}
Declare entity names and controller routes from the namespace helpers instead of hardcoded global strings:
@Entity(CONTRACT_TABLE)
export class ContractEntity {}
@Controller(CONTROLLER_ROUTE)
export class ContractController {}
Model for review and recovery, not only for successful tool calls. Persist source document identity, page or location, evidence text, confidence, Agent rationale, human review status, comments, retry jobs, and failure reasons when relevant.
Every plugin entity must support tenant and organization isolation. Add nullable tenantId and organizationId columns plus a composite index to each persisted entity, populate them from the Integration record or current RequestContext on every write, and scope reads, updates, deletes, list endpoints, duplicate checks, cache keys, and xpert/integration reverse lookups by these fields whenever they are available. Do not query plugin-owned data by only business IDs such as integrationId, xpertId, or external account IDs when tenant or organization context is known.
Agent Middleware Tools
Expose business actions through middleware tools. Keep each tool narrow and explicit. Prefer ordered, restartable workflows over one giant tool.
When creating, changing, or reviewing model-visible middleware tools, also use the Xpert Plugin Development skill and read its Tool Contract Design reference. Treat that document as the canonical detailed contract for schemas, DTOs, pagination, authorization, localized ChatKit titles, Tool/Middleware icon inheritance, changeSummary, event payload filtering, and tests.
Good document-intake pattern:
contract_upsert_header
-> contract_upsert_line
-> contract_finalize_parse
Tool design rules:
- Use zod schemas and precise field descriptions.
- Set
verboseParsingErrors: trueon every LangChain structured tool configuration so schema failures include actionable Zod or JSON Schema details that the Agent can use to correct its next call instead of receiving onlyReceived tool input did not match expected schema. - State call order in tool descriptions.
- Save long lists one item at a time.
- Include required
technicalAttributes/differencesarrays, or domain equivalents, even when empty. - Require source evidence for important extracted values.
- Provide a failure-reporting tool for unreadable files or incomplete parsing.
- Return compact operation DTOs by default: business id, revision/status, a human message, changed ids/counts, blocking diagnostics, and the next recovery action. Never return a full document, scene, IR, binary payload, or complete history from a mutation or validation tool. Expose full content only through an explicit paged/item-level read tool.
- Follow the Tool Contract Design display contract: use localized
metadata.toolNameas the default title, require boundedchangeSummaryonly for genuinely dynamic business descriptions, and never exposechangeSummaryin ChatKit structured details. - Define the tool-family default icon once on the owning middleware strategy's
meta.icon; usemetadata.toolIcononly for a semantically distinct tool override. Never set the host-reservedmiddlewareIcondirectly or hardcode business tool-name icon mappings in ChatKit. - Await every asynchronous service call before serializing the tool result. Never pass a live Promise to
JSON.stringifyor detach a rejecting Promise from the tool invocation.
Example:
const saveContractHeaderTool = tool(
async (input) => {
const contract = await service.upsertContractHeader(input)
return JSON.stringify({ message: 'Contract header was saved.', contractId: contract.id, status: contract.status })
},
{
name: 'contract_upsert_header',
description: 'Create or reset the parsed contract header. Call this before saving line items.',
schema: contractHeaderSchema,
verboseParsingErrors: true
}
)
Optional MCP Tools and MCP Apps
This skill is not the primary guide for plugin-managed MCP tools or MCP Apps. When the user explicitly asks for MCP tools, MCP Apps, .xpertai-plugin/plugin.json mcpServers, ui:// resources, or ChatKit inline MCP App rendering, switch to the dedicated plugin development guidance for that surface.
Keep the boundary clear:
- Use this skill for Xpert-native Agentic Apps built around server modules, Agent middleware tools, Workbench extension views, remote components, Assistant templates, and persisted business state.
- Use plugin-managed MCP tools when the callable surface must be standard MCP and installed as a Toolset resource.
- Use MCP Apps only for inline interactive HTML returned by MCP tool calls; do not substitute MCP Apps for persistent Workbench or integration pages.
- If an Agentic App also exposes MCP tools, keep the MCP server packaging and bridge details isolated from the extension view implementation.
Workbench View
Add a Workbench view when users must review, correct, approve, reject, upload files, or submit results. Use a remote component iframe when the UI needs custom interaction beyond declarative tables and forms. Before adding review gates, approval prompts, warnings, diagnostics, or dense detail surfaces, read references/human-decision-load-and-progressive-disclosure.md; keep the system behavior thorough while minimizing mandatory user decisions and progressively disclosing non-decision information.
Business-First Information Architecture
Keep one business decision on one page. Use steps only for distinct, separately completable stages; do not present context, progress, or status as navigation. Prioritize business values, status, blockers, impact, and the next action, while moving implementation identifiers and diagnostics into accessible progressive disclosure.
Choose shared shadcn primitives by interaction intent: Tooltip or HoverCard for optional context, Popover or DropdownMenu for compact choices, Dialog for bounded tasks, Drawer or Sheet for sustained detail, and the designated alert-dialog primitive only for justified consequential confirmation. Never hide essential instructions or the only action path in hover-only UI, and preserve evidence and audit details outside the primary reading path.
For React remote component views, prefer TSX as the default development mode. Implement the view as maintainable React TypeScript source, preferably remote-components/<entry>/src/main.tsx plus supporting *.ts/*.tsx files, and generate the iframe entry app.js through a repeatable build step such as esbuild. Do not hand-maintain a large React.createElement app.js as the source of truth unless the user explicitly asks for a no-build static script or the existing plugin already has a deliberate no-build convention. Keep the generated app.js only as the runtime artifact read by renderRemoteReactIframeHtml, and wire build, typecheck or an equivalent check so stale generated output is caught.
Unless the user explicitly requests another design system or the existing View has a documented compatibility constraint, use this default implementation baseline for React Extension Views and Workbench Remote Views:
- Resolve the shadcn source with references/shadcn-ui.md: use
@xpert-ai/plugin-shadcn-uionly when it is already available from the current repository/workspace; otherwise install the required official components into the current project with the shadcn CLI and import that project-local source. Never reach into another checkout through filesystem paths, aliases,file:dependencies, orNODE_PATHto obtain the UI package. Do not substitute native selects, ad hoc buttons, copied sibling-project source, emoji, or CSS-drawn control glyphs for an available shadcn primitive or standard icon. - Compile Tailwind CSS from the maintained TSX source. In shared-package mode, loading its prebuilt stylesheet does not compile consumer utility classes; in CLI-local mode, include the generated component directory in the local scan. Configure the Remote View build to scan its own maintained sources and emit the production
app.css. - Load the selected shadcn source's stylesheet once, apply host
--xui-*tokens, then run the current project's canonical semantic-variable adapter after initialization and on theme changes. UseinstallShadcnThemeVarsfrom the local shared package when available; for CLI-local components, keep the equivalent host-token mapping in the current project. Do not treat stylesheet loading as theme installation. - Use a Studio floorplan by default: a fixed
width: 100%; height: 100%shell,min-width: 0; min-height: 0; overflow: hiddenthrough the flex/grid ancestor chain, a compact command toolbar, a dominant workspace, and independently scrolling side panels. - Render every outermost large content section as a flat three-part section, not as a
Card: a header row containing the section Title and only the Actions scoped to that section; a full-width semantic divider; then the Content. Do not useCardmerely to create the outer section boundary. Within Content, useCardonly for genuinely independent business objects or interaction surfaces; render repeated content as rows, lists, tables, or plain subsections with shared dividers. - Make navigation, object-list, library, or inspector side panels collapsible when their content competes with the primary workspace. Keep collapse state in React memory unless durable state is explicitly required; do not introduce
localStorageorsessionStorage. - Preserve accessibility names, keyboard focus, iframe-local portals, host theme/density, and responsive behavior in both expanded and collapsed states. A wide screenshot of the expanded happy path is not sufficient acceptance evidence.
Any exception to this baseline must be explicit in the task or recorded beside the View with its reason, scope, risk, and removal condition. A domain canvas or specialized editor may use justified custom layout CSS, but ordinary controls, theme installation, bounded-height behavior, and panel disclosure still follow the baseline.
Before designing a new Workbench or Extension View, or substantially changing its information architecture or visual hierarchy, read references/enterprise-shadcn-design-principles.md and treat it as the product-design standard. For every React Remote View, also read references/shadcn-ui.md before implementation unless an explicit, documented exception selects another UI stack. Treat host theme installation as a runtime contract: load the selected current-project stylesheet, map host --xui-* tokens to shadcn semantic variables through the selected current-project adapter, propagate density, keep the documented CSS fallback, and verify computed styles in the installed host iframe.
When a Remote View calls invokeClientCommand, read references/view-client-commands.md before implementation. Treat command availability as a closed capability protocol: allowlist the exact key in the View manifest clientCommands, register the handler in every intended host surface, invoke it through the remote bridge, consume structured failure results, and verify the real click path in an installed host. Host handler registration alone never grants a View permission to invoke the command. When a plugin View exposes deep-linkable route, selection, filter, tab, or layout state—or opens Assistant conversations while that View remains active—also read references/view-navigation-state.md. Keep plugin code at the public View-query boundary: declare and send typed state through the bridge, restore it from initialQuery, separate business state from UI-only state, and never construct host URLs or depend on host query-parameter names. When an Extension View publishes the current UI selection to an Assistant with assistant.context.set, also read references/extension-view-agent-context.md; do not assume request.context or config.configurable.context is visible to the model, and deliberately inject a filtered model context, resolve omitted tool arguments from runtime context, or use both patterns.
Workbench E2E and Visual Validation
For substantive Workbench or remote-component changes, treat end-to-end browser tests as executable acceptance specifications rather than optional smoke tests. Read references/workbench-e2e-visual-validation.md before implementing or validating multi-step UI workflows, host-bridge actions, persistence/reload behavior, timeline or canvas interactions, screenshot-driven designs, or visual regressions.
Test the real generated remote-component assets inside a representative Xpert View Host harness, assert both visible behavior and persisted/host-side state, and capture deterministic screenshots at important interaction states. Never make a failing interaction pass with forced clicks or arbitrary sleeps; diagnose layout, state, or event-ordering defects. Follow simulated-host E2E with an installed-platform browser pass whenever the change depends on authentication, permissions, Workspace Files, cookies/CORS, Managed Queue, Sandbox Runtime, or real plugin registration.
When installed-platform browser interaction involves Shadow DOM, nested or cross-origin iframes, React-controlled inputs or contenteditable, unreliable locators, or repeated click/type failures, read references/layered-browser-interaction.md. This includes embedded links or citations that invoke host commands, and dialogs or controlled inputs inside embedded views. Escalate through DOM and frame boundaries deliberately, preserve the host page and bridge context, and verify the UI event, bridge transport when applicable, and business postcondition instead of treating a reported click or visible text as success.
When designing, reviewing, or accepting an interactive Remote View prototype, read references/prototype-as-production-remote-view.md and follow it as a development standard. Build the prototype in the maintained production TSX with the resolved current-project shadcn source, Tailwind, host theme bridge, and generated assets; use the shared Preview Host plus a plugin-owned fixture only as the replaceable business adapter, never a disposable native HTML/CSS implementation or preview-specific branch in business components. Read references/remote-view-preview-host.md for the harness contract.
Confirmation Dialog Standard
First use references/human-decision-load-and-progressive-disclosure.md to decide whether a confirmation is actually necessary. Treat confirmation as a dedicated interaction pattern only for consequential, destructive, security-sensitive, or irreversible actions; showing information or proving that the user saw it is not sufficient reason to interrupt the flow. Do not use browser-native dialogs or a generic content modal as a confirmation substitute. Reserve ordinary dialogs for forms, details, previews, and other non-confirmation content.
Provide an explicit title, consequence-focused description, Cancel action, and confirmation action. Visually distinguish destructive actions. Resolve dismiss, Escape, overlay close, and Cancel as cancellation. Execute the protected operation only after explicit confirmation. For asynchronous mutations, prevent duplicate submission, expose pending state, and keep failures recoverable. Keep confirmation copy localized and state-driven.
Audit existing confirmation flows when touching this interaction pattern. Verify that maintained UI source contains no browser-native confirmation calls, rebuild generated remote assets, and exercise both cancel and confirm paths in tests or browser verification. For React implementations using shadcn UI, read references/shadcn-ui.md before editing.
For React project and remote component development, especially when React is supplied by the host iframe runtime or when TypeScript hover/types appear as any, read references/react-project-development.md before editing.
Plugin i18n Standard
Before adding, reviewing, or migrating user-visible copy or locale support, read references/i18n.md. Use one plugin-owned, typed i18n facade per UI surface; components call semantic translation keys and shared Intl formatters instead of importing an engine directly.
Never branch on locale to choose user-visible text inside JSX or ordinary component helpers. This includes labels, tooltips, placeholders, empty states, validation errors, confirmations, toast messages, table actions, filenames, and accessibility text. Organize catalogs by view/domain namespace, use the default catalog as the typed key schema, and enforce catalog parity in CI. Keep domain data raw unless its contract explicitly provides localized variants.
Normalize the host iframe locale once at the remote entrypoint with explicit BCP 47 aliases and fallback. Keep zh-Hans and zh-Hant distinct; do not map every zh* locale to Simplified Chinese. Centralize platform key conversion (en_US / zh_Hans) and third-party editor locale mapping at narrow adapter boundaries.
Backend services and Agent middleware should return stable language-neutral status/error codes plus structured parameters. Localize display labels at the UI boundary and never return localized prose as the only state representation. Pass a normalized locale only when producing inherently user-facing artifacts such as Excel, PDF, Word, email, toast-style action messages, localized manifest metadata, or export filenames.
Agent middleware tool schemas and descriptions should generally remain stable English unless the platform explicitly supports localized tool metadata. For Workbench manifests and platform metadata, use platform localized objects such as { en_US, zh_Hans } and keep their conversion out of business components.
For remote component data loading, route iframe requests through the platform bridge (requestData / executeAction) and the view provider. Keep initial getViewData responses light enough for first paint, then use tab-specific remote pagination for large tables. A stable pattern is:
- Frontend sends
requestDatawithquery.page,query.pageSize,query.search, andquery.parameters.table. - Use one table key per dataset, such as
accounts,conversations,messages, orlogs. - Return
{ tableKey, table: { key, items, total, page, pageSize } }from the view provider. - Keep each tab's filters, page, and page size independent in component state.
- Reset the page to
1whenever filters change.
XpertViewQuery.parameters only supports scalar values or scalar arrays. Do not send nested filter objects directly from a remote component. Serialize complex filters as a JSON string parameter such as filtersJson, parse it in the view provider, and tolerate malformed JSON by falling back to {}.
Before adding or changing icons for a Workbench, extension view, or remote component, read and follow references/remote-view-icons.md.
Manifest essentials:
{
key: 'contract_review',
title: { en_US: 'Contract Review', zh_Hans: '合同审核' },
hostType: 'agent',
view: {
type: 'remote_component',
runtime: 'react',
protocolVersion: 1,
component: {
isolation: 'iframe',
entry: 'contract-review'
},
dataSource: { mode: 'platform' }
},
actions: [
{
key: 'approve_line',
label: { en_US: 'Approve', zh_Hans: '确认' },
actionType: 'invoke',
placement: 'row'
}
]
}
Security and integration rules:
- Do not send tokens, API URLs, assistant IDs, tenant IDs, or organization IDs into the iframe.
- Route iframe data and actions through the platform bridge and view-host.
- Declare every backend interaction in the manifest before the remote component uses it.
- Use file actions for uploads and JSON actions for normal commands.
- For table views, declare pagination/search support in
querySchema, and keep backend list endpoints tenant/organization scoped before filtering and paginating.
Tool Completion Events
Use host event subscriptions so existing Workbench views update when Assistant middleware tools finish.
hostEvents: {
subscriptions: [
{
key: 'contract-review-tool-completed',
event: 'assistant.tool.completed',
filter: {
sources: ['chatkit'],
toolNames: ['contract_upsert_header', 'contract_upsert_line']
},
action: {
type: 'forward',
debounceMs: 1000
}
}
]
}
Use refresh for simple declarative views. Use forward for remote components so the iframe can switch tabs, update query parameters, or refresh only affected panels.
For remote components, implement the event path as a closed protocol, not a best-effort side effect:
- Middleware mutation tools must return a compact result that includes the mutated business id whenever possible, such as
documentId,drawingId,recordId,versionId, and a humanmessage. - The host event publisher must preserve a compact
data.inputanddata.outputsummary when forwarding ChatKit tool logs. It may redact host ids before iframe delivery, but it should not drop the target business id. Apply the Tool Contract Design display and payload-filtering rules before forwarding the event. - The view manifest must declare
hostEvents.subscriptionswith stablekey, exactevent,sources, andtoolNames. Useaction.type: 'forward'for remote components and a small debounce only for duplicate bursts. - The remote bridge must forward the normalized event to the iframe and tolerate common envelope shapes:
event,payload,data,result, and the whole message as fallback. - The remote component must normalize tool events in one tested helper. Read tool name from top-level fields,
payload/data,toolCall/tool_call,function,content, and JSON string previews. Read target ids from top-level fields,input,args,target,output/result,document/item, and truncatedargsPreviewwhen possible. - The remote component must keep current selection, current business id, editor instance, dirty flag, and
loadData/refresh callbacks in refs used by the host event handler. Do not let auseEffect([])event listener call a stale render closure. - The event handler must log, when debug is enabled,
received -> normalized -> target resolved -> request started -> response received -> state applied/skipped. - Refresh behavior must match the mutation: update lists and metadata, then apply only the affected remote state. For canvas-like editors, use the domain library's remote-change API such as
store.mergeRemoteChanges; avoid remounting the whole editor for autosave or tool insertions unless the document id changed. - Protect local edits deliberately. If the current scene is dirty and the event targets the same document, either merge safely, defer with a visible warning, or force an autosave first. Do not silently discard local changes.
Add tests for the whole event contract:
- Host event conversion from ChatKit logs includes tool name and target id.
- Renderer forwards the event to the iframe and preserves
data.input/data.outputsummaries. - Remote event parser handles direct, nested,
toolCall,content, and truncatedargsPreviewshapes. - Remote host event handler uses the latest refs, resolves the correct target id, calls
requestData, and applies or skips state with an explicit reason. - Generated remote component output is checked so the runtime
app.jscannot drift from TSX source.
Assistant Template
Contribute an Assistant template so users do not manually assemble middleware, prompts, model settings, state variables, and starter prompts.
Treat template installation and template upgrade as different lifecycle operations. Read references/assistant-template-lifecycle.md before acting. For an existing digital expert, open its canvas, use Assistant Settings -> Update from Template, review the graph changes, then save and publish the same Xpert. Do not use the creation wizard or manually redraw the graph as an upgrade mechanism.
When an App declares appConfig, its assistantTemplateKey must match exactly one raw templates[].key from the same loaded p
…(truncated)