@particle-academy/agent-integrations 0.52.0 → 0.53.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +38 -0
- package/README.md +45 -3
- package/dist/bridges/forms.d.cts +13 -6
- package/dist/bridges/forms.d.ts +13 -6
- package/dist/bridges-forms.cjs +184 -43
- package/dist/bridges-forms.cjs.map +1 -1
- package/dist/bridges-forms.js +1 -1
- package/dist/{chunk-AL77RO25.js → chunk-AET2R7DJ.js} +186 -45
- package/dist/chunk-AET2R7DJ.js.map +1 -0
- package/dist/index.cjs +192 -51
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +2 -2
- package/docs/multi-form-hosts.md +70 -4
- package/package.json +1 -1
- package/dist/chunk-AL77RO25.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/mcp/server.ts","../src/presence/wrap-tool-with-activity.ts","../src/undo/undo-tools.ts","../src/bridges/host-commit.ts","../src/bridges/forms.ts"],"names":["emitActivity","undoOne","redoOne","readHistory"],"mappings":"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","file":"bridges-forms.cjs","sourcesContent":["import {\n type CallToolResult,\n type JsonObject,\n type JsonRpcMessage,\n type JsonRpcRequest,\n type JsonRpcId,\n type RegisteredTool,\n type ServerCapabilities,\n type ServerInfo,\n type ToolDefinition,\n type ToolHandler,\n JSONRPC_INTERNAL_ERROR,\n JSONRPC_INVALID_PARAMS,\n JSONRPC_METHOD_NOT_FOUND,\n MCP_PROTOCOL_VERSION,\n} from \"./types\";\nimport { ToolRegistry } from \"./tool-host\";\n\nexport type McpServerOptions = {\n info: ServerInfo;\n /** Defaults to { tools: { listChanged: true } } */\n capabilities?: ServerCapabilities;\n /** Free-text instructions surfaced to clients during initialize. */\n instructions?: string;\n /**\n * The protocol revisions this server speaks, NEWEST FIRST.\n *\n * `initialize` echoes the client's requested revision when it is in this\n * list, and answers the first entry otherwise — the spec's rule, which leaves\n * the client to decide whether it can live with the answer.\n *\n * Defaults to `[MCP_PROTOCOL_VERSION]`: one revision, answered whatever the\n * client asks, which is exactly what this server did before the option\n * existed. Only list a revision the tools you register are correct under.\n */\n protocolVersions?: readonly string[];\n};\n\nexport type Transport = {\n /** Called by the server when it has a message to deliver to the client. */\n send: (message: JsonRpcMessage) => void;\n /** Called by the server when it's torn down so the transport can clean up. */\n close?: () => void;\n};\n\n/**\n * MicroMcpServer — protocol-level MCP server, transport-agnostic.\n *\n * Use it like:\n *\n * const server = new MicroMcpServer({ info: { name: \"session\", version: \"0.1\" } });\n * server.registerTool({ name: \"...\", inputSchema: { type: \"object\" } }, async (args) => ({...}));\n * const transport = new InProcessTransport();\n * server.attach(transport);\n * transport.deliver({ ... }); // client → server frames\n *\n * The same server can serve multiple transports (e.g. an in-process agent\n * AND a relayed external client) by attaching each one.\n */\nexport class MicroMcpServer extends ToolRegistry {\n private transports = new Set<Transport>();\n /**\n * Transports owed a `tools/list_changed` at the end of this tick. Empty when\n * none is scheduled. See {@link scheduleListChangedNotification}.\n */\n private listChangedOwed = new Set<Transport>();\n\n readonly info: ServerInfo;\n readonly capabilities: ServerCapabilities;\n readonly instructions?: string;\n readonly protocolVersions: readonly string[];\n\n constructor(options: McpServerOptions) {\n super();\n this.info = options.info;\n this.capabilities = options.capabilities ?? { tools: { listChanged: true } };\n this.instructions = options.instructions;\n if (options.protocolVersions && options.protocolVersions.length === 0) {\n // No answer exists for a client's initialize. Fail here, next to the\n // mistake, rather than in a handshake nobody is watching.\n throw new Error(\"MicroMcpServer: protocolVersions is empty — a server must speak at least one protocol revision.\");\n }\n this.protocolVersions = options.protocolVersions ? [...options.protocolVersions] : [MCP_PROTOCOL_VERSION];\n }\n\n attach(transport: Transport): () => void {\n this.transports.add(transport);\n return () => this.detach(transport);\n }\n\n detach(transport: Transport): void {\n if (this.transports.delete(transport)) {\n transport.close?.();\n }\n }\n\n unregisterTool(name: string): void {\n if (this.tools.delete(name)) {\n this.scheduleListChangedNotification();\n }\n }\n\n protected onToolsChanged(): void {\n this.scheduleListChangedNotification();\n }\n\n /**\n * Receive a JSON-RPC frame from a client (called by the transport).\n * The transport is responsible for sending the response back.\n */\n async receive(transport: Transport, message: JsonRpcMessage): Promise<void> {\n if (!(\"method\" in message)) return; // It's a response, not a request — ignore.\n\n const isNotification = !(\"id\" in message);\n if (isNotification) {\n // Notifications are fire-and-forget. We ignore unknown methods.\n return;\n }\n\n const request = message as JsonRpcRequest;\n try {\n const result = await this.handle(request, transport);\n transport.send({ jsonrpc: \"2.0\", id: request.id, result });\n } catch (err) {\n transport.send({\n jsonrpc: \"2.0\",\n id: request.id,\n error: this.toRpcError(err),\n });\n }\n }\n\n private async handle(request: JsonRpcRequest, transport: Transport): Promise<any> {\n const { method, params } = request;\n switch (method) {\n case \"initialize\":\n return {\n protocolVersion: this.negotiate(params?.protocolVersion),\n capabilities: this.capabilities,\n serverInfo: this.info,\n ...(this.instructions ? { instructions: this.instructions } : {}),\n };\n\n case \"tools/list\":\n return { tools: this.listTools() };\n\n case \"tools/call\": {\n const name = params?.name;\n const args = (params?.arguments ?? {}) as JsonObject;\n if (typeof name !== \"string\") {\n throw rpcError(JSONRPC_INVALID_PARAMS, \"tools/call requires `name`\");\n }\n const tool = this.tools.get(name);\n if (!tool) {\n throw rpcError(JSONRPC_METHOD_NOT_FOUND, this.missingToolMessage(name));\n }\n const result = await tool.handler(args, { transport });\n return result satisfies CallToolResult;\n }\n\n case \"ping\":\n return {};\n\n default:\n throw rpcError(JSONRPC_METHOD_NOT_FOUND, `Unsupported method: ${method}`);\n }\n }\n\n /** The revision to answer `initialize` with: the one asked for if spoken, else the newest. */\n private negotiate(requested: unknown): string {\n return typeof requested === \"string\" && this.protocolVersions.includes(requested)\n ? requested\n : this.protocolVersions[0];\n }\n\n /**\n * Tell the transports that SAW the old tool list that it changed.\n *\n * Changes in one tick coalesce into one notification. The recipients are the\n * transports attached at the moment of a change, not at the moment the\n * notification goes out: a transport attached afterwards never had the old\n * list, so \"your list is stale\" is noise to it — and on stdio, where a host\n * builds the server and attaches stdin/stdout in the same tick, it was the\n * first frame a client received, before it had even sent `initialize`.\n */\n private scheduleListChangedNotification(): void {\n if (this.transports.size === 0) return;\n const scheduled = this.listChangedOwed.size > 0;\n for (const t of this.transports) this.listChangedOwed.add(t);\n if (scheduled) return;\n queueMicrotask(() => {\n const owed = this.listChangedOwed;\n this.listChangedOwed = new Set();\n for (const t of owed) {\n // Detached since the change: it is not listening any more.\n if (this.transports.has(t)) t.send({ jsonrpc: \"2.0\", method: \"notifications/tools/list_changed\" });\n }\n });\n }\n\n /** Send an application notification to every client, or one attached transport. */\n notify(message: JsonRpcMessage, transport?: Transport): void {\n if (transport) transport.send(message);\n else this.broadcast(message);\n }\n\n private broadcast(message: JsonRpcMessage): void {\n for (const t of this.transports) t.send(message);\n }\n\n private toRpcError(err: unknown): { code: number; message: string; data?: any } {\n if (err && typeof err === \"object\" && \"code\" in err && \"message\" in err) {\n return err as any;\n }\n return {\n code: JSONRPC_INTERNAL_ERROR,\n message: err instanceof Error ? err.message : String(err),\n };\n }\n}\n\nexport function rpcError(code: number, message: string, data?: any) {\n return { code, message, ...(data !== undefined ? { data } : {}) };\n}\n\n/**\n * Helper to build a CallToolResult from a string or structured value.\n */\nexport function textResult(text: string, structured?: any): CallToolResult {\n return {\n content: [{ type: \"text\", text }],\n ...(structured !== undefined ? { structuredContent: structured } : {}),\n };\n}\n\nexport function errorResult(text: string): CallToolResult {\n return { content: [{ type: \"text\", text }], isError: true };\n}\n\n// Internal helper so the JsonRpcId import isn't dropped by tsup\ntype _KeepIdImport = JsonRpcId;\n","import type { CallToolResult } from \"../mcp/types\";\nimport { emitActivity } from \"./registry\";\nimport type { AgentTarget } from \"./types\";\n\nexport type ActivityAgent = { id: string; name?: string; color?: string };\n\nexport type ActivityResolverContext<TArgs = Record<string, unknown>> = {\n /** Tool name as registered. */\n toolName: string;\n /** Arguments the tool was called with. */\n args: TArgs;\n /** The CallToolResult the underlying handler produced. */\n result: CallToolResult;\n};\n\n/**\n * Resolves an `AgentTarget` for an executed tool. Bridges declare one of\n * these per registration so the wrapper knows which surface / element /\n * screen the activity belongs to.\n *\n * The resolver runs AFTER the tool handler so it can inspect the result\n * (e.g. read a newly-created item id from `structuredContent`).\n */\nexport type ActivityTargetResolver<TArgs = Record<string, unknown>> = (\n ctx: ActivityResolverContext<TArgs>,\n) => AgentTarget | null;\n\nexport type ToolHandler<TArgs = Record<string, unknown>> = (\n args: TArgs,\n) => Promise<CallToolResult> | CallToolResult;\n\n/**\n * wrapToolWithActivity — decorate a bridge tool handler so every successful\n * call emits an `AgentActivityEvent`. Returns a new handler with the same shape.\n *\n * Usage in a bridge:\n *\n * server.registerTool(\n * definition,\n * wrapToolWithActivity(\n * handler,\n * { agent, kind: \"whiteboard\", resolveTarget: ({ args }) => ({\n * kind: \"whiteboard\", elementId: args.id as string,\n * }) },\n * ),\n * );\n */\nexport function wrapToolWithActivity<TArgs = Record<string, unknown>>(\n handler: ToolHandler<TArgs>,\n options: {\n toolName: string;\n agent: ActivityAgent;\n /** Optional fancy-screens screen id this bridge is scoped to. */\n screenId?: string;\n /** Default target kind if the resolver returns one without `kind`. */\n kind: AgentTarget[\"kind\"];\n /** Per-call resolver. Return `null` to skip emitting (e.g. for read-only tools). */\n resolveTarget?: ActivityTargetResolver<TArgs>;\n /** Optional ttl override. */\n ttlMs?: number;\n /**\n * Build the `meta` broadcast to all relay peers. Defaults to the tool's full\n * `structuredContent`. Override to REDACT sensitive fields before fan-out —\n * e.g. the terminal bridge strips raw command/keystroke bytes so a command\n * line (or its secrets) is never broadcast verbatim to every peer.\n */\n buildMeta?: (result: CallToolResult) => Record<string, unknown> | undefined;\n },\n): ToolHandler<TArgs> {\n return async (args) => {\n const result = await handler(args);\n if (result.isError) return result;\n\n let target: AgentTarget | null;\n if (options.resolveTarget) {\n target = options.resolveTarget({ toolName: options.toolName, args, result });\n } else {\n target = { kind: options.kind, screenId: options.screenId };\n }\n if (!target) return result;\n\n emitActivity({\n agentId: options.agent.id,\n agentName: options.agent.name,\n agentColor: options.agent.color,\n target: { ...target, kind: target.kind ?? options.kind, screenId: target.screenId ?? options.screenId },\n action: options.toolName,\n timestamp: Date.now(),\n meta: options.buildMeta ? options.buildMeta(result) : extractMeta(result),\n ttlMs: options.ttlMs,\n });\n return result;\n };\n}\n\nfunction extractMeta(result: CallToolResult): Record<string, unknown> | undefined {\n const sc = result.structuredContent;\n if (sc && typeof sc === \"object\" && !Array.isArray(sc)) {\n return sc as Record<string, unknown>;\n }\n return undefined;\n}\n","import { textResult, errorResult } from \"../mcp/server\";\nimport type { ToolHost } from \"../mcp/tool-host\";\nimport { readHistory, redoOne, undoOne } from \"./undo-stack\";\n\nexport type UndoToolsOptions = {\n /** Default agent id when the caller doesn't pass one. */\n defaultAgentId?: string;\n};\n\n/**\n * Idempotent tracker so multiple bridges on the same server only register\n * agent_undo / agent_redo / agent_history once.\n */\nconst installedHosts = new WeakSet<ToolHost>();\n\n/**\n * The one owner every copy of the undo tools registers under. They are SHARED\n * on purpose — every bridge ensures them, and a host may also call\n * `registerUndoTools` itself — so all of those are the same registrant, and a\n * second registration replaces the first instead of being refused as a\n * collision. Anything else claiming `agent_undo` is still refused.\n */\nconst UNDO_TOOLS_OWNER = \"agent-undo\";\n\n/**\n * ensureUndoToolsRegistered — bridges call this on construction. Safe to\n * call repeatedly with the same server; subsequent calls are no-ops.\n */\nexport function ensureUndoToolsRegistered(host: ToolHost, options: UndoToolsOptions = {}): void {\n if (installedHosts.has(host)) return;\n installedHosts.add(host);\n registerUndoTools(host, options);\n}\n\n/**\n * registerUndoTools — add agent_undo / agent_redo / agent_history to the\n * server. Returns a disposer that unregisters all three.\n */\nexport function registerUndoTools(host: ToolHost, options: UndoToolsOptions = {}): () => void {\n const defaultAgent = options.defaultAgentId ?? \"agent\";\n const disposers: Array<() => void> = [];\n const agentOf = (args: any): string =>\n typeof args?.agentId === \"string\" ? args.agentId : defaultAgent;\n\n disposers.push(\n host.registerTool(\n {\n name: \"agent_undo\",\n description: \"Undo the most recent action on the agent's stack. Optional agentId targets a specific agent.\",\n inputSchema: {\n type: \"object\",\n properties: { agentId: { type: \"string\" } },\n additionalProperties: false,\n },\n },\n async (args) => {\n const entry = await undoOne(agentOf(args));\n if (!entry) return errorResult(\"Nothing to undo.\");\n return textResult(`Undid: ${entry.label}`, { entry: serialize(entry) });\n },\n { owner: UNDO_TOOLS_OWNER },\n ),\n );\n\n disposers.push(\n host.registerTool(\n {\n name: \"agent_redo\",\n description: \"Redo the most recently undone action.\",\n inputSchema: {\n type: \"object\",\n properties: { agentId: { type: \"string\" } },\n additionalProperties: false,\n },\n },\n async (args) => {\n const entry = await redoOne(agentOf(args));\n if (!entry) return errorResult(\"Nothing to redo.\");\n return textResult(`Redid: ${entry.label}`, { entry: serialize(entry) });\n },\n { owner: UNDO_TOOLS_OWNER },\n ),\n );\n\n disposers.push(\n host.registerTool(\n {\n name: \"agent_history\",\n description: \"List the agent's undo stack (oldest first). Useful for understanding what's reversible.\",\n inputSchema: {\n type: \"object\",\n properties: { agentId: { type: \"string\" } },\n additionalProperties: false,\n },\n },\n async (args) => {\n const history = readHistory(agentOf(args)).map(serialize);\n const text = history.map((e) => `${new Date(e.timestamp).toISOString()} ${e.bridgeId} ${e.action}: ${e.label}`).join(\"\\n\");\n return textResult(text || \"(empty)\", history);\n },\n { owner: UNDO_TOOLS_OWNER },\n ),\n );\n\n return () => disposers.forEach((d) => d());\n}\n\nfunction serialize(entry: import(\"./undo-stack\").UndoEntry) {\n return {\n timestamp: entry.timestamp,\n bridgeId: entry.bridgeId,\n action: entry.action,\n label: entry.label,\n };\n}\n","/**\n * Shared plumbing for bridges whose adapter mutators may commit asynchronously\n * (screens since 0.49.0, forms since 0.52.0). Internal: not an entry point.\n *\n * The contract: a mutator returns `void` (sync, unchanged behaviour) or a\n * Promise resolving to `void` or `{ ok: true } | { ok: false; error }`. The\n * bridge awaits it, and only an accepted commit counts as success.\n */\n\n/** What an async mutator may resolve to, to acknowledge or refuse a commit. */\nexport type HostCommitResult = { ok: true } | { ok: false; error: string; code?: string };\n\nexport type CommitOutcome = { ok: true } | { ok: false; error: string };\n\n/**\n * The agent-facing text for a host failure. Only the message is used — never\n * `stack`, which carries file paths — matching the other bridges. A message\n * that itself embeds stack frames is cut at the first frame. A non-Error\n * rejection (a plain object, a response) has no reliable message, so it gets\n * the fallback rather than \"[object Object]\".\n */\nexport function hostErrorMessage(e: unknown, fallback: string): string {\n let msg: string | undefined;\n if (e instanceof Error) msg = e.message;\n else if (typeof e === \"string\") msg = e;\n else if (e && typeof e === \"object\" && typeof (e as { message?: unknown }).message === \"string\") {\n msg = (e as { message: string }).message;\n }\n const firstFrame = msg ? msg.search(/\\n\\s+at\\s/) : -1;\n if (msg && firstFrame >= 0) msg = msg.slice(0, firstFrame);\n msg = msg?.trim();\n return msg ? msg : fallback;\n}\n\n/** Run one adapter commit, await it, and normalise every way it can fail. Never rejects. */\nexport async function commit(\n run: () => void | Promise<void | HostCommitResult>,\n fallback: string,\n): Promise<CommitOutcome> {\n try {\n const r = await run();\n if (r && typeof r === \"object\" && r.ok === false) {\n return { ok: false, error: hostErrorMessage(r.error, fallback) };\n }\n return { ok: true };\n } catch (e) {\n return { ok: false, error: hostErrorMessage(e, fallback) };\n }\n}\n","import { textResult, errorResult } from \"../mcp/server\";\nimport type { ToolHost } from \"../mcp/tool-host\";\nimport type { JsonObject } from \"../mcp/types\";\nimport type { Bridge } from \"./types\";\nimport { wrapToolWithActivity } from \"../presence/wrap-tool-with-activity\";\nimport { ensureUndoToolsRegistered } from \"../undo/undo-tools\";\nimport type { AgentTarget } from \"../presence/types\";\nimport { emitActivity } from \"../presence/registry\";\nimport { commit, hostErrorMessage } from \"./host-commit\";\n\n/**\n * Field descriptor — what the host says about each form field. Mirrors the\n * subset of HTML / react-fancy input shapes agents care about.\n */\nexport type FormFieldDescriptor = {\n /** Form-local field name. Matches the key the host uses in its values map. */\n name: string;\n /** Display label. */\n label?: string;\n /** Logical type. Drives validation + agent expectations. */\n type:\n | \"text\"\n | \"textarea\"\n | \"number\"\n | \"email\"\n | \"password\"\n | \"url\"\n | \"date\"\n | \"select\"\n | \"multi-select\"\n | \"checkbox\"\n | \"switch\"\n | \"radio\"\n | \"file\"\n | \"json\";\n /** Allowed values for select / radio. */\n options?: Array<{ value: string; label: string }>;\n /** Whether the field must be filled before submit. */\n required?: boolean;\n /** Free-text hint surfaced to the agent. */\n description?: string;\n /** Default value (presented in get_value when nothing set). */\n defaultValue?: unknown;\n};\n\n/**\n * Adapter the host wires per form. Matches the controlled-state pattern\n * react-fancy already uses (`value` / `onValueChange`); this adapter just\n * exposes those state slots in a form-shaped way the bridge can call.\n *\n * `setValue` / `setValues` may be sync (`void`) or async (a Promise); see\n * {@link FormAdapterCommit}. Return a Promise when the host's write is\n * authoritative — a persisted draft, or one the host can refuse or report\n * stale — so the agent is told the truth instead of \"success\" before anything\n * was accepted.\n *\n * This bridge pushes NO undo entries: `agent_undo` cannot revert a form write.\n */\nexport type FormBridgeAdapter = {\n /** Stable id for the form (used in form_describe + presence target). */\n id: string;\n /** Display title for human-readable logs. */\n title?: string;\n /** Optional fancy-screens screen id this form belongs to. */\n screenId?: string;\n /** Field descriptors. The bridge uses these for schema introspection\n * (form_describe). Agents call this first to know what to fill. */\n getFields: () => FormFieldDescriptor[];\n /** Read a single field's current value. */\n getValue: (name: string) => unknown;\n /** Read all values as { fieldName: value }. */\n getValues: () => Record<string, unknown>;\n /**\n * Set a single field's value. The host wires this to its setState, or to\n * an authoritative write it awaits. Sync `void` behaves as it always has; a\n * Promise is awaited, and a rejection or `{ ok: false }` is the tool's error.\n */\n setValue: (name: string, value: unknown) => FormAdapterCommit;\n /**\n * Set many at once, ideally as one host commit. Same sync/async contract as\n * `setValue`. When absent, the bridge awaits `setValue` once per field in\n * order and stops at the first refusal (so that path is NOT atomic).\n */\n setValues?: (values: Record<string, unknown>) => FormAdapterCommit;\n /** Programmatically focus a field (host implements DOM focus). Optional. */\n focus?: (name: string) => void;\n /** Submit the form. Returns the values that were submitted (or rejection). */\n submit?: () => Promise<{ ok: boolean; values?: Record<string, unknown>; error?: string }>;\n /**\n * Human confirm gate for `form_submit` (trust-but-verify). When `pendingMode`\n * is on, the bridge calls this before submitting; return false to decline.\n * Wire it to a human control — submitting a form is a human-visible,\n * often-destructive action (checkout, delete, invite).\n */\n confirm?: (request: FormConfirmRequest) => Promise<boolean> | boolean;\n};\n\n/**\n * What an async `setValue` / `setValues` may resolve to, to acknowledge (or\n * refuse) a write. `{ ok: false }` is a refusal the agent can act on: `error`\n * is shown to the agent verbatim, so keep it a human-readable sentence (e.g.\n * \"Draft changed since you read it; re-read and retry\"), never a stack trace or\n * an internal path. `code` (e.g. `\"stale\"`, `\"forbidden\"`) is for the host's own\n * use; it is not shown to the agent.\n */\nexport type FormAdapterResult = { ok: true } | { ok: false; error: string; code?: string };\n\n/**\n * Return type of `setValue` / `setValues`. A sync `void` return keeps the\n * original behaviour. A Promise makes the bridge WAIT: the tool reports\n * success, and emits its AgentActivity event, only after the promise resolves\n * (to `void` or `{ ok: true }`). A rejection or `{ ok: false }` becomes that\n * tool call's error result and emits no activity. If the bridge is disposed\n * while the write is pending, the call settles at once as an error saying the\n * outcome is unknown, and a later settle is ignored.\n */\nexport type FormAdapterCommit = void | Promise<void | FormAdapterResult>;\n\n/** What a `form_submit` confirm gate is asked to approve. */\nexport type FormConfirmRequest = {\n action: \"submit\";\n form: string;\n title?: string;\n values: Record<string, unknown>;\n};\n\nexport type FormBridgeOptions = {\n adapter: FormBridgeAdapter;\n /** Identity tagged into activity events. */\n agent?: { id: string; name?: string; color?: string };\n /**\n * Stage `form_submit` for human confirmation via `adapter.confirm` instead of\n * submitting immediately. **Default: ON** (matches the navigation bridge and\n * the Human+ trust-but-verify contract). Set false only when auto-submit is\n * safe. If on without an `adapter.confirm`, submit proceeds (nothing to gate).\n */\n pendingMode?: boolean;\n};\n\nconst DEFAULT_AGENT = { id: \"agent\", name: \"Agent\", color: \"#a855f7\" };\n\n/**\n * registerFormBridge — wires schema-driven MCP access to a single form.\n *\n * ONE form bridge per host. The tool names (`form_set_value`, …) are not\n * scoped to the form id, so since 0.51.0 a second form bridge with a different\n * adapter id on the same host throws (the duplicate-registrant refusal); a\n * remount of the SAME form re-registers fine. For several forms, give each its\n * own host for now — see `docs/multi-form-hosts.md`.\n */\nexport function registerFormBridge(\n host: ToolHost,\n options: FormBridgeOptions,\n): Bridge {\n const { adapter } = options;\n const agent = { ...DEFAULT_AGENT, ...(options.agent ?? {}) };\n const pendingMode = options.pendingMode ?? true;\n const disposers: Array<() => void> = [];\n // The bridge id is also its registrant identity on the host: the same bridge\n // may re-register (a remount), a different one claiming a name is refused.\n const bridgeId = `form:${adapter.id}`;\n\n // agent_undo / agent_redo / agent_history are registered whenever any bridge\n // mounts, so undo availability doesn't hinge on which bridges are co-present.\n ensureUndoToolsRegistered(host, { defaultAgentId: agent.id });\n const formId = adapter.id;\n\n // Close handling. A host may dispose the bridge (the form unmounts) while a\n // write or a human confirm is still pending; its promise may then never\n // settle. Every awaited host call is raced against dispose, so the tool call\n // settles at once instead of hanging, and a late settle is ignored.\n let disposed = false;\n const onDispose = new Set<() => void>();\n const CLOSED: unique symbol = Symbol(\"form-closed\");\n const untilClosed = <T>(p: Promise<T>): Promise<T | typeof CLOSED> => {\n if (disposed) {\n p.catch(() => {}); // a late rejection must never be unhandled\n return Promise.resolve(CLOSED);\n }\n return new Promise<T | typeof CLOSED>((resolve, reject) => {\n const close = () => resolve(CLOSED);\n onDispose.add(close);\n p.then(\n (v) => { onDispose.delete(close); resolve(v); },\n (e) => { onDispose.delete(close); reject(e); },\n );\n });\n };\n const closedUnknown = (what: string) =>\n errorResult(\n `Form \"${formId}\" was closed before the host confirmed ${what}, so whether it was applied is unknown. ` +\n \"Do not assume it was. If the form is open again, read it back (form_get_values) before retrying.\",\n );\n\n const target =(args: any): AgentTarget => ({\n kind: \"form\",\n screenId: adapter.screenId,\n elementId: typeof args?.field === \"string\" ? `${formId}:${args.field}` : formId,\n label: typeof args?.field === \"string\"\n ? `${adapter.title ?? formId} → ${args.field}`\n : adapter.title ?? formId,\n });\n\n const reg = (\n name: string,\n description: string,\n properties: Record<string, unknown>,\n required: string[],\n handler: (args: JsonObject) => Promise<any> | any,\n isMutation: boolean,\n ) => {\n const wrapped = async (args: JsonObject) => {\n try {\n return await handler(args);\n } catch (e) {\n return errorResult(hostErrorMessage(e, `${name} failed.`));\n }\n };\n const final = isMutation\n ? wrapToolWithActivity(wrapped, {\n toolName: name,\n agent: { id: agent.id, name: agent.name, color: agent.color },\n kind: \"form\",\n screenId: adapter.screenId,\n resolveTarget: ({ args }) => target(args),\n })\n : wrapped;\n disposers.push(\n host.registerTool(\n {\n name,\n description,\n inputSchema: { type: \"object\", properties: properties as any, required, additionalProperties: false },\n },\n final as any,\n { owner: bridgeId },\n ),\n );\n };\n\n // ───────────── Read tools ─────────────\n\n reg(\n \"form_describe\",\n `Describe the form \"${formId}\" — fields, types, options, required flags. Call this first to know what's fillable.`,\n {},\n [],\n () => {\n const fields = adapter.getFields();\n const text = fields\n .map((f) => `${f.name}${f.required ? \"*\" : \"\"} (${f.type})${f.label ? ` — ${f.label}` : \"\"}`)\n .join(\"\\n\");\n return textResult(text || \"(no fields)\", { id: formId, title: adapter.title, fields });\n },\n false,\n );\n\n reg(\n \"form_get_value\",\n \"Read a single field's current value.\",\n { field: { type: \"string\" } },\n [\"field\"],\n (args) => {\n const name = String(args.field ?? \"\");\n if (!adapter.getFields().find((f) => f.name === name)) {\n return errorResult(`Unknown field: ${name}`);\n }\n const value = adapter.getValue(name);\n return textResult(JSON.stringify(value), { field: name, value });\n },\n false,\n );\n\n reg(\n \"form_get_values\",\n \"Read every field's current value as a JSON object.\",\n {},\n [],\n () => {\n const values = adapter.getValues();\n return textResult(JSON.stringify(values, null, 2), values);\n },\n false,\n );\n\n // ───────────── Mutation tools ─────────────\n\n reg(\n \"form_set_value\",\n \"Set one field's value. The host's controlled state updates and the human sees the field change.\",\n {\n field: { type: \"string\" },\n value: { description: \"Value to set. Type depends on the field's `type`.\" },\n },\n [\"field\", \"value\"],\n async (args) => {\n const name = String(args.field ?? \"\");\n const fieldDef = adapter.getFields().find((f) => f.name === name);\n if (!fieldDef) return errorResult(`Unknown field: ${name}`);\n const done = await untilClosed(\n commit(() => adapter.setValue(name, args.value), `Host refused the write to \"${name}\".`),\n );\n if (done === CLOSED) return closedUnknown(`the write to \"${name}\"`);\n if (!done.ok) return errorResult(done.error);\n return textResult(`${name} ← ${JSON.stringify(args.value)}`, { field: name, value: args.value });\n },\n true,\n );\n\n reg(\n \"form_set_values\",\n \"Set multiple fields atomically. Pass a `values` object keyed by field name.\",\n { values: { type: \"object\" } },\n [\"values\"],\n async (args) => {\n const values = (args.values && typeof args.values === \"object\") ? args.values as Record<string, unknown> : {};\n const fields = adapter.getFields();\n const known = new Set(fields.map((f) => f.name));\n const unknownKeys = Object.keys(values).filter((k) => !known.has(k));\n if (unknownKeys.length) return errorResult(`Unknown fields: ${unknownKeys.join(\", \")}`);\n const setValues = adapter.setValues;\n if (setValues) {\n const done = await untilClosed(commit(() => setValues(values), \"Host refused the write.\"));\n if (done === CLOSED) return closedUnknown(\"the write\");\n if (!done.ok) return errorResult(done.error);\n } else {\n // No batch setter: one host commit per field, in order, stopping at the\n // first refusal. Earlier fields stay committed, so say exactly which.\n const keys = Object.keys(values);\n const committed: string[] = [];\n for (const [i, k] of keys.entries()) {\n const done = await untilClosed(\n commit(() => adapter.setValue(k, values[k]), `Host refused the write to \"${k}\".`),\n );\n if (done === CLOSED) {\n return {\n ...closedUnknown(`the write to \"${k}\"`),\n structuredContent: { committed, pending: k, notAttempted: keys.slice(i + 1) },\n };\n }\n if (!done.ok) {\n const state = { committed, failed: k, notAttempted: keys.slice(i + 1) };\n if (committed.length) {\n // Those writes DID commit; every committed mutation is observable,\n // and wrapToolWithActivity skips error results, so emit it here.\n emitActivity({\n agentId: agent.id,\n agentName: agent.name,\n agentColor: agent.color,\n target: target({}),\n action: \"form_set_values\",\n timestamp: Date.now(),\n meta: state,\n });\n }\n return {\n ...errorResult(\n `\"${k}\" was refused: ${done.error} ` +\n (committed.length\n ? `Already committed and NOT rolled back: ${committed.join(\", \")}. `\n : \"Nothing was committed. \") +\n (state.notAttempted.length ? `Not attempted: ${state.notAttempted.join(\", \")}.` : \"\"),\n ),\n structuredContent: state,\n };\n }\n committed.push(k);\n }\n }\n return textResult(`Set ${Object.keys(values).length} field(s)`, { values });\n },\n true,\n );\n\n reg(\n \"form_focus\",\n \"Move browser focus to a field (host-implemented). Useful before streaming text into it.\",\n { field: { type: \"string\" } },\n [\"field\"],\n (args) => {\n const name = String(args.field ?? \"\");\n if (!adapter.focus) return errorResult(\"Host did not provide a focus implementation.\");\n if (!adapter.getFields().find((f) => f.name === name)) {\n return errorResult(`Unknown field: ${name}`);\n }\n adapter.focus(name);\n return textResult(`Focused ${name}`, { field: name });\n },\n true,\n );\n\n reg(\n \"form_submit\",\n \"Submit the form. Host returns ok + values (or an error). In pendingMode this is staged for the human to confirm first.\",\n {},\n [],\n async () => {\n if (!adapter.submit) return errorResult(\"Host did not provide a submit implementation.\");\n // Trust-but-verify: a form submit is a human-visible / often-destructive\n // action, so gate it on a human confirm when one is wired.\n if (pendingMode && adapter.confirm) {\n const confirm = adapter.confirm;\n const ok = await untilClosed(Promise.resolve().then(() => confirm({\n action: \"submit\",\n form: formId,\n title: adapter.title,\n values: adapter.getValues(),\n })));\n // The form went away while the human was deciding: never submit a\n // form that is no longer on screen, whatever the late answer is.\n if (ok === CLOSED) {\n return errorResult(`Form \"${formId}\" was closed before the submit was confirmed. Nothing was submitted.`);\n }\n if (!ok) return errorResult(\"Declined by user\");\n }\n const submit = adapter.submit;\n const result = await untilClosed(Promise.resolve().then(() => submit()));\n if (result === CLOSED) return closedUnknown(\"the submit\");\n if (!result.ok) return errorResult(hostErrorMessage(result.error, \"Submit failed\"));\n return textResult(\"Submitted\", { values: result.values });\n },\n true,\n );\n\n return {\n id: bridgeId,\n title: adapter.title ?? formId,\n dispose: () => {\n disposed = true;\n for (const d of disposers) d();\n // Settle every call still waiting on the host; see untilClosed.\n for (const close of [...onDispose]) close();\n onDispose.clear();\n },\n };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/mcp/server.ts","../src/presence/wrap-tool-with-activity.ts","../src/undo/undo-tools.ts","../src/bridges/host-commit.ts","../src/bridges/multi-instance.ts","../src/bridges/forms.ts"],"names":["emitActivity","undoOne","redoOne","readHistory","resolve"],"mappings":";;;;;AAoOO,SAAS,UAAA,CAAW,MAAc,UAAA,EAAkC;AACzE,EAAA,OAAO;AAAA,IACL,SAAS,CAAC,EAAE,IAAA,EAAM,MAAA,EAAQ,MAAM,CAAA;AAAA,IAChC,GAAI,UAAA,KAAe,MAAA,GAAY,EAAE,iBAAA,EAAmB,UAAA,KAAe;AAAC,GACtE;AACF;AAEO,SAAS,YAAY,IAAA,EAA8B;AACxD,EAAA,OAAO,EAAE,OAAA,EAAS,CAAC,EAAE,IAAA,EAAM,QAAQ,IAAA,EAAM,CAAA,EAAG,OAAA,EAAS,IAAA,EAAK;AAC5D;;;AC9LO,SAAS,oBAAA,CACd,SACA,OAAA,EAmBoB;AACpB,EAAA,OAAO,OAAO,IAAA,KAAS;AACrB,IAAA,MAAM,MAAA,GAAS,MAAM,OAAA,CAAQ,IAAI,CAAA;AACjC,IAAA,IAAI,MAAA,CAAO,SAAS,OAAO,MAAA;AAE3B,IAAA,IAAI,MAAA;AACJ,IAAA,IAAI,QAAQ,aAAA,EAAe;AACzB,MAAA,MAAA,GAAS,OAAA,CAAQ,cAAc,EAAE,QAAA,EAAU,QAAQ,QAAA,EAAU,IAAA,EAAM,QAAQ,CAAA;AAAA,IAC7E,CAAA,MAAO;AACL,MAAA,MAAA,GAAS,EAAE,IAAA,EAAM,OAAA,CAAQ,IAAA,EAAM,QAAA,EAAU,QAAQ,QAAA,EAAS;AAAA,IAC5D;AACA,IAAA,IAAI,CAAC,QAAQ,OAAO,MAAA;AAEpB,IAAAA,4BAAA,CAAa;AAAA,MACX,OAAA,EAAS,QAAQ,KAAA,CAAM,EAAA;AAAA,MACvB,SAAA,EAAW,QAAQ,KAAA,CAAM,IAAA;AAAA,MACzB,UAAA,EAAY,QAAQ,KAAA,CAAM,KAAA;AAAA,MAC1B,MAAA,EAAQ,EAAE,GAAG,MAAA,EAAQ,IAAA,EAAM,MAAA,CAAO,IAAA,IAAQ,OAAA,CAAQ,IAAA,EAAM,QAAA,EAAU,MAAA,CAAO,QAAA,IAAY,QAAQ,QAAA,EAAS;AAAA,MACtG,QAAQ,OAAA,CAAQ,QAAA;AAAA,MAChB,SAAA,EAAW,KAAK,GAAA,EAAI;AAAA,MACpB,IAAA,EAAM,QAAQ,SAAA,GAAY,OAAA,CAAQ,UAAU,MAAM,CAAA,GAAI,YAAY,MAAM,CAAA;AAAA,MACxE,OAAO,OAAA,CAAQ;AAAA,KAChB,CAAA;AACD,IAAA,OAAO,MAAA;AAAA,EACT,CAAA;AACF;AAEA,SAAS,YAAY,MAAA,EAA6D;AAChF,EAAA,MAAM,KAAK,MAAA,CAAO,iBAAA;AAClB,EAAA,IAAI,EAAA,IAAM,OAAO,EAAA,KAAO,QAAA,IAAY,CAAC,KAAA,CAAM,OAAA,CAAQ,EAAE,CAAA,EAAG;AACtD,IAAA,OAAO,EAAA;AAAA,EACT;AACA,EAAA,OAAO,MAAA;AACT;;;ACxFA,IAAM,cAAA,uBAAqB,OAAA,EAAkB;AAS7C,IAAM,gBAAA,GAAmB,YAAA;AAMlB,SAAS,yBAAA,CAA0B,IAAA,EAAgB,OAAA,GAA4B,EAAC,EAAS;AAC9F,EAAA,IAAI,cAAA,CAAe,GAAA,CAAI,IAAI,CAAA,EAAG;AAC9B,EAAA,cAAA,CAAe,IAAI,IAAI,CAAA;AACvB,EAAA,iBAAA,CAAkB,MAAM,OAAO,CAAA;AACjC;AAMO,SAAS,iBAAA,CAAkB,IAAA,EAAgB,OAAA,GAA4B,EAAC,EAAe;AAC5F,EAAA,MAAM,YAAA,GAAe,QAAQ,cAAA,IAAkB,OAAA;AAC/C,EAAA,MAAM,YAA+B,EAAC;AACtC,EAAA,MAAM,OAAA,GAAU,CAAC,IAAA,KACf,OAAO,MAAM,OAAA,KAAY,QAAA,GAAW,KAAK,OAAA,GAAU,YAAA;AAErD,EAAA,SAAA,CAAU,IAAA;AAAA,IACR,IAAA,CAAK,YAAA;AAAA,MACH;AAAA,QACE,IAAA,EAAM,YAAA;AAAA,QACN,WAAA,EAAa,8FAAA;AAAA,QACb,WAAA,EAAa;AAAA,UACX,IAAA,EAAM,QAAA;AAAA,UACN,YAAY,EAAE,OAAA,EAAS,EAAE,IAAA,EAAM,UAAS,EAAE;AAAA,UAC1C,oBAAA,EAAsB;AAAA;AACxB,OACF;AAAA,MACA,OAAO,IAAA,KAAS;AACd,QAAA,MAAM,KAAA,GAAQ,MAAMC,uBAAA,CAAQ,OAAA,CAAQ,IAAI,CAAC,CAAA;AACzC,QAAA,IAAI,CAAC,KAAA,EAAO,OAAO,WAAA,CAAY,kBAAkB,CAAA;AACjD,QAAA,OAAO,UAAA,CAAW,CAAA,OAAA,EAAU,KAAA,CAAM,KAAK,CAAA,CAAA,EAAI,EAAE,KAAA,EAAO,SAAA,CAAU,KAAK,CAAA,EAAG,CAAA;AAAA,MACxE,CAAA;AAAA,MACA,EAAE,OAAO,gBAAA;AAAiB;AAC5B,GACF;AAEA,EAAA,SAAA,CAAU,IAAA;AAAA,IACR,IAAA,CAAK,YAAA;AAAA,MACH;AAAA,QACE,IAAA,EAAM,YAAA;AAAA,QACN,WAAA,EAAa,uCAAA;AAAA,QACb,WAAA,EAAa;AAAA,UACX,IAAA,EAAM,QAAA;AAAA,UACN,YAAY,EAAE,OAAA,EAAS,EAAE,IAAA,EAAM,UAAS,EAAE;AAAA,UAC1C,oBAAA,EAAsB;AAAA;AACxB,OACF;AAAA,MACA,OAAO,IAAA,KAAS;AACd,QAAA,MAAM,KAAA,GAAQ,MAAMC,uBAAA,CAAQ,OAAA,CAAQ,IAAI,CAAC,CAAA;AACzC,QAAA,IAAI,CAAC,KAAA,EAAO,OAAO,WAAA,CAAY,kBAAkB,CAAA;AACjD,QAAA,OAAO,UAAA,CAAW,CAAA,OAAA,EAAU,KAAA,CAAM,KAAK,CAAA,CAAA,EAAI,EAAE,KAAA,EAAO,SAAA,CAAU,KAAK,CAAA,EAAG,CAAA;AAAA,MACxE,CAAA;AAAA,MACA,EAAE,OAAO,gBAAA;AAAiB;AAC5B,GACF;AAEA,EAAA,SAAA,CAAU,IAAA;AAAA,IACR,IAAA,CAAK,YAAA;AAAA,MACH;AAAA,QACE,IAAA,EAAM,eAAA;AAAA,QACN,WAAA,EAAa,yFAAA;AAAA,QACb,WAAA,EAAa;AAAA,UACX,IAAA,EAAM,QAAA;AAAA,UACN,YAAY,EAAE,OAAA,EAAS,EAAE,IAAA,EAAM,UAAS,EAAE;AAAA,UAC1C,oBAAA,EAAsB;AAAA;AACxB,OACF;AAAA,MACA,OAAO,IAAA,KAAS;AACd,QAAA,MAAM,UAAUC,2BAAA,CAAY,OAAA,CAAQ,IAAI,CAAC,CAAA,CAAE,IAAI,SAAS,CAAA;AACxD,QAAA,MAAM,IAAA,GAAO,OAAA,CAAQ,GAAA,CAAI,CAAC,CAAA,KAAM,GAAG,IAAI,IAAA,CAAK,CAAA,CAAE,SAAS,CAAA,CAAE,WAAA,EAAa,CAAA,CAAA,EAAI,CAAA,CAAE,QAAQ,CAAA,CAAA,EAAI,CAAA,CAAE,MAAM,CAAA,EAAA,EAAK,CAAA,CAAE,KAAK,CAAA,CAAE,CAAA,CAAE,IAAA,CAAK,IAAI,CAAA;AACzH,QAAA,OAAO,UAAA,CAAW,IAAA,IAAQ,SAAA,EAAW,OAAO,CAAA;AAAA,MAC9C,CAAA;AAAA,MACA,EAAE,OAAO,gBAAA;AAAiB;AAC5B,GACF;AAEA,EAAA,OAAO,MAAM,SAAA,CAAU,OAAA,CAAQ,CAAC,CAAA,KAAM,GAAG,CAAA;AAC3C;AAEA,SAAS,UAAU,KAAA,EAAyC;AAC1D,EAAA,OAAO;AAAA,IACL,WAAW,KAAA,CAAM,SAAA;AAAA,IACjB,UAAU,KAAA,CAAM,QAAA;AAAA,IAChB,QAAQ,KAAA,CAAM,MAAA;AAAA,IACd,OAAO,KAAA,CAAM;AAAA,GACf;AACF;;;AC7FO,SAAS,gBAAA,CAAiB,GAAY,QAAA,EAA0B;AACrE,EAAA,IAAI,GAAA;AACJ,EAAA,IAAI,CAAA,YAAa,KAAA,EAAO,GAAA,GAAM,CAAA,CAAE,OAAA;AAAA,OAAA,IACvB,OAAO,CAAA,KAAM,QAAA,EAAU,GAAA,GAAM,CAAA;AAAA,OAAA,IAC7B,KAAK,OAAO,CAAA,KAAM,YAAY,OAAQ,CAAA,CAA4B,YAAY,QAAA,EAAU;AAC/F,IAAA,GAAA,GAAO,CAAA,CAA0B,OAAA;AAAA,EACnC;AACA,EAAA,MAAM,UAAA,GAAa,GAAA,GAAM,GAAA,CAAI,MAAA,CAAO,WAAW,CAAA,GAAI,EAAA;AACnD,EAAA,IAAI,OAAO,UAAA,IAAc,CAAA,QAAS,GAAA,CAAI,KAAA,CAAM,GAAG,UAAU,CAAA;AACzD,EAAA,GAAA,GAAM,KAAK,IAAA,EAAK;AAChB,EAAA,OAAO,MAAM,GAAA,GAAM,QAAA;AACrB;AAGA,eAAsB,MAAA,CACpB,KACA,QAAA,EACwB;AACxB,EAAA,IAAI;AACF,IAAA,MAAM,CAAA,GAAI,MAAM,GAAA,EAAI;AACpB,IAAA,IAAI,KAAK,OAAO,CAAA,KAAM,QAAA,IAAY,CAAA,CAAE,OAAO,KAAA,EAAO;AAChD,MAAA,OAAO,EAAE,IAAI,KAAA,EAAO,KAAA,EAAO,iBAAiB,CAAA,CAAE,KAAA,EAAO,QAAQ,CAAA,EAAE;AAAA,IACjE;AACA,IAAA,OAAO,EAAE,IAAI,IAAA,EAAK;AAAA,EACpB,SAAS,CAAA,EAAG;AACV,IAAA,OAAO,EAAE,EAAA,EAAI,KAAA,EAAO,OAAO,gBAAA,CAAiB,CAAA,EAAG,QAAQ,CAAA,EAAE;AAAA,EAC3D;AACF;;;ACkBA,IAAM,OAAA,uBAAc,OAAA,EAAqD;AAEzE,IAAM,QAAA,GAAW,CAAC,GAAA,KAAkB,GAAA,CAAI,GAAA,CAAI,CAAC,EAAA,KAAO,CAAA,CAAA,EAAI,EAAE,CAAA,CAAA,CAAG,CAAA,CAAE,IAAA,CAAK,IAAI,CAAA;AAWjE,SAAS,kBAAA,CACd,IAAA,EACA,OAAA,EACA,EAAA,EACA,QAAA,EACuB;AACvB,EAAA,IAAI,OAAO,EAAA,KAAO,QAAA,IAAY,EAAA,CAAG,IAAA,OAAW,EAAA,EAAI;AAC9C,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,CAAA,EAAA,EAAK,OAAA,CAAQ,MAAM,CAAA,mDAAA,EAAsD,IAAA,CAAK,UAAU,EAAE,CAAC,CAAA,4CAAA,EAC3C,OAAA,CAAQ,QAAQ,CAAA,WAAA;AAAA,KAClE;AAAA,EACF;AAEA,EAAA,IAAI,QAAA,GAAW,OAAA,CAAQ,GAAA,CAAI,IAAI,CAAA;AAC/B,EAAA,IAAI,CAAC,QAAA,EAAU;AACb,IAAA,QAAA,uBAAe,GAAA,EAAI;AACnB,IAAA,OAAA,CAAQ,GAAA,CAAI,MAAM,QAAQ,CAAA;AAAA,EAC5B;AAEA,EAAA,IAAI,KAAA,GAAQ,QAAA,CAAS,GAAA,CAAI,OAAA,CAAQ,MAAM,CAAA;AACvC,EAAA,IAAI,CAAC,KAAA,EAAO;AACV,IAAA,MAAM,SAAA,uBAAgB,GAAA,EAAe;AACrC,IAAA,MAAM,MAAA,GAA4B;AAAA,MAChC,KAAK,MAAM,CAAC,GAAG,SAAA,CAAU,MAAM,CAAA;AAAA,MAC/B,SAAS,MAAM,CAAC,GAAG,SAAA,CAAU,SAAS,CAAA;AAAA,MACtC,SAAS,CAAC,SAAA,KAAc,OAAA,CAAQ,OAAA,EAAS,WAAW,SAAS;AAAA,KAC/D;AAGA,IAAA,MAAM,SAAA,GAAY,OAAA,CAAQ,OAAA,CAAQ,MAAM,CAAA;AACxC,IAAA,KAAA,GAAQ,EAAE,SAAA,EAAW,MAAA,EAAQ,SAAA,EAAU;AACvC,IAAA,QAAA,CAAS,GAAA,CAAI,OAAA,CAAQ,MAAA,EAAQ,KAA6B,CAAA;AAAA,EAC5D;AAEA,EAAA,MAAM,OAAA,GAAU,KAAA;AAChB,EAAA,OAAA,CAAQ,SAAA,CAAU,GAAA,CAAI,EAAA,EAAI,QAAQ,CAAA;AAGlC,EAAA,IAAI,IAAA,GAAO,KAAA;AACX,EAAA,MAAM,QAAQ,MAAM;AAClB,IAAA,IAAI,IAAA,EAAM;AACV,IAAA,IAAA,GAAO,IAAA;AACP,IAAA,IAAI,OAAA,CAAQ,SAAA,CAAU,GAAA,CAAI,EAAE,MAAM,QAAA,EAAU;AAC5C,IAAA,OAAA,CAAQ,SAAA,CAAU,OAAO,EAAE,CAAA;AAC3B,IAAA,IAAI,OAAA,CAAQ,UAAU,IAAA,KAAS,CAAA,IAAK,SAAU,GAAA,CAAI,OAAA,CAAQ,MAAM,CAAA,KAAM,OAAA,EAAS;AAC7E,MAAA,QAAA,CAAU,MAAA,CAAO,QAAQ,MAAM,CAAA;AAC/B,MAAA,OAAA,CAAQ,SAAA,EAAU;AAAA,IACpB;AAAA,EACF,CAAA;AAEA,EAAA,OAAO,EAAE,MAAA,EAAQ,OAAA,CAAQ,MAAA,EAAQ,KAAA,EAAM;AACzC;AAEA,SAAS,OAAA,CACP,OAAA,EACA,SAAA,EACA,SAAA,EACuB;AACvB,EAAA,MAAM,GAAA,GAAM,CAAC,GAAG,SAAA,CAAU,MAAM,CAAA;AAChC,EAAA,MAAM,EAAE,MAAA,EAAQ,QAAA,EAAU,QAAA,EAAS,GAAI,OAAA;AACvC,EAAA,IAAI,SAAA,KAAc,MAAA,IAAa,SAAA,KAAc,IAAA,IAAQ,cAAc,EAAA,EAAI;AACrE,IAAA,IAAI,IAAI,MAAA,KAAW,CAAA,EAAG,OAAO,EAAE,IAAI,IAAA,EAAM,EAAA,EAAI,GAAA,CAAI,CAAC,GAAG,QAAA,EAAU,SAAA,CAAU,IAAI,GAAA,CAAI,CAAC,CAAC,CAAA,EAAO;AAC1F,IAAA,IAAI,GAAA,CAAI,MAAA,KAAW,CAAA,EAAG,OAAO,EAAE,IAAI,KAAA,EAAO,KAAA,EAAO,CAAA,GAAA,EAAM,MAAM,CAAA,YAAA,CAAA,EAAe;AAC5E,IAAA,OAAO;AAAA,MACL,EAAA,EAAI,KAAA;AAAA,MACJ,KAAA,EACE,CAAA,EAAG,GAAA,CAAI,MAAM,CAAA,CAAA,EAAI,MAAM,CAAA,wBAAA,EAA2B,QAAQ,CAAA,eAAA,EAAkB,QAAA,CAAS,GAAG,CAAC,KACpF,QAAQ,CAAA,gBAAA;AAAA,KACjB;AAAA,EACF;AACA,EAAA,IAAI,OAAO,cAAc,QAAA,EAAU;AACjC,IAAA,OAAO,EAAE,EAAA,EAAI,KAAA,EAAO,KAAA,EAAO,CAAA,CAAA,EAAI,QAAQ,CAAA,YAAA,EAAe,MAAM,CAAA,qBAAA,EAAwB,QAAA,CAAS,GAAG,CAAC,CAAA,CAAA,CAAA,EAAI;AAAA,EACvG;AACA,EAAA,IAAI,CAAC,SAAA,CAAU,GAAA,CAAI,SAAS,CAAA,EAAG;AAC7B,IAAA,OAAO;AAAA,MACL,EAAA,EAAI,KAAA;AAAA,MACJ,KAAA,EACE,CAAA,GAAA,EAAM,MAAM,CAAA,EAAA,EAAK,SAAS,CAAA,cAAA,CAAA,IACvB,GAAA,CAAI,MAAA,GAAS,CAAA,SAAA,EAAY,SAAS,GAAG,CAAC,CAAA,EAAA,CAAA,GAAO,EAAA,CAAA,GAC9C,GAAG,QAAQ,CAAA,oBAAA;AAAA,KACjB;AAAA,EACF;AACA,EAAA,OAAO,EAAE,IAAI,IAAA,EAAM,EAAA,EAAI,WAAW,QAAA,EAAU,SAAA,CAAU,GAAA,CAAI,SAAS,CAAA,EAAO;AAC5E;;;ACrBA,IAAM,gBAAgB,EAAE,EAAA,EAAI,SAAS,IAAA,EAAM,OAAA,EAAS,OAAO,SAAA,EAAU;AAOrE,IAAM,gBAAA,GAAmB,aAAA;AAGzB,IAAM,QAAA,GAAW;AAAA,EACf,IAAA,EAAM,QAAA;AAAA,EACN,WAAA,EAAa;AACf,CAAA;AAUA,IAAM,cAAA,GAAgC;AAAA,EACpC;AAAA,IACE,IAAA,EAAM,eAAA;AAAA,IACN,WAAA,EAAa,yGAAA;AAAA,IACb,YAAY,EAAC;AAAA,IACb,UAAU;AAAC,GACb;AAAA,EACA,EAAE,IAAA,EAAM,gBAAA,EAAkB,WAAA,EAAa,sCAAA,EAAwC,YAAY,EAAE,KAAA,EAAO,EAAE,IAAA,EAAM,UAAS,EAAE,EAAG,QAAA,EAAU,CAAC,OAAO,CAAA,EAAE;AAAA,EAC9I,EAAE,IAAA,EAAM,iBAAA,EAAmB,WAAA,EAAa,oDAAA,EAAsD,YAAY,EAAC,EAAG,QAAA,EAAU,EAAC,EAAE;AAAA,EAC3H;AAAA,IACE,IAAA,EAAM,gBAAA;AAAA,IACN,WAAA,EAAa,iGAAA;AAAA,IACb,UAAA,EAAY;AAAA,MACV,KAAA,EAAO,EAAE,IAAA,EAAM,QAAA,EAAS;AAAA,MACxB,KAAA,EAAO,EAAE,WAAA,EAAa,mDAAA;AAAoD,KAC5E;AAAA,IACA,QAAA,EAAU,CAAC,OAAA,EAAS,OAAO;AAAA,GAC7B;AAAA,EACA;AAAA,IACE,IAAA,EAAM,iBAAA;AAAA,IACN,WAAA,EAAa,6EAAA;AAAA,IACb,YAAY,EAAE,MAAA,EAAQ,EAAE,IAAA,EAAM,UAAS,EAAE;AAAA,IACzC,QAAA,EAAU,CAAC,QAAQ;AAAA,GACrB;AAAA,EACA;AAAA,IACE,IAAA,EAAM,YAAA;AAAA,IACN,WAAA,EAAa,yFAAA;AAAA,IACb,YAAY,EAAE,KAAA,EAAO,EAAE,IAAA,EAAM,UAAS,EAAE;AAAA,IACxC,QAAA,EAAU,CAAC,OAAO;AAAA,GACpB;AAAA,EACA;AAAA,IACE,IAAA,EAAM,aAAA;AAAA,IACN,WAAA,EAAa,wHAAA;AAAA,IACb,YAAY,EAAC;AAAA,IACb,UAAU;AAAC;AAEf,CAAA;AAWA,SAAS,gBAAA,CAAiB,MAAgB,MAAA,EAAkD;AAC1F,EAAA,MAAM,YAA+B,EAAC;AACtC,EAAA,IAAI;AACF,IAAA,KAAA,MAAW,OAAO,cAAA,EAAgB;AAChC,MAAA,SAAA,CAAU,IAAA;AAAA,QACR,IAAA,CAAK,YAAA;AAAA,UACH;AAAA,YACE,MAAM,GAAA,CAAI,IAAA;AAAA,YACV,aAAa,GAAA,CAAI,WAAA;AAAA,YACjB,WAAA,EAAa;AAAA,cACX,IAAA,EAAM,QAAA;AAAA,cACN,YAAY,EAAE,IAAA,EAAM,QAAA,EAAU,GAAG,IAAI,UAAA,EAAW;AAAA,cAChD,UAAU,GAAA,CAAI,QAAA;AAAA,cACd,oBAAA,EAAsB;AAAA;AACxB,WACF;AAAA,UACA,OAAO,IAAA,KAAS;AACd,YAAA,MAAM,MAAA,GAAS,MAAA,CAAO,OAAA,CAAQ,IAAA,EAAM,IAAI,CAAA;AACxC,YAAA,IAAI,CAAC,MAAA,CAAO,EAAA,EAAI,OAAO,WAAA,CAAY,OAAO,KAAK,CAAA;AAC/C,YAAA,OAAO,OAAO,QAAA,CAAS,QAAA,CAAS,GAAA,CAAI,IAAI,EAAE,IAAI,CAAA;AAAA,UAChD,CAAA;AAAA,UACA,EAAE,OAAO,gBAAA;AAAiB;AAC5B,OACF;AAAA,IACF;AACA,IAAA,SAAA,CAAU,IAAA;AAAA,MACR,IAAA,CAAK,YAAA;AAAA,QACH;AAAA,UACE,IAAA,EAAM,WAAA;AAAA,UACN,WAAA,EACE,sJAAA;AAAA,UACF,WAAA,EAAa,EAAE,IAAA,EAAM,QAAA,EAAU,YAAY,EAAC,EAAG,sBAAsB,KAAA;AAAM,SAC7E;AAAA,QACA,MAAM;AACJ,UAAA,IAAI;AACF,YAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,OAAA,EAAQ,CAAE,GAAA,CAAI,CAAC,CAAC,EAAA,EAAI,EAAE,OAAA,EAAS,CAAA,MAAO;AAAA,cACzD,EAAA;AAAA,cACA,OAAO,OAAA,CAAQ,KAAA;AAAA,cACf,UAAU,OAAA,CAAQ,QAAA;AAAA,cAClB,MAAA,EAAQ,QAAQ,SAAA;AAAU,aAC5B,CAAE,CAAA;AACF,YAAA,MAAM,IAAA,GAAO,KAAA,CACV,GAAA,CAAI,CAAC,MAAM,CAAA,EAAG,CAAA,CAAE,EAAE,CAAA,EAAG,CAAA,CAAE,KAAA,GAAQ,CAAA,QAAA,EAAM,CAAA,CAAE,KAAK,CAAA,CAAA,GAAK,EAAE,CAAA,EAAA,EAAK,CAAA,CAAE,MAAA,CAAO,GAAA,CAAI,CAAC,CAAA,KAAM,EAAE,IAAI,CAAA,CAAE,IAAA,CAAK,IAAI,CAAA,IAAK,aAAa,CAAA,CAAE,CAAA,CACjH,KAAK,IAAI,CAAA;AACZ,YAAA,OAAO,UAAA,CAAW,IAAA,IAAQ,YAAA,EAAc,EAAE,OAAO,CAAA;AAAA,UACnD,SAAS,CAAA,EAAG;AACV,YAAA,OAAO,WAAA,CAAY,gBAAA,CAAiB,CAAA,EAAG,mBAAmB,CAAC,CAAA;AAAA,UAC7D;AAAA,QACF,CAAA;AAAA,QACA,EAAE,OAAO,gBAAA;AAAiB;AAC5B,KACF;AAAA,EACF,SAAS,CAAA,EAAG;AAGV,IAAA,KAAA,MAAW,CAAA,IAAK,WAAW,CAAA,EAAE;AAC7B,IAAA,MAAM,CAAA;AAAA,EACR;AACA,EAAA,OAAO,MAAM;AACX,IAAA,KAAA,MAAW,CAAA,IAAK,WAAW,CAAA,EAAE;AAAA,EAC/B,CAAA;AACF;AAkBO,SAAS,kBAAA,CACd,MACA,OAAA,EACQ;AACR,EAAA,MAAM,EAAE,SAAQ,GAAI,OAAA;AACpB,EAAA,MAAM,KAAA,GAAQ,EAAE,GAAG,aAAA,EAAe,GAAI,OAAA,CAAQ,KAAA,IAAS,EAAC,EAAG;AAC3D,EAAA,MAAM,WAAA,GAAc,QAAQ,WAAA,IAAe,IAAA;AAC3C,EAAA,MAAM,SAAS,OAAA,EAAS,EAAA,IAAM,OAAO,EAAA,GAAK,MAAA,CAAO,QAAQ,EAAE,CAAA;AAC3D,EAAA,IAAI,MAAA,CAAO,IAAA,EAAK,KAAM,EAAA,EAAI;AACxB,IAAA,MAAM,IAAI,KAAA;AAAA,MACR;AAAA,KAEF;AAAA,EACF;AAEA,EAAA,MAAM,QAAA,GAAW,QAAQ,MAAM,CAAA,CAAA;AAI/B,EAAA,yBAAA,CAA0B,IAAA,EAAM,EAAE,cAAA,EAAgB,KAAA,CAAM,IAAI,CAAA;AAC5D,EAAA,MAAM,WAAwC,EAAC;AAM/C,EAAA,IAAI,QAAA,GAAW,KAAA;AACf,EAAA,MAAM,SAAA,uBAAgB,GAAA,EAAgB;AACtC,EAAA,MAAM,MAAA,0BAA+B,aAAa,CAAA;AAClD,EAAA,MAAM,WAAA,GAAc,CAAI,CAAA,KAA8C;AACpE,IAAA,IAAI,QAAA,EAAU;AACZ,MAAA,CAAA,CAAE,MAAM,MAAM;AAAA,MAAC,CAAC,CAAA;AAChB,MAAA,OAAO,OAAA,CAAQ,QAAQ,MAAM,CAAA;AAAA,IAC/B;AACA,IAAA,OAAO,IAAI,OAAA,CAA2B,CAACC,QAAAA,EAAS,MAAA,KAAW;AACzD,MAAA,MAAM,KAAA,GAAQ,MAAMA,QAAAA,CAAQ,MAAM,CAAA;AAClC,MAAA,SAAA,CAAU,IAAI,KAAK,CAAA;AACnB,MAAA,CAAA,CAAE,IAAA;AAAA,QACA,CAAC,CAAA,KAAM;AAAE,UAAA,SAAA,CAAU,OAAO,KAAK,CAAA;AAAG,UAAAA,SAAQ,CAAC,CAAA;AAAA,QAAG,CAAA;AAAA,QAC9C,CAAC,CAAA,KAAM;AAAE,UAAA,SAAA,CAAU,OAAO,KAAK,CAAA;AAAG,UAAA,MAAA,CAAO,CAAC,CAAA;AAAA,QAAG;AAAA,OAC/C;AAAA,IACF,CAAC,CAAA;AAAA,EACH,CAAA;AACA,EAAA,MAAM,aAAA,GAAgB,CAAC,IAAA,KACrB,WAAA;AAAA,IACE,CAAA,MAAA,EAAS,MAAM,CAAA,uCAAA,EAA0C,IAAI,CAAA,wIAAA;AAAA,GAE/D;AAEF,EAAA,MAAM,MAAA,GAAQ,CAAC,IAAA,MAA4B;AAAA,IACzC,IAAA,EAAM,MAAA;AAAA,IACN,UAAU,OAAA,CAAQ,QAAA;AAAA,IAClB,SAAA,EAAW,OAAO,IAAA,EAAM,KAAA,KAAU,QAAA,GAAW,GAAG,MAAM,CAAA,CAAA,EAAI,IAAA,CAAK,KAAK,CAAA,CAAA,GAAK,MAAA;AAAA,IACzE,KAAA,EAAO,OAAO,IAAA,EAAM,KAAA,KAAU,WAC1B,CAAA,EAAG,OAAA,CAAQ,KAAA,IAAS,MAAM,CAAA,QAAA,EAAM,IAAA,CAAK,KAAK,CAAA,CAAA,GAC1C,QAAQ,KAAA,IAAS;AAAA,GACvB,CAAA;AAIA,EAAA,MAAM,GAAA,GAAM,CACV,IAAA,EACA,OAAA,EACA,UAAA,KACG;AACH,IAAA,MAAM,OAAA,GAAU,OAAO,IAAA,KAAqB;AAC1C,MAAA,IAAI;AACF,QAAA,OAAO,MAAM,QAAQ,IAAI,CAAA;AAAA,MAC3B,SAAS,CAAA,EAAG;AACV,QAAA,OAAO,YAAY,gBAAA,CAAiB,CAAA,EAAG,CAAA,EAAG,IAAI,UAAU,CAAC,CAAA;AAAA,MAC3D;AAAA,IACF,CAAA;AACA,IAAA,MAAM,KAAA,GAAQ,UAAA,GACV,oBAAA,CAAqB,OAAA,EAAS;AAAA,MAC5B,QAAA,EAAU,IAAA;AAAA,MACV,KAAA,EAAO,EAAE,EAAA,EAAI,KAAA,CAAM,EAAA,EAAI,MAAM,KAAA,CAAM,IAAA,EAAM,KAAA,EAAO,KAAA,CAAM,KAAA,EAAM;AAAA,MAC5D,IAAA,EAAM,MAAA;AAAA,MACN,UAAU,OAAA,CAAQ,QAAA;AAAA,MAClB,eAAe,CAAC,EAAE,IAAA,EAAK,KAAM,OAAO,IAAI;AAAA,KACzC,CAAA,GACD,OAAA;AACJ,IAAA,QAAA,CAAS,IAAI,CAAA,GAAI,KAAA;AAAA,EACnB,CAAA;AAIA,EAAA,GAAA;AAAA,IACE,eAAA;AAAA,IACA,MAAM;AACJ,MAAA,MAAM,MAAA,GAAS,QAAQ,SAAA,EAAU;AACjC,MAAA,MAAM,IAAA,GAAO,MAAA,CACV,GAAA,CAAI,CAAC,CAAA,KAAM,CAAA,EAAG,CAAA,CAAE,IAAI,CAAA,EAAG,CAAA,CAAE,QAAA,GAAW,GAAA,GAAM,EAAE,CAAA,EAAA,EAAK,CAAA,CAAE,IAAI,CAAA,CAAA,EAAI,CAAA,CAAE,KAAA,GAAQ,CAAA,QAAA,EAAM,CAAA,CAAE,KAAK,CAAA,CAAA,GAAK,EAAE,CAAA,CAAE,CAAA,CAC3F,IAAA,CAAK,IAAI,CAAA;AACZ,MAAA,OAAO,UAAA,CAAW,IAAA,IAAQ,aAAA,EAAe,EAAE,EAAA,EAAI,QAAQ,KAAA,EAAO,OAAA,CAAQ,KAAA,EAAO,MAAA,EAAQ,CAAA;AAAA,IACvF,CAAA;AAAA,IACA;AAAA,GACF;AAEA,EAAA,GAAA;AAAA,IACE,gBAAA;AAAA,IACA,CAAC,IAAA,KAAS;AACR,MAAA,MAAM,IAAA,GAAO,MAAA,CAAO,IAAA,CAAK,KAAA,IAAS,EAAE,CAAA;AACpC,MAAA,IAAI,CAAC,OAAA,CAAQ,SAAA,EAAU,CAAE,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,IAAA,KAAS,IAAI,CAAA,EAAG;AACrD,QAAA,OAAO,WAAA,CAAY,CAAA,eAAA,EAAkB,IAAI,CAAA,CAAE,CAAA;AAAA,MAC7C;AACA,MAAA,MAAM,KAAA,GAAQ,OAAA,CAAQ,QAAA,CAAS,IAAI,CAAA;AACnC,MAAA,OAAO,UAAA,CAAW,KAAK,SAAA,CAAU,KAAK,GAAG,EAAE,KAAA,EAAO,IAAA,EAAM,KAAA,EAAO,CAAA;AAAA,IACjE,CAAA;AAAA,IACA;AAAA,GACF;AAEA,EAAA,GAAA;AAAA,IACE,iBAAA;AAAA,IACA,MAAM;AACJ,MAAA,MAAM,MAAA,GAAS,QAAQ,SAAA,EAAU;AACjC,MAAA,OAAO,WAAW,IAAA,CAAK,SAAA,CAAU,QAAQ,IAAA,EAAM,CAAC,GAAG,MAAM,CAAA;AAAA,IAC3D,CAAA;AAAA,IACA;AAAA,GACF;AAIA,EAAA,GAAA;AAAA,IACE,gBAAA;AAAA,IACA,OAAO,IAAA,KAAS;AACd,MAAA,MAAM,IAAA,GAAO,MAAA,CAAO,IAAA,CAAK,KAAA,IAAS,EAAE,CAAA;AACpC,MAAA,MAAM,QAAA,GAAW,QAAQ,SAAA,EAAU,CAAE,KAAK,CAAC,CAAA,KAAM,CAAA,CAAE,IAAA,KAAS,IAAI,CAAA;AAChE,MAAA,IAAI,CAAC,QAAA,EAAU,OAAO,WAAA,CAAY,CAAA,eAAA,EAAkB,IAAI,CAAA,CAAE,CAAA;AAC1D,MAAA,MAAM,OAAO,MAAM,WAAA;AAAA,QACjB,MAAA,CAAO,MAAM,OAAA,CAAQ,QAAA,CAAS,IAAA,EAAM,KAAK,KAAK,CAAA,EAAG,CAAA,2BAAA,EAA8B,IAAI,CAAA,EAAA,CAAI;AAAA,OACzF;AACA,MAAA,IAAI,SAAS,MAAA,EAAQ,OAAO,aAAA,CAAc,CAAA,cAAA,EAAiB,IAAI,CAAA,CAAA,CAAG,CAAA;AAClE,MAAA,IAAI,CAAC,IAAA,CAAK,EAAA,EAAI,OAAO,WAAA,CAAY,KAAK,KAAK,CAAA;AAC3C,MAAA,OAAO,WAAW,CAAA,EAAG,IAAI,CAAA,QAAA,EAAM,IAAA,CAAK,UAAU,IAAA,CAAK,KAAK,CAAC,CAAA,CAAA,EAAI,EAAE,KAAA,EAAO,IAAA,EAAM,KAAA,EAAO,IAAA,CAAK,OAAO,CAAA;AAAA,IACjG,CAAA;AAAA,IACA;AAAA,GACF;AAEA,EAAA,GAAA;AAAA,IACE,iBAAA;AAAA,IACA,OAAO,IAAA,KAAS;AACd,MAAA,MAAM,MAAA,GAAU,KAAK,MAAA,IAAU,OAAO,KAAK,MAAA,KAAW,QAAA,GAAY,IAAA,CAAK,MAAA,GAAoC,EAAC;AAC5G,MAAA,MAAM,MAAA,GAAS,QAAQ,SAAA,EAAU;AACjC,MAAA,MAAM,KAAA,GAAQ,IAAI,GAAA,CAAI,MAAA,CAAO,IAAI,CAAC,CAAA,KAAM,CAAA,CAAE,IAAI,CAAC,CAAA;AAC/C,MAAA,MAAM,WAAA,GAAc,MAAA,CAAO,IAAA,CAAK,MAAM,CAAA,CAAE,MAAA,CAAO,CAAC,CAAA,KAAM,CAAC,KAAA,CAAM,GAAA,CAAI,CAAC,CAAC,CAAA;AACnE,MAAA,IAAI,WAAA,CAAY,QAAQ,OAAO,WAAA,CAAY,mBAAmB,WAAA,CAAY,IAAA,CAAK,IAAI,CAAC,CAAA,CAAE,CAAA;AACtF,MAAA,MAAM,YAAY,OAAA,CAAQ,SAAA;AAC1B,MAAA,IAAI,SAAA,EAAW;AACb,QAAA,MAAM,IAAA,GAAO,MAAM,WAAA,CAAY,MAAA,CAAO,MAAM,SAAA,CAAU,MAAM,CAAA,EAAG,yBAAyB,CAAC,CAAA;AACzF,QAAA,IAAI,IAAA,KAAS,MAAA,EAAQ,OAAO,aAAA,CAAc,WAAW,CAAA;AACrD,QAAA,IAAI,CAAC,IAAA,CAAK,EAAA,EAAI,OAAO,WAAA,CAAY,KAAK,KAAK,CAAA;AAAA,MAC7C,CAAA,MAAO;AAGL,QAAA,MAAM,IAAA,GAAO,MAAA,CAAO,IAAA,CAAK,MAAM,CAAA;AAC/B,QAAA,MAAM,YAAsB,EAAC;AAC7B,QAAA,KAAA,MAAW,CAAC,CAAA,EAAG,CAAC,CAAA,IAAK,IAAA,CAAK,SAAQ,EAAG;AACnC,UAAA,MAAM,OAAO,MAAM,WAAA;AAAA,YACjB,MAAA,CAAO,MAAM,OAAA,CAAQ,QAAA,CAAS,CAAA,EAAG,MAAA,CAAO,CAAC,CAAC,CAAA,EAAG,CAAA,2BAAA,EAA8B,CAAC,CAAA,EAAA,CAAI;AAAA,WAClF;AACA,UAAA,IAAI,SAAS,MAAA,EAAQ;AACnB,YAAA,OAAO;AAAA,cACL,GAAG,aAAA,CAAc,CAAA,cAAA,EAAiB,CAAC,CAAA,CAAA,CAAG,CAAA;AAAA,cACtC,iBAAA,EAAmB,EAAE,SAAA,EAAW,OAAA,EAAS,CAAA,EAAG,cAAc,IAAA,CAAK,KAAA,CAAM,CAAA,GAAI,CAAC,CAAA;AAAE,aAC9E;AAAA,UACF;AACA,UAAA,IAAI,CAAC,KAAK,EAAA,EAAI;AACZ,YAAA,MAAM,KAAA,GAAQ,EAAE,SAAA,EAAW,MAAA,EAAQ,CAAA,EAAG,cAAc,IAAA,CAAK,KAAA,CAAM,CAAA,GAAI,CAAC,CAAA,EAAE;AACtE,YAAA,IAAI,UAAU,MAAA,EAAQ;AAGpB,cAAAJ,4BAAA,CAAa;AAAA,gBACX,SAAS,KAAA,CAAM,EAAA;AAAA,gBACf,WAAW,KAAA,CAAM,IAAA;AAAA,gBACjB,YAAY,KAAA,CAAM,KAAA;AAAA,gBAClB,MAAA,EAAQ,MAAA,CAAO,EAAE,CAAA;AAAA,gBACjB,MAAA,EAAQ,iBAAA;AAAA,gBACR,SAAA,EAAW,KAAK,GAAA,EAAI;AAAA,gBACpB,IAAA,EAAM;AAAA,eACP,CAAA;AAAA,YACH;AACA,YAAA,OAAO;AAAA,cACL,GAAG,WAAA;AAAA,gBACD,CAAA,CAAA,EAAI,CAAC,CAAA,eAAA,EAAkB,IAAA,CAAK,KAAK,OAC9B,SAAA,CAAU,MAAA,GACP,CAAA,uCAAA,EAA0C,SAAA,CAAU,IAAA,CAAK,IAAI,CAAC,CAAA,EAAA,CAAA,GAC9D,yBAAA,CAAA,IACH,KAAA,CAAM,YAAA,CAAa,MAAA,GAAS,CAAA,eAAA,EAAkB,MAAM,YAAA,CAAa,IAAA,CAAK,IAAI,CAAC,CAAA,CAAA,CAAA,GAAM,EAAA;AAAA,eACtF;AAAA,cACA,iBAAA,EAAmB;AAAA,aACrB;AAAA,UACF;AACA,UAAA,SAAA,CAAU,KAAK,CAAC,CAAA;AAAA,QAClB;AAAA,MACF;AACA,MAAA,OAAO,UAAA,CAAW,CAAA,IAAA,EAAO,MAAA,CAAO,IAAA,CAAK,MAAM,EAAE,MAAM,CAAA,SAAA,CAAA,EAAa,EAAE,MAAA,EAAQ,CAAA;AAAA,IAC5E,CAAA;AAAA,IACA;AAAA,GACF;AAEA,EAAA,GAAA;AAAA,IACE,YAAA;AAAA,IACA,CAAC,IAAA,KAAS;AACR,MAAA,MAAM,IAAA,GAAO,MAAA,CAAO,IAAA,CAAK,KAAA,IAAS,EAAE,CAAA;AACpC,MAAA,IAAI,CAAC,OAAA,CAAQ,KAAA,EAAO,OAAO,YAAY,8CAA8C,CAAA;AACrF,MAAA,IAAI,CAAC,OAAA,CAAQ,SAAA,EAAU,CAAE,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,IAAA,KAAS,IAAI,CAAA,EAAG;AACrD,QAAA,OAAO,WAAA,CAAY,CAAA,eAAA,EAAkB,IAAI,CAAA,CAAE,CAAA;AAAA,MAC7C;AACA,MAAA,OAAA,CAAQ,MAAM,IAAI,CAAA;AAClB,MAAA,OAAO,WAAW,CAAA,QAAA,EAAW,IAAI,IAAI,EAAE,KAAA,EAAO,MAAM,CAAA;AAAA,IACtD,CAAA;AAAA,IACA;AAAA,GACF;AAEA,EAAA,GAAA;AAAA,IACE,aAAA;AAAA,IACA,YAAY;AACV,MAAA,IAAI,CAAC,OAAA,CAAQ,MAAA,EAAQ,OAAO,YAAY,+CAA+C,CAAA;AAGvF,MAAA,IAAI,WAAA,IAAe,QAAQ,OAAA,EAAS;AAClC,QAAA,MAAM,UAAU,OAAA,CAAQ,OAAA;AACxB,QAAA,MAAM,EAAA,GAAK,MAAM,WAAA,CAAY,OAAA,CAAQ,SAAQ,CAAE,IAAA,CAAK,MAAM,OAAA,CAAQ;AAAA,UAChE,MAAA,EAAQ,QAAA;AAAA,UACR,IAAA,EAAM,MAAA;AAAA,UACN,OAAO,OAAA,CAAQ,KAAA;AAAA,UACf,MAAA,EAAQ,QAAQ,SAAA;AAAU,SAC3B,CAAC,CAAC,CAAA;AAGH,QAAA,IAAI,OAAO,MAAA,EAAQ;AACjB,UAAA,OAAO,WAAA,CAAY,CAAA,MAAA,EAAS,MAAM,CAAA,oEAAA,CAAsE,CAAA;AAAA,QAC1G;AACA,QAAA,IAAI,CAAC,EAAA,EAAI,OAAO,WAAA,CAAY,kBAAkB,CAAA;AAAA,MAChD;AACA,MAAA,MAAM,SAAS,OAAA,CAAQ,MAAA;AACvB,MAAA,MAAM,MAAA,GAAS,MAAM,WAAA,CAAY,OAAA,CAAQ,OAAA,GAAU,IAAA,CAAK,MAAM,MAAA,EAAQ,CAAC,CAAA;AACvE,MAAA,IAAI,MAAA,KAAW,MAAA,EAAQ,OAAO,aAAA,CAAc,YAAY,CAAA;AACxD,MAAA,IAAI,CAAC,OAAO,EAAA,EAAI,OAAO,YAAY,gBAAA,CAAiB,MAAA,CAAO,KAAA,EAAO,eAAe,CAAC,CAAA;AAClF,MAAA,OAAO,WAAW,WAAA,EAAa,EAAE,MAAA,EAAQ,MAAA,CAAO,QAAQ,CAAA;AAAA,IAC1D,CAAA;AAAA,IACA;AAAA,GACF;AAIA,EAAA,MAAM,UAAA,GAAa,kBAAA;AAAA,IACjB,IAAA;AAAA,IACA,EAAE,MAAA,EAAQ,MAAA,EAAQ,QAAA,EAAU,MAAA,EAAQ,QAAA,EAAU,WAAA,EAAa,OAAA,EAAS,CAAC,MAAA,KAAW,gBAAA,CAAiB,IAAA,EAAM,MAAM,CAAA,EAAE;AAAA,IAC/G,MAAA;AAAA,IACA,EAAE,SAAS,QAAA;AAAS,GACtB;AAEA,EAAA,OAAO;AAAA,IACL,EAAA,EAAI,QAAA;AAAA,IACJ,KAAA,EAAO,QAAQ,KAAA,IAAS,MAAA;AAAA,IACxB,SAAS,MAAM;AACb,MAAA,IAAI,QAAA,EAAU;AACd,MAAA,QAAA,GAAW,IAAA;AAEX,MAAA,UAAA,CAAW,KAAA,EAAM;AAEjB,MAAA,KAAA,MAAW,KAAA,IAAS,CAAC,GAAG,SAAS,GAAG,KAAA,EAAM;AAC1C,MAAA,SAAA,CAAU,KAAA,EAAM;AAAA,IAClB;AAAA,GACF;AACF","file":"bridges-forms.cjs","sourcesContent":["import {\n type CallToolResult,\n type JsonObject,\n type JsonRpcMessage,\n type JsonRpcRequest,\n type JsonRpcId,\n type RegisteredTool,\n type ServerCapabilities,\n type ServerInfo,\n type ToolDefinition,\n type ToolHandler,\n JSONRPC_INTERNAL_ERROR,\n JSONRPC_INVALID_PARAMS,\n JSONRPC_METHOD_NOT_FOUND,\n MCP_PROTOCOL_VERSION,\n} from \"./types\";\nimport { ToolRegistry } from \"./tool-host\";\n\nexport type McpServerOptions = {\n info: ServerInfo;\n /** Defaults to { tools: { listChanged: true } } */\n capabilities?: ServerCapabilities;\n /** Free-text instructions surfaced to clients during initialize. */\n instructions?: string;\n /**\n * The protocol revisions this server speaks, NEWEST FIRST.\n *\n * `initialize` echoes the client's requested revision when it is in this\n * list, and answers the first entry otherwise — the spec's rule, which leaves\n * the client to decide whether it can live with the answer.\n *\n * Defaults to `[MCP_PROTOCOL_VERSION]`: one revision, answered whatever the\n * client asks, which is exactly what this server did before the option\n * existed. Only list a revision the tools you register are correct under.\n */\n protocolVersions?: readonly string[];\n};\n\nexport type Transport = {\n /** Called by the server when it has a message to deliver to the client. */\n send: (message: JsonRpcMessage) => void;\n /** Called by the server when it's torn down so the transport can clean up. */\n close?: () => void;\n};\n\n/**\n * MicroMcpServer — protocol-level MCP server, transport-agnostic.\n *\n * Use it like:\n *\n * const server = new MicroMcpServer({ info: { name: \"session\", version: \"0.1\" } });\n * server.registerTool({ name: \"...\", inputSchema: { type: \"object\" } }, async (args) => ({...}));\n * const transport = new InProcessTransport();\n * server.attach(transport);\n * transport.deliver({ ... }); // client → server frames\n *\n * The same server can serve multiple transports (e.g. an in-process agent\n * AND a relayed external client) by attaching each one.\n */\nexport class MicroMcpServer extends ToolRegistry {\n private transports = new Set<Transport>();\n /**\n * Transports owed a `tools/list_changed` at the end of this tick. Empty when\n * none is scheduled. See {@link scheduleListChangedNotification}.\n */\n private listChangedOwed = new Set<Transport>();\n\n readonly info: ServerInfo;\n readonly capabilities: ServerCapabilities;\n readonly instructions?: string;\n readonly protocolVersions: readonly string[];\n\n constructor(options: McpServerOptions) {\n super();\n this.info = options.info;\n this.capabilities = options.capabilities ?? { tools: { listChanged: true } };\n this.instructions = options.instructions;\n if (options.protocolVersions && options.protocolVersions.length === 0) {\n // No answer exists for a client's initialize. Fail here, next to the\n // mistake, rather than in a handshake nobody is watching.\n throw new Error(\"MicroMcpServer: protocolVersions is empty — a server must speak at least one protocol revision.\");\n }\n this.protocolVersions = options.protocolVersions ? [...options.protocolVersions] : [MCP_PROTOCOL_VERSION];\n }\n\n attach(transport: Transport): () => void {\n this.transports.add(transport);\n return () => this.detach(transport);\n }\n\n detach(transport: Transport): void {\n if (this.transports.delete(transport)) {\n transport.close?.();\n }\n }\n\n unregisterTool(name: string): void {\n if (this.tools.delete(name)) {\n this.scheduleListChangedNotification();\n }\n }\n\n protected onToolsChanged(): void {\n this.scheduleListChangedNotification();\n }\n\n /**\n * Receive a JSON-RPC frame from a client (called by the transport).\n * The transport is responsible for sending the response back.\n */\n async receive(transport: Transport, message: JsonRpcMessage): Promise<void> {\n if (!(\"method\" in message)) return; // It's a response, not a request — ignore.\n\n const isNotification = !(\"id\" in message);\n if (isNotification) {\n // Notifications are fire-and-forget. We ignore unknown methods.\n return;\n }\n\n const request = message as JsonRpcRequest;\n try {\n const result = await this.handle(request, transport);\n transport.send({ jsonrpc: \"2.0\", id: request.id, result });\n } catch (err) {\n transport.send({\n jsonrpc: \"2.0\",\n id: request.id,\n error: this.toRpcError(err),\n });\n }\n }\n\n private async handle(request: JsonRpcRequest, transport: Transport): Promise<any> {\n const { method, params } = request;\n switch (method) {\n case \"initialize\":\n return {\n protocolVersion: this.negotiate(params?.protocolVersion),\n capabilities: this.capabilities,\n serverInfo: this.info,\n ...(this.instructions ? { instructions: this.instructions } : {}),\n };\n\n case \"tools/list\":\n return { tools: this.listTools() };\n\n case \"tools/call\": {\n const name = params?.name;\n const args = (params?.arguments ?? {}) as JsonObject;\n if (typeof name !== \"string\") {\n throw rpcError(JSONRPC_INVALID_PARAMS, \"tools/call requires `name`\");\n }\n const tool = this.tools.get(name);\n if (!tool) {\n throw rpcError(JSONRPC_METHOD_NOT_FOUND, this.missingToolMessage(name));\n }\n const result = await tool.handler(args, { transport });\n return result satisfies CallToolResult;\n }\n\n case \"ping\":\n return {};\n\n default:\n throw rpcError(JSONRPC_METHOD_NOT_FOUND, `Unsupported method: ${method}`);\n }\n }\n\n /** The revision to answer `initialize` with: the one asked for if spoken, else the newest. */\n private negotiate(requested: unknown): string {\n return typeof requested === \"string\" && this.protocolVersions.includes(requested)\n ? requested\n : this.protocolVersions[0];\n }\n\n /**\n * Tell the transports that SAW the old tool list that it changed.\n *\n * Changes in one tick coalesce into one notification. The recipients are the\n * transports attached at the moment of a change, not at the moment the\n * notification goes out: a transport attached afterwards never had the old\n * list, so \"your list is stale\" is noise to it — and on stdio, where a host\n * builds the server and attaches stdin/stdout in the same tick, it was the\n * first frame a client received, before it had even sent `initialize`.\n */\n private scheduleListChangedNotification(): void {\n if (this.transports.size === 0) return;\n const scheduled = this.listChangedOwed.size > 0;\n for (const t of this.transports) this.listChangedOwed.add(t);\n if (scheduled) return;\n queueMicrotask(() => {\n const owed = this.listChangedOwed;\n this.listChangedOwed = new Set();\n for (const t of owed) {\n // Detached since the change: it is not listening any more.\n if (this.transports.has(t)) t.send({ jsonrpc: \"2.0\", method: \"notifications/tools/list_changed\" });\n }\n });\n }\n\n /** Send an application notification to every client, or one attached transport. */\n notify(message: JsonRpcMessage, transport?: Transport): void {\n if (transport) transport.send(message);\n else this.broadcast(message);\n }\n\n private broadcast(message: JsonRpcMessage): void {\n for (const t of this.transports) t.send(message);\n }\n\n private toRpcError(err: unknown): { code: number; message: string; data?: any } {\n if (err && typeof err === \"object\" && \"code\" in err && \"message\" in err) {\n return err as any;\n }\n return {\n code: JSONRPC_INTERNAL_ERROR,\n message: err instanceof Error ? err.message : String(err),\n };\n }\n}\n\nexport function rpcError(code: number, message: string, data?: any) {\n return { code, message, ...(data !== undefined ? { data } : {}) };\n}\n\n/**\n * Helper to build a CallToolResult from a string or structured value.\n */\nexport function textResult(text: string, structured?: any): CallToolResult {\n return {\n content: [{ type: \"text\", text }],\n ...(structured !== undefined ? { structuredContent: structured } : {}),\n };\n}\n\nexport function errorResult(text: string): CallToolResult {\n return { content: [{ type: \"text\", text }], isError: true };\n}\n\n// Internal helper so the JsonRpcId import isn't dropped by tsup\ntype _KeepIdImport = JsonRpcId;\n","import type { CallToolResult } from \"../mcp/types\";\nimport { emitActivity } from \"./registry\";\nimport type { AgentTarget } from \"./types\";\n\nexport type ActivityAgent = { id: string; name?: string; color?: string };\n\nexport type ActivityResolverContext<TArgs = Record<string, unknown>> = {\n /** Tool name as registered. */\n toolName: string;\n /** Arguments the tool was called with. */\n args: TArgs;\n /** The CallToolResult the underlying handler produced. */\n result: CallToolResult;\n};\n\n/**\n * Resolves an `AgentTarget` for an executed tool. Bridges declare one of\n * these per registration so the wrapper knows which surface / element /\n * screen the activity belongs to.\n *\n * The resolver runs AFTER the tool handler so it can inspect the result\n * (e.g. read a newly-created item id from `structuredContent`).\n */\nexport type ActivityTargetResolver<TArgs = Record<string, unknown>> = (\n ctx: ActivityResolverContext<TArgs>,\n) => AgentTarget | null;\n\nexport type ToolHandler<TArgs = Record<string, unknown>> = (\n args: TArgs,\n) => Promise<CallToolResult> | CallToolResult;\n\n/**\n * wrapToolWithActivity — decorate a bridge tool handler so every successful\n * call emits an `AgentActivityEvent`. Returns a new handler with the same shape.\n *\n * Usage in a bridge:\n *\n * server.registerTool(\n * definition,\n * wrapToolWithActivity(\n * handler,\n * { agent, kind: \"whiteboard\", resolveTarget: ({ args }) => ({\n * kind: \"whiteboard\", elementId: args.id as string,\n * }) },\n * ),\n * );\n */\nexport function wrapToolWithActivity<TArgs = Record<string, unknown>>(\n handler: ToolHandler<TArgs>,\n options: {\n toolName: string;\n agent: ActivityAgent;\n /** Optional fancy-screens screen id this bridge is scoped to. */\n screenId?: string;\n /** Default target kind if the resolver returns one without `kind`. */\n kind: AgentTarget[\"kind\"];\n /** Per-call resolver. Return `null` to skip emitting (e.g. for read-only tools). */\n resolveTarget?: ActivityTargetResolver<TArgs>;\n /** Optional ttl override. */\n ttlMs?: number;\n /**\n * Build the `meta` broadcast to all relay peers. Defaults to the tool's full\n * `structuredContent`. Override to REDACT sensitive fields before fan-out —\n * e.g. the terminal bridge strips raw command/keystroke bytes so a command\n * line (or its secrets) is never broadcast verbatim to every peer.\n */\n buildMeta?: (result: CallToolResult) => Record<string, unknown> | undefined;\n },\n): ToolHandler<TArgs> {\n return async (args) => {\n const result = await handler(args);\n if (result.isError) return result;\n\n let target: AgentTarget | null;\n if (options.resolveTarget) {\n target = options.resolveTarget({ toolName: options.toolName, args, result });\n } else {\n target = { kind: options.kind, screenId: options.screenId };\n }\n if (!target) return result;\n\n emitActivity({\n agentId: options.agent.id,\n agentName: options.agent.name,\n agentColor: options.agent.color,\n target: { ...target, kind: target.kind ?? options.kind, screenId: target.screenId ?? options.screenId },\n action: options.toolName,\n timestamp: Date.now(),\n meta: options.buildMeta ? options.buildMeta(result) : extractMeta(result),\n ttlMs: options.ttlMs,\n });\n return result;\n };\n}\n\nfunction extractMeta(result: CallToolResult): Record<string, unknown> | undefined {\n const sc = result.structuredContent;\n if (sc && typeof sc === \"object\" && !Array.isArray(sc)) {\n return sc as Record<string, unknown>;\n }\n return undefined;\n}\n","import { textResult, errorResult } from \"../mcp/server\";\nimport type { ToolHost } from \"../mcp/tool-host\";\nimport { readHistory, redoOne, undoOne } from \"./undo-stack\";\n\nexport type UndoToolsOptions = {\n /** Default agent id when the caller doesn't pass one. */\n defaultAgentId?: string;\n};\n\n/**\n * Idempotent tracker so multiple bridges on the same server only register\n * agent_undo / agent_redo / agent_history once.\n */\nconst installedHosts = new WeakSet<ToolHost>();\n\n/**\n * The one owner every copy of the undo tools registers under. They are SHARED\n * on purpose — every bridge ensures them, and a host may also call\n * `registerUndoTools` itself — so all of those are the same registrant, and a\n * second registration replaces the first instead of being refused as a\n * collision. Anything else claiming `agent_undo` is still refused.\n */\nconst UNDO_TOOLS_OWNER = \"agent-undo\";\n\n/**\n * ensureUndoToolsRegistered — bridges call this on construction. Safe to\n * call repeatedly with the same server; subsequent calls are no-ops.\n */\nexport function ensureUndoToolsRegistered(host: ToolHost, options: UndoToolsOptions = {}): void {\n if (installedHosts.has(host)) return;\n installedHosts.add(host);\n registerUndoTools(host, options);\n}\n\n/**\n * registerUndoTools — add agent_undo / agent_redo / agent_history to the\n * server. Returns a disposer that unregisters all three.\n */\nexport function registerUndoTools(host: ToolHost, options: UndoToolsOptions = {}): () => void {\n const defaultAgent = options.defaultAgentId ?? \"agent\";\n const disposers: Array<() => void> = [];\n const agentOf = (args: any): string =>\n typeof args?.agentId === \"string\" ? args.agentId : defaultAgent;\n\n disposers.push(\n host.registerTool(\n {\n name: \"agent_undo\",\n description: \"Undo the most recent action on the agent's stack. Optional agentId targets a specific agent.\",\n inputSchema: {\n type: \"object\",\n properties: { agentId: { type: \"string\" } },\n additionalProperties: false,\n },\n },\n async (args) => {\n const entry = await undoOne(agentOf(args));\n if (!entry) return errorResult(\"Nothing to undo.\");\n return textResult(`Undid: ${entry.label}`, { entry: serialize(entry) });\n },\n { owner: UNDO_TOOLS_OWNER },\n ),\n );\n\n disposers.push(\n host.registerTool(\n {\n name: \"agent_redo\",\n description: \"Redo the most recently undone action.\",\n inputSchema: {\n type: \"object\",\n properties: { agentId: { type: \"string\" } },\n additionalProperties: false,\n },\n },\n async (args) => {\n const entry = await redoOne(agentOf(args));\n if (!entry) return errorResult(\"Nothing to redo.\");\n return textResult(`Redid: ${entry.label}`, { entry: serialize(entry) });\n },\n { owner: UNDO_TOOLS_OWNER },\n ),\n );\n\n disposers.push(\n host.registerTool(\n {\n name: \"agent_history\",\n description: \"List the agent's undo stack (oldest first). Useful for understanding what's reversible.\",\n inputSchema: {\n type: \"object\",\n properties: { agentId: { type: \"string\" } },\n additionalProperties: false,\n },\n },\n async (args) => {\n const history = readHistory(agentOf(args)).map(serialize);\n const text = history.map((e) => `${new Date(e.timestamp).toISOString()} ${e.bridgeId} ${e.action}: ${e.label}`).join(\"\\n\");\n return textResult(text || \"(empty)\", history);\n },\n { owner: UNDO_TOOLS_OWNER },\n ),\n );\n\n return () => disposers.forEach((d) => d());\n}\n\nfunction serialize(entry: import(\"./undo-stack\").UndoEntry) {\n return {\n timestamp: entry.timestamp,\n bridgeId: entry.bridgeId,\n action: entry.action,\n label: entry.label,\n };\n}\n","/**\n * Shared plumbing for bridges whose adapter mutators may commit asynchronously\n * (screens since 0.49.0, forms since 0.52.0). Internal: not an entry point.\n *\n * The contract: a mutator returns `void` (sync, unchanged behaviour) or a\n * Promise resolving to `void` or `{ ok: true } | { ok: false; error }`. The\n * bridge awaits it, and only an accepted commit counts as success.\n */\n\n/** What an async mutator may resolve to, to acknowledge or refuse a commit. */\nexport type HostCommitResult = { ok: true } | { ok: false; error: string; code?: string };\n\nexport type CommitOutcome = { ok: true } | { ok: false; error: string };\n\n/**\n * The agent-facing text for a host failure. Only the message is used — never\n * `stack`, which carries file paths — matching the other bridges. A message\n * that itself embeds stack frames is cut at the first frame. A non-Error\n * rejection (a plain object, a response) has no reliable message, so it gets\n * the fallback rather than \"[object Object]\".\n */\nexport function hostErrorMessage(e: unknown, fallback: string): string {\n let msg: string | undefined;\n if (e instanceof Error) msg = e.message;\n else if (typeof e === \"string\") msg = e;\n else if (e && typeof e === \"object\" && typeof (e as { message?: unknown }).message === \"string\") {\n msg = (e as { message: string }).message;\n }\n const firstFrame = msg ? msg.search(/\\n\\s+at\\s/) : -1;\n if (msg && firstFrame >= 0) msg = msg.slice(0, firstFrame);\n msg = msg?.trim();\n return msg ? msg : fallback;\n}\n\n/** Run one adapter commit, await it, and normalise every way it can fail. Never rejects. */\nexport async function commit(\n run: () => void | Promise<void | HostCommitResult>,\n fallback: string,\n): Promise<CommitOutcome> {\n try {\n const r = await run();\n if (r && typeof r === \"object\" && r.ok === false) {\n return { ok: false, error: hostErrorMessage(r.error, fallback) };\n }\n return { ok: true };\n } catch (e) {\n return { ok: false, error: hostErrorMessage(e, fallback) };\n }\n}\n","import type { ToolHost } from \"../mcp/tool-host\";\n\n/**\n * One bridge serving N instances on a host (docs/multi-form-hosts.md, option A).\n * Internal: not an entry point.\n *\n * A bridge whose tool names are not scoped to its adapter (`form_set_value`,\n * not `form_billing_set_value`) can only serve one instance per host unless the\n * instances share the tools. This is the sharing: the FIRST instance of a family\n * on a host installs the family's tools; later ones join a per-host router; each\n * tool resolves its target instance from an argument (`form`); the tools are\n * withdrawn when the last instance leaves. The tool list therefore stays the\n * same size however many instances come and go.\n *\n * Resolution rules, which are the agent-facing contract:\n * - exactly one instance mounted: the argument is optional;\n * - two or more: a call without it is refused with the ids listed;\n * - an id that is not mounted is refused with the ids listed, even when only\n * one is mounted (a call aimed at a form that closed must not land on another).\n *\n * The router does not register tools itself. `install` does, under one owner\n * the bridge chooses, so the 0.51.0 refusal still stops an unrelated registrant\n * taking a family tool name. If `install` throws (a name is already held), no\n * router is left behind.\n */\n\nexport type InstanceResolution<T> =\n | { ok: true; id: string; instance: T }\n | { ok: false; error: string };\n\nexport type InstanceRouter<T> = {\n /** Mounted instance ids, in mount order. */\n ids(): string[];\n /** Mounted instances, in mount order. */\n entries(): Array<[string, T]>;\n /** Pick the instance a tool call targets, from the raw argument value. */\n resolve(requested: unknown): InstanceResolution<T>;\n};\n\nexport type InstanceRouterOptions<T> = {\n /** Family key: one router per (host, family). E.g. \"form\". */\n family: string;\n /** Name of the tool argument that selects an instance. E.g. \"form\". */\n argument: string;\n /** The family's listing tool, named in errors so an agent can recover. E.g. \"form_list\". */\n listTool: string;\n /**\n * Register the family's shared tools on `host`, dispatching through `router`.\n * Called once, when the first instance joins. Returns the disposer that\n * withdraws them, called when the last instance leaves.\n */\n install: (router: InstanceRouter<T>) => () => void;\n};\n\nexport type InstanceMembership<T> = {\n router: InstanceRouter<T>;\n /** Remove THIS instance. Idempotent; a no-op if a later join replaced it. */\n leave: () => void;\n};\n\ntype RouterState<T> = {\n instances: Map<string, T>;\n router: InstanceRouter<T>;\n uninstall: () => void;\n};\n\nconst routers = new WeakMap<ToolHost, Map<string, RouterState<unknown>>>();\n\nconst quoteAll = (ids: string[]) => ids.map((id) => `\"${id}\"`).join(\", \");\n\n/**\n * Join `instance` under `id` to the host's router for `options.family`,\n * creating (and installing) the router if this is the first instance.\n *\n * A second join under an id that is already mounted REPLACES that entry: it\n * cannot be told apart from a remount whose cleanup has not run yet, and the\n * earlier membership's `leave` then becomes a no-op, so a late cleanup cannot\n * take the newer instance down.\n */\nexport function joinInstanceRouter<T>(\n host: ToolHost,\n options: InstanceRouterOptions<T>,\n id: string,\n instance: T,\n): InstanceMembership<T> {\n if (typeof id !== \"string\" || id.trim() === \"\") {\n throw new Error(\n `A ${options.family} on a shared host needs a non-empty string id (got ${JSON.stringify(id)}); `\n + `agents select it by that id through the \"${options.argument}\" argument.`,\n );\n }\n\n let byFamily = routers.get(host);\n if (!byFamily) {\n byFamily = new Map();\n routers.set(host, byFamily);\n }\n\n let state = byFamily.get(options.family) as RouterState<T> | undefined;\n if (!state) {\n const instances = new Map<string, T>();\n const router: InstanceRouter<T> = {\n ids: () => [...instances.keys()],\n entries: () => [...instances.entries()],\n resolve: (requested) => resolve(options, instances, requested),\n };\n // Installing may throw (a tool name held by an unrelated registrant). Nothing\n // has been recorded yet, so a failed install leaves no router behind.\n const uninstall = options.install(router);\n state = { instances, router, uninstall };\n byFamily.set(options.family, state as RouterState<unknown>);\n }\n\n const current = state;\n current.instances.set(id, instance);\n // Re-insertion keeps Map order; a remount should not jump the listing order.\n\n let left = false;\n const leave = () => {\n if (left) return;\n left = true;\n if (current.instances.get(id) !== instance) return; // replaced by a later join\n current.instances.delete(id);\n if (current.instances.size === 0 && byFamily!.get(options.family) === current) {\n byFamily!.delete(options.family);\n current.uninstall();\n }\n };\n\n return { router: current.router, leave };\n}\n\nfunction resolve<T>(\n options: InstanceRouterOptions<T>,\n instances: Map<string, T>,\n requested: unknown,\n): InstanceResolution<T> {\n const ids = [...instances.keys()];\n const { family, argument, listTool } = options;\n if (requested === undefined || requested === null || requested === \"\") {\n if (ids.length === 1) return { ok: true, id: ids[0], instance: instances.get(ids[0]) as T };\n if (ids.length === 0) return { ok: false, error: `No ${family} is mounted.` };\n return {\n ok: false,\n error:\n `${ids.length} ${family}s are mounted, so pass \"${argument}\" with one of: ${quoteAll(ids)}. `\n + `${listTool} describes them.`,\n };\n }\n if (typeof requested !== \"string\") {\n return { ok: false, error: `\"${argument}\" must be a ${family} id string. Mounted: ${quoteAll(ids)}.` };\n }\n if (!instances.has(requested)) {\n return {\n ok: false,\n error:\n `No ${family} \"${requested}\" is mounted. `\n + (ids.length ? `Mounted: ${quoteAll(ids)}. ` : \"\")\n + `${listTool} lists what is open.`,\n };\n }\n return { ok: true, id: requested, instance: instances.get(requested) as T };\n}\n","import { textResult, errorResult } from \"../mcp/server\";\nimport type { ToolHost } from \"../mcp/tool-host\";\nimport type { JsonObject } from \"../mcp/types\";\nimport type { Bridge } from \"./types\";\nimport { wrapToolWithActivity } from \"../presence/wrap-tool-with-activity\";\nimport { ensureUndoToolsRegistered } from \"../undo/undo-tools\";\nimport type { AgentTarget } from \"../presence/types\";\nimport { emitActivity } from \"../presence/registry\";\nimport { commit, hostErrorMessage } from \"./host-commit\";\nimport { joinInstanceRouter, type InstanceRouter } from \"./multi-instance\";\nimport type { CallToolResult } from \"../mcp/types\";\n\n/**\n * Field descriptor — what the host says about each form field. Mirrors the\n * subset of HTML / react-fancy input shapes agents care about.\n */\nexport type FormFieldDescriptor = {\n /** Form-local field name. Matches the key the host uses in its values map. */\n name: string;\n /** Display label. */\n label?: string;\n /** Logical type. Drives validation + agent expectations. */\n type:\n | \"text\"\n | \"textarea\"\n | \"number\"\n | \"email\"\n | \"password\"\n | \"url\"\n | \"date\"\n | \"select\"\n | \"multi-select\"\n | \"checkbox\"\n | \"switch\"\n | \"radio\"\n | \"file\"\n | \"json\";\n /** Allowed values for select / radio. */\n options?: Array<{ value: string; label: string }>;\n /** Whether the field must be filled before submit. */\n required?: boolean;\n /** Free-text hint surfaced to the agent. */\n description?: string;\n /** Default value (presented in get_value when nothing set). */\n defaultValue?: unknown;\n};\n\n/**\n * Adapter the host wires per form. Matches the controlled-state pattern\n * react-fancy already uses (`value` / `onValueChange`); this adapter just\n * exposes those state slots in a form-shaped way the bridge can call.\n *\n * `setValue` / `setValues` may be sync (`void`) or async (a Promise); see\n * {@link FormAdapterCommit}. Return a Promise when the host's write is\n * authoritative — a persisted draft, or one the host can refuse or report\n * stale — so the agent is told the truth instead of \"success\" before anything\n * was accepted.\n *\n * This bridge pushes NO undo entries: `agent_undo` cannot revert a form write.\n */\nexport type FormBridgeAdapter = {\n /** Stable id for the form (used in form_describe + presence target). */\n id: string;\n /** Display title for human-readable logs. */\n title?: string;\n /** Optional fancy-screens screen id this form belongs to. */\n screenId?: string;\n /** Field descriptors. The bridge uses these for schema introspection\n * (form_describe). Agents call this first to know what to fill. */\n getFields: () => FormFieldDescriptor[];\n /** Read a single field's current value. */\n getValue: (name: string) => unknown;\n /** Read all values as { fieldName: value }. */\n getValues: () => Record<string, unknown>;\n /**\n * Set a single field's value. The host wires this to its setState, or to\n * an authoritative write it awaits. Sync `void` behaves as it always has; a\n * Promise is awaited, and a rejection or `{ ok: false }` is the tool's error.\n */\n setValue: (name: string, value: unknown) => FormAdapterCommit;\n /**\n * Set many at once, ideally as one host commit. Same sync/async contract as\n * `setValue`. When absent, the bridge awaits `setValue` once per field in\n * order and stops at the first refusal (so that path is NOT atomic).\n */\n setValues?: (values: Record<string, unknown>) => FormAdapterCommit;\n /** Programmatically focus a field (host implements DOM focus). Optional. */\n focus?: (name: string) => void;\n /** Submit the form. Returns the values that were submitted (or rejection). */\n submit?: () => Promise<{ ok: boolean; values?: Record<string, unknown>; error?: string }>;\n /**\n * Human confirm gate for `form_submit` (trust-but-verify). When `pendingMode`\n * is on, the bridge calls this before submitting; return false to decline.\n * Wire it to a human control — submitting a form is a human-visible,\n * often-destructive action (checkout, delete, invite).\n */\n confirm?: (request: FormConfirmRequest) => Promise<boolean> | boolean;\n};\n\n/**\n * What an async `setValue` / `setValues` may resolve to, to acknowledge (or\n * refuse) a write. `{ ok: false }` is a refusal the agent can act on: `error`\n * is shown to the agent verbatim, so keep it a human-readable sentence (e.g.\n * \"Draft changed since you read it; re-read and retry\"), never a stack trace or\n * an internal path. `code` (e.g. `\"stale\"`, `\"forbidden\"`) is for the host's own\n * use; it is not shown to the agent.\n */\nexport type FormAdapterResult = { ok: true } | { ok: false; error: string; code?: string };\n\n/**\n * Return type of `setValue` / `setValues`. A sync `void` return keeps the\n * original behaviour. A Promise makes the bridge WAIT: the tool reports\n * success, and emits its AgentActivity event, only after the promise resolves\n * (to `void` or `{ ok: true }`). A rejection or `{ ok: false }` becomes that\n * tool call's error result and emits no activity. If the bridge is disposed\n * while the write is pending, the call settles at once as an error saying the\n * outcome is unknown, and a later settle is ignored.\n */\nexport type FormAdapterCommit = void | Promise<void | FormAdapterResult>;\n\n/** What a `form_submit` confirm gate is asked to approve. */\nexport type FormConfirmRequest = {\n action: \"submit\";\n form: string;\n title?: string;\n values: Record<string, unknown>;\n};\n\nexport type FormBridgeOptions = {\n adapter: FormBridgeAdapter;\n /** Identity tagged into activity events. */\n agent?: { id: string; name?: string; color?: string };\n /**\n * Stage `form_submit` for human confirmation via `adapter.confirm` instead of\n * submitting immediately. **Default: ON** (matches the navigation bridge and\n * the Human+ trust-but-verify contract). Set false only when auto-submit is\n * safe. If on without an `adapter.confirm`, submit proceeds (nothing to gate).\n */\n pendingMode?: boolean;\n};\n\nconst DEFAULT_AGENT = { id: \"agent\", name: \"Agent\", color: \"#a855f7\" };\n\n/**\n * The one registrant every form tool is held by. The tools are shared by all\n * forms on a host, so the forms themselves are not separate registrants; an\n * UNRELATED registrant claiming a form tool name is still refused (0.51.0).\n */\nconst FORM_TOOLS_OWNER = \"form-bridge\";\n\n/** Every form tool except `form_list` selects its form through this argument. */\nconst FORM_ARG = {\n type: \"string\",\n description: \"Id of the form to act on (see form_list). Optional while exactly one form is mounted; required when several are.\",\n};\n\ntype FormToolDef = {\n name: string;\n description: string;\n properties: Record<string, unknown>;\n required: string[];\n};\n\n/** The shared tool surface, registered once per host however many forms mount. */\nconst FORM_TOOL_DEFS: FormToolDef[] = [\n {\n name: \"form_describe\",\n description: \"Describe a form — fields, types, options, required flags. Call this first to know what's fillable.\",\n properties: {},\n required: [],\n },\n { name: \"form_get_value\", description: \"Read a single field's current value.\", properties: { field: { type: \"string\" } }, required: [\"field\"] },\n { name: \"form_get_values\", description: \"Read every field's current value as a JSON object.\", properties: {}, required: [] },\n {\n name: \"form_set_value\",\n description: \"Set one field's value. The host's controlled state updates and the human sees the field change.\",\n properties: {\n field: { type: \"string\" },\n value: { description: \"Value to set. Type depends on the field's `type`.\" },\n },\n required: [\"field\", \"value\"],\n },\n {\n name: \"form_set_values\",\n description: \"Set multiple fields atomically. Pass a `values` object keyed by field name.\",\n properties: { values: { type: \"object\" } },\n required: [\"values\"],\n },\n {\n name: \"form_focus\",\n description: \"Move browser focus to a field (host-implemented). Useful before streaming text into it.\",\n properties: { field: { type: \"string\" } },\n required: [\"field\"],\n },\n {\n name: \"form_submit\",\n description: \"Submit the form. Host returns ok + values (or an error). In pendingMode this is staged for the human to confirm first.\",\n properties: {},\n required: [],\n },\n];\n\ntype FormHandler = (args: JsonObject) => Promise<CallToolResult>;\n\n/** One mounted form: its adapter plus handlers built with ITS options. */\ntype FormInstance = {\n adapter: FormBridgeAdapter;\n handlers: Record<string, FormHandler>;\n};\n\n/** Register the shared form tools on `host`, routing each call to one form. */\nfunction installFormTools(host: ToolHost, router: InstanceRouter<FormInstance>): () => void {\n const disposers: Array<() => void> = [];\n try {\n for (const def of FORM_TOOL_DEFS) {\n disposers.push(\n host.registerTool(\n {\n name: def.name,\n description: def.description,\n inputSchema: {\n type: \"object\",\n properties: { form: FORM_ARG, ...def.properties } as any,\n required: def.required,\n additionalProperties: false,\n },\n },\n async (args) => {\n const target = router.resolve(args?.form);\n if (!target.ok) return errorResult(target.error);\n return target.instance.handlers[def.name](args);\n },\n { owner: FORM_TOOLS_OWNER },\n ),\n );\n }\n disposers.push(\n host.registerTool(\n {\n name: \"form_list\",\n description:\n \"List the forms mounted on this host — id, title, screen and fields. Pass an id as `form` to the other form tools when more than one is mounted.\",\n inputSchema: { type: \"object\", properties: {}, additionalProperties: false },\n },\n () => {\n try {\n const forms = router.entries().map(([id, { adapter }]) => ({\n id,\n title: adapter.title,\n screenId: adapter.screenId,\n fields: adapter.getFields(),\n }));\n const text = forms\n .map((f) => `${f.id}${f.title ? ` — ${f.title}` : \"\"}: ${f.fields.map((x) => x.name).join(\", \") || \"(no fields)\"}`)\n .join(\"\\n\");\n return textResult(text || \"(no forms)\", { forms });\n } catch (e) {\n return errorResult(hostErrorMessage(e, \"form_list failed.\"));\n }\n },\n { owner: FORM_TOOLS_OWNER },\n ),\n );\n } catch (e) {\n // A name was held by an unrelated registrant: withdraw what did register,\n // so a refused mount leaves nothing half-installed.\n for (const d of disposers) d();\n throw e;\n }\n return () => {\n for (const d of disposers) d();\n };\n}\n\n/**\n * registerFormBridge — wires schema-driven MCP access to a form.\n *\n * Several forms may share one host. The first form on a host registers the\n * form tools; later ones JOIN them (since 0.53.0; before, a second form threw).\n * Each tool takes an optional `form` argument naming the form by `adapter.id`:\n * optional while exactly one form is mounted, so a single-form host behaves as\n * it always did, and required once two or more are. `form_list` lists what is\n * mounted. The returned `dispose()` removes only this form; the tools are\n * withdrawn when the last form on the host is disposed. A second registration\n * with an id that is already mounted replaces it (a remount); the earlier\n * registration's `dispose()` then leaves the newer one in place.\n *\n * `pendingMode`, the confirm gate, the agent identity and the activity target\n * stay per form. See `docs/multi-form-hosts.md`.\n */\nexport function registerFormBridge(\n host: ToolHost,\n options: FormBridgeOptions,\n): Bridge {\n const { adapter } = options;\n const agent = { ...DEFAULT_AGENT, ...(options.agent ?? {}) };\n const pendingMode = options.pendingMode ?? true;\n const formId = adapter?.id == null ? \"\" : String(adapter.id);\n if (formId.trim() === \"\") {\n throw new Error(\n \"registerFormBridge: adapter.id must be a non-empty string. It is how agents pick this form \" +\n \"(the `form` argument) when several forms share a host.\",\n );\n }\n // Surfaces in activity logs; per form, as before.\n const bridgeId = `form:${formId}`;\n\n // agent_undo / agent_redo / agent_history are registered whenever any bridge\n // mounts, so undo availability doesn't hinge on which bridges are co-present.\n ensureUndoToolsRegistered(host, { defaultAgentId: agent.id });\n const handlers: Record<string, FormHandler> = {};\n\n // Close handling. A host may dispose the bridge (the form unmounts) while a\n // write or a human confirm is still pending; its promise may then never\n // settle. Every awaited host call is raced against dispose, so the tool call\n // settles at once instead of hanging, and a late settle is ignored.\n let disposed = false;\n const onDispose = new Set<() => void>();\n const CLOSED: unique symbol = Symbol(\"form-closed\");\n const untilClosed = <T>(p: Promise<T>): Promise<T | typeof CLOSED> => {\n if (disposed) {\n p.catch(() => {}); // a late rejection must never be unhandled\n return Promise.resolve(CLOSED);\n }\n return new Promise<T | typeof CLOSED>((resolve, reject) => {\n const close = () => resolve(CLOSED);\n onDispose.add(close);\n p.then(\n (v) => { onDispose.delete(close); resolve(v); },\n (e) => { onDispose.delete(close); reject(e); },\n );\n });\n };\n const closedUnknown = (what: string) =>\n errorResult(\n `Form \"${formId}\" was closed before the host confirmed ${what}, so whether it was applied is unknown. ` +\n \"Do not assume it was. If the form is open again, read it back (form_get_values) before retrying.\",\n );\n\n const target =(args: any): AgentTarget => ({\n kind: \"form\",\n screenId: adapter.screenId,\n elementId: typeof args?.field === \"string\" ? `${formId}:${args.field}` : formId,\n label: typeof args?.field === \"string\"\n ? `${adapter.title ?? formId} → ${args.field}`\n : adapter.title ?? formId,\n });\n\n // Builds THIS form's handler for a shared tool. The tool definitions live in\n // FORM_TOOL_DEFS; the router dispatches to these after resolving `form`.\n const reg = (\n name: string,\n handler: (args: JsonObject) => Promise<any> | any,\n isMutation: boolean,\n ) => {\n const wrapped = async (args: JsonObject) => {\n try {\n return await handler(args);\n } catch (e) {\n return errorResult(hostErrorMessage(e, `${name} failed.`));\n }\n };\n const final = isMutation\n ? wrapToolWithActivity(wrapped, {\n toolName: name,\n agent: { id: agent.id, name: agent.name, color: agent.color },\n kind: \"form\",\n screenId: adapter.screenId,\n resolveTarget: ({ args }) => target(args),\n })\n : wrapped;\n handlers[name] = final as FormHandler;\n };\n\n // ───────────── Read tools ─────────────\n\n reg(\n \"form_describe\",\n () => {\n const fields = adapter.getFields();\n const text = fields\n .map((f) => `${f.name}${f.required ? \"*\" : \"\"} (${f.type})${f.label ? ` — ${f.label}` : \"\"}`)\n .join(\"\\n\");\n return textResult(text || \"(no fields)\", { id: formId, title: adapter.title, fields });\n },\n false,\n );\n\n reg(\n \"form_get_value\",\n (args) => {\n const name = String(args.field ?? \"\");\n if (!adapter.getFields().find((f) => f.name === name)) {\n return errorResult(`Unknown field: ${name}`);\n }\n const value = adapter.getValue(name);\n return textResult(JSON.stringify(value), { field: name, value });\n },\n false,\n );\n\n reg(\n \"form_get_values\",\n () => {\n const values = adapter.getValues();\n return textResult(JSON.stringify(values, null, 2), values);\n },\n false,\n );\n\n // ───────────── Mutation tools ─────────────\n\n reg(\n \"form_set_value\",\n async (args) => {\n const name = String(args.field ?? \"\");\n const fieldDef = adapter.getFields().find((f) => f.name === name);\n if (!fieldDef) return errorResult(`Unknown field: ${name}`);\n const done = await untilClosed(\n commit(() => adapter.setValue(name, args.value), `Host refused the write to \"${name}\".`),\n );\n if (done === CLOSED) return closedUnknown(`the write to \"${name}\"`);\n if (!done.ok) return errorResult(done.error);\n return textResult(`${name} ← ${JSON.stringify(args.value)}`, { field: name, value: args.value });\n },\n true,\n );\n\n reg(\n \"form_set_values\",\n async (args) => {\n const values = (args.values && typeof args.values === \"object\") ? args.values as Record<string, unknown> : {};\n const fields = adapter.getFields();\n const known = new Set(fields.map((f) => f.name));\n const unknownKeys = Object.keys(values).filter((k) => !known.has(k));\n if (unknownKeys.length) return errorResult(`Unknown fields: ${unknownKeys.join(\", \")}`);\n const setValues = adapter.setValues;\n if (setValues) {\n const done = await untilClosed(commit(() => setValues(values), \"Host refused the write.\"));\n if (done === CLOSED) return closedUnknown(\"the write\");\n if (!done.ok) return errorResult(done.error);\n } else {\n // No batch setter: one host commit per field, in order, stopping at the\n // first refusal. Earlier fields stay committed, so say exactly which.\n const keys = Object.keys(values);\n const committed: string[] = [];\n for (const [i, k] of keys.entries()) {\n const done = await untilClosed(\n commit(() => adapter.setValue(k, values[k]), `Host refused the write to \"${k}\".`),\n );\n if (done === CLOSED) {\n return {\n ...closedUnknown(`the write to \"${k}\"`),\n structuredContent: { committed, pending: k, notAttempted: keys.slice(i + 1) },\n };\n }\n if (!done.ok) {\n const state = { committed, failed: k, notAttempted: keys.slice(i + 1) };\n if (committed.length) {\n // Those writes DID commit; every committed mutation is observable,\n // and wrapToolWithActivity skips error results, so emit it here.\n emitActivity({\n agentId: agent.id,\n agentName: agent.name,\n agentColor: agent.color,\n target: target({}),\n action: \"form_set_values\",\n timestamp: Date.now(),\n meta: state,\n });\n }\n return {\n ...errorResult(\n `\"${k}\" was refused: ${done.error} ` +\n (committed.length\n ? `Already committed and NOT rolled back: ${committed.join(\", \")}. `\n : \"Nothing was committed. \") +\n (state.notAttempted.length ? `Not attempted: ${state.notAttempted.join(\", \")}.` : \"\"),\n ),\n structuredContent: state,\n };\n }\n committed.push(k);\n }\n }\n return textResult(`Set ${Object.keys(values).length} field(s)`, { values });\n },\n true,\n );\n\n reg(\n \"form_focus\",\n (args) => {\n const name = String(args.field ?? \"\");\n if (!adapter.focus) return errorResult(\"Host did not provide a focus implementation.\");\n if (!adapter.getFields().find((f) => f.name === name)) {\n return errorResult(`Unknown field: ${name}`);\n }\n adapter.focus(name);\n return textResult(`Focused ${name}`, { field: name });\n },\n true,\n );\n\n reg(\n \"form_submit\",\n async () => {\n if (!adapter.submit) return errorResult(\"Host did not provide a submit implementation.\");\n // Trust-but-verify: a form submit is a human-visible / often-destructive\n // action, so gate it on a human confirm when one is wired.\n if (pendingMode && adapter.confirm) {\n const confirm = adapter.confirm;\n const ok = await untilClosed(Promise.resolve().then(() => confirm({\n action: \"submit\",\n form: formId,\n title: adapter.title,\n values: adapter.getValues(),\n })));\n // The form went away while the human was deciding: never submit a\n // form that is no longer on screen, whatever the late answer is.\n if (ok === CLOSED) {\n return errorResult(`Form \"${formId}\" was closed before the submit was confirmed. Nothing was submitted.`);\n }\n if (!ok) return errorResult(\"Declined by user\");\n }\n const submit = adapter.submit;\n const result = await untilClosed(Promise.resolve().then(() => submit()));\n if (result === CLOSED) return closedUnknown(\"the submit\");\n if (!result.ok) return errorResult(hostErrorMessage(result.error, \"Submit failed\"));\n return textResult(\"Submitted\", { values: result.values });\n },\n true,\n );\n\n // Join the host's form router. The first form installs the shared tools; a\n // refused install (a name held by an unrelated registrant) throws from here.\n const membership = joinInstanceRouter<FormInstance>(\n host,\n { family: \"form\", argument: \"form\", listTool: \"form_list\", install: (router) => installFormTools(host, router) },\n formId,\n { adapter, handlers },\n );\n\n return {\n id: bridgeId,\n title: adapter.title ?? formId,\n dispose: () => {\n if (disposed) return;\n disposed = true;\n // Only this form leaves; the tools go when the last form does.\n membership.leave();\n // Settle every call still waiting on the host; see untilClosed.\n for (const close of [...onDispose]) close();\n onDispose.clear();\n },\n };\n}\n"]}
|
package/dist/bridges-forms.js
CHANGED
|
@@ -4,16 +4,184 @@ import { wrapToolWithActivity } from './chunk-2VENWGLZ.js';
|
|
|
4
4
|
import { emitActivity } from './chunk-C3TYI5TJ.js';
|
|
5
5
|
import { textResult, errorResult } from './chunk-3GFKC6OK.js';
|
|
6
6
|
|
|
7
|
+
// src/bridges/multi-instance.ts
|
|
8
|
+
var routers = /* @__PURE__ */ new WeakMap();
|
|
9
|
+
var quoteAll = (ids) => ids.map((id) => `"${id}"`).join(", ");
|
|
10
|
+
function joinInstanceRouter(host, options, id, instance) {
|
|
11
|
+
if (typeof id !== "string" || id.trim() === "") {
|
|
12
|
+
throw new Error(
|
|
13
|
+
`A ${options.family} on a shared host needs a non-empty string id (got ${JSON.stringify(id)}); agents select it by that id through the "${options.argument}" argument.`
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
let byFamily = routers.get(host);
|
|
17
|
+
if (!byFamily) {
|
|
18
|
+
byFamily = /* @__PURE__ */ new Map();
|
|
19
|
+
routers.set(host, byFamily);
|
|
20
|
+
}
|
|
21
|
+
let state = byFamily.get(options.family);
|
|
22
|
+
if (!state) {
|
|
23
|
+
const instances = /* @__PURE__ */ new Map();
|
|
24
|
+
const router = {
|
|
25
|
+
ids: () => [...instances.keys()],
|
|
26
|
+
entries: () => [...instances.entries()],
|
|
27
|
+
resolve: (requested) => resolve(options, instances, requested)
|
|
28
|
+
};
|
|
29
|
+
const uninstall = options.install(router);
|
|
30
|
+
state = { instances, router, uninstall };
|
|
31
|
+
byFamily.set(options.family, state);
|
|
32
|
+
}
|
|
33
|
+
const current = state;
|
|
34
|
+
current.instances.set(id, instance);
|
|
35
|
+
let left = false;
|
|
36
|
+
const leave = () => {
|
|
37
|
+
if (left) return;
|
|
38
|
+
left = true;
|
|
39
|
+
if (current.instances.get(id) !== instance) return;
|
|
40
|
+
current.instances.delete(id);
|
|
41
|
+
if (current.instances.size === 0 && byFamily.get(options.family) === current) {
|
|
42
|
+
byFamily.delete(options.family);
|
|
43
|
+
current.uninstall();
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
return { router: current.router, leave };
|
|
47
|
+
}
|
|
48
|
+
function resolve(options, instances, requested) {
|
|
49
|
+
const ids = [...instances.keys()];
|
|
50
|
+
const { family, argument, listTool } = options;
|
|
51
|
+
if (requested === void 0 || requested === null || requested === "") {
|
|
52
|
+
if (ids.length === 1) return { ok: true, id: ids[0], instance: instances.get(ids[0]) };
|
|
53
|
+
if (ids.length === 0) return { ok: false, error: `No ${family} is mounted.` };
|
|
54
|
+
return {
|
|
55
|
+
ok: false,
|
|
56
|
+
error: `${ids.length} ${family}s are mounted, so pass "${argument}" with one of: ${quoteAll(ids)}. ${listTool} describes them.`
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
if (typeof requested !== "string") {
|
|
60
|
+
return { ok: false, error: `"${argument}" must be a ${family} id string. Mounted: ${quoteAll(ids)}.` };
|
|
61
|
+
}
|
|
62
|
+
if (!instances.has(requested)) {
|
|
63
|
+
return {
|
|
64
|
+
ok: false,
|
|
65
|
+
error: `No ${family} "${requested}" is mounted. ` + (ids.length ? `Mounted: ${quoteAll(ids)}. ` : "") + `${listTool} lists what is open.`
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
return { ok: true, id: requested, instance: instances.get(requested) };
|
|
69
|
+
}
|
|
70
|
+
|
|
7
71
|
// src/bridges/forms.ts
|
|
8
72
|
var DEFAULT_AGENT = { id: "agent", name: "Agent", color: "#a855f7" };
|
|
73
|
+
var FORM_TOOLS_OWNER = "form-bridge";
|
|
74
|
+
var FORM_ARG = {
|
|
75
|
+
type: "string",
|
|
76
|
+
description: "Id of the form to act on (see form_list). Optional while exactly one form is mounted; required when several are."
|
|
77
|
+
};
|
|
78
|
+
var FORM_TOOL_DEFS = [
|
|
79
|
+
{
|
|
80
|
+
name: "form_describe",
|
|
81
|
+
description: "Describe a form \u2014 fields, types, options, required flags. Call this first to know what's fillable.",
|
|
82
|
+
properties: {},
|
|
83
|
+
required: []
|
|
84
|
+
},
|
|
85
|
+
{ name: "form_get_value", description: "Read a single field's current value.", properties: { field: { type: "string" } }, required: ["field"] },
|
|
86
|
+
{ name: "form_get_values", description: "Read every field's current value as a JSON object.", properties: {}, required: [] },
|
|
87
|
+
{
|
|
88
|
+
name: "form_set_value",
|
|
89
|
+
description: "Set one field's value. The host's controlled state updates and the human sees the field change.",
|
|
90
|
+
properties: {
|
|
91
|
+
field: { type: "string" },
|
|
92
|
+
value: { description: "Value to set. Type depends on the field's `type`." }
|
|
93
|
+
},
|
|
94
|
+
required: ["field", "value"]
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
name: "form_set_values",
|
|
98
|
+
description: "Set multiple fields atomically. Pass a `values` object keyed by field name.",
|
|
99
|
+
properties: { values: { type: "object" } },
|
|
100
|
+
required: ["values"]
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
name: "form_focus",
|
|
104
|
+
description: "Move browser focus to a field (host-implemented). Useful before streaming text into it.",
|
|
105
|
+
properties: { field: { type: "string" } },
|
|
106
|
+
required: ["field"]
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
name: "form_submit",
|
|
110
|
+
description: "Submit the form. Host returns ok + values (or an error). In pendingMode this is staged for the human to confirm first.",
|
|
111
|
+
properties: {},
|
|
112
|
+
required: []
|
|
113
|
+
}
|
|
114
|
+
];
|
|
115
|
+
function installFormTools(host, router) {
|
|
116
|
+
const disposers = [];
|
|
117
|
+
try {
|
|
118
|
+
for (const def of FORM_TOOL_DEFS) {
|
|
119
|
+
disposers.push(
|
|
120
|
+
host.registerTool(
|
|
121
|
+
{
|
|
122
|
+
name: def.name,
|
|
123
|
+
description: def.description,
|
|
124
|
+
inputSchema: {
|
|
125
|
+
type: "object",
|
|
126
|
+
properties: { form: FORM_ARG, ...def.properties },
|
|
127
|
+
required: def.required,
|
|
128
|
+
additionalProperties: false
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
async (args) => {
|
|
132
|
+
const target = router.resolve(args?.form);
|
|
133
|
+
if (!target.ok) return errorResult(target.error);
|
|
134
|
+
return target.instance.handlers[def.name](args);
|
|
135
|
+
},
|
|
136
|
+
{ owner: FORM_TOOLS_OWNER }
|
|
137
|
+
)
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
disposers.push(
|
|
141
|
+
host.registerTool(
|
|
142
|
+
{
|
|
143
|
+
name: "form_list",
|
|
144
|
+
description: "List the forms mounted on this host \u2014 id, title, screen and fields. Pass an id as `form` to the other form tools when more than one is mounted.",
|
|
145
|
+
inputSchema: { type: "object", properties: {}, additionalProperties: false }
|
|
146
|
+
},
|
|
147
|
+
() => {
|
|
148
|
+
try {
|
|
149
|
+
const forms = router.entries().map(([id, { adapter }]) => ({
|
|
150
|
+
id,
|
|
151
|
+
title: adapter.title,
|
|
152
|
+
screenId: adapter.screenId,
|
|
153
|
+
fields: adapter.getFields()
|
|
154
|
+
}));
|
|
155
|
+
const text = forms.map((f) => `${f.id}${f.title ? ` \u2014 ${f.title}` : ""}: ${f.fields.map((x) => x.name).join(", ") || "(no fields)"}`).join("\n");
|
|
156
|
+
return textResult(text || "(no forms)", { forms });
|
|
157
|
+
} catch (e) {
|
|
158
|
+
return errorResult(hostErrorMessage(e, "form_list failed."));
|
|
159
|
+
}
|
|
160
|
+
},
|
|
161
|
+
{ owner: FORM_TOOLS_OWNER }
|
|
162
|
+
)
|
|
163
|
+
);
|
|
164
|
+
} catch (e) {
|
|
165
|
+
for (const d of disposers) d();
|
|
166
|
+
throw e;
|
|
167
|
+
}
|
|
168
|
+
return () => {
|
|
169
|
+
for (const d of disposers) d();
|
|
170
|
+
};
|
|
171
|
+
}
|
|
9
172
|
function registerFormBridge(host, options) {
|
|
10
173
|
const { adapter } = options;
|
|
11
174
|
const agent = { ...DEFAULT_AGENT, ...options.agent ?? {} };
|
|
12
175
|
const pendingMode = options.pendingMode ?? true;
|
|
13
|
-
const
|
|
14
|
-
|
|
176
|
+
const formId = adapter?.id == null ? "" : String(adapter.id);
|
|
177
|
+
if (formId.trim() === "") {
|
|
178
|
+
throw new Error(
|
|
179
|
+
"registerFormBridge: adapter.id must be a non-empty string. It is how agents pick this form (the `form` argument) when several forms share a host."
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
const bridgeId = `form:${formId}`;
|
|
15
183
|
ensureUndoToolsRegistered(host, { defaultAgentId: agent.id });
|
|
16
|
-
const
|
|
184
|
+
const handlers = {};
|
|
17
185
|
let disposed = false;
|
|
18
186
|
const onDispose = /* @__PURE__ */ new Set();
|
|
19
187
|
const CLOSED = /* @__PURE__ */ Symbol("form-closed");
|
|
@@ -23,13 +191,13 @@ function registerFormBridge(host, options) {
|
|
|
23
191
|
});
|
|
24
192
|
return Promise.resolve(CLOSED);
|
|
25
193
|
}
|
|
26
|
-
return new Promise((
|
|
27
|
-
const close = () =>
|
|
194
|
+
return new Promise((resolve2, reject) => {
|
|
195
|
+
const close = () => resolve2(CLOSED);
|
|
28
196
|
onDispose.add(close);
|
|
29
197
|
p.then(
|
|
30
198
|
(v) => {
|
|
31
199
|
onDispose.delete(close);
|
|
32
|
-
|
|
200
|
+
resolve2(v);
|
|
33
201
|
},
|
|
34
202
|
(e) => {
|
|
35
203
|
onDispose.delete(close);
|
|
@@ -47,7 +215,7 @@ function registerFormBridge(host, options) {
|
|
|
47
215
|
elementId: typeof args?.field === "string" ? `${formId}:${args.field}` : formId,
|
|
48
216
|
label: typeof args?.field === "string" ? `${adapter.title ?? formId} \u2192 ${args.field}` : adapter.title ?? formId
|
|
49
217
|
});
|
|
50
|
-
const reg = (name,
|
|
218
|
+
const reg = (name, handler, isMutation) => {
|
|
51
219
|
const wrapped = async (args) => {
|
|
52
220
|
try {
|
|
53
221
|
return await handler(args);
|
|
@@ -62,23 +230,10 @@ function registerFormBridge(host, options) {
|
|
|
62
230
|
screenId: adapter.screenId,
|
|
63
231
|
resolveTarget: ({ args }) => target(args)
|
|
64
232
|
}) : wrapped;
|
|
65
|
-
|
|
66
|
-
host.registerTool(
|
|
67
|
-
{
|
|
68
|
-
name,
|
|
69
|
-
description,
|
|
70
|
-
inputSchema: { type: "object", properties, required, additionalProperties: false }
|
|
71
|
-
},
|
|
72
|
-
final,
|
|
73
|
-
{ owner: bridgeId }
|
|
74
|
-
)
|
|
75
|
-
);
|
|
233
|
+
handlers[name] = final;
|
|
76
234
|
};
|
|
77
235
|
reg(
|
|
78
236
|
"form_describe",
|
|
79
|
-
`Describe the form "${formId}" \u2014 fields, types, options, required flags. Call this first to know what's fillable.`,
|
|
80
|
-
{},
|
|
81
|
-
[],
|
|
82
237
|
() => {
|
|
83
238
|
const fields = adapter.getFields();
|
|
84
239
|
const text = fields.map((f) => `${f.name}${f.required ? "*" : ""} (${f.type})${f.label ? ` \u2014 ${f.label}` : ""}`).join("\n");
|
|
@@ -88,9 +243,6 @@ function registerFormBridge(host, options) {
|
|
|
88
243
|
);
|
|
89
244
|
reg(
|
|
90
245
|
"form_get_value",
|
|
91
|
-
"Read a single field's current value.",
|
|
92
|
-
{ field: { type: "string" } },
|
|
93
|
-
["field"],
|
|
94
246
|
(args) => {
|
|
95
247
|
const name = String(args.field ?? "");
|
|
96
248
|
if (!adapter.getFields().find((f) => f.name === name)) {
|
|
@@ -103,9 +255,6 @@ function registerFormBridge(host, options) {
|
|
|
103
255
|
);
|
|
104
256
|
reg(
|
|
105
257
|
"form_get_values",
|
|
106
|
-
"Read every field's current value as a JSON object.",
|
|
107
|
-
{},
|
|
108
|
-
[],
|
|
109
258
|
() => {
|
|
110
259
|
const values = adapter.getValues();
|
|
111
260
|
return textResult(JSON.stringify(values, null, 2), values);
|
|
@@ -114,12 +263,6 @@ function registerFormBridge(host, options) {
|
|
|
114
263
|
);
|
|
115
264
|
reg(
|
|
116
265
|
"form_set_value",
|
|
117
|
-
"Set one field's value. The host's controlled state updates and the human sees the field change.",
|
|
118
|
-
{
|
|
119
|
-
field: { type: "string" },
|
|
120
|
-
value: { description: "Value to set. Type depends on the field's `type`." }
|
|
121
|
-
},
|
|
122
|
-
["field", "value"],
|
|
123
266
|
async (args) => {
|
|
124
267
|
const name = String(args.field ?? "");
|
|
125
268
|
const fieldDef = adapter.getFields().find((f) => f.name === name);
|
|
@@ -135,9 +278,6 @@ function registerFormBridge(host, options) {
|
|
|
135
278
|
);
|
|
136
279
|
reg(
|
|
137
280
|
"form_set_values",
|
|
138
|
-
"Set multiple fields atomically. Pass a `values` object keyed by field name.",
|
|
139
|
-
{ values: { type: "object" } },
|
|
140
|
-
["values"],
|
|
141
281
|
async (args) => {
|
|
142
282
|
const values = args.values && typeof args.values === "object" ? args.values : {};
|
|
143
283
|
const fields = adapter.getFields();
|
|
@@ -191,9 +331,6 @@ function registerFormBridge(host, options) {
|
|
|
191
331
|
);
|
|
192
332
|
reg(
|
|
193
333
|
"form_focus",
|
|
194
|
-
"Move browser focus to a field (host-implemented). Useful before streaming text into it.",
|
|
195
|
-
{ field: { type: "string" } },
|
|
196
|
-
["field"],
|
|
197
334
|
(args) => {
|
|
198
335
|
const name = String(args.field ?? "");
|
|
199
336
|
if (!adapter.focus) return errorResult("Host did not provide a focus implementation.");
|
|
@@ -207,9 +344,6 @@ function registerFormBridge(host, options) {
|
|
|
207
344
|
);
|
|
208
345
|
reg(
|
|
209
346
|
"form_submit",
|
|
210
|
-
"Submit the form. Host returns ok + values (or an error). In pendingMode this is staged for the human to confirm first.",
|
|
211
|
-
{},
|
|
212
|
-
[],
|
|
213
347
|
async () => {
|
|
214
348
|
if (!adapter.submit) return errorResult("Host did not provide a submit implementation.");
|
|
215
349
|
if (pendingMode && adapter.confirm) {
|
|
@@ -233,12 +367,19 @@ function registerFormBridge(host, options) {
|
|
|
233
367
|
},
|
|
234
368
|
true
|
|
235
369
|
);
|
|
370
|
+
const membership = joinInstanceRouter(
|
|
371
|
+
host,
|
|
372
|
+
{ family: "form", argument: "form", listTool: "form_list", install: (router) => installFormTools(host, router) },
|
|
373
|
+
formId,
|
|
374
|
+
{ adapter, handlers }
|
|
375
|
+
);
|
|
236
376
|
return {
|
|
237
377
|
id: bridgeId,
|
|
238
378
|
title: adapter.title ?? formId,
|
|
239
379
|
dispose: () => {
|
|
380
|
+
if (disposed) return;
|
|
240
381
|
disposed = true;
|
|
241
|
-
|
|
382
|
+
membership.leave();
|
|
242
383
|
for (const close of [...onDispose]) close();
|
|
243
384
|
onDispose.clear();
|
|
244
385
|
}
|
|
@@ -246,5 +387,5 @@ function registerFormBridge(host, options) {
|
|
|
246
387
|
}
|
|
247
388
|
|
|
248
389
|
export { registerFormBridge };
|
|
249
|
-
//# sourceMappingURL=chunk-
|
|
250
|
-
//# sourceMappingURL=chunk-
|
|
390
|
+
//# sourceMappingURL=chunk-AET2R7DJ.js.map
|
|
391
|
+
//# sourceMappingURL=chunk-AET2R7DJ.js.map
|