theokit 0.43.2 → 0.43.4
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/dist/actions-virtual-module-N7GKUS3D.js +0 -0
- package/dist/add-W6FNTAFS.js +0 -0
- package/dist/agent-U35G3WNE.js +0 -0
- package/dist/agents-typed-client-NJ73Z6XY.js +0 -0
- package/dist/app-typed-client-6YZRDAPY.js +0 -0
- package/dist/aws-lambda-7GSCNLPY.js +0 -0
- package/dist/body-parser-web-FV5HWCY3.js +0 -0
- package/dist/broadcast-LUMOJIJT.js +0 -0
- package/dist/build-BYLQZJT2.js +0 -0
- package/dist/build-request-body-preview-QMWD2IXK.js +0 -0
- package/dist/bun-KP2KES6S.js +0 -0
- package/dist/check-PD2FTKMM.js +0 -0
- package/dist/chunk-2CVV6CNN.js +0 -0
- package/dist/chunk-45C3WUQ7.js +0 -0
- package/dist/{chunk-UI6FWWOF.js → chunk-5AVPWHUE.js} +3 -1
- package/dist/{chunk-UI6FWWOF.js.map → chunk-5AVPWHUE.js.map} +1 -1
- package/dist/chunk-5ODOE6EF.js +0 -0
- package/dist/chunk-AFVTEAES.js +0 -0
- package/dist/chunk-FEDSPOY6.js +0 -0
- package/dist/chunk-FOZIR3TG.js +0 -0
- package/dist/chunk-FRUEDJFX.js +0 -0
- package/dist/chunk-GDN3PXFH.js +0 -0
- package/dist/chunk-GEQJAFSV.js +0 -0
- package/dist/chunk-GSO77FSM.js +0 -0
- package/dist/chunk-HGZL5EOI.js +0 -0
- package/dist/chunk-HNBWZKIQ.js +0 -0
- package/dist/chunk-IEES3CHD.js +0 -0
- package/dist/chunk-IPC5UREF.js +0 -0
- package/dist/chunk-JAIKGP3Q.js +0 -0
- package/dist/{chunk-ALMCG4EL.js → chunk-KPRJS3II.js} +5 -1
- package/dist/chunk-KPRJS3II.js.map +1 -0
- package/dist/chunk-L4GCEMNW.js +0 -0
- package/dist/chunk-M6NMS5LR.js +0 -0
- package/dist/chunk-NQRNDPZJ.js +0 -0
- package/dist/chunk-P37RZRFV.js +0 -0
- package/dist/chunk-SPJNVWOK.js +0 -0
- package/dist/chunk-UNLG36Z5.js +0 -0
- package/dist/chunk-UO7WS32S.js +0 -0
- package/dist/client/core.js +1 -1
- package/dist/client/index.js +1 -1
- package/dist/cloudflare-C6E5SPAE.js +0 -0
- package/dist/configure-agent-registry-ZOBVU3MV.js +0 -0
- package/dist/controller-swc-transform-XV3ETSE3.js +0 -0
- package/dist/db-3UNAMSFK.js +0 -0
- package/dist/deno-deploy-RFZN56X4.js +0 -0
- package/dist/dev-PS2NGT6I.js +0 -0
- package/dist/dev-emit-XVTJXZGA.js +0 -0
- package/dist/dispatcher-EJHL6JMJ.js +0 -0
- package/dist/docker-LZZB4D5E.js +0 -0
- package/dist/generate-5X4LG77M.js +0 -0
- package/dist/index.d.ts +4 -4
- package/dist/info-2OOXFTJX.js +0 -0
- package/dist/internal-api-FBFV265J.js +0 -0
- package/dist/load-config-3EAQTXPH.js +0 -0
- package/dist/mcp-ML4QK5ON.js +0 -0
- package/dist/netlify-PMLHVPN4.js +0 -0
- package/dist/node-BPJ3Z4DT.js +0 -0
- package/dist/openapi-2HVGJMSN.js +0 -0
- package/dist/registry-FKPH7XEL.js +0 -0
- package/dist/router-TLEAOFID.js +0 -0
- package/dist/routes-XNS3UM3B.js +0 -0
- package/dist/scan-7MJC6PYU.js +0 -0
- package/dist/schema-LYOLNHZ7.js +0 -0
- package/dist/server/agent/index.d.ts +5 -0
- package/dist/server/agent/index.js +1 -1
- package/dist/server/index.js +1 -1
- package/dist/server-error-to-envelope-UUXDSLAZ.js +0 -0
- package/dist/server-routes-hmr-VZXOP5YT.js +0 -0
- package/dist/services-json-Y3XODNB5.js +0 -0
- package/dist/services-typed-client-32KMTTXR.js +0 -0
- package/dist/start-YLR2SFHU.js +0 -0
- package/dist/static-MB2CD5Y3.js +0 -0
- package/dist/storage-manager-ZSQFLEYT.js +0 -0
- package/dist/theo-cloud-DQRP6S6M.js +0 -0
- package/dist/upgrade-readiness-ACGW44JC.js +0 -0
- package/dist/vercel-J6G7ZHYQ.js +0 -0
- package/dist/vite-plugin-6VVU6PPC.js +0 -0
- package/package.json +10 -10
- package/LICENSE +0 -201
- package/dist/chunk-ALMCG4EL.js.map +0 -1
|
File without changes
|
package/dist/add-W6FNTAFS.js
CHANGED
|
File without changes
|
package/dist/agent-U35G3WNE.js
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/dist/build-BYLQZJT2.js
CHANGED
|
File without changes
|
|
File without changes
|
package/dist/bun-KP2KES6S.js
CHANGED
|
File without changes
|
package/dist/check-PD2FTKMM.js
CHANGED
|
File without changes
|
package/dist/chunk-2CVV6CNN.js
CHANGED
|
File without changes
|
package/dist/chunk-45C3WUQ7.js
CHANGED
|
File without changes
|
|
@@ -275,6 +275,8 @@ function streamAgentTurnInProcess(mod, apiKey, input, deps = { stream: streamAge
|
|
|
275
275
|
}
|
|
276
276
|
yield* deps.stream(compiled, apiKey, {
|
|
277
277
|
message: input.message,
|
|
278
|
+
// M35 — pass images through so the SDK send uses the structured `{ text, images }` form.
|
|
279
|
+
...input.images !== void 0 ? { images: input.images } : {},
|
|
278
280
|
sessionId,
|
|
279
281
|
hitl,
|
|
280
282
|
signal: input.signal
|
|
@@ -323,4 +325,4 @@ export {
|
|
|
323
325
|
handleMcpStdioLine,
|
|
324
326
|
serveMcpStdio
|
|
325
327
|
};
|
|
326
|
-
//# sourceMappingURL=chunk-
|
|
328
|
+
//# sourceMappingURL=chunk-5AVPWHUE.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/server/agent/workflow-tool.ts","../src/server/agent/acp-tool.ts","../src/server/agent/vendor-agent-tool.ts","../src/server/agent/code-mode.ts","../src/server/agent/channel-webhook.ts","../src/server/agent/stream-agent-turn-in-process.ts","../src/server/agent/mcp-stdio.ts"],"sourcesContent":["/**\n * M26 (ADR-0041) — `createWorkflowTool`: wrap an SDK `Workflow` as a `CustomTool`.\n *\n * THIN adapter. `packages/workflows/` stays G13-forbidden — the workflow ENGINE is the SDK's\n * (`Workflow.create(...).run(input)`). This exposes an already-built `Workflow` to an agent as one\n * callable tool: it validates the tool input, delegates to `workflow.run(input)`, and shapes the\n * result for the model. It calls no LLM, dispatches no tool, and runs no orchestration of its own —\n * the SDK owns all of that (sdk-runtime.md / G2).\n */\nimport { z } from 'zod'\n\nimport { defineAgentTool, type CustomTool } from '../define/define-agent-tool.js'\n\n/**\n * Structural stand-in for the SDK `Workflow` (the adapter never imports the SDK type — keeps the\n * SDK an optional peer). Any object with a `run(input)` resolving to `{ status, output }` matches.\n */\nexport interface WorkflowLike {\n run(input: unknown): Promise<{ status: string; output: unknown; runId?: string }>\n}\n\n/** Config for {@link createWorkflowTool}. `inputSchema` defaults to an open object. */\nexport interface WorkflowToolConfig {\n /** Tool name surfaced to the LLM. */\n name: string\n /** Tool description surfaced to the LLM. */\n description: string\n /** Zod schema for the workflow input (defaults to `z.object({}).passthrough()`). */\n inputSchema?: z.ZodType\n}\n\n/** Statuses `Workflow.run` reports as a non-success terminal state. */\nconst FAILURE_STATUSES = new Set(['failed', 'error', 'cancelled', 'canceled'])\n\n/**\n * Wrap an SDK `Workflow` as a {@link CustomTool}. Fails fast if `workflow` does not expose a\n * `run()` method (the SDK Workflow contract), so a mis-wired call is caught at definition time, not\n * at the first invocation (error-handling.md).\n */\nexport function createWorkflowTool(workflow: WorkflowLike, config: WorkflowToolConfig): CustomTool {\n const runFn = (workflow as { run?: unknown } | null | undefined)?.run\n if (typeof runFn !== 'function') {\n throw new Error(\n 'createWorkflowTool: the SDK does not expose a Workflow (expected an object with a run() method). ' +\n 'Pass a `Workflow.create(...).…build()` instance from @theokit/sdk.',\n )\n }\n // Open object by default so arbitrary workflow inputs pass through un-stripped.\n const inputSchema = config.inputSchema ?? z.looseObject({})\n\n return defineAgentTool({\n name: config.name,\n description: config.description,\n inputSchema: inputSchema as z.ZodObject<z.ZodRawShape>,\n handler: async (input: unknown): Promise<string> => {\n const run = await workflow.run(input)\n if (FAILURE_STATUSES.has(run.status)) {\n throw new Error(\n `createWorkflowTool(${JSON.stringify(config.name)}): workflow run ${\n run.runId ? `'${run.runId}' ` : ''\n }failed with status '${run.status}'.`,\n )\n }\n return typeof run.output === 'string' ? run.output : JSON.stringify(run.output)\n },\n })\n}\n","/**\n * M17 (theokit-ai-first) — createACPTool: wrap a coding agent (Claude Code, Amp, Codex) as a tool.\n *\n * Spawns the agent as a subprocess (Node `child_process` — an adapter concern per G8), drives it\n * with the transport-agnostic {@link AcpClient} over newline-delimited JSON-RPC, and returns a\n * `CustomTool`. `onPermissionRequest` is REQUIRED — security by default (no default-allow for file/\n * shell operations). The transport is injectable for tests.\n */\nimport { spawn, type ChildProcessByStdio } from 'node:child_process'\nimport type { Readable, Writable } from 'node:stream'\n\nimport { AcpClient, type AcpTransport } from '@theokit/agents'\nimport { encodeAcpMessage } from '@theokit/agents'\nimport type { CustomTool } from '@theokit/sdk'\n\n/** Stdio transport backed by a spawned subprocess (the default for {@link createACPTool}). */\nexport class NodeAcpTransport implements AcpTransport {\n // stdin=pipe, stdout=pipe, stderr=inherit → the third stream is null.\n private readonly proc: ChildProcessByStdio<Writable, Readable, null>\n\n constructor(command: string, args: string[] = [], cwd?: string) {\n this.proc = spawn(command, args, { cwd, stdio: ['pipe', 'pipe', 'inherit'] })\n }\n\n send(line: string): void {\n this.proc.stdin.write(line)\n }\n\n subscribe(onData: (chunk: string) => void): void {\n this.proc.stdout.on('data', (buf: Buffer) => {\n onData(buf.toString('utf8'))\n })\n }\n\n close(): void {\n this.proc.kill()\n }\n}\n\nexport interface AcpToolConfig {\n /** Executable for the coding agent (e.g. `claude`, `amp`, `codex`). */\n command: string\n /** Command-line arguments. */\n args?: string[]\n /** Working directory for the spawned agent. */\n cwd?: string\n /** Tool name the model calls. */\n name: string\n /** Tool description surfaced to the model. */\n description: string\n /**\n * REQUIRED — decide file/shell permission requests from the coding agent. Security by default:\n * there is NO default-allow. Return `{ granted: boolean }` (may be async).\n */\n onPermissionRequest: (params: unknown) => { granted: boolean } | Promise<{ granted: boolean }>\n /** Injected transport factory (defaults to spawning via {@link NodeAcpTransport}) — for tests. */\n transportFactory?: (config: AcpToolConfig) => AcpTransport\n}\n\nfunction defaultTransport(config: AcpToolConfig): AcpTransport {\n return new NodeAcpTransport(config.command, config.args, config.cwd)\n}\n\n/** Wrap a coding agent as a `CustomTool`. Fails fast if `onPermissionRequest` is missing. */\nexport function createACPTool(config: AcpToolConfig): CustomTool {\n if (typeof config.onPermissionRequest !== 'function') {\n throw new Error('[theokit] createACPTool requires onPermissionRequest (security by default — no default-allow)')\n }\n const makeTransport = config.transportFactory ?? defaultTransport\n return {\n name: config.name,\n description: config.description,\n inputSchema: {\n type: 'object',\n properties: { message: { type: 'string', description: 'The task/prompt for the coding agent.' } },\n required: ['message'],\n },\n handler: async (input: Record<string, unknown>): Promise<string> => {\n const message = typeof input.message === 'string' ? input.message : ''\n const client = new AcpClient(makeTransport(config))\n client.onRequest('session/request_permission', (params) => config.onPermissionRequest(params))\n const result = (await client.request('session/prompt', { message })) as { text?: string }\n return result.text ?? ''\n },\n }\n}\n\n// `encodeAcpMessage` is re-exported for callers building custom transports/handshakes.\nexport { encodeAcpMessage }\n","/**\n * M28 (ADR-0041) — `createVendorAgentTool`: expose a third-party agent SDK (Claude Agent SDK,\n * OpenAI, Cursor) behind a uniform `CustomTool`, mirroring the M17 ACP pattern.\n *\n * The vendor RUNTIME stays theirs — TheoKit only wires. The vendor client is INJECTED (the real\n * vendor SDK client in prod, a fake in tests), so no vendor dependency enters core; vendor-specific\n * client packages belong under `@theokit/agent-*`, never here. This calls no LLM of its own and runs\n * no loop — it delegates each prompt to `client.query(...)` (sdk-runtime.md / G2). Resume is threaded\n * via the vendor's own session id.\n */\nimport type { CustomTool } from '../define/define-agent-tool.js'\n\n/**\n * Structural contract a vendor agent client must satisfy (the adapter never imports a vendor type).\n * `query` runs one turn; `resumeSessionId` continues a prior vendor session; the returned\n * `sessionId` identifies the session to resume next.\n */\nexport interface VendorAgentClient {\n query(\n prompt: string,\n opts?: { resumeSessionId?: string },\n ): Promise<{ text: string; sessionId?: string }>\n}\n\n/** Config for {@link createVendorAgentTool}. */\nexport interface VendorAgentToolConfig {\n /** Vendor label (e.g. `claude`, `openai`, `cursor`). Drives the default tool name. */\n vendor: string\n /** The injected vendor client (real SDK client in prod, a fake in tests). */\n client: VendorAgentClient\n /** Tool name the model calls (defaults to `<vendor>_agent`). */\n name?: string\n /** Tool description surfaced to the model (defaults to a one-line delegate hint). */\n description?: string\n /**\n * Side-channel callback invoked with the vendor session id after each turn — lets the app capture\n * it for a later resume WITHOUT leaking session bookkeeping into the model's view of the result.\n */\n onSession?: (sessionId: string) => void\n}\n\n/**\n * Wrap a vendor agent SDK as a {@link CustomTool}. Fails fast if `vendor` is empty or the client\n * does not expose `query()` (error-handling.md) — a mis-wired call is caught at definition time.\n */\nexport function createVendorAgentTool(config: VendorAgentToolConfig): CustomTool {\n if (!config.vendor || config.vendor.length === 0) {\n throw new Error('createVendorAgentTool: `vendor` is required (e.g. \"claude\", \"openai\").')\n }\n const queryFn = (config.client as { query?: unknown } | null | undefined)?.query\n if (typeof queryFn !== 'function') {\n throw new Error(\n `createVendorAgentTool(${JSON.stringify(config.vendor)}): the vendor client does not expose a query() method. ` +\n 'Pass the vendor SDK client (or a @theokit/agent-* wrapper).',\n )\n }\n\n const name = config.name ?? `${config.vendor}_agent`\n const description =\n config.description ?? `Delegate a task to the ${config.vendor} agent and return its answer.`\n\n return {\n name,\n description,\n inputSchema: {\n type: 'object',\n properties: {\n prompt: { type: 'string', description: 'The task/prompt for the vendor agent.' },\n resumeSessionId: {\n type: 'string',\n description: 'Optional vendor session id to resume a prior conversation.',\n },\n },\n required: ['prompt'],\n },\n handler: async (input: Record<string, unknown>): Promise<string> => {\n const prompt = typeof input.prompt === 'string' ? input.prompt : ''\n const resumeSessionId =\n typeof input.resumeSessionId === 'string' ? input.resumeSessionId : undefined\n const result = await config.client.query(\n prompt,\n resumeSessionId !== undefined ? { resumeSessionId } : undefined,\n )\n if (result.sessionId !== undefined && config.onSession) config.onSession(result.sessionId)\n return result.text\n },\n }\n}\n","/**\n * M29 (ADR-0041) — `createCodeMode`: expose a set of tools to agent-authored code run inside an\n * ISOLATION boundary, so the agent composes tools programmatically instead of one call at a time.\n *\n * Security posture (the whole point of this feature):\n * - The isolation boundary (`sandbox`) is **injected**, never hand-rolled here (Top-risk 1). The app\n * supplies a vetted sandbox — isolated-vm, QuickJS-WASM, or a locked-down worker. TheoKit core\n * ships no VM and adds no sandbox dependency (same posture as the injected deploy adapter / the\n * M17 transport). `node:vm` is NOT a security boundary and MUST NOT be used as the sandbox.\n * - TheoKit owns the **restricted API** (only the declared tools are reachable from the code — no\n * `fs`, `process`, `require`, or network unless a declared, permission-gated tool provides it) and\n * the **mandatory permission gate**: every tool call from the code passes `onPermissionRequest`\n * first, and there is NO default-allow (mirrors M17 `onPermissionRequest`).\n *\n * Threat model (summary): a malicious model could author code that (a) calls a dangerous tool, or\n * (b) tries to reach a host capability. (a) is stopped by the permission gate (deny → the API call\n * throws). (b) is stopped by the injected sandbox (the restricted API is the ONLY surface the code\n * sees). If the app injects a weak sandbox, (b) is on the app — hence the vetted-sandbox requirement.\n */\nimport type { CustomTool } from '../define/define-agent-tool.js'\n\n/** The restricted API handed to sandboxed code: declared tool names → permission-gated callables. */\nexport type CodeModeApi = Record<string, (args: unknown) => Promise<unknown>>\n\n/** The injected isolation boundary. The app supplies a vetted implementation. */\nexport interface Sandbox {\n /** Run `code` with access to ONLY `api` (the restricted tool surface). Resolve the code's result. */\n run(code: string, api: CodeModeApi): Promise<unknown>\n}\n\n/** A permission decision for one tool call from sandboxed code. */\nexport interface CodeModePermission {\n granted: boolean\n /** Optional reason surfaced to the model on denial. */\n reason?: string\n}\n\nexport interface CodeModeConfig {\n /** The tools reachable from the code (the restricted API). */\n tools: CustomTool[]\n /** The injected isolation boundary (vetted sandbox — NEVER node:vm). */\n sandbox: Sandbox\n /**\n * REQUIRED — decide each tool call the code attempts. Security by default: NO default-allow.\n * Return `{ granted }` (may be async). Mirrors M17 `onPermissionRequest`.\n */\n onPermissionRequest: (req: {\n tool: string\n args: unknown\n }) => CodeModePermission | Promise<CodeModePermission>\n /** Tool name the model calls (default `run_code`). */\n name?: string\n /** Tool description surfaced to the model. */\n description?: string\n}\n\n/** Thrown when the permission gate denies a tool call from sandboxed code. */\nexport class CodeModePermissionDeniedError extends Error {\n constructor(tool: string, reason?: string) {\n const suffix = reason ? `: ${reason}` : ''\n super(`code-mode: tool '${tool}' denied by permission gate${suffix}`)\n this.name = 'CodeModePermissionDeniedError'\n }\n}\n\n/** M40 (ADR-0049) — render a tool's JSON-Schema input as a readable arg shape, e.g. `{ limit: number, region?: string }`. */\nfunction describeToolInput(inputSchema: Record<string, unknown>): string {\n const props = (inputSchema.properties as Record<string, { type?: unknown }> | undefined) ?? {}\n const required = new Set((inputSchema.required as string[] | undefined) ?? [])\n const entries = Object.entries(props).map(([key, spec]) => {\n // Complex Zod types (union/intersection/enum) may emit no top-level `type` → 'unknown' (safe).\n const type = typeof spec.type === 'string' ? spec.type : 'unknown'\n return `${key}${required.has(key) ? '' : '?'}: ${type}`\n })\n return entries.length > 0 ? `{ ${entries.join(', ')} }` : '{}'\n}\n\n/**\n * M40 (ADR-0049) — generate the model-facing instructions from the SAME `tools` allow-list\n * `createCodeMode` captures (DRY — cannot drift from the api surface it describes). Teaches the model\n * that its code runs in a sandbox, the available `api.<name>(input)` calls (ONLY this allow-list —\n * least-privilege scoping), and the return contract. Add it to the agent's system prompt.\n */\nfunction generateCodeModeInstructions(tools: CustomTool[], toolName: string): string {\n const calls = tools\n .map((t) => `- \\`await api.${t.name}(${describeToolInput(t.inputSchema)})\\` — ${t.description}`)\n .join('\\n')\n return [\n `The \\`${toolName}\\` tool runs your code in a sandbox. Your code may call ONLY these functions (each bridges to a real, validated tool on the host):`,\n calls,\n 'Write an async function body that composes these calls and return exactly ONE structured result. Prefer `Promise.all` for independent calls; do arithmetic and aggregation in code, not in prose.',\n ].join('\\n\\n')\n}\n\n/**\n * Build a code-mode tool + its generated model instructions (M40 / ADR-0049). Fails fast if\n * `onPermissionRequest` or `sandbox` is missing (security by default). `tool` takes `{ code }`,\n * assembles the permission-gated restricted API from `tools`, runs the code in the injected sandbox,\n * and returns the code's result. `instructions` (add it to the agent's system prompt) teaches the\n * model the sandboxed-code contract + the available `api.<name>(input)` calls.\n */\nexport function createCodeMode(config: CodeModeConfig): { tool: CustomTool; instructions: string } {\n if (typeof config.onPermissionRequest !== 'function') {\n throw new Error(\n 'createCodeMode requires onPermissionRequest (security by default — no default-allow for any tool).',\n )\n }\n const sandboxRun = (config.sandbox as { run?: unknown } | null | undefined)?.run\n if (typeof sandboxRun !== 'function') {\n throw new Error(\n 'createCodeMode requires an injected `sandbox` with a run() method (a vetted isolation boundary — never node:vm).',\n )\n }\n // M40 — an empty allow-list is a config mistake: the restricted API (and the generated\n // instructions) would be empty, and the code could call nothing. Fail fast.\n if (config.tools.length === 0) {\n throw new Error(\n 'createCodeMode requires a non-empty tools[] — the restricted API would be empty.',\n )\n }\n\n // Assemble the restricted API: each declared tool becomes a permission-gated callable.\n const api: CodeModeApi = {}\n for (const tool of config.tools) {\n api[tool.name] = async (args: unknown): Promise<unknown> => {\n const decision = await config.onPermissionRequest({ tool: tool.name, args })\n if (!decision.granted) throw new CodeModePermissionDeniedError(tool.name, decision.reason)\n return tool.handler(args as Record<string, unknown>)\n }\n }\n\n const name = config.name ?? 'run_code'\n const tool: CustomTool = {\n name,\n description:\n config.description ??\n 'Run code that composes the available tools. Only the declared tools are callable.',\n inputSchema: {\n type: 'object',\n properties: { code: { type: 'string', description: 'The code to run in the sandbox.' } },\n required: ['code'],\n },\n handler: async (input: Record<string, unknown>): Promise<string> => {\n const code = typeof input.code === 'string' ? input.code : ''\n const result = await config.sandbox.run(code, api)\n return typeof result === 'string' ? result : JSON.stringify(result)\n },\n }\n return { tool, instructions: generateCodeModeInstructions(config.tools, name) }\n}\n","/**\n * M27 (ADR-0041) — channel webhook routes: `POST /api/agents/<name>/channels/<platform>/webhook`.\n *\n * Auto-generates a per-platform inbound webhook endpoint that VALIDATES the platform signature\n * (reusing the existing webhook `VerifyFn` providers — Slack/Telegram/Discord — never a hand-rolled\n * scheme) and hands the parsed payload to an injected `onMessage` seam. The seam is where an app\n * wires the SDK gateway package (`@theokit/gateway-*`) that translates the payload into an agent\n * turn — TheoKit provides the route + signature gate, NOT the gateway's parsing (G2 / it does not\n * reimplement the gateway).\n */\nimport type { VerifyFn } from '../webhook/webhook-types.js'\n\nconst CHANNEL_PATH = /^\\/api\\/agents\\/([^/]+)\\/channels\\/([^/]+)\\/webhook$/\n\n/** Parsed `{ agent, platform }` from a channel webhook path, or `null` when it doesn't match. */\nexport function parseChannelPath(urlPath: string): { agent: string; platform: string } | null {\n const match = CHANNEL_PATH.exec(urlPath)\n if (!match) return null\n return { agent: decodeURIComponent(match[1]), platform: decodeURIComponent(match[2]) }\n}\n\n/** True when `urlPath` targets a channel webhook (dev/prod routing branches on this). */\nexport function isChannelPath(urlPath: string): boolean {\n return CHANNEL_PATH.test(urlPath)\n}\n\n/** The inbound message handed to the app after signature validation passes. */\nexport interface ChannelMessage {\n agent: string\n platform: string\n /** The parsed JSON payload from the platform (the gateway translates this to an agent turn). */\n payload: unknown\n}\n\nexport interface ChannelWebhookConfig {\n /** Per-platform signature validators (e.g. `{ slack: slack({...}), telegram: telegram({...}) }`). */\n validators: Record<string, VerifyFn>\n /** Handoff seam — wire the SDK gateway / agent here. Invoked only after signature validation. */\n onMessage: (message: ChannelMessage) => void | Promise<void>\n}\n\nfunction jsonError(status: number, code: string, message: string): Response {\n return new Response(JSON.stringify({ error: { code, message } }), {\n status,\n headers: { 'content-type': 'application/json' },\n })\n}\n\n/**\n * Handle one channel webhook request. Returns:\n * 404 UNKNOWN_PLATFORM — no validator configured for `<platform>`\n * 400 BAD_REQUEST — path is not a channel webhook, or the body is not JSON\n * 401 INVALID_SIGNATURE — the platform signature check failed (negative case)\n * 200 { ok: true } — validated + handed to `onMessage`\n */\nexport async function handleChannelWebhook(\n request: Request,\n urlPath: string,\n config: ChannelWebhookConfig,\n): Promise<Response> {\n const parsed = parseChannelPath(urlPath)\n if (parsed === null) {\n return jsonError(\n 400,\n 'BAD_REQUEST',\n 'Path must be /api/agents/<name>/channels/<platform>/webhook.',\n )\n }\n if (!Object.hasOwn(config.validators, parsed.platform)) {\n return jsonError(\n 404,\n 'UNKNOWN_PLATFORM',\n `No validator configured for platform '${parsed.platform}'.`,\n )\n }\n const verify = config.validators[parsed.platform]\n\n // Validate the signature against a CLONE so the body stays readable for the payload parse.\n const verifyResult = await verify(request.clone())\n if (!verifyResult.ok) {\n return jsonError(\n 401,\n 'INVALID_SIGNATURE',\n `Signature validation failed: ${verifyResult.reason}`,\n )\n }\n\n let payload: unknown\n try {\n payload = await request.json()\n } catch {\n return jsonError(400, 'BAD_REQUEST', 'Request body must be JSON.')\n }\n\n await config.onMessage({ agent: parsed.agent, platform: parsed.platform, payload })\n return new Response(JSON.stringify({ ok: true }), {\n status: 200,\n headers: { 'content-type': 'application/json' },\n })\n}\n","/**\n * M35 (multi-surface) — the in-process agent-turn seam (Model A).\n *\n * The FRAMEWORK-owned sibling of the HTTP `mountAgent` and the stdout `runAgentInTerminal`: it runs a\n * compiled agent with the SAME `compileAgentModule` + SAME `streamAgentUIMessages` (G2 — reuses the\n * SDK runtime, reimplements nothing), but returns the raw `UIMessageChunk` generator so ANY consumer\n * drives it directly — the Ink TUI (M35), a Tauri window (M36), or a test — in a SINGLE process with\n * NO HTTP loopback, NO port, and NO CSRF (there is no network boundary to defend).\n *\n * The ONLY difference from the HTTP mount is HITL resolution: the mount pauses the run and resolves\n * the approval via a SECOND HTTP request to `/approve/:id` (the approval registry). In-process there\n * is no second request — the caller resolves the approval INLINE via `awaitApproval` (e.g. the Ink\n * TUI's y/n prompt). The gated-tool map is `compiled.hitl` verbatim, so the pause semantics are\n * byte-identical to the HTTP path; only the resolver differs. Parity with the mount is by\n * construction: both compile the module, resolve function-form skills, and call `streamAgentUIMessages`\n * with the same `{ message, sessionId, hitl }`.\n *\n * Consumers WILL still receive `tool-approval-request` chunks from the returned generator — they are\n * INFORMATIONAL (render them or ignore them). The authoritative human gate is `awaitApproval`, which\n * the SDK awaits BEFORE the gated tool runs; the chunk is not the gate.\n */\nimport {\n compileAgentModule,\n resolveEnabledSkills,\n streamAgentUIMessages,\n type HitlDecision,\n type HumanInTheLoopOptions,\n} from '@theokit/agents'\nimport type { UIMessageChunk } from 'ai'\n\n/** An inline approval request handed to the caller's `awaitApproval` (the Ink/Tauri prompt). */\nexport interface InProcessApprovalRequest {\n approvalId: string\n toolName: string\n opts: HumanInTheLoopOptions\n}\n\n/** Resolve one gated-tool approval inline (approve/deny, or a structured {@link HitlDecision}). */\nexport type InProcessAwaitApproval = (\n req: InProcessApprovalRequest,\n) => Promise<boolean | HitlDecision>\n\nexport interface StreamAgentTurnInProcessInput {\n message: string\n /** Resume key; a fresh id per run when omitted. */\n sessionId?: string\n /**\n * Inline HITL resolver — required IFF the agent has `@HumanInTheLoop`-gated tools. Omitting it for a\n * gated agent is a fail-fast error, never a silent bypass (Rule 8, the #99 lesson).\n */\n awaitApproval?: InProcessAwaitApproval\n /** Labels a fail-fast `AgentDefinitionError` (the file path). */\n source?: string\n /** Abort signal forwarded to the SDK stream (client disconnect / window close). */\n signal?: AbortSignal\n}\n\n/** Injectable stream fn (defaults to the real SDK bridge) — lets tests drive a deterministic stream. */\nexport interface StreamAgentTurnDeps {\n stream: typeof streamAgentUIMessages\n}\n\n/**\n * Thrown when a gated agent is run in-process without an `awaitApproval` resolver. Refusing loudly is\n * the correct posture: silently running a `@HumanInTheLoop`-gated tool with no human gate is exactly\n * the #99 class of bug. Typed so callers can catch it distinctly.\n */\nexport class InProcessApprovalRequiredError extends Error {\n constructor(toolNames: readonly string[]) {\n super(\n `Agent has HITL-gated tool(s) [${toolNames.join(', ')}] but no \\`awaitApproval\\` resolver was ` +\n `supplied to streamAgentTurnInProcess. In-process runs must resolve approvals inline — pass ` +\n `awaitApproval, or remove the gate. Refused (fail-closed).`,\n )\n this.name = 'InProcessApprovalRequiredError'\n }\n}\n\n/**\n * Run a compiled agent in-process and return its `UIMessageChunk` stream. `apiKey` is resolved by the\n * caller (same contract as the HTTP mount). Validation + compile happen SYNCHRONOUSLY (so a gated\n * agent without a resolver throws at call time, not lazily on first iteration); the returned value is\n * the SDK's `streamAgentUIMessages` generator.\n */\nexport function streamAgentTurnInProcess(\n mod: unknown,\n apiKey: string,\n input: StreamAgentTurnInProcessInput,\n deps: StreamAgentTurnDeps = { stream: streamAgentUIMessages },\n): AsyncGenerator<UIMessageChunk> {\n const compiled = compileAgentModule(mod, input.source)\n const gated = compiled.hitl\n\n // Fail-fast BEFORE building the stream: a gated agent with no inline resolver would silently bypass\n // the human gate (the #99 class of bug). Refuse loudly (Rule 8, fail-closed).\n if (gated && gated.size > 0 && !input.awaitApproval) {\n throw new InProcessApprovalRequiredError([...gated.keys()])\n }\n const resolve = input.awaitApproval\n\n // Mirror mount-agent's HITL wiring cast-free (structural inference); absent gate ⇒ the non-HITL\n // stream path (M2), unchanged. The SDK calls (approvalId, opts, toolName); route to the caller.\n const hitl =\n gated && gated.size > 0 && resolve\n ? {\n gated,\n awaitApproval: (approvalId: string, opts: HumanInTheLoopOptions, toolName: string) =>\n resolve({ approvalId, toolName, opts }),\n }\n : undefined\n\n const sessionId = input.sessionId ?? crypto.randomUUID()\n\n // Resolve function-form skills (`defineAgent({ skills: (ctx) => [...] })`) BEFORE streaming — exact\n // parity with mount-agent. Done INSIDE the returned generator so the synchronous fail-fast above is\n // preserved (the caller still gets a plain `AsyncGenerator`, no `await` at the call site). A static\n // skill list leaves `skillsResolver` undefined and this is a no-op.\n return (async function* () {\n if (compiled.skillsResolver) {\n const enabled = await resolveEnabledSkills(compiled.skillsResolver, compiled.runContext ?? {})\n if (enabled !== undefined) compiled.skills = { enabled, autoInject: true }\n }\n yield* deps.stream(compiled, apiKey, {\n message: input.message,\n sessionId,\n hitl,\n signal: input.signal,\n })\n })()\n}\n","/**\n * MCP stdio transport (M16 follow-up) — expose a TheoKit agent as an MCP server over stdin/stdout,\n * the sibling of the M16 HTTP route (`POST /api/agents/<name>/mcp`). A desktop MCP client (e.g.\n * Claude Desktop) spawns `theokit mcp <agent>` and speaks newline-delimited JSON-RPC over the pipe.\n *\n * This is a TRANSPORT over the framework's OWN {@link handleMcpJsonRpc} — it reuses the exact handler\n * the HTTP route uses; it calls no LLM, spawns no MCP client, and reimplements no runtime (G2 /\n * sdk-runtime.md). Distinct from the SDK's MCP CLIENT stdio (which spawns external MCP servers via\n * `mcpServers` command/args) — that stays SDK-side. Here TheoKit is the SERVER. The framework-side\n * placement of this server-exposure transport is recorded in ADR-0042 (refining ADR-0040's M16 note).\n */\nimport type { AppResource } from './mcp-app-resources.js'\nimport { handleMcpJsonRpc } from './mcp-handler.js'\n\n/**\n * Handle one newline-delimited JSON-RPC line. Returns the response line to write to stdout, or\n * `null` for a blank line (nothing to emit). A malformed JSON line yields a `-32700` (Parse error)\n * envelope — never throws, so the stdio loop never dies on bad input.\n */\nexport async function handleMcpStdioLine(\n line: string,\n mod: unknown,\n name: string,\n appResources: readonly AppResource[] = [],\n): Promise<string | null> {\n const trimmed = line.trim()\n if (trimmed.length === 0) return null\n let body: unknown\n try {\n body = JSON.parse(trimmed)\n } catch {\n return JSON.stringify({\n jsonrpc: '2.0',\n id: null,\n error: { code: -32700, message: 'Parse error' },\n })\n }\n const response = await handleMcpJsonRpc(mod, name, body, appResources)\n const payload: unknown = await response.json()\n return JSON.stringify(payload)\n}\n\n/** A minimal readable line source (an async iterable of lines) + a writable sink. */\nexport interface StdioStreams {\n /** Async iterable of newline-delimited input lines (e.g. `readline.createInterface({ input })`). */\n lines: AsyncIterable<string>\n /** Write a response line (the caller appends no newline). */\n write: (line: string) => void\n}\n\n/**\n * Drive the MCP stdio server loop: for each input line, dispatch via {@link handleMcpStdioLine} and\n * write the response line (with a trailing `\\n`). Returns when the input stream ends (EOF).\n */\nexport async function serveMcpStdio(\n mod: unknown,\n name: string,\n appResources: readonly AppResource[],\n streams: StdioStreams,\n): Promise<void> {\n for await (const line of streams.lines) {\n const out = await handleMcpStdioLine(line, mod, name, appResources)\n if (out !== null) streams.write(`${out}\\n`)\n }\n}\n"],"mappings":";;;;;;;;AASA,SAAS,SAAS;AAuBlB,IAAM,mBAAmB,oBAAI,IAAI,CAAC,UAAU,SAAS,aAAa,UAAU,CAAC;AAOtE,SAAS,mBAAmB,UAAwB,QAAwC;AACjG,QAAM,QAAS,UAAmD;AAClE,MAAI,OAAO,UAAU,YAAY;AAC/B,UAAM,IAAI;AAAA,MACR;AAAA,IAEF;AAAA,EACF;AAEA,QAAM,cAAc,OAAO,eAAe,EAAE,YAAY,CAAC,CAAC;AAE1D,SAAO,gBAAgB;AAAA,IACrB,MAAM,OAAO;AAAA,IACb,aAAa,OAAO;AAAA,IACpB;AAAA,IACA,SAAS,OAAO,UAAoC;AAClD,YAAM,MAAM,MAAM,SAAS,IAAI,KAAK;AACpC,UAAI,iBAAiB,IAAI,IAAI,MAAM,GAAG;AACpC,cAAM,IAAI;AAAA,UACR,sBAAsB,KAAK,UAAU,OAAO,IAAI,CAAC,mBAC/C,IAAI,QAAQ,IAAI,IAAI,KAAK,OAAO,EAClC,uBAAuB,IAAI,MAAM;AAAA,QACnC;AAAA,MACF;AACA,aAAO,OAAO,IAAI,WAAW,WAAW,IAAI,SAAS,KAAK,UAAU,IAAI,MAAM;AAAA,IAChF;AAAA,EACF,CAAC;AACH;;;AC1DA,SAAS,aAAuC;AAGhD,SAAS,iBAAoC;AAC7C,SAAS,wBAAwB;AAI1B,IAAM,mBAAN,MAA+C;AAAA;AAAA,EAEnC;AAAA,EAEjB,YAAY,SAAiB,OAAiB,CAAC,GAAG,KAAc;AAC9D,SAAK,OAAO,MAAM,SAAS,MAAM,EAAE,KAAK,OAAO,CAAC,QAAQ,QAAQ,SAAS,EAAE,CAAC;AAAA,EAC9E;AAAA,EAEA,KAAK,MAAoB;AACvB,SAAK,KAAK,MAAM,MAAM,IAAI;AAAA,EAC5B;AAAA,EAEA,UAAU,QAAuC;AAC/C,SAAK,KAAK,OAAO,GAAG,QAAQ,CAAC,QAAgB;AAC3C,aAAO,IAAI,SAAS,MAAM,CAAC;AAAA,IAC7B,CAAC;AAAA,EACH;AAAA,EAEA,QAAc;AACZ,SAAK,KAAK,KAAK;AAAA,EACjB;AACF;AAsBA,SAAS,iBAAiB,QAAqC;AAC7D,SAAO,IAAI,iBAAiB,OAAO,SAAS,OAAO,MAAM,OAAO,GAAG;AACrE;AAGO,SAAS,cAAc,QAAmC;AAC/D,MAAI,OAAO,OAAO,wBAAwB,YAAY;AACpD,UAAM,IAAI,MAAM,oGAA+F;AAAA,EACjH;AACA,QAAM,gBAAgB,OAAO,oBAAoB;AACjD,SAAO;AAAA,IACL,MAAM,OAAO;AAAA,IACb,aAAa,OAAO;AAAA,IACpB,aAAa;AAAA,MACX,MAAM;AAAA,MACN,YAAY,EAAE,SAAS,EAAE,MAAM,UAAU,aAAa,wCAAwC,EAAE;AAAA,MAChG,UAAU,CAAC,SAAS;AAAA,IACtB;AAAA,IACA,SAAS,OAAO,UAAoD;AAClE,YAAM,UAAU,OAAO,MAAM,YAAY,WAAW,MAAM,UAAU;AACpE,YAAM,SAAS,IAAI,UAAU,cAAc,MAAM,CAAC;AAClD,aAAO,UAAU,8BAA8B,CAAC,WAAW,OAAO,oBAAoB,MAAM,CAAC;AAC7F,YAAM,SAAU,MAAM,OAAO,QAAQ,kBAAkB,EAAE,QAAQ,CAAC;AAClE,aAAO,OAAO,QAAQ;AAAA,IACxB;AAAA,EACF;AACF;;;ACxCO,SAAS,sBAAsB,QAA2C;AAC/E,MAAI,CAAC,OAAO,UAAU,OAAO,OAAO,WAAW,GAAG;AAChD,UAAM,IAAI,MAAM,wEAAwE;AAAA,EAC1F;AACA,QAAM,UAAW,OAAO,QAAmD;AAC3E,MAAI,OAAO,YAAY,YAAY;AACjC,UAAM,IAAI;AAAA,MACR,yBAAyB,KAAK,UAAU,OAAO,MAAM,CAAC;AAAA,IAExD;AAAA,EACF;AAEA,QAAM,OAAO,OAAO,QAAQ,GAAG,OAAO,MAAM;AAC5C,QAAM,cACJ,OAAO,eAAe,0BAA0B,OAAO,MAAM;AAE/D,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,aAAa;AAAA,MACX,MAAM;AAAA,MACN,YAAY;AAAA,QACV,QAAQ,EAAE,MAAM,UAAU,aAAa,wCAAwC;AAAA,QAC/E,iBAAiB;AAAA,UACf,MAAM;AAAA,UACN,aAAa;AAAA,QACf;AAAA,MACF;AAAA,MACA,UAAU,CAAC,QAAQ;AAAA,IACrB;AAAA,IACA,SAAS,OAAO,UAAoD;AAClE,YAAM,SAAS,OAAO,MAAM,WAAW,WAAW,MAAM,SAAS;AACjE,YAAM,kBACJ,OAAO,MAAM,oBAAoB,WAAW,MAAM,kBAAkB;AACtE,YAAM,SAAS,MAAM,OAAO,OAAO;AAAA,QACjC;AAAA,QACA,oBAAoB,SAAY,EAAE,gBAAgB,IAAI;AAAA,MACxD;AACA,UAAI,OAAO,cAAc,UAAa,OAAO,UAAW,QAAO,UAAU,OAAO,SAAS;AACzF,aAAO,OAAO;AAAA,IAChB;AAAA,EACF;AACF;;;AC9BO,IAAM,gCAAN,cAA4C,MAAM;AAAA,EACvD,YAAY,MAAc,QAAiB;AACzC,UAAM,SAAS,SAAS,KAAK,MAAM,KAAK;AACxC,UAAM,oBAAoB,IAAI,8BAA8B,MAAM,EAAE;AACpE,SAAK,OAAO;AAAA,EACd;AACF;AAGA,SAAS,kBAAkB,aAA8C;AACvE,QAAM,QAAS,YAAY,cAAiE,CAAC;AAC7F,QAAM,WAAW,IAAI,IAAK,YAAY,YAAqC,CAAC,CAAC;AAC7E,QAAM,UAAU,OAAO,QAAQ,KAAK,EAAE,IAAI,CAAC,CAAC,KAAK,IAAI,MAAM;AAEzD,UAAM,OAAO,OAAO,KAAK,SAAS,WAAW,KAAK,OAAO;AACzD,WAAO,GAAG,GAAG,GAAG,SAAS,IAAI,GAAG,IAAI,KAAK,GAAG,KAAK,IAAI;AAAA,EACvD,CAAC;AACD,SAAO,QAAQ,SAAS,IAAI,KAAK,QAAQ,KAAK,IAAI,CAAC,OAAO;AAC5D;AAQA,SAAS,6BAA6B,OAAqB,UAA0B;AACnF,QAAM,QAAQ,MACX,IAAI,CAAC,MAAM,iBAAiB,EAAE,IAAI,IAAI,kBAAkB,EAAE,WAAW,CAAC,cAAS,EAAE,WAAW,EAAE,EAC9F,KAAK,IAAI;AACZ,SAAO;AAAA,IACL,SAAS,QAAQ;AAAA,IACjB;AAAA,IACA;AAAA,EACF,EAAE,KAAK,MAAM;AACf;AASO,SAAS,eAAe,QAAoE;AACjG,MAAI,OAAO,OAAO,wBAAwB,YAAY;AACpD,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,QAAM,aAAc,OAAO,SAAkD;AAC7E,MAAI,OAAO,eAAe,YAAY;AACpC,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAGA,MAAI,OAAO,MAAM,WAAW,GAAG;AAC7B,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAGA,QAAM,MAAmB,CAAC;AAC1B,aAAWA,SAAQ,OAAO,OAAO;AAC/B,QAAIA,MAAK,IAAI,IAAI,OAAO,SAAoC;AAC1D,YAAM,WAAW,MAAM,OAAO,oBAAoB,EAAE,MAAMA,MAAK,MAAM,KAAK,CAAC;AAC3E,UAAI,CAAC,SAAS,QAAS,OAAM,IAAI,8BAA8BA,MAAK,MAAM,SAAS,MAAM;AACzF,aAAOA,MAAK,QAAQ,IAA+B;AAAA,IACrD;AAAA,EACF;AAEA,QAAM,OAAO,OAAO,QAAQ;AAC5B,QAAM,OAAmB;AAAA,IACvB;AAAA,IACA,aACE,OAAO,eACP;AAAA,IACF,aAAa;AAAA,MACX,MAAM;AAAA,MACN,YAAY,EAAE,MAAM,EAAE,MAAM,UAAU,aAAa,kCAAkC,EAAE;AAAA,MACvF,UAAU,CAAC,MAAM;AAAA,IACnB;AAAA,IACA,SAAS,OAAO,UAAoD;AAClE,YAAM,OAAO,OAAO,MAAM,SAAS,WAAW,MAAM,OAAO;AAC3D,YAAM,SAAS,MAAM,OAAO,QAAQ,IAAI,MAAM,GAAG;AACjD,aAAO,OAAO,WAAW,WAAW,SAAS,KAAK,UAAU,MAAM;AAAA,IACpE;AAAA,EACF;AACA,SAAO,EAAE,MAAM,cAAc,6BAA6B,OAAO,OAAO,IAAI,EAAE;AAChF;;;ACzIA,IAAM,eAAe;AAGd,SAAS,iBAAiB,SAA6D;AAC5F,QAAM,QAAQ,aAAa,KAAK,OAAO;AACvC,MAAI,CAAC,MAAO,QAAO;AACnB,SAAO,EAAE,OAAO,mBAAmB,MAAM,CAAC,CAAC,GAAG,UAAU,mBAAmB,MAAM,CAAC,CAAC,EAAE;AACvF;AAGO,SAAS,cAAc,SAA0B;AACtD,SAAO,aAAa,KAAK,OAAO;AAClC;AAiBA,SAAS,UAAU,QAAgB,MAAc,SAA2B;AAC1E,SAAO,IAAI,SAAS,KAAK,UAAU,EAAE,OAAO,EAAE,MAAM,QAAQ,EAAE,CAAC,GAAG;AAAA,IAChE;AAAA,IACA,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,EAChD,CAAC;AACH;AASA,eAAsB,qBACpB,SACA,SACA,QACmB;AACnB,QAAM,SAAS,iBAAiB,OAAO;AACvC,MAAI,WAAW,MAAM;AACnB,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACA,MAAI,CAAC,OAAO,OAAO,OAAO,YAAY,OAAO,QAAQ,GAAG;AACtD,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA,yCAAyC,OAAO,QAAQ;AAAA,IAC1D;AAAA,EACF;AACA,QAAM,SAAS,OAAO,WAAW,OAAO,QAAQ;AAGhD,QAAM,eAAe,MAAM,OAAO,QAAQ,MAAM,CAAC;AACjD,MAAI,CAAC,aAAa,IAAI;AACpB,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA,gCAAgC,aAAa,MAAM;AAAA,IACrD;AAAA,EACF;AAEA,MAAI;AACJ,MAAI;AACF,cAAU,MAAM,QAAQ,KAAK;AAAA,EAC/B,QAAQ;AACN,WAAO,UAAU,KAAK,eAAe,4BAA4B;AAAA,EACnE;AAEA,QAAM,OAAO,UAAU,EAAE,OAAO,OAAO,OAAO,UAAU,OAAO,UAAU,QAAQ,CAAC;AAClF,SAAO,IAAI,SAAS,KAAK,UAAU,EAAE,IAAI,KAAK,CAAC,GAAG;AAAA,IAChD,QAAQ;AAAA,IACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,EAChD,CAAC;AACH;;;AC9EA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OAGK;AAwCA,IAAM,iCAAN,cAA6C,MAAM;AAAA,EACxD,YAAY,WAA8B;AACxC;AAAA,MACE,iCAAiC,UAAU,KAAK,IAAI,CAAC;AAAA,IAGvD;AACA,SAAK,OAAO;AAAA,EACd;AACF;AAQO,SAAS,yBACd,KACA,QACA,OACA,OAA4B,EAAE,QAAQ,sBAAsB,GAC5B;AAChC,QAAM,WAAW,mBAAmB,KAAK,MAAM,MAAM;AACrD,QAAM,QAAQ,SAAS;AAIvB,MAAI,SAAS,MAAM,OAAO,KAAK,CAAC,MAAM,eAAe;AACnD,UAAM,IAAI,+BAA+B,CAAC,GAAG,MAAM,KAAK,CAAC,CAAC;AAAA,EAC5D;AACA,QAAM,UAAU,MAAM;AAItB,QAAM,OACJ,SAAS,MAAM,OAAO,KAAK,UACvB;AAAA,IACE;AAAA,IACA,eAAe,CAAC,YAAoB,MAA6B,aAC/D,QAAQ,EAAE,YAAY,UAAU,KAAK,CAAC;AAAA,EAC1C,IACA;AAEN,QAAM,YAAY,MAAM,aAAa,OAAO,WAAW;AAMvD,UAAQ,mBAAmB;AACzB,QAAI,SAAS,gBAAgB;AAC3B,YAAM,UAAU,MAAM,qBAAqB,SAAS,gBAAgB,SAAS,cAAc,CAAC,CAAC;AAC7F,UAAI,YAAY,OAAW,UAAS,SAAS,EAAE,SAAS,YAAY,KAAK;AAAA,IAC3E;AACA,WAAO,KAAK,OAAO,UAAU,QAAQ;AAAA,MACnC,SAAS,MAAM;AAAA,MACf;AAAA,MACA;AAAA,MACA,QAAQ,MAAM;AAAA,IAChB,CAAC;AAAA,EACH,GAAG;AACL;;;AC9GA,eAAsB,mBACpB,MACA,KACA,MACA,eAAuC,CAAC,GAChB;AACxB,QAAM,UAAU,KAAK,KAAK;AAC1B,MAAI,QAAQ,WAAW,EAAG,QAAO;AACjC,MAAI;AACJ,MAAI;AACF,WAAO,KAAK,MAAM,OAAO;AAAA,EAC3B,QAAQ;AACN,WAAO,KAAK,UAAU;AAAA,MACpB,SAAS;AAAA,MACT,IAAI;AAAA,MACJ,OAAO,EAAE,MAAM,QAAQ,SAAS,cAAc;AAAA,IAChD,CAAC;AAAA,EACH;AACA,QAAM,WAAW,MAAM,iBAAiB,KAAK,MAAM,MAAM,YAAY;AACrE,QAAM,UAAmB,MAAM,SAAS,KAAK;AAC7C,SAAO,KAAK,UAAU,OAAO;AAC/B;AAcA,eAAsB,cACpB,KACA,MACA,cACA,SACe;AACf,mBAAiB,QAAQ,QAAQ,OAAO;AACtC,UAAM,MAAM,MAAM,mBAAmB,MAAM,KAAK,MAAM,YAAY;AAClE,QAAI,QAAQ,KAAM,SAAQ,MAAM,GAAG,GAAG;AAAA,CAAI;AAAA,EAC5C;AACF;","names":["tool"]}
|
|
1
|
+
{"version":3,"sources":["../src/server/agent/workflow-tool.ts","../src/server/agent/acp-tool.ts","../src/server/agent/vendor-agent-tool.ts","../src/server/agent/code-mode.ts","../src/server/agent/channel-webhook.ts","../src/server/agent/stream-agent-turn-in-process.ts","../src/server/agent/mcp-stdio.ts"],"sourcesContent":["/**\n * M26 (ADR-0041) — `createWorkflowTool`: wrap an SDK `Workflow` as a `CustomTool`.\n *\n * THIN adapter. `packages/workflows/` stays G13-forbidden — the workflow ENGINE is the SDK's\n * (`Workflow.create(...).run(input)`). This exposes an already-built `Workflow` to an agent as one\n * callable tool: it validates the tool input, delegates to `workflow.run(input)`, and shapes the\n * result for the model. It calls no LLM, dispatches no tool, and runs no orchestration of its own —\n * the SDK owns all of that (sdk-runtime.md / G2).\n */\nimport { z } from 'zod'\n\nimport { defineAgentTool, type CustomTool } from '../define/define-agent-tool.js'\n\n/**\n * Structural stand-in for the SDK `Workflow` (the adapter never imports the SDK type — keeps the\n * SDK an optional peer). Any object with a `run(input)` resolving to `{ status, output }` matches.\n */\nexport interface WorkflowLike {\n run(input: unknown): Promise<{ status: string; output: unknown; runId?: string }>\n}\n\n/** Config for {@link createWorkflowTool}. `inputSchema` defaults to an open object. */\nexport interface WorkflowToolConfig {\n /** Tool name surfaced to the LLM. */\n name: string\n /** Tool description surfaced to the LLM. */\n description: string\n /** Zod schema for the workflow input (defaults to `z.object({}).passthrough()`). */\n inputSchema?: z.ZodType\n}\n\n/** Statuses `Workflow.run` reports as a non-success terminal state. */\nconst FAILURE_STATUSES = new Set(['failed', 'error', 'cancelled', 'canceled'])\n\n/**\n * Wrap an SDK `Workflow` as a {@link CustomTool}. Fails fast if `workflow` does not expose a\n * `run()` method (the SDK Workflow contract), so a mis-wired call is caught at definition time, not\n * at the first invocation (error-handling.md).\n */\nexport function createWorkflowTool(workflow: WorkflowLike, config: WorkflowToolConfig): CustomTool {\n const runFn = (workflow as { run?: unknown } | null | undefined)?.run\n if (typeof runFn !== 'function') {\n throw new Error(\n 'createWorkflowTool: the SDK does not expose a Workflow (expected an object with a run() method). ' +\n 'Pass a `Workflow.create(...).…build()` instance from @theokit/sdk.',\n )\n }\n // Open object by default so arbitrary workflow inputs pass through un-stripped.\n const inputSchema = config.inputSchema ?? z.looseObject({})\n\n return defineAgentTool({\n name: config.name,\n description: config.description,\n inputSchema: inputSchema as z.ZodObject<z.ZodRawShape>,\n handler: async (input: unknown): Promise<string> => {\n const run = await workflow.run(input)\n if (FAILURE_STATUSES.has(run.status)) {\n throw new Error(\n `createWorkflowTool(${JSON.stringify(config.name)}): workflow run ${\n run.runId ? `'${run.runId}' ` : ''\n }failed with status '${run.status}'.`,\n )\n }\n return typeof run.output === 'string' ? run.output : JSON.stringify(run.output)\n },\n })\n}\n","/**\n * M17 (theokit-ai-first) — createACPTool: wrap a coding agent (Claude Code, Amp, Codex) as a tool.\n *\n * Spawns the agent as a subprocess (Node `child_process` — an adapter concern per G8), drives it\n * with the transport-agnostic {@link AcpClient} over newline-delimited JSON-RPC, and returns a\n * `CustomTool`. `onPermissionRequest` is REQUIRED — security by default (no default-allow for file/\n * shell operations). The transport is injectable for tests.\n */\nimport { spawn, type ChildProcessByStdio } from 'node:child_process'\nimport type { Readable, Writable } from 'node:stream'\n\nimport { AcpClient, type AcpTransport } from '@theokit/agents'\nimport { encodeAcpMessage } from '@theokit/agents'\nimport type { CustomTool } from '@theokit/sdk'\n\n/** Stdio transport backed by a spawned subprocess (the default for {@link createACPTool}). */\nexport class NodeAcpTransport implements AcpTransport {\n // stdin=pipe, stdout=pipe, stderr=inherit → the third stream is null.\n private readonly proc: ChildProcessByStdio<Writable, Readable, null>\n\n constructor(command: string, args: string[] = [], cwd?: string) {\n this.proc = spawn(command, args, { cwd, stdio: ['pipe', 'pipe', 'inherit'] })\n }\n\n send(line: string): void {\n this.proc.stdin.write(line)\n }\n\n subscribe(onData: (chunk: string) => void): void {\n this.proc.stdout.on('data', (buf: Buffer) => {\n onData(buf.toString('utf8'))\n })\n }\n\n close(): void {\n this.proc.kill()\n }\n}\n\nexport interface AcpToolConfig {\n /** Executable for the coding agent (e.g. `claude`, `amp`, `codex`). */\n command: string\n /** Command-line arguments. */\n args?: string[]\n /** Working directory for the spawned agent. */\n cwd?: string\n /** Tool name the model calls. */\n name: string\n /** Tool description surfaced to the model. */\n description: string\n /**\n * REQUIRED — decide file/shell permission requests from the coding agent. Security by default:\n * there is NO default-allow. Return `{ granted: boolean }` (may be async).\n */\n onPermissionRequest: (params: unknown) => { granted: boolean } | Promise<{ granted: boolean }>\n /** Injected transport factory (defaults to spawning via {@link NodeAcpTransport}) — for tests. */\n transportFactory?: (config: AcpToolConfig) => AcpTransport\n}\n\nfunction defaultTransport(config: AcpToolConfig): AcpTransport {\n return new NodeAcpTransport(config.command, config.args, config.cwd)\n}\n\n/** Wrap a coding agent as a `CustomTool`. Fails fast if `onPermissionRequest` is missing. */\nexport function createACPTool(config: AcpToolConfig): CustomTool {\n if (typeof config.onPermissionRequest !== 'function') {\n throw new Error('[theokit] createACPTool requires onPermissionRequest (security by default — no default-allow)')\n }\n const makeTransport = config.transportFactory ?? defaultTransport\n return {\n name: config.name,\n description: config.description,\n inputSchema: {\n type: 'object',\n properties: { message: { type: 'string', description: 'The task/prompt for the coding agent.' } },\n required: ['message'],\n },\n handler: async (input: Record<string, unknown>): Promise<string> => {\n const message = typeof input.message === 'string' ? input.message : ''\n const client = new AcpClient(makeTransport(config))\n client.onRequest('session/request_permission', (params) => config.onPermissionRequest(params))\n const result = (await client.request('session/prompt', { message })) as { text?: string }\n return result.text ?? ''\n },\n }\n}\n\n// `encodeAcpMessage` is re-exported for callers building custom transports/handshakes.\nexport { encodeAcpMessage }\n","/**\n * M28 (ADR-0041) — `createVendorAgentTool`: expose a third-party agent SDK (Claude Agent SDK,\n * OpenAI, Cursor) behind a uniform `CustomTool`, mirroring the M17 ACP pattern.\n *\n * The vendor RUNTIME stays theirs — TheoKit only wires. The vendor client is INJECTED (the real\n * vendor SDK client in prod, a fake in tests), so no vendor dependency enters core; vendor-specific\n * client packages belong under `@theokit/agent-*`, never here. This calls no LLM of its own and runs\n * no loop — it delegates each prompt to `client.query(...)` (sdk-runtime.md / G2). Resume is threaded\n * via the vendor's own session id.\n */\nimport type { CustomTool } from '../define/define-agent-tool.js'\n\n/**\n * Structural contract a vendor agent client must satisfy (the adapter never imports a vendor type).\n * `query` runs one turn; `resumeSessionId` continues a prior vendor session; the returned\n * `sessionId` identifies the session to resume next.\n */\nexport interface VendorAgentClient {\n query(\n prompt: string,\n opts?: { resumeSessionId?: string },\n ): Promise<{ text: string; sessionId?: string }>\n}\n\n/** Config for {@link createVendorAgentTool}. */\nexport interface VendorAgentToolConfig {\n /** Vendor label (e.g. `claude`, `openai`, `cursor`). Drives the default tool name. */\n vendor: string\n /** The injected vendor client (real SDK client in prod, a fake in tests). */\n client: VendorAgentClient\n /** Tool name the model calls (defaults to `<vendor>_agent`). */\n name?: string\n /** Tool description surfaced to the model (defaults to a one-line delegate hint). */\n description?: string\n /**\n * Side-channel callback invoked with the vendor session id after each turn — lets the app capture\n * it for a later resume WITHOUT leaking session bookkeeping into the model's view of the result.\n */\n onSession?: (sessionId: string) => void\n}\n\n/**\n * Wrap a vendor agent SDK as a {@link CustomTool}. Fails fast if `vendor` is empty or the client\n * does not expose `query()` (error-handling.md) — a mis-wired call is caught at definition time.\n */\nexport function createVendorAgentTool(config: VendorAgentToolConfig): CustomTool {\n if (!config.vendor || config.vendor.length === 0) {\n throw new Error('createVendorAgentTool: `vendor` is required (e.g. \"claude\", \"openai\").')\n }\n const queryFn = (config.client as { query?: unknown } | null | undefined)?.query\n if (typeof queryFn !== 'function') {\n throw new Error(\n `createVendorAgentTool(${JSON.stringify(config.vendor)}): the vendor client does not expose a query() method. ` +\n 'Pass the vendor SDK client (or a @theokit/agent-* wrapper).',\n )\n }\n\n const name = config.name ?? `${config.vendor}_agent`\n const description =\n config.description ?? `Delegate a task to the ${config.vendor} agent and return its answer.`\n\n return {\n name,\n description,\n inputSchema: {\n type: 'object',\n properties: {\n prompt: { type: 'string', description: 'The task/prompt for the vendor agent.' },\n resumeSessionId: {\n type: 'string',\n description: 'Optional vendor session id to resume a prior conversation.',\n },\n },\n required: ['prompt'],\n },\n handler: async (input: Record<string, unknown>): Promise<string> => {\n const prompt = typeof input.prompt === 'string' ? input.prompt : ''\n const resumeSessionId =\n typeof input.resumeSessionId === 'string' ? input.resumeSessionId : undefined\n const result = await config.client.query(\n prompt,\n resumeSessionId !== undefined ? { resumeSessionId } : undefined,\n )\n if (result.sessionId !== undefined && config.onSession) config.onSession(result.sessionId)\n return result.text\n },\n }\n}\n","/**\n * M29 (ADR-0041) — `createCodeMode`: expose a set of tools to agent-authored code run inside an\n * ISOLATION boundary, so the agent composes tools programmatically instead of one call at a time.\n *\n * Security posture (the whole point of this feature):\n * - The isolation boundary (`sandbox`) is **injected**, never hand-rolled here (Top-risk 1). The app\n * supplies a vetted sandbox — isolated-vm, QuickJS-WASM, or a locked-down worker. TheoKit core\n * ships no VM and adds no sandbox dependency (same posture as the injected deploy adapter / the\n * M17 transport). `node:vm` is NOT a security boundary and MUST NOT be used as the sandbox.\n * - TheoKit owns the **restricted API** (only the declared tools are reachable from the code — no\n * `fs`, `process`, `require`, or network unless a declared, permission-gated tool provides it) and\n * the **mandatory permission gate**: every tool call from the code passes `onPermissionRequest`\n * first, and there is NO default-allow (mirrors M17 `onPermissionRequest`).\n *\n * Threat model (summary): a malicious model could author code that (a) calls a dangerous tool, or\n * (b) tries to reach a host capability. (a) is stopped by the permission gate (deny → the API call\n * throws). (b) is stopped by the injected sandbox (the restricted API is the ONLY surface the code\n * sees). If the app injects a weak sandbox, (b) is on the app — hence the vetted-sandbox requirement.\n */\nimport type { CustomTool } from '../define/define-agent-tool.js'\n\n/** The restricted API handed to sandboxed code: declared tool names → permission-gated callables. */\nexport type CodeModeApi = Record<string, (args: unknown) => Promise<unknown>>\n\n/** The injected isolation boundary. The app supplies a vetted implementation. */\nexport interface Sandbox {\n /** Run `code` with access to ONLY `api` (the restricted tool surface). Resolve the code's result. */\n run(code: string, api: CodeModeApi): Promise<unknown>\n}\n\n/** A permission decision for one tool call from sandboxed code. */\nexport interface CodeModePermission {\n granted: boolean\n /** Optional reason surfaced to the model on denial. */\n reason?: string\n}\n\nexport interface CodeModeConfig {\n /** The tools reachable from the code (the restricted API). */\n tools: CustomTool[]\n /** The injected isolation boundary (vetted sandbox — NEVER node:vm). */\n sandbox: Sandbox\n /**\n * REQUIRED — decide each tool call the code attempts. Security by default: NO default-allow.\n * Return `{ granted }` (may be async). Mirrors M17 `onPermissionRequest`.\n */\n onPermissionRequest: (req: {\n tool: string\n args: unknown\n }) => CodeModePermission | Promise<CodeModePermission>\n /** Tool name the model calls (default `run_code`). */\n name?: string\n /** Tool description surfaced to the model. */\n description?: string\n}\n\n/** Thrown when the permission gate denies a tool call from sandboxed code. */\nexport class CodeModePermissionDeniedError extends Error {\n constructor(tool: string, reason?: string) {\n const suffix = reason ? `: ${reason}` : ''\n super(`code-mode: tool '${tool}' denied by permission gate${suffix}`)\n this.name = 'CodeModePermissionDeniedError'\n }\n}\n\n/** M40 (ADR-0049) — render a tool's JSON-Schema input as a readable arg shape, e.g. `{ limit: number, region?: string }`. */\nfunction describeToolInput(inputSchema: Record<string, unknown>): string {\n const props = (inputSchema.properties as Record<string, { type?: unknown }> | undefined) ?? {}\n const required = new Set((inputSchema.required as string[] | undefined) ?? [])\n const entries = Object.entries(props).map(([key, spec]) => {\n // Complex Zod types (union/intersection/enum) may emit no top-level `type` → 'unknown' (safe).\n const type = typeof spec.type === 'string' ? spec.type : 'unknown'\n return `${key}${required.has(key) ? '' : '?'}: ${type}`\n })\n return entries.length > 0 ? `{ ${entries.join(', ')} }` : '{}'\n}\n\n/**\n * M40 (ADR-0049) — generate the model-facing instructions from the SAME `tools` allow-list\n * `createCodeMode` captures (DRY — cannot drift from the api surface it describes). Teaches the model\n * that its code runs in a sandbox, the available `api.<name>(input)` calls (ONLY this allow-list —\n * least-privilege scoping), and the return contract. Add it to the agent's system prompt.\n */\nfunction generateCodeModeInstructions(tools: CustomTool[], toolName: string): string {\n const calls = tools\n .map((t) => `- \\`await api.${t.name}(${describeToolInput(t.inputSchema)})\\` — ${t.description}`)\n .join('\\n')\n return [\n `The \\`${toolName}\\` tool runs your code in a sandbox. Your code may call ONLY these functions (each bridges to a real, validated tool on the host):`,\n calls,\n 'Write an async function body that composes these calls and return exactly ONE structured result. Prefer `Promise.all` for independent calls; do arithmetic and aggregation in code, not in prose.',\n ].join('\\n\\n')\n}\n\n/**\n * Build a code-mode tool + its generated model instructions (M40 / ADR-0049). Fails fast if\n * `onPermissionRequest` or `sandbox` is missing (security by default). `tool` takes `{ code }`,\n * assembles the permission-gated restricted API from `tools`, runs the code in the injected sandbox,\n * and returns the code's result. `instructions` (add it to the agent's system prompt) teaches the\n * model the sandboxed-code contract + the available `api.<name>(input)` calls.\n */\nexport function createCodeMode(config: CodeModeConfig): { tool: CustomTool; instructions: string } {\n if (typeof config.onPermissionRequest !== 'function') {\n throw new Error(\n 'createCodeMode requires onPermissionRequest (security by default — no default-allow for any tool).',\n )\n }\n const sandboxRun = (config.sandbox as { run?: unknown } | null | undefined)?.run\n if (typeof sandboxRun !== 'function') {\n throw new Error(\n 'createCodeMode requires an injected `sandbox` with a run() method (a vetted isolation boundary — never node:vm).',\n )\n }\n // M40 — an empty allow-list is a config mistake: the restricted API (and the generated\n // instructions) would be empty, and the code could call nothing. Fail fast.\n if (config.tools.length === 0) {\n throw new Error(\n 'createCodeMode requires a non-empty tools[] — the restricted API would be empty.',\n )\n }\n\n // Assemble the restricted API: each declared tool becomes a permission-gated callable.\n const api: CodeModeApi = {}\n for (const tool of config.tools) {\n api[tool.name] = async (args: unknown): Promise<unknown> => {\n const decision = await config.onPermissionRequest({ tool: tool.name, args })\n if (!decision.granted) throw new CodeModePermissionDeniedError(tool.name, decision.reason)\n return tool.handler(args as Record<string, unknown>)\n }\n }\n\n const name = config.name ?? 'run_code'\n const tool: CustomTool = {\n name,\n description:\n config.description ??\n 'Run code that composes the available tools. Only the declared tools are callable.',\n inputSchema: {\n type: 'object',\n properties: { code: { type: 'string', description: 'The code to run in the sandbox.' } },\n required: ['code'],\n },\n handler: async (input: Record<string, unknown>): Promise<string> => {\n const code = typeof input.code === 'string' ? input.code : ''\n const result = await config.sandbox.run(code, api)\n return typeof result === 'string' ? result : JSON.stringify(result)\n },\n }\n return { tool, instructions: generateCodeModeInstructions(config.tools, name) }\n}\n","/**\n * M27 (ADR-0041) — channel webhook routes: `POST /api/agents/<name>/channels/<platform>/webhook`.\n *\n * Auto-generates a per-platform inbound webhook endpoint that VALIDATES the platform signature\n * (reusing the existing webhook `VerifyFn` providers — Slack/Telegram/Discord — never a hand-rolled\n * scheme) and hands the parsed payload to an injected `onMessage` seam. The seam is where an app\n * wires the SDK gateway package (`@theokit/gateway-*`) that translates the payload into an agent\n * turn — TheoKit provides the route + signature gate, NOT the gateway's parsing (G2 / it does not\n * reimplement the gateway).\n */\nimport type { VerifyFn } from '../webhook/webhook-types.js'\n\nconst CHANNEL_PATH = /^\\/api\\/agents\\/([^/]+)\\/channels\\/([^/]+)\\/webhook$/\n\n/** Parsed `{ agent, platform }` from a channel webhook path, or `null` when it doesn't match. */\nexport function parseChannelPath(urlPath: string): { agent: string; platform: string } | null {\n const match = CHANNEL_PATH.exec(urlPath)\n if (!match) return null\n return { agent: decodeURIComponent(match[1]), platform: decodeURIComponent(match[2]) }\n}\n\n/** True when `urlPath` targets a channel webhook (dev/prod routing branches on this). */\nexport function isChannelPath(urlPath: string): boolean {\n return CHANNEL_PATH.test(urlPath)\n}\n\n/** The inbound message handed to the app after signature validation passes. */\nexport interface ChannelMessage {\n agent: string\n platform: string\n /** The parsed JSON payload from the platform (the gateway translates this to an agent turn). */\n payload: unknown\n}\n\nexport interface ChannelWebhookConfig {\n /** Per-platform signature validators (e.g. `{ slack: slack({...}), telegram: telegram({...}) }`). */\n validators: Record<string, VerifyFn>\n /** Handoff seam — wire the SDK gateway / agent here. Invoked only after signature validation. */\n onMessage: (message: ChannelMessage) => void | Promise<void>\n}\n\nfunction jsonError(status: number, code: string, message: string): Response {\n return new Response(JSON.stringify({ error: { code, message } }), {\n status,\n headers: { 'content-type': 'application/json' },\n })\n}\n\n/**\n * Handle one channel webhook request. Returns:\n * 404 UNKNOWN_PLATFORM — no validator configured for `<platform>`\n * 400 BAD_REQUEST — path is not a channel webhook, or the body is not JSON\n * 401 INVALID_SIGNATURE — the platform signature check failed (negative case)\n * 200 { ok: true } — validated + handed to `onMessage`\n */\nexport async function handleChannelWebhook(\n request: Request,\n urlPath: string,\n config: ChannelWebhookConfig,\n): Promise<Response> {\n const parsed = parseChannelPath(urlPath)\n if (parsed === null) {\n return jsonError(\n 400,\n 'BAD_REQUEST',\n 'Path must be /api/agents/<name>/channels/<platform>/webhook.',\n )\n }\n if (!Object.hasOwn(config.validators, parsed.platform)) {\n return jsonError(\n 404,\n 'UNKNOWN_PLATFORM',\n `No validator configured for platform '${parsed.platform}'.`,\n )\n }\n const verify = config.validators[parsed.platform]\n\n // Validate the signature against a CLONE so the body stays readable for the payload parse.\n const verifyResult = await verify(request.clone())\n if (!verifyResult.ok) {\n return jsonError(\n 401,\n 'INVALID_SIGNATURE',\n `Signature validation failed: ${verifyResult.reason}`,\n )\n }\n\n let payload: unknown\n try {\n payload = await request.json()\n } catch {\n return jsonError(400, 'BAD_REQUEST', 'Request body must be JSON.')\n }\n\n await config.onMessage({ agent: parsed.agent, platform: parsed.platform, payload })\n return new Response(JSON.stringify({ ok: true }), {\n status: 200,\n headers: { 'content-type': 'application/json' },\n })\n}\n","/**\n * M35 (multi-surface) — the in-process agent-turn seam (Model A).\n *\n * The FRAMEWORK-owned sibling of the HTTP `mountAgent` and the stdout `runAgentInTerminal`: it runs a\n * compiled agent with the SAME `compileAgentModule` + SAME `streamAgentUIMessages` (G2 — reuses the\n * SDK runtime, reimplements nothing), but returns the raw `UIMessageChunk` generator so ANY consumer\n * drives it directly — the Ink TUI (M35), a Tauri window (M36), or a test — in a SINGLE process with\n * NO HTTP loopback, NO port, and NO CSRF (there is no network boundary to defend).\n *\n * The ONLY difference from the HTTP mount is HITL resolution: the mount pauses the run and resolves\n * the approval via a SECOND HTTP request to `/approve/:id` (the approval registry). In-process there\n * is no second request — the caller resolves the approval INLINE via `awaitApproval` (e.g. the Ink\n * TUI's y/n prompt). The gated-tool map is `compiled.hitl` verbatim, so the pause semantics are\n * byte-identical to the HTTP path; only the resolver differs. Parity with the mount is by\n * construction: both compile the module, resolve function-form skills, and call `streamAgentUIMessages`\n * with the same `{ message, sessionId, hitl }`.\n *\n * Consumers WILL still receive `tool-approval-request` chunks from the returned generator — they are\n * INFORMATIONAL (render them or ignore them). The authoritative human gate is `awaitApproval`, which\n * the SDK awaits BEFORE the gated tool runs; the chunk is not the gate.\n */\nimport {\n compileAgentModule,\n resolveEnabledSkills,\n streamAgentUIMessages,\n type HitlDecision,\n type HumanInTheLoopOptions,\n} from '@theokit/agents'\nimport type { UIMessageChunk } from 'ai'\n\n/** An inline approval request handed to the caller's `awaitApproval` (the Ink/Tauri prompt). */\nexport interface InProcessApprovalRequest {\n approvalId: string\n toolName: string\n opts: HumanInTheLoopOptions\n}\n\n/** Resolve one gated-tool approval inline (approve/deny, or a structured {@link HitlDecision}). */\nexport type InProcessAwaitApproval = (\n req: InProcessApprovalRequest,\n) => Promise<boolean | HitlDecision>\n\nexport interface StreamAgentTurnInProcessInput {\n message: string\n /**\n * M35 (multimodal) — images to send alongside the text. Threaded to the SDK's structured\n * `SDKUserMessage { text, images }` send form. Absent ⇒ the string send path is byte-unchanged.\n */\n images?: Parameters<typeof streamAgentUIMessages>[2]['images']\n /** Resume key; a fresh id per run when omitted. */\n sessionId?: string\n /**\n * Inline HITL resolver — required IFF the agent has `@HumanInTheLoop`-gated tools. Omitting it for a\n * gated agent is a fail-fast error, never a silent bypass (Rule 8, the #99 lesson).\n */\n awaitApproval?: InProcessAwaitApproval\n /** Labels a fail-fast `AgentDefinitionError` (the file path). */\n source?: string\n /** Abort signal forwarded to the SDK stream (client disconnect / window close). */\n signal?: AbortSignal\n}\n\n/** Injectable stream fn (defaults to the real SDK bridge) — lets tests drive a deterministic stream. */\nexport interface StreamAgentTurnDeps {\n stream: typeof streamAgentUIMessages\n}\n\n/**\n * Thrown when a gated agent is run in-process without an `awaitApproval` resolver. Refusing loudly is\n * the correct posture: silently running a `@HumanInTheLoop`-gated tool with no human gate is exactly\n * the #99 class of bug. Typed so callers can catch it distinctly.\n */\nexport class InProcessApprovalRequiredError extends Error {\n constructor(toolNames: readonly string[]) {\n super(\n `Agent has HITL-gated tool(s) [${toolNames.join(', ')}] but no \\`awaitApproval\\` resolver was ` +\n `supplied to streamAgentTurnInProcess. In-process runs must resolve approvals inline — pass ` +\n `awaitApproval, or remove the gate. Refused (fail-closed).`,\n )\n this.name = 'InProcessApprovalRequiredError'\n }\n}\n\n/**\n * Run a compiled agent in-process and return its `UIMessageChunk` stream. `apiKey` is resolved by the\n * caller (same contract as the HTTP mount). Validation + compile happen SYNCHRONOUSLY (so a gated\n * agent without a resolver throws at call time, not lazily on first iteration); the returned value is\n * the SDK's `streamAgentUIMessages` generator.\n */\nexport function streamAgentTurnInProcess(\n mod: unknown,\n apiKey: string,\n input: StreamAgentTurnInProcessInput,\n deps: StreamAgentTurnDeps = { stream: streamAgentUIMessages },\n): AsyncGenerator<UIMessageChunk> {\n const compiled = compileAgentModule(mod, input.source)\n const gated = compiled.hitl\n\n // Fail-fast BEFORE building the stream: a gated agent with no inline resolver would silently bypass\n // the human gate (the #99 class of bug). Refuse loudly (Rule 8, fail-closed).\n if (gated && gated.size > 0 && !input.awaitApproval) {\n throw new InProcessApprovalRequiredError([...gated.keys()])\n }\n const resolve = input.awaitApproval\n\n // Mirror mount-agent's HITL wiring cast-free (structural inference); absent gate ⇒ the non-HITL\n // stream path (M2), unchanged. The SDK calls (approvalId, opts, toolName); route to the caller.\n const hitl =\n gated && gated.size > 0 && resolve\n ? {\n gated,\n awaitApproval: (approvalId: string, opts: HumanInTheLoopOptions, toolName: string) =>\n resolve({ approvalId, toolName, opts }),\n }\n : undefined\n\n const sessionId = input.sessionId ?? crypto.randomUUID()\n\n // Resolve function-form skills (`defineAgent({ skills: (ctx) => [...] })`) BEFORE streaming — exact\n // parity with mount-agent. Done INSIDE the returned generator so the synchronous fail-fast above is\n // preserved (the caller still gets a plain `AsyncGenerator`, no `await` at the call site). A static\n // skill list leaves `skillsResolver` undefined and this is a no-op.\n return (async function* () {\n if (compiled.skillsResolver) {\n const enabled = await resolveEnabledSkills(compiled.skillsResolver, compiled.runContext ?? {})\n if (enabled !== undefined) compiled.skills = { enabled, autoInject: true }\n }\n yield* deps.stream(compiled, apiKey, {\n message: input.message,\n // M35 — pass images through so the SDK send uses the structured `{ text, images }` form.\n ...(input.images !== undefined ? { images: input.images } : {}),\n sessionId,\n hitl,\n signal: input.signal,\n })\n })()\n}\n","/**\n * MCP stdio transport (M16 follow-up) — expose a TheoKit agent as an MCP server over stdin/stdout,\n * the sibling of the M16 HTTP route (`POST /api/agents/<name>/mcp`). A desktop MCP client (e.g.\n * Claude Desktop) spawns `theokit mcp <agent>` and speaks newline-delimited JSON-RPC over the pipe.\n *\n * This is a TRANSPORT over the framework's OWN {@link handleMcpJsonRpc} — it reuses the exact handler\n * the HTTP route uses; it calls no LLM, spawns no MCP client, and reimplements no runtime (G2 /\n * sdk-runtime.md). Distinct from the SDK's MCP CLIENT stdio (which spawns external MCP servers via\n * `mcpServers` command/args) — that stays SDK-side. Here TheoKit is the SERVER. The framework-side\n * placement of this server-exposure transport is recorded in ADR-0042 (refining ADR-0040's M16 note).\n */\nimport type { AppResource } from './mcp-app-resources.js'\nimport { handleMcpJsonRpc } from './mcp-handler.js'\n\n/**\n * Handle one newline-delimited JSON-RPC line. Returns the response line to write to stdout, or\n * `null` for a blank line (nothing to emit). A malformed JSON line yields a `-32700` (Parse error)\n * envelope — never throws, so the stdio loop never dies on bad input.\n */\nexport async function handleMcpStdioLine(\n line: string,\n mod: unknown,\n name: string,\n appResources: readonly AppResource[] = [],\n): Promise<string | null> {\n const trimmed = line.trim()\n if (trimmed.length === 0) return null\n let body: unknown\n try {\n body = JSON.parse(trimmed)\n } catch {\n return JSON.stringify({\n jsonrpc: '2.0',\n id: null,\n error: { code: -32700, message: 'Parse error' },\n })\n }\n const response = await handleMcpJsonRpc(mod, name, body, appResources)\n const payload: unknown = await response.json()\n return JSON.stringify(payload)\n}\n\n/** A minimal readable line source (an async iterable of lines) + a writable sink. */\nexport interface StdioStreams {\n /** Async iterable of newline-delimited input lines (e.g. `readline.createInterface({ input })`). */\n lines: AsyncIterable<string>\n /** Write a response line (the caller appends no newline). */\n write: (line: string) => void\n}\n\n/**\n * Drive the MCP stdio server loop: for each input line, dispatch via {@link handleMcpStdioLine} and\n * write the response line (with a trailing `\\n`). Returns when the input stream ends (EOF).\n */\nexport async function serveMcpStdio(\n mod: unknown,\n name: string,\n appResources: readonly AppResource[],\n streams: StdioStreams,\n): Promise<void> {\n for await (const line of streams.lines) {\n const out = await handleMcpStdioLine(line, mod, name, appResources)\n if (out !== null) streams.write(`${out}\\n`)\n }\n}\n"],"mappings":"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","names":["tool"]}
|
package/dist/chunk-5ODOE6EF.js
CHANGED
|
File without changes
|
package/dist/chunk-AFVTEAES.js
CHANGED
|
File without changes
|
package/dist/chunk-FEDSPOY6.js
CHANGED
|
File without changes
|
package/dist/chunk-FOZIR3TG.js
CHANGED
|
File without changes
|
package/dist/chunk-FRUEDJFX.js
CHANGED
|
File without changes
|
package/dist/chunk-GDN3PXFH.js
CHANGED
|
File without changes
|
package/dist/chunk-GEQJAFSV.js
CHANGED
|
File without changes
|
package/dist/chunk-GSO77FSM.js
CHANGED
|
File without changes
|
package/dist/chunk-HGZL5EOI.js
CHANGED
|
File without changes
|
package/dist/chunk-HNBWZKIQ.js
CHANGED
|
File without changes
|
package/dist/chunk-IEES3CHD.js
CHANGED
|
File without changes
|
package/dist/chunk-IPC5UREF.js
CHANGED
|
File without changes
|
package/dist/chunk-JAIKGP3Q.js
CHANGED
|
File without changes
|
|
@@ -176,6 +176,10 @@ var AgentClient = class {
|
|
|
176
176
|
abort = () => {
|
|
177
177
|
this.#controller?.abort();
|
|
178
178
|
this.#controller = null;
|
|
179
|
+
if (this.#status === "streaming") {
|
|
180
|
+
this.#status = this.#committed.length > 0 || this.#messages.length > 0 ? "done" : "idle";
|
|
181
|
+
this.#emit();
|
|
182
|
+
}
|
|
179
183
|
};
|
|
180
184
|
/** Clear messages + error, back to idle. */
|
|
181
185
|
reset = () => {
|
|
@@ -498,4 +502,4 @@ export {
|
|
|
498
502
|
HttpTransport,
|
|
499
503
|
createAgentClient
|
|
500
504
|
};
|
|
501
|
-
//# sourceMappingURL=chunk-
|
|
505
|
+
//# sourceMappingURL=chunk-KPRJS3II.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/client/consume-ui-message-stream.ts","../src/client/agent-client.ts","../src/client/last-user-text.ts","../src/client/channel-transport.ts","../src/client/in-process-transport.ts","../src/client/http-transport.ts","../src/client/create-agent-client.ts"],"sourcesContent":["import type { UIMessage, UIMessageChunk } from 'ai'\n\n/**\n * M2 (theokit-ai-first) — read a TheoKit agent endpoint's `UIMessageStream` SSE `Response`\n * into reconstructed assistant `UIMessage`s, reusing the `ai` package's own consumer\n * primitives (`parseJsonEventStream` + `readUIMessageStream`) — the exact path\n * `@ai-sdk/react`'s `useChat` runs internally. No reinvented wire parser (Rule 9).\n *\n * `ai` is an OPTIONAL peer dependency, so it is imported dynamically: an app that never\n * calls an agent never pays for it, and importing `theokit/client` does not hard-require\n * `ai` (mirrors how the agent runtime dynamically imports `@theokit/sdk`). An agent app\n * always has `ai` installed (it is the UIMessageStream consumer).\n *\n * `onMessage` is invoked on every reconstruction step with the latest snapshot of the\n * assistant message, so a caller (the `useAgent` hook) can render streaming updates.\n */\nexport async function consumeUIMessageStream(\n response: Response,\n onMessage: (message: UIMessage) => void,\n): Promise<void> {\n const chunkStream = await responseToChunkStream(response)\n await consumeChunkStream(chunkStream, onMessage)\n}\n\n/**\n * M41 (ADR-0050 D3) — the reusable middle piece: a UIMessageStream SSE `Response` →\n * `ReadableStream<UIMessageChunk>`, reusing `ai`'s own `parseJsonEventStream` (the exact primitive\n * `useChat` runs). This is precisely what a `ChatTransport.sendMessages` returns, so `HttpTransport`\n * builds on it directly (no reinvented wire parser — Rule 9). A body-less response yields an empty stream.\n */\nexport async function responseToChunkStream(\n response: Response,\n): Promise<ReadableStream<UIMessageChunk>> {\n if (response.body === null) {\n return new ReadableStream<UIMessageChunk>({\n start(controller) {\n controller.close()\n },\n })\n }\n\n const { parseJsonEventStream, uiMessageChunkSchema } = await import('ai')\n\n // ai validates each SSE JSON frame against its own strict chunk schema (the exact gate `useChat`\n // runs), then yields `{ success, value }`; forward the valid chunks.\n const parsed = parseJsonEventStream({ stream: response.body, schema: uiMessageChunkSchema })\n return new ReadableStream<UIMessageChunk>({\n async start(controller) {\n for await (const result of parsed) {\n if (result.success) controller.enqueue(result.value)\n }\n controller.close()\n },\n })\n}\n\n/**\n * M41 (ADR-0050 D6) — read a `ReadableStream<UIMessageChunk>` into reconstructed assistant\n * `UIMessage`s via `ai`'s `readUIMessageStream`. Shared by `consumeUIMessageStream` (Response path)\n * and the framework-agnostic `AgentClient` store (transport path). `onMessage` fires on every\n * reconstruction step so a caller can render streaming updates.\n */\nexport async function consumeChunkStream(\n stream: ReadableStream<UIMessageChunk>,\n onMessage: (message: UIMessage) => void,\n): Promise<void> {\n const { readUIMessageStream } = await import('ai')\n // #136 — a provider failure (401/429/5xx) arrives as a `{ type: 'error', errorText }` chunk, NOT a\n // thrown rejection (the in-process runner and the SSE path both emit it as data). With the default\n // `readUIMessageStream({ stream })` (no `onError`, `terminateOnError` off) that chunk is silently\n // swallowed — the stream ends \"clean\" and the store settles to 'done' instead of 'error'.\n // `onError` captures the error; `terminateOnError` stops reconstructing partial messages after it AND\n // (under ai@7.0.14) errors the underlying iterator — so the `for await` below rejects and\n // `AgentClient.#drive`'s existing catch surfaces it (status='error', error set). The post-loop\n // `throw` is a defensive fallback that still surfaces the captured error if a future `ai` version\n // stops rejecting under `terminateOnError`; it is dead code under ai@7.0.14 but cheap version-robustness.\n let streamError: Error | undefined\n for await (const message of readUIMessageStream({\n stream,\n onError: (err) => {\n streamError = err instanceof Error ? err : new Error(String(err))\n },\n terminateOnError: true,\n })) {\n onMessage(message)\n }\n if (streamError !== undefined) throw streamError\n}\n","import type { UIMessage, UIMessageChunk } from 'ai'\n\nimport { consumeChunkStream } from './consume-ui-message-stream.js'\nimport type { AgentTransport, ApprovalDecision, RequestContext } from './transport.js'\n\nexport type UseAgentStatus = 'idle' | 'streaming' | 'done' | 'error'\n\n/** The observable state the store exposes (stable reference between emits — `useSyncExternalStore` contract). */\nexport interface AgentClientState {\n /** The CURRENT turn's assistant messages (per-turn; reset each `send`). Back-compat — unchanged since M41. */\n messages: UIMessage[]\n /**\n * M46 — the full conversation: committed turns + the current turn's user message + in-flight assistant.\n * Accumulated across sends (never reset except by `reset()`), with stable ids committed exactly once.\n * Render this instead of hand-rolling a transcript from `messages`.\n */\n thread: UIMessage[]\n status: UseAgentStatus\n error: Error | undefined\n}\n\n/** Derive the turn text from a typed input: `input.message` when present, else the serialized input. */\nfunction inputToText(input: unknown): string {\n if (\n typeof input === 'object' &&\n input !== null &&\n typeof (input as { message?: unknown }).message === 'string'\n ) {\n return (input as { message: string }).message\n }\n if (typeof input === 'string') return input\n return JSON.stringify(input)\n}\n\n/** Build a user `UIMessage` from a typed input (text from `{ message }`, else the serialized input). */\nfunction buildUserMessage(input: unknown): UIMessage {\n return {\n id: crypto.randomUUID(),\n role: 'user',\n parts: [{ type: 'text', text: inputToText(input) }],\n }\n}\n\n/**\n * M41 (ADR-0050 D6) — the framework-agnostic agent client store.\n *\n * Holds `messages`/`status`/`error`, drives an {@link AgentTransport}, and notifies subscribers on\n * change. It is the SINGLE consolidation point: web (`HttpTransport`) and terminal/desktop\n * (`InProcessTransport`) run the SAME store. `useAgent` is a thin React binding over it via\n * `useSyncExternalStore`; a standalone (no-React) client (M44) can subscribe directly. Being\n * framework-agnostic, it is unit-tested without a DOM.\n */\nexport class AgentClient<TInput = unknown> {\n readonly #transport: AgentTransport\n readonly #chatId = crypto.randomUUID()\n readonly #listeners = new Set<() => void>()\n /** M43 — resolves per-request context (evaluated on every send/reconnect — dynamic, never stale). */\n readonly #contextResolver: (() => RequestContext | undefined) | undefined\n\n #messages: UIMessage[] = []\n #status: UseAgentStatus = 'idle'\n #error: Error | undefined\n #controller: AbortController | null = null\n // M46 — conversation accumulation (React-free; all surfaces inherit it via the snapshot).\n /** Committed (finished) turns — user + assistant, with stable fabricated ids. */\n #committed: UIMessage[] = []\n /** The current turn's user message (in `thread` but never in `messages` — back-compat). */\n #currentUser: UIMessage | undefined\n /** A stable id for the current turn's assistant (the SDK leaves it empty — we fabricate one). */\n #currentAssistantId = ''\n #snapshot: AgentClientState = { messages: [], thread: [], status: 'idle', error: undefined }\n\n constructor(transport: AgentTransport, contextResolver?: () => RequestContext | undefined) {\n this.#transport = transport\n this.#contextResolver = contextResolver\n }\n\n /** Subscribe to state changes; returns an unsubscribe fn. */\n subscribe = (listener: () => void): (() => void) => {\n this.#listeners.add(listener)\n return () => {\n this.#listeners.delete(listener)\n }\n }\n\n /** The current immutable snapshot (stable reference until the next emit). */\n getSnapshot = (): AgentClientState => this.#snapshot\n\n #emit(): void {\n // thread = committed turns + this turn's user + the in-flight assistant (`messages`). New array per\n // emit is fine — the SNAPSHOT reference only changes here, satisfying useSyncExternalStore.\n const thread = this.#currentUser\n ? [...this.#committed, this.#currentUser, ...this.#messages]\n : [...this.#committed, ...this.#messages]\n this.#snapshot = { messages: this.#messages, thread, status: this.#status, error: this.#error }\n for (const listener of this.#listeners) listener()\n }\n\n #upsert(message: UIMessage): void {\n const next = [...this.#messages]\n const idx = next.findIndex((existing) => existing.id === message.id)\n if (idx >= 0) next[idx] = message\n else next.push(message)\n this.#messages = next\n }\n\n async #drive(\n open: () => Promise<ReadableStream<UIMessageChunk> | null>,\n controller: AbortController,\n ): Promise<void> {\n // Read via a function (not a narrowed local) — `aborted` flips ASYNC across the awaits below, so the\n // control-flow narrowing of a direct `signal.aborted` read would be wrong. A stale drive (its\n // controller aborted because a newer send/abort took over) MUST NOT clobber the newer status.\n const aborted = (): boolean => controller.signal.aborted\n try {\n const stream = await open()\n if (aborted()) return\n if (stream === null) {\n // Nothing to resume (e.g. reconnect after the run completed). Settle without error.\n this.#status = this.#messages.length > 0 ? 'done' : 'idle'\n this.#emit()\n return\n }\n await consumeChunkStream(stream, (message) => {\n if (aborted()) return\n // The SDK leaves the assistant message id empty — fabricate a stable per-turn id so every chunk\n // upserts into the SAME message and the committed copy has a collision-free key (M46).\n const stamped = message.id ? message : { ...message, id: this.#currentAssistantId }\n this.#upsert(stamped)\n this.#emit()\n })\n if (aborted()) return\n this.#status = 'done'\n this.#emit()\n } catch (err) {\n if (aborted()) return\n this.#error = err instanceof Error ? err : new Error(String(err))\n this.#status = 'error'\n this.#emit()\n }\n }\n\n /** Send a typed input; opens a fresh stream (replaces prior messages). */\n send = (input: TInput): void => {\n // M46 — commit the PRIOR turn into history exactly once, but ONLY if it finished cleanly (`done`).\n // An errored or aborted turn (status !== 'done') is dropped, keeping committed history uncorrupted.\n if (this.#status === 'done' && this.#currentUser) {\n this.#committed = [...this.#committed, this.#currentUser, ...this.#messages]\n }\n this.abort()\n const controller = new AbortController()\n this.#controller = controller\n const userMsg = buildUserMessage(input)\n this.#currentUser = userMsg\n this.#currentAssistantId = crypto.randomUUID()\n this.#messages = []\n this.#error = undefined\n this.#status = 'streaming'\n this.#emit()\n const context = this.#contextResolver?.()\n void this.#drive(\n () =>\n this.#transport.sendMessages({\n trigger: 'submit-message',\n chatId: this.#chatId,\n messageId: undefined,\n messages: [userMsg],\n abortSignal: controller.signal,\n // Only object inputs flow as the request `body` (the turn text is always in `messages`);\n // a primitive input is carried by the user message, never spread into the body.\n body: typeof input === 'object' && input !== null ? input : undefined,\n // M43 — per-request context reaches every transport (headers → HTTP, metadata → in-process/channel).\n headers: context?.headers,\n metadata: context?.metadata,\n }),\n controller,\n )\n }\n\n /** Resume an interrupted stream via the transport's `reconnectToStream` (no-op when unavailable). */\n reconnect = (): void => {\n const controller = new AbortController()\n this.#controller = controller\n // Reconnecting before any send() (or after reset()) leaves #currentAssistantId empty — fabricate one\n // so a replayed assistant never lands in `thread` with an empty, non-unique id (M46 invariant).\n if (!this.#currentAssistantId) this.#currentAssistantId = crypto.randomUUID()\n // Reconnect means \"resume/retry\" — a stale error must not linger next to a fresh 'streaming' status.\n this.#error = undefined\n this.#status = 'streaming'\n this.#emit()\n const context = this.#contextResolver?.()\n void this.#drive(\n () =>\n this.#transport.reconnectToStream({\n chatId: this.#chatId,\n headers: context?.headers,\n metadata: context?.metadata,\n }),\n controller,\n )\n }\n\n /** Abort an in-flight stream (not an error — leaves messages as-is). */\n abort = (): void => {\n this.#controller?.abort()\n this.#controller = null\n // Finalize the status when the USER aborts an in-flight turn: the aborted `#drive` early-returns\n // without touching status (so a stale drive can't clobber a newer turn), which would otherwise leave\n // `status` stuck on 'streaming' — a lingering spinner + an unusable surface. A caller that aborts to\n // start a NEW turn (`send`/`sendMessages`) sets 'streaming' again immediately after, so this is safe.\n if (this.#status === 'streaming') {\n this.#status = this.#committed.length > 0 || this.#messages.length > 0 ? 'done' : 'idle'\n this.#emit()\n }\n }\n\n /** Clear messages + error, back to idle. */\n reset = (): void => {\n this.abort()\n this.#messages = []\n // M46 — reset means a NEW conversation: clear committed history + the current turn's user too.\n this.#committed = []\n this.#currentUser = undefined\n this.#error = undefined\n this.#status = 'idle'\n this.#emit()\n }\n\n /** Settle a paused HITL approval via the transport's HITL path (HTTP POST or inline callback). */\n approve = async (approvalId: string, decision: ApprovalDecision): Promise<void> => {\n await this.#transport.approve?.(approvalId, decision)\n }\n}\n","import type { UIMessage } from 'ai'\n\n/**\n * M41/M42 — extract the turn text from the last user message's text parts. Shared by the transports\n * that hand a plain `message` string to an in-process/push runner (`InProcessTransport`,\n * `ChannelTransport`) rather than POSTing the `messages[]` array (`HttpTransport`). One definition (G12).\n */\nexport function extractLastUserText(messages: readonly UIMessage[]): string {\n for (let i = messages.length - 1; i >= 0; i--) {\n const message = messages[i]\n if (message.role !== 'user') continue\n const text = message.parts\n .filter((part): part is { type: 'text'; text: string } => part.type === 'text')\n .map((part) => part.text)\n .join('')\n if (text.length > 0) return text\n }\n return ''\n}\n","import type { ChatTransport, UIMessage, UIMessageChunk } from 'ai'\n\nimport { extractLastUserText } from './last-user-text.js'\nimport type { AgentTransport, ApprovalDecision } from './transport.js'\n\n/** Handlers the transport hands to the injected push source for one turn. */\nexport interface ChannelTurnHandlers {\n /** One pushed JSONL line (a serialized `UIMessageChunk`). */\n onLine: (line: string) => void\n /** The turn ended — no more lines. */\n onClose: () => void\n /** The push source failed. */\n onError?: (err: unknown) => void\n}\n\n/**\n * The injected push source — a Tauri `Channel`/`invoke` bridge, kept STRUCTURAL so core adds no\n * `@tauri-apps/*` dependency and the transport is testable with a fake (ADR-0051 D2). The Tauri app\n * wires it: `new Channel()`, `channel.onmessage = onLine`, `invoke('run_agent', { message, channel })`,\n * returning a teardown that aborts the sidecar turn.\n */\nexport interface ChannelPushSource {\n /**\n * Start a turn; deliver each JSONL `UIMessageChunk` line to `onLine`, then `onClose`. Return a teardown.\n * `turn.context` (M43) is the per-request context (from the seam's `metadata`) — the Tauri `invoke`\n * forwards it to the sidecar. When no context is set it is present as `context: undefined` (the key is\n * NOT absent) — a sidecar that checks `'context' in turn` should treat `undefined` as \"no context\".\n */\n start(turn: { message: string; context?: unknown }, handlers: ChannelTurnHandlers): () => void\n /** Optional HITL settle (another Tauri `invoke`). */\n settle?(approvalId: string, decision: ApprovalDecision): Promise<void>\n}\n\nexport interface ChannelTransportOptions {\n source: ChannelPushSource\n}\n\n/**\n * M42 (ADR-0051) — `ChatTransport` over a Tauri-`Channel`-shaped push source, for the desktop webview.\n *\n * - `sendMessages`: start the turn via the injected source and bridge its pushed JSONL frames into a\n * `ReadableStream<UIMessageChunk>` (built in `start` — a Channel is push, so the stream's queue buffers\n * frames; ADR-0051 D3). A malformed JSONL line is SKIPPED, never fatal (ADR-0051 D4, Rule 8). `abortSignal`\n * tears down the source and closes the stream.\n * - `reconnectToStream`: always `null` — the M36 sidecar runs the turn directly (no durable server stream);\n * this is the honest parity for a single-process push surface (ADR-0051 D5; mirrors `InProcessTransport`).\n * - `approve`: routes to the injected `settle` (another Tauri `invoke`); absent `settle` → a typed error.\n *\n * The push source is INJECTED — core stays Tauri-agnostic and this transport is unit-tested with a fake.\n */\nexport class ChannelTransport implements AgentTransport {\n readonly #source: ChannelPushSource\n\n constructor(options: ChannelTransportOptions) {\n this.#source = options.source\n }\n\n sendMessages(\n options: Parameters<ChatTransport<UIMessage>['sendMessages']>[0],\n ): Promise<ReadableStream<UIMessageChunk>> {\n const { messages, abortSignal, metadata } = options\n const message = extractLastUserText(messages)\n const source = this.#source\n\n // Per-stream teardown/abort-detach, hoisted so `cancel` (consumer stops reading) can also tear the\n // source down. `closed` makes every terminal transition idempotent (no enqueue/close after close).\n let closed = false\n let teardown: () => void = () => undefined\n let detachAbort: () => void = () => undefined\n\n const stream = new ReadableStream<UIMessageChunk>({\n start(controller) {\n const finish = (settle: () => void): void => {\n if (closed) return\n closed = true\n detachAbort()\n settle()\n }\n teardown = source.start(\n { message, context: metadata },\n {\n onLine: (line) => {\n if (closed) return\n // Skip a malformed pushed line — one bad frame must never crash the webview (ADR-0051 D4).\n let parsed: unknown\n try {\n parsed = JSON.parse(line)\n } catch {\n return\n }\n // Discriminant guard: a `UIMessageChunk` is an object with a string `type`. The trust\n // boundary here is the LOCAL sidecar (not the network), so a discriminant check — not the\n // full `ai` schema (which isn't exposed standalone) — is proportionate: it rejects\n // structureless / wrong-shape payloads before they reach `readUIMessageStream`. Same\n // skip-not-crash policy as a parse error.\n if (\n typeof parsed !== 'object' ||\n parsed === null ||\n typeof (parsed as { type?: unknown }).type !== 'string'\n ) {\n return\n }\n controller.enqueue(parsed as UIMessageChunk)\n },\n onClose: () => {\n finish(() => {\n controller.close()\n })\n },\n onError: (err) => {\n finish(() => {\n controller.error(err)\n })\n },\n },\n )\n if (abortSignal !== undefined) {\n const onAbort = (): void => {\n finish(() => {\n teardown()\n controller.close()\n })\n }\n if (abortSignal.aborted) onAbort()\n else {\n abortSignal.addEventListener('abort', onAbort)\n detachAbort = () => {\n abortSignal.removeEventListener('abort', onAbort)\n }\n }\n }\n },\n cancel() {\n // The consumer stopped reading (reader.cancel) — abort the source turn + drop the abort listener.\n if (closed) return\n closed = true\n detachAbort()\n teardown()\n },\n })\n return Promise.resolve(stream)\n }\n\n reconnectToStream(): Promise<ReadableStream<UIMessageChunk> | null> {\n return Promise.resolve(null)\n }\n\n async approve(approvalId: string, decision: ApprovalDecision): Promise<void> {\n if (this.#source.settle === undefined) {\n throw new Error(`This channel source has no HITL settle — cannot approve '${approvalId}'.`)\n }\n await this.#source.settle(approvalId, decision)\n }\n}\n","import type { ChatTransport, UIMessage, UIMessageChunk } from 'ai'\n\nimport { extractLastUserText } from './last-user-text.js'\nimport type { AgentTransport, ApprovalDecision } from './transport.js'\n\n/** An inline approval request handed to the transport's resolver (structural — no server import). */\nexport interface InProcessApprovalRequestLike {\n approvalId: string\n toolName: string\n opts: unknown\n}\n\n/** Resolve one gated-tool approval inline (mirrors the SDK's `boolean | HitlDecision` return). */\nexport type InProcessAwaitApproval = (\n req: InProcessApprovalRequestLike,\n) => Promise<boolean | ApprovalDecision>\n\n/** The input the injected runner receives (structurally compatible with `StreamAgentTurnInProcessInput`). */\nexport interface InProcessRunInput {\n message: string\n sessionId?: string\n signal?: AbortSignal\n awaitApproval?: InProcessAwaitApproval\n /** M43 — per-request context (from `sendMessages`'s `metadata`) — tenant / provider / auth for the runner. */\n context?: unknown\n}\n\n/**\n * The in-process turn runner. The consumer binds `streamAgentTurnInProcess(mod, apiKey, …)`:\n * `new InProcessTransport({ run: (input) => streamAgentTurnInProcess(mod, apiKey, input) })`.\n * Injecting it keeps this client module decoupled from `server/` and makes the transport testable.\n */\nexport type InProcessRunner = (input: InProcessRunInput) => AsyncGenerator<UIMessageChunk>\n\nexport interface InProcessTransportOptions {\n run: InProcessRunner\n}\n\n/** Bridge an `AsyncGenerator<UIMessageChunk>` into a pull-based `ReadableStream<UIMessageChunk>`. */\nfunction generatorToStream(gen: AsyncGenerator<UIMessageChunk>): ReadableStream<UIMessageChunk> {\n return new ReadableStream<UIMessageChunk>({\n async pull(controller) {\n try {\n const result = await gen.next()\n if (result.done === true) {\n controller.close()\n return\n }\n // After the done-guard, `result` is an IteratorYieldResult<UIMessageChunk> — value is typed.\n controller.enqueue(result.value)\n } catch (err) {\n controller.error(err)\n }\n },\n async cancel() {\n await gen.return(undefined)\n },\n })\n}\n\n/**\n * M41 (ADR-0050 D4) — `ChatTransport` over the in-process seam (`streamAgentTurnInProcess`), for the\n * terminal/desktop surfaces that run client + server in ONE process (no HTTP loopback).\n *\n * - `sendMessages`: bridge the injected runner's `AsyncGenerator<UIMessageChunk>` into a\n * `ReadableStream<UIMessageChunk>` (honoring `abortSignal`, which the runner forwards to the SDK).\n * - `reconnectToStream`: always `null` — a single process has no dropped server-side stream to resume\n * (mirrors `ai`'s `DirectChatTransport`).\n * - `approve`: resolve the pending inline approval by id (the run parks on `awaitApproval`). An unknown\n * id rejects (fail-fast, Rule 8 — never a silent resolve).\n *\n * Error asymmetry vs `HttpTransport` (by design, matching the `ChatTransport` contract): a runner that\n * throws SYNCHRONOUSLY surfaces the error when the stream is READ (via `controller.error`), not from the\n * `sendMessages` promise — whereas `HttpTransport` throws from `sendMessages` on a non-2xx response.\n */\nexport class InProcessTransport implements AgentTransport {\n readonly #run: InProcessRunner\n /** Pending inline approvals: approvalId → resolver of the parked `awaitApproval` promise. */\n readonly #pending = new Map<string, (decision: boolean | ApprovalDecision) => void>()\n\n constructor(options: InProcessTransportOptions) {\n this.#run = options.run\n }\n\n #awaitApproval: InProcessAwaitApproval = (req) =>\n new Promise<boolean | ApprovalDecision>((resolve, reject) => {\n // Approval ids are server-minted UUIDs — a collision means a real bug (two turns reusing an id).\n // Fail fast rather than silently overwrite the earlier turn's parked resolver (Rule 8).\n if (this.#pending.has(req.approvalId)) {\n reject(new Error(`Duplicate pending approval id '${req.approvalId}' — ids must be unique.`))\n return\n }\n this.#pending.set(req.approvalId, resolve)\n })\n\n sendMessages(\n options: Parameters<ChatTransport<UIMessage>['sendMessages']>[0],\n ): Promise<ReadableStream<UIMessageChunk>> {\n const { messages, abortSignal, metadata } = options\n const generator = this.#run({\n message: extractLastUserText(messages),\n signal: abortSignal ?? undefined,\n awaitApproval: this.#awaitApproval,\n // M43 — forward per-request context (the seam's `metadata`) to the runner.\n context: metadata,\n })\n return Promise.resolve(generatorToStream(generator))\n }\n\n reconnectToStream(): Promise<ReadableStream<UIMessageChunk> | null> {\n return Promise.resolve(null)\n }\n\n approve(approvalId: string, decision: ApprovalDecision): Promise<void> {\n const resolve = this.#pending.get(approvalId)\n if (resolve === undefined) {\n return Promise.reject(\n new Error(`No pending approval '${approvalId}' (unknown or already settled).`),\n )\n }\n this.#pending.delete(approvalId)\n resolve(decision)\n return Promise.resolve()\n }\n}\n","import type { ChatTransport, UIMessage, UIMessageChunk } from 'ai'\n\nimport { responseToChunkStream } from './consume-ui-message-stream.js'\nimport type { AgentTransport, ApprovalDecision } from './transport.js'\n\n/** Extra request headers — a static record OR a resolver called per request (for dynamic auth). */\nexport type HeadersResolver = Record<string, string> | (() => Record<string, string> | undefined)\n\nexport interface HttpTransportOptions {\n /** Agent endpoint path or URL, e.g. `/api/agents/support`. */\n api: string\n /**\n * Extra request headers (e.g. auth). Static record OR a resolver evaluated on EVERY request — pass a\n * resolver when the value is dynamic (a rotating JWT), so a stale header is never sent. Merged UNDER\n * per-request headers.\n */\n headers?: HeadersResolver\n /** Override fetch (primarily for tests / non-browser hosts) — static; resolved once at construction. */\n fetch?: typeof fetch\n}\n\n/** Normalize `ai`'s `Record | Headers | undefined` header option into a plain record. */\nfunction toRecord(headers: Record<string, string> | Headers | undefined): Record<string, string> {\n if (headers === undefined) return {}\n if (headers instanceof Headers) return Object.fromEntries(headers.entries())\n return headers\n}\n\n/**\n * M41 (ADR-0050 D3) — `ChatTransport` over the web agent path.\n *\n * - `sendMessages`: `POST <api>` with the UIMessageStream `accept` + the `X-Theo-Action` CSRF header\n * (HTTP method + headers are identical to the pre-M41 `useAgent` fetch; the body shape is a superset —\n * `{ ...input, messages: [UIMessage] }` — which the server's dual-path parser accepts, so no\n * regression), captures the server-minted `x-theokit-run-id`, and returns `ReadableStream<UIMessageChunk>`\n * via `ai`'s own SSE parser (`responseToChunkStream`).\n * - `reconnectToStream`: `GET <api>/runs/<runId>/stream` (M37 durable transport); 404 → `null` (the run\n * completed / was evicted). A caller may pass a `Last-Event-ID` header to resume only the tail; by\n * default the server replays the run from the start and the client upserts by message id (idempotent).\n * - `approve`: `POST <api>/approve/<id>` (out-of-band HITL settle).\n *\n * Implemented directly (not by subclassing `DefaultChatTransport`) because reconnect keys on our\n * server-minted `runId` captured from a response header, which the base class does not expose — see\n * ADR-0050 D3.\n */\nexport class HttpTransport implements AgentTransport {\n readonly #api: string\n readonly #headers: HeadersResolver\n readonly #fetch: typeof fetch\n /** Server-minted id of the last run (from `x-theokit-run-id`) — the reconnect key. */\n #lastRunId: string | undefined\n\n constructor(options: HttpTransportOptions) {\n this.#api = options.api\n this.#headers = options.headers ?? {}\n // BIND the default fetch to globalThis — the native `fetch` throws `TypeError: Illegal invocation` when\n // invoked as a method (`this.#fetch(...)` would set `this` to this transport instance, not the window).\n // An injected fetch (tests / non-browser hosts) is a plain function and is used as-is.\n this.#fetch = options.fetch ?? globalThis.fetch.bind(globalThis)\n }\n\n /** Resolve the configured headers per request (a resolver evaluates now — dynamic auth is never stale). */\n #resolveHeaders(): Record<string, string> {\n return (typeof this.#headers === 'function' ? this.#headers() : this.#headers) ?? {}\n }\n\n async sendMessages(\n options: Parameters<ChatTransport<UIMessage>['sendMessages']>[0],\n ): Promise<ReadableStream<UIMessageChunk>> {\n const { messages, abortSignal, headers, body, chatId } = options\n // Only spread an object body (never a primitive — that would emit char-indexed keys). `body` is typed\n // `object | undefined` (ai's `ChatRequestOptions`), so no cast is needed — the guard narrows to\n // `object`. (A runtime-`null` body, which the type forbids, spreads to `{}` — harmless.) The server\n // accepts `{ messages }` (ai shape) AND `{ ...input }` (typed input), preferring the turn text.\n const extra = typeof body === 'object' ? body : undefined\n const response = await this.#fetch(this.#api, {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n accept: 'text/event-stream',\n 'X-Theo-Action': '1',\n ...this.#resolveHeaders(),\n ...toRecord(headers),\n },\n // Send the stable `chatId` as the top-level `id` — the server reads it as the sessionId, so ONE\n // conversation (SDK history + session-scoped tools like `todolist`) persists across turns instead of\n // resetting on a fresh random session each request. Placed last so a session is never shadowed by an\n // `id` field inside the typed input. Undefined chatId ⇒ key omitted ⇒ server falls back (unchanged).\n body: JSON.stringify({ ...extra, messages, id: chatId }),\n signal: abortSignal,\n })\n if (!response.ok) {\n throw new Error(\n `Agent request to ${this.#api} failed: ${response.status} ${response.statusText}`,\n )\n }\n this.#lastRunId = response.headers.get('x-theokit-run-id') ?? undefined\n return responseToChunkStream(response)\n }\n\n async reconnectToStream(\n options: Parameters<ChatTransport<UIMessage>['reconnectToStream']>[0],\n ): Promise<ReadableStream<UIMessageChunk> | null> {\n if (this.#lastRunId === undefined) return null\n const response = await this.#fetch(`${this.#api}/runs/${this.#lastRunId}/stream`, {\n method: 'GET',\n headers: { ...this.#resolveHeaders(), ...toRecord(options.headers) },\n })\n if (response.status === 404) return null\n if (!response.ok) {\n throw new Error(\n `Agent reconnect to run ${this.#lastRunId} failed: ${response.status} ${response.statusText}`,\n )\n }\n return responseToChunkStream(response)\n }\n\n async approve(approvalId: string, decision: ApprovalDecision): Promise<void> {\n const response = await this.#fetch(`${this.#api}/approve/${approvalId}`, {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n 'X-Theo-Action': '1',\n ...this.#resolveHeaders(),\n },\n body: JSON.stringify(decision),\n })\n if (!response.ok) {\n throw new Error(`Approve ${approvalId} failed: ${response.status} ${response.statusText}`)\n }\n }\n}\n","import type { UIMessage } from 'ai'\n\nimport { AgentClient, type AgentClientState } from './agent-client.js'\nimport type { AgentTransport, ApprovalDecision, RequestContext } from './transport.js'\n\nexport interface CreateAgentClientOptions {\n /**\n * M43 — per-request context attached to every turn (`headers` → HTTP; `metadata` → in-process/channel).\n * A value OR a resolver. **To keep context live across sends, pass a RESOLVER** (`() => ({...})`) — a\n * plain value is captured at construction and not re-read, so mutating `options.context` afterwards has\n * no effect (unlike `useAgent`, which reads a live ref). A resolver is re-evaluated every send/reconnect.\n */\n context?: RequestContext | (() => RequestContext | undefined)\n}\n\n/**\n * M44 (ADR-0053) — the standalone (no-React) agent client. A plain handle over the framework-agnostic\n * {@link AgentClient} store: drive a turn, observe state, settle HITL — from a node script, a CLI, a\n * test, or a non-React UI, over ANY {@link AgentTransport}.\n */\nexport interface AgentClientHandle<TInput = unknown> {\n /** Send a typed input; opens a fresh stream (replaces prior messages). */\n send(input: TInput): void\n /** Abort an in-flight stream. */\n abort(): void\n /** Clear messages + error, back to idle. */\n reset(): void\n /** Settle a paused HITL approval via the transport's HITL path. */\n approve(approvalId: string, decision: ApprovalDecision): Promise<void>\n /** Resume an interrupted stream (no-op when the transport can't). */\n reconnect(): void\n /** Subscribe to state changes; returns an unsubscribe fn. */\n subscribe(listener: () => void): () => void\n /**\n * The current immutable snapshot: per-turn `messages` (back-compat) + the full conversation `thread`\n * (M46 — committed history + in-flight, accumulated across sends) + `status` / `error`. A TUI / vanilla\n * consumer renders `thread` directly without hand-rolling a transcript.\n */\n getState(): AgentClientState\n /**\n * Drive one turn and yield the assistant `UIMessage` as it streams (progressive snapshots); iteration\n * ends when the turn settles. A `for await` script takes the last yielded value as the final result;\n * a failed turn rejects the iterator with the run error.\n */\n stream(input: TInput): AsyncIterable<UIMessage>\n}\n\n/** Drive a turn on the store and yield the latest assistant message on each streaming update. */\nfunction streamTurn<TInput>(client: AgentClient<TInput>, input: TInput): AsyncIterable<UIMessage> {\n return {\n async *[Symbol.asyncIterator]() {\n const pending: UIMessage[] = []\n let finished = false\n let streamError: Error | undefined\n let notify: (() => void) | null = null\n // Read via functions — `finished`/`streamError` flip ASYNC in the subscribe callback, so a direct\n // read would be wrongly narrowed to its initial literal by control-flow analysis (M41 pattern).\n const isFinished = (): boolean => finished\n const takeError = (): Error | undefined => streamError\n\n const unsubscribe = client.subscribe(() => {\n const snapshot = client.getSnapshot()\n if (snapshot.status === 'streaming') {\n // Push the growing assistant message ONLY on streaming updates. The terminal ('done'/'error')\n // emit carries the SAME final message already yielded on the last streaming update — pushing it\n // again would yield a duplicate. So the terminal emit only flips `finished` (+ captures error).\n const last = snapshot.messages.at(-1)\n if (last !== undefined) pending.push(last)\n } else {\n if (snapshot.status === 'error') streamError = snapshot.error\n finished = true\n }\n notify?.()\n })\n\n client.send(input)\n try {\n for (;;) {\n while (pending.length > 0) {\n const message = pending.shift()\n if (message !== undefined) yield message\n }\n if (isFinished()) break\n await new Promise<void>((resolve) => {\n notify = resolve\n // Close the lost-wakeup window: an emit may have fired during the drain, BEFORE `notify` was\n // set (its `notify?.()` was then a no-op). If work already arrived, resolve now, don't sleep.\n if (pending.length > 0 || isFinished()) resolve()\n })\n notify = null\n }\n const err = takeError()\n if (err !== undefined) throw err\n } finally {\n // Runs on normal completion AND on early `break` (the for-await calls the generator's return()):\n // stop observing, and abort the turn so an early-exiting consumer doesn't leave it running (abort\n // is a no-op once the turn has settled).\n unsubscribe()\n client.abort()\n }\n },\n }\n}\n\n/**\n * Create a standalone (no-React) agent client over a transport. `useAgent` is the React binding over the\n * same store; this is the equivalent for any JS runtime — one agent, consumable everywhere on the seam.\n */\nexport function createAgentClient<TInput = unknown>(\n transport: AgentTransport,\n options: CreateAgentClientOptions = {},\n): AgentClientHandle<TInput> {\n const { context } = options\n const contextResolver =\n context === undefined\n ? undefined\n : (): RequestContext | undefined => (typeof context === 'function' ? context() : context)\n const client = new AgentClient<TInput>(transport, contextResolver)\n\n return {\n send: client.send,\n abort: client.abort,\n reset: client.reset,\n approve: client.approve,\n reconnect: client.reconnect,\n subscribe: client.subscribe,\n getState: client.getSnapshot,\n stream: (input: TInput) => streamTurn(client, input),\n }\n}\n"],"mappings":"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","names":[]}
|
package/dist/chunk-L4GCEMNW.js
CHANGED
|
File without changes
|
package/dist/chunk-M6NMS5LR.js
CHANGED
|
File without changes
|
package/dist/chunk-NQRNDPZJ.js
CHANGED
|
File without changes
|
package/dist/chunk-P37RZRFV.js
CHANGED
|
File without changes
|
package/dist/chunk-SPJNVWOK.js
CHANGED
|
File without changes
|
package/dist/chunk-UNLG36Z5.js
CHANGED
|
File without changes
|
package/dist/chunk-UO7WS32S.js
CHANGED
|
File without changes
|
package/dist/client/core.js
CHANGED
package/dist/client/index.js
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/dist/db-3UNAMSFK.js
CHANGED
|
File without changes
|
|
File without changes
|
package/dist/dev-PS2NGT6I.js
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/dist/docker-LZZB4D5E.js
CHANGED
|
File without changes
|
|
File without changes
|
package/dist/index.d.ts
CHANGED
|
@@ -53,8 +53,8 @@ declare const theoConfigSchema: z.ZodObject<{
|
|
|
53
53
|
unhandledErrors: z.ZodOptional<z.ZodBoolean>;
|
|
54
54
|
logLevels: z.ZodOptional<z.ZodArray<z.ZodEnum<{
|
|
55
55
|
error: "error";
|
|
56
|
-
warn: "warn";
|
|
57
56
|
info: "info";
|
|
57
|
+
warn: "warn";
|
|
58
58
|
debug: "debug";
|
|
59
59
|
log: "log";
|
|
60
60
|
}>>>;
|
|
@@ -92,24 +92,24 @@ declare const theoConfigSchema: z.ZodObject<{
|
|
|
92
92
|
logging: z.ZodOptional<z.ZodObject<{
|
|
93
93
|
level: z.ZodDefault<z.ZodEnum<{
|
|
94
94
|
error: "error";
|
|
95
|
-
warn: "warn";
|
|
96
95
|
info: "info";
|
|
96
|
+
warn: "warn";
|
|
97
97
|
debug: "debug";
|
|
98
98
|
silent: "silent";
|
|
99
99
|
}>>;
|
|
100
100
|
}, z.core.$strip>>;
|
|
101
101
|
security: z.ZodOptional<z.ZodObject<{
|
|
102
102
|
csrf: z.ZodDefault<z.ZodEnum<{
|
|
103
|
-
off: "off";
|
|
104
103
|
warn: "warn";
|
|
104
|
+
off: "off";
|
|
105
105
|
strict: "strict";
|
|
106
106
|
}>>;
|
|
107
107
|
headers: z.ZodOptional<z.ZodObject<{
|
|
108
108
|
csp: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodLiteral<false>]>>;
|
|
109
109
|
cspMode: z.ZodDefault<z.ZodEnum<{
|
|
110
|
-
off: "off";
|
|
111
110
|
enforce: "enforce";
|
|
112
111
|
"report-only": "report-only";
|
|
112
|
+
off: "off";
|
|
113
113
|
}>>;
|
|
114
114
|
hsts: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodLiteral<false>]>>;
|
|
115
115
|
frameOptions: z.ZodDefault<z.ZodEnum<{
|
package/dist/info-2OOXFTJX.js
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/dist/mcp-ML4QK5ON.js
CHANGED
|
File without changes
|
package/dist/netlify-PMLHVPN4.js
CHANGED
|
File without changes
|
package/dist/node-BPJ3Z4DT.js
CHANGED
|
File without changes
|
package/dist/openapi-2HVGJMSN.js
CHANGED
|
File without changes
|
|
File without changes
|
package/dist/router-TLEAOFID.js
CHANGED
|
File without changes
|
package/dist/routes-XNS3UM3B.js
CHANGED
|
File without changes
|
package/dist/scan-7MJC6PYU.js
CHANGED
|
File without changes
|
package/dist/schema-LYOLNHZ7.js
CHANGED
|
File without changes
|
|
@@ -261,6 +261,11 @@ interface InProcessApprovalRequest {
|
|
|
261
261
|
type InProcessAwaitApproval = (req: InProcessApprovalRequest) => Promise<boolean | HitlDecision>;
|
|
262
262
|
interface StreamAgentTurnInProcessInput {
|
|
263
263
|
message: string;
|
|
264
|
+
/**
|
|
265
|
+
* M35 (multimodal) — images to send alongside the text. Threaded to the SDK's structured
|
|
266
|
+
* `SDKUserMessage { text, images }` send form. Absent ⇒ the string send path is byte-unchanged.
|
|
267
|
+
*/
|
|
268
|
+
images?: Parameters<typeof streamAgentUIMessages>[2]['images'];
|
|
264
269
|
/** Resume key; a fresh id per run when omitted. */
|
|
265
270
|
sessionId?: string;
|
|
266
271
|
/**
|
package/dist/server/index.js
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/dist/start-YLR2SFHU.js
CHANGED
|
File without changes
|
package/dist/static-MB2CD5Y3.js
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/dist/vercel-J6G7ZHYQ.js
CHANGED
|
File without changes
|
|
File without changes
|
package/package.json
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "theokit",
|
|
3
|
-
"version": "0.43.
|
|
3
|
+
"version": "0.43.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"license": "Apache-2.0",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "tsup",
|
|
9
|
+
"test": "echo 'theokit tests run from root: pnpm test'"
|
|
10
|
+
},
|
|
7
11
|
"bin": {
|
|
8
12
|
"theokit": "./dist/cli/index.js"
|
|
9
13
|
},
|
|
@@ -109,6 +113,8 @@
|
|
|
109
113
|
"dist"
|
|
110
114
|
],
|
|
111
115
|
"dependencies": {
|
|
116
|
+
"@theokit/agents": "workspace:^",
|
|
117
|
+
"@theokit/http": "workspace:^",
|
|
112
118
|
"@vitejs/plugin-react": "^4.7.0",
|
|
113
119
|
"busboy": "^1.6.0",
|
|
114
120
|
"cac": "^6.7.14",
|
|
@@ -122,9 +128,7 @@
|
|
|
122
128
|
"superjson": "^2.2.6",
|
|
123
129
|
"tsx": "^4.22.4",
|
|
124
130
|
"typescript": "^5.9.3",
|
|
125
|
-
"vite": "^6.4.3"
|
|
126
|
-
"@theokit/agents": "^0.43.0",
|
|
127
|
-
"@theokit/http": "^0.7.0"
|
|
131
|
+
"vite": "^6.4.3"
|
|
128
132
|
},
|
|
129
133
|
"peerDependencies": {
|
|
130
134
|
"@theokit/sdk": "^4.0.1",
|
|
@@ -171,14 +175,10 @@
|
|
|
171
175
|
"unstorage": "^1.17.5"
|
|
172
176
|
},
|
|
173
177
|
"publishConfig": {
|
|
174
|
-
"provenance":
|
|
178
|
+
"provenance": true,
|
|
175
179
|
"access": "public"
|
|
176
180
|
},
|
|
177
181
|
"engines": {
|
|
178
182
|
"node": ">=22.12.0"
|
|
179
|
-
},
|
|
180
|
-
"scripts": {
|
|
181
|
-
"build": "tsup",
|
|
182
|
-
"test": "echo 'theokit tests run from root: pnpm test'"
|
|
183
183
|
}
|
|
184
|
-
}
|
|
184
|
+
}
|
package/LICENSE
DELETED
|
@@ -1,201 +0,0 @@
|
|
|
1
|
-
Apache License
|
|
2
|
-
Version 2.0, January 2004
|
|
3
|
-
http://www.apache.org/licenses/
|
|
4
|
-
|
|
5
|
-
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
6
|
-
|
|
7
|
-
1. Definitions.
|
|
8
|
-
|
|
9
|
-
"License" shall mean the terms and conditions for use, reproduction,
|
|
10
|
-
and distribution as defined by Sections 1 through 9 of this document.
|
|
11
|
-
|
|
12
|
-
"Licensor" shall mean the copyright owner or entity authorized by
|
|
13
|
-
the copyright owner that is granting the License.
|
|
14
|
-
|
|
15
|
-
"Legal Entity" shall mean the union of the acting entity and all
|
|
16
|
-
other entities that control, are controlled by, or are under common
|
|
17
|
-
control with that entity. For the purposes of this definition,
|
|
18
|
-
"control" means (i) the power, direct or indirect, to cause the
|
|
19
|
-
direction or management of such entity, whether by contract or
|
|
20
|
-
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
21
|
-
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
22
|
-
|
|
23
|
-
"You" (or "Your") shall mean an individual or Legal Entity
|
|
24
|
-
exercising permissions granted by this License.
|
|
25
|
-
|
|
26
|
-
"Source" form shall mean the preferred form for making modifications,
|
|
27
|
-
including but not limited to software source code, documentation
|
|
28
|
-
source, and configuration files.
|
|
29
|
-
|
|
30
|
-
"Object" form shall mean any form resulting from mechanical
|
|
31
|
-
transformation or translation of a Source form, including but
|
|
32
|
-
not limited to compiled object code, generated documentation,
|
|
33
|
-
and conversions to other media types.
|
|
34
|
-
|
|
35
|
-
"Work" shall mean the work of authorship, whether in Source or
|
|
36
|
-
Object form, made available under the License, as indicated by a
|
|
37
|
-
copyright notice that is included in or attached to the work
|
|
38
|
-
(an example is provided in the Appendix below).
|
|
39
|
-
|
|
40
|
-
"Derivative Works" shall mean any work, whether in Source or Object
|
|
41
|
-
form, that is based on (or derived from) the Work and for which the
|
|
42
|
-
editorial revisions, annotations, elaborations, or other modifications
|
|
43
|
-
represent, as a whole, an original work of authorship. For the purposes
|
|
44
|
-
of this License, Derivative Works shall not include works that remain
|
|
45
|
-
separable from, or merely link (or bind by name) to the interfaces of,
|
|
46
|
-
the Work and Derivative Works thereof.
|
|
47
|
-
|
|
48
|
-
"Contribution" shall mean any work of authorship, including
|
|
49
|
-
the original version of the Work and any modifications or additions
|
|
50
|
-
to that Work or Derivative Works thereof, that is intentionally
|
|
51
|
-
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
52
|
-
or by an individual or Legal Entity authorized to submit on behalf of
|
|
53
|
-
the copyright owner. For the purposes of this definition, "submitted"
|
|
54
|
-
means any form of electronic, verbal, or written communication sent
|
|
55
|
-
to the Licensor or its representatives, including but not limited to
|
|
56
|
-
communication on electronic mailing lists, source code control systems,
|
|
57
|
-
and issue tracking systems that are managed by, or on behalf of, the
|
|
58
|
-
Licensor for the purpose of discussing and improving the Work, but
|
|
59
|
-
excluding communication that is conspicuously marked or otherwise
|
|
60
|
-
designated in writing by the copyright owner as "Not a Contribution."
|
|
61
|
-
|
|
62
|
-
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
63
|
-
on behalf of whom a Contribution has been received by Licensor and
|
|
64
|
-
subsequently incorporated within the Work.
|
|
65
|
-
|
|
66
|
-
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
67
|
-
this License, each Contributor hereby grants to You a perpetual,
|
|
68
|
-
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
69
|
-
copyright license to reproduce, prepare Derivative Works of,
|
|
70
|
-
publicly display, publicly perform, sublicense, and distribute the
|
|
71
|
-
Work and such Derivative Works in Source or Object form.
|
|
72
|
-
|
|
73
|
-
3. Grant of Patent License. Subject to the terms and conditions of
|
|
74
|
-
this License, each Contributor hereby grants to You a perpetual,
|
|
75
|
-
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
76
|
-
(except as stated in this section) patent license to make, have made,
|
|
77
|
-
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
78
|
-
where such license applies only to those patent claims licensable
|
|
79
|
-
by such Contributor that are necessarily infringed by their
|
|
80
|
-
Contribution(s) alone or by combination of their Contribution(s)
|
|
81
|
-
with the Work to which such Contribution(s) was submitted. If You
|
|
82
|
-
institute patent litigation against any entity (including a
|
|
83
|
-
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
84
|
-
or a Contribution incorporated within the Work constitutes direct
|
|
85
|
-
or contributory patent infringement, then any patent licenses
|
|
86
|
-
granted to You under this License for that Work shall terminate
|
|
87
|
-
as of the date such litigation is filed.
|
|
88
|
-
|
|
89
|
-
4. Redistribution. You may reproduce and distribute copies of the
|
|
90
|
-
Work or Derivative Works thereof in any medium, with or without
|
|
91
|
-
modifications, and in Source or Object form, provided that You
|
|
92
|
-
meet the following conditions:
|
|
93
|
-
|
|
94
|
-
(a) You must give any other recipients of the Work or
|
|
95
|
-
Derivative Works a copy of this License; and
|
|
96
|
-
|
|
97
|
-
(b) You must cause any modified files to carry prominent notices
|
|
98
|
-
stating that You changed the files; and
|
|
99
|
-
|
|
100
|
-
(c) You must retain, in the Source form of any Derivative Works
|
|
101
|
-
that You distribute, all copyright, patent, trademark, and
|
|
102
|
-
attribution notices from the Source form of the Work,
|
|
103
|
-
excluding those notices that do not pertain to any part of
|
|
104
|
-
the Derivative Works; and
|
|
105
|
-
|
|
106
|
-
(d) If the Work includes a "NOTICE" text file as part of its
|
|
107
|
-
distribution, then any Derivative Works that You distribute must
|
|
108
|
-
include a readable copy of the attribution notices contained
|
|
109
|
-
within such NOTICE file, excluding those notices that do not
|
|
110
|
-
pertain to any part of the Derivative Works, in at least one
|
|
111
|
-
of the following places: within a NOTICE text file distributed
|
|
112
|
-
as part of the Derivative Works; within the Source form or
|
|
113
|
-
documentation, if provided along with the Derivative Works; or,
|
|
114
|
-
within a display generated by the Derivative Works, if and
|
|
115
|
-
wherever such third-party notices normally appear. The contents
|
|
116
|
-
of the NOTICE file are for informational purposes only and
|
|
117
|
-
do not modify the License. You may add Your own attribution
|
|
118
|
-
notices within Derivative Works that You distribute, alongside
|
|
119
|
-
or as an addendum to the NOTICE text from the Work, provided
|
|
120
|
-
that such additional attribution notices cannot be construed
|
|
121
|
-
as modifying the License.
|
|
122
|
-
|
|
123
|
-
You may add Your own copyright statement to Your modifications and
|
|
124
|
-
may provide additional or different license terms and conditions
|
|
125
|
-
for use, reproduction, or distribution of Your modifications, or
|
|
126
|
-
for any such Derivative Works as a whole, provided Your use,
|
|
127
|
-
reproduction, and distribution of the Work otherwise complies with
|
|
128
|
-
the conditions stated in this License.
|
|
129
|
-
|
|
130
|
-
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
131
|
-
any Contribution intentionally submitted for inclusion in the Work
|
|
132
|
-
by You to the Licensor shall be under the terms and conditions of
|
|
133
|
-
this License, without any additional terms or conditions.
|
|
134
|
-
Notwithstanding the above, nothing herein shall supersede or modify
|
|
135
|
-
the terms of any separate license agreement you may have executed
|
|
136
|
-
with Licensor regarding such Contributions.
|
|
137
|
-
|
|
138
|
-
6. Trademarks. This License does not grant permission to use the trade
|
|
139
|
-
names, trademarks, service marks, or product names of the Licensor,
|
|
140
|
-
except as required for describing the origin of the Work and
|
|
141
|
-
reproducing the content of the NOTICE file.
|
|
142
|
-
|
|
143
|
-
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
144
|
-
agreed to in writing, Licensor provides the Work (and each
|
|
145
|
-
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
146
|
-
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
147
|
-
implied, including, without limitation, any warranties or conditions
|
|
148
|
-
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
149
|
-
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
150
|
-
appropriateness of using or redistributing the Work and assume any
|
|
151
|
-
risks associated with Your exercise of permissions under this License.
|
|
152
|
-
|
|
153
|
-
8. Limitation of Liability. In no event and under no legal theory,
|
|
154
|
-
whether in tort (including negligence), contract, or otherwise,
|
|
155
|
-
unless required by applicable law (such as deliberate and grossly
|
|
156
|
-
negligent acts) or agreed to in writing, shall any Contributor be
|
|
157
|
-
liable to You for damages, including any direct, indirect, special,
|
|
158
|
-
incidental, or consequential damages of any character arising as a
|
|
159
|
-
result of this License or out of the use or inability to use the
|
|
160
|
-
Work (including but not limited to damages for loss of goodwill,
|
|
161
|
-
work stoppage, computer failure or malfunction, or any and all
|
|
162
|
-
other commercial damages or losses), even if such Contributor
|
|
163
|
-
has been advised of the possibility of such damages.
|
|
164
|
-
|
|
165
|
-
9. Accepting Warranty or Additional Liability. While redistributing
|
|
166
|
-
the Work or Derivative Works thereof, You may choose to offer,
|
|
167
|
-
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
168
|
-
or other liability obligations and/or rights consistent with this
|
|
169
|
-
License. However, in accepting such obligations, You may act only
|
|
170
|
-
on Your own behalf and on Your sole responsibility, not on behalf
|
|
171
|
-
of any other Contributor, and only if You agree to indemnify,
|
|
172
|
-
defend, and hold each Contributor harmless for any liability
|
|
173
|
-
incurred by, or claims asserted against, such Contributor by reason
|
|
174
|
-
of your accepting any such warranty or additional liability.
|
|
175
|
-
|
|
176
|
-
END OF TERMS AND CONDITIONS
|
|
177
|
-
|
|
178
|
-
APPENDIX: How to apply the Apache License to your work.
|
|
179
|
-
|
|
180
|
-
To apply the Apache License to your work, attach the following
|
|
181
|
-
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
182
|
-
replaced with your own identifying information. (Don't include
|
|
183
|
-
the brackets!) The text should be enclosed in the appropriate
|
|
184
|
-
comment syntax for the file format. We also recommend that a
|
|
185
|
-
file or class name and description of purpose be included on the
|
|
186
|
-
same "printed page" as the copyright notice for easier
|
|
187
|
-
identification within third-party archives.
|
|
188
|
-
|
|
189
|
-
Copyright 2026 usetheo.dev
|
|
190
|
-
|
|
191
|
-
Licensed under the Apache License, Version 2.0 (the "License");
|
|
192
|
-
you may not use this file except in compliance with the License.
|
|
193
|
-
You may obtain a copy of the License at
|
|
194
|
-
|
|
195
|
-
http://www.apache.org/licenses/LICENSE-2.0
|
|
196
|
-
|
|
197
|
-
Unless required by applicable law or agreed to in writing, software
|
|
198
|
-
distributed under the License is distributed on an "AS IS" BASIS,
|
|
199
|
-
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
200
|
-
See the License for the specific language governing permissions and
|
|
201
|
-
limitations under the License.
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/client/consume-ui-message-stream.ts","../src/client/agent-client.ts","../src/client/last-user-text.ts","../src/client/channel-transport.ts","../src/client/in-process-transport.ts","../src/client/http-transport.ts","../src/client/create-agent-client.ts"],"sourcesContent":["import type { UIMessage, UIMessageChunk } from 'ai'\n\n/**\n * M2 (theokit-ai-first) — read a TheoKit agent endpoint's `UIMessageStream` SSE `Response`\n * into reconstructed assistant `UIMessage`s, reusing the `ai` package's own consumer\n * primitives (`parseJsonEventStream` + `readUIMessageStream`) — the exact path\n * `@ai-sdk/react`'s `useChat` runs internally. No reinvented wire parser (Rule 9).\n *\n * `ai` is an OPTIONAL peer dependency, so it is imported dynamically: an app that never\n * calls an agent never pays for it, and importing `theokit/client` does not hard-require\n * `ai` (mirrors how the agent runtime dynamically imports `@theokit/sdk`). An agent app\n * always has `ai` installed (it is the UIMessageStream consumer).\n *\n * `onMessage` is invoked on every reconstruction step with the latest snapshot of the\n * assistant message, so a caller (the `useAgent` hook) can render streaming updates.\n */\nexport async function consumeUIMessageStream(\n response: Response,\n onMessage: (message: UIMessage) => void,\n): Promise<void> {\n const chunkStream = await responseToChunkStream(response)\n await consumeChunkStream(chunkStream, onMessage)\n}\n\n/**\n * M41 (ADR-0050 D3) — the reusable middle piece: a UIMessageStream SSE `Response` →\n * `ReadableStream<UIMessageChunk>`, reusing `ai`'s own `parseJsonEventStream` (the exact primitive\n * `useChat` runs). This is precisely what a `ChatTransport.sendMessages` returns, so `HttpTransport`\n * builds on it directly (no reinvented wire parser — Rule 9). A body-less response yields an empty stream.\n */\nexport async function responseToChunkStream(\n response: Response,\n): Promise<ReadableStream<UIMessageChunk>> {\n if (response.body === null) {\n return new ReadableStream<UIMessageChunk>({\n start(controller) {\n controller.close()\n },\n })\n }\n\n const { parseJsonEventStream, uiMessageChunkSchema } = await import('ai')\n\n // ai validates each SSE JSON frame against its own strict chunk schema (the exact gate `useChat`\n // runs), then yields `{ success, value }`; forward the valid chunks.\n const parsed = parseJsonEventStream({ stream: response.body, schema: uiMessageChunkSchema })\n return new ReadableStream<UIMessageChunk>({\n async start(controller) {\n for await (const result of parsed) {\n if (result.success) controller.enqueue(result.value)\n }\n controller.close()\n },\n })\n}\n\n/**\n * M41 (ADR-0050 D6) — read a `ReadableStream<UIMessageChunk>` into reconstructed assistant\n * `UIMessage`s via `ai`'s `readUIMessageStream`. Shared by `consumeUIMessageStream` (Response path)\n * and the framework-agnostic `AgentClient` store (transport path). `onMessage` fires on every\n * reconstruction step so a caller can render streaming updates.\n */\nexport async function consumeChunkStream(\n stream: ReadableStream<UIMessageChunk>,\n onMessage: (message: UIMessage) => void,\n): Promise<void> {\n const { readUIMessageStream } = await import('ai')\n // #136 — a provider failure (401/429/5xx) arrives as a `{ type: 'error', errorText }` chunk, NOT a\n // thrown rejection (the in-process runner and the SSE path both emit it as data). With the default\n // `readUIMessageStream({ stream })` (no `onError`, `terminateOnError` off) that chunk is silently\n // swallowed — the stream ends \"clean\" and the store settles to 'done' instead of 'error'.\n // `onError` captures the error; `terminateOnError` stops reconstructing partial messages after it AND\n // (under ai@7.0.14) errors the underlying iterator — so the `for await` below rejects and\n // `AgentClient.#drive`'s existing catch surfaces it (status='error', error set). The post-loop\n // `throw` is a defensive fallback that still surfaces the captured error if a future `ai` version\n // stops rejecting under `terminateOnError`; it is dead code under ai@7.0.14 but cheap version-robustness.\n let streamError: Error | undefined\n for await (const message of readUIMessageStream({\n stream,\n onError: (err) => {\n streamError = err instanceof Error ? err : new Error(String(err))\n },\n terminateOnError: true,\n })) {\n onMessage(message)\n }\n if (streamError !== undefined) throw streamError\n}\n","import type { UIMessage, UIMessageChunk } from 'ai'\n\nimport { consumeChunkStream } from './consume-ui-message-stream.js'\nimport type { AgentTransport, ApprovalDecision, RequestContext } from './transport.js'\n\nexport type UseAgentStatus = 'idle' | 'streaming' | 'done' | 'error'\n\n/** The observable state the store exposes (stable reference between emits — `useSyncExternalStore` contract). */\nexport interface AgentClientState {\n /** The CURRENT turn's assistant messages (per-turn; reset each `send`). Back-compat — unchanged since M41. */\n messages: UIMessage[]\n /**\n * M46 — the full conversation: committed turns + the current turn's user message + in-flight assistant.\n * Accumulated across sends (never reset except by `reset()`), with stable ids committed exactly once.\n * Render this instead of hand-rolling a transcript from `messages`.\n */\n thread: UIMessage[]\n status: UseAgentStatus\n error: Error | undefined\n}\n\n/** Derive the turn text from a typed input: `input.message` when present, else the serialized input. */\nfunction inputToText(input: unknown): string {\n if (\n typeof input === 'object' &&\n input !== null &&\n typeof (input as { message?: unknown }).message === 'string'\n ) {\n return (input as { message: string }).message\n }\n if (typeof input === 'string') return input\n return JSON.stringify(input)\n}\n\n/** Build a user `UIMessage` from a typed input (text from `{ message }`, else the serialized input). */\nfunction buildUserMessage(input: unknown): UIMessage {\n return {\n id: crypto.randomUUID(),\n role: 'user',\n parts: [{ type: 'text', text: inputToText(input) }],\n }\n}\n\n/**\n * M41 (ADR-0050 D6) — the framework-agnostic agent client store.\n *\n * Holds `messages`/`status`/`error`, drives an {@link AgentTransport}, and notifies subscribers on\n * change. It is the SINGLE consolidation point: web (`HttpTransport`) and terminal/desktop\n * (`InProcessTransport`) run the SAME store. `useAgent` is a thin React binding over it via\n * `useSyncExternalStore`; a standalone (no-React) client (M44) can subscribe directly. Being\n * framework-agnostic, it is unit-tested without a DOM.\n */\nexport class AgentClient<TInput = unknown> {\n readonly #transport: AgentTransport\n readonly #chatId = crypto.randomUUID()\n readonly #listeners = new Set<() => void>()\n /** M43 — resolves per-request context (evaluated on every send/reconnect — dynamic, never stale). */\n readonly #contextResolver: (() => RequestContext | undefined) | undefined\n\n #messages: UIMessage[] = []\n #status: UseAgentStatus = 'idle'\n #error: Error | undefined\n #controller: AbortController | null = null\n // M46 — conversation accumulation (React-free; all surfaces inherit it via the snapshot).\n /** Committed (finished) turns — user + assistant, with stable fabricated ids. */\n #committed: UIMessage[] = []\n /** The current turn's user message (in `thread` but never in `messages` — back-compat). */\n #currentUser: UIMessage | undefined\n /** A stable id for the current turn's assistant (the SDK leaves it empty — we fabricate one). */\n #currentAssistantId = ''\n #snapshot: AgentClientState = { messages: [], thread: [], status: 'idle', error: undefined }\n\n constructor(transport: AgentTransport, contextResolver?: () => RequestContext | undefined) {\n this.#transport = transport\n this.#contextResolver = contextResolver\n }\n\n /** Subscribe to state changes; returns an unsubscribe fn. */\n subscribe = (listener: () => void): (() => void) => {\n this.#listeners.add(listener)\n return () => {\n this.#listeners.delete(listener)\n }\n }\n\n /** The current immutable snapshot (stable reference until the next emit). */\n getSnapshot = (): AgentClientState => this.#snapshot\n\n #emit(): void {\n // thread = committed turns + this turn's user + the in-flight assistant (`messages`). New array per\n // emit is fine — the SNAPSHOT reference only changes here, satisfying useSyncExternalStore.\n const thread = this.#currentUser\n ? [...this.#committed, this.#currentUser, ...this.#messages]\n : [...this.#committed, ...this.#messages]\n this.#snapshot = { messages: this.#messages, thread, status: this.#status, error: this.#error }\n for (const listener of this.#listeners) listener()\n }\n\n #upsert(message: UIMessage): void {\n const next = [...this.#messages]\n const idx = next.findIndex((existing) => existing.id === message.id)\n if (idx >= 0) next[idx] = message\n else next.push(message)\n this.#messages = next\n }\n\n async #drive(\n open: () => Promise<ReadableStream<UIMessageChunk> | null>,\n controller: AbortController,\n ): Promise<void> {\n // Read via a function (not a narrowed local) — `aborted` flips ASYNC across the awaits below, so the\n // control-flow narrowing of a direct `signal.aborted` read would be wrong. A stale drive (its\n // controller aborted because a newer send/abort took over) MUST NOT clobber the newer status.\n const aborted = (): boolean => controller.signal.aborted\n try {\n const stream = await open()\n if (aborted()) return\n if (stream === null) {\n // Nothing to resume (e.g. reconnect after the run completed). Settle without error.\n this.#status = this.#messages.length > 0 ? 'done' : 'idle'\n this.#emit()\n return\n }\n await consumeChunkStream(stream, (message) => {\n if (aborted()) return\n // The SDK leaves the assistant message id empty — fabricate a stable per-turn id so every chunk\n // upserts into the SAME message and the committed copy has a collision-free key (M46).\n const stamped = message.id ? message : { ...message, id: this.#currentAssistantId }\n this.#upsert(stamped)\n this.#emit()\n })\n if (aborted()) return\n this.#status = 'done'\n this.#emit()\n } catch (err) {\n if (aborted()) return\n this.#error = err instanceof Error ? err : new Error(String(err))\n this.#status = 'error'\n this.#emit()\n }\n }\n\n /** Send a typed input; opens a fresh stream (replaces prior messages). */\n send = (input: TInput): void => {\n // M46 — commit the PRIOR turn into history exactly once, but ONLY if it finished cleanly (`done`).\n // An errored or aborted turn (status !== 'done') is dropped, keeping committed history uncorrupted.\n if (this.#status === 'done' && this.#currentUser) {\n this.#committed = [...this.#committed, this.#currentUser, ...this.#messages]\n }\n this.abort()\n const controller = new AbortController()\n this.#controller = controller\n const userMsg = buildUserMessage(input)\n this.#currentUser = userMsg\n this.#currentAssistantId = crypto.randomUUID()\n this.#messages = []\n this.#error = undefined\n this.#status = 'streaming'\n this.#emit()\n const context = this.#contextResolver?.()\n void this.#drive(\n () =>\n this.#transport.sendMessages({\n trigger: 'submit-message',\n chatId: this.#chatId,\n messageId: undefined,\n messages: [userMsg],\n abortSignal: controller.signal,\n // Only object inputs flow as the request `body` (the turn text is always in `messages`);\n // a primitive input is carried by the user message, never spread into the body.\n body: typeof input === 'object' && input !== null ? input : undefined,\n // M43 — per-request context reaches every transport (headers → HTTP, metadata → in-process/channel).\n headers: context?.headers,\n metadata: context?.metadata,\n }),\n controller,\n )\n }\n\n /** Resume an interrupted stream via the transport's `reconnectToStream` (no-op when unavailable). */\n reconnect = (): void => {\n const controller = new AbortController()\n this.#controller = controller\n // Reconnecting before any send() (or after reset()) leaves #currentAssistantId empty — fabricate one\n // so a replayed assistant never lands in `thread` with an empty, non-unique id (M46 invariant).\n if (!this.#currentAssistantId) this.#currentAssistantId = crypto.randomUUID()\n // Reconnect means \"resume/retry\" — a stale error must not linger next to a fresh 'streaming' status.\n this.#error = undefined\n this.#status = 'streaming'\n this.#emit()\n const context = this.#contextResolver?.()\n void this.#drive(\n () =>\n this.#transport.reconnectToStream({\n chatId: this.#chatId,\n headers: context?.headers,\n metadata: context?.metadata,\n }),\n controller,\n )\n }\n\n /** Abort an in-flight stream (not an error — leaves messages as-is). */\n abort = (): void => {\n this.#controller?.abort()\n this.#controller = null\n }\n\n /** Clear messages + error, back to idle. */\n reset = (): void => {\n this.abort()\n this.#messages = []\n // M46 — reset means a NEW conversation: clear committed history + the current turn's user too.\n this.#committed = []\n this.#currentUser = undefined\n this.#error = undefined\n this.#status = 'idle'\n this.#emit()\n }\n\n /** Settle a paused HITL approval via the transport's HITL path (HTTP POST or inline callback). */\n approve = async (approvalId: string, decision: ApprovalDecision): Promise<void> => {\n await this.#transport.approve?.(approvalId, decision)\n }\n}\n","import type { UIMessage } from 'ai'\n\n/**\n * M41/M42 — extract the turn text from the last user message's text parts. Shared by the transports\n * that hand a plain `message` string to an in-process/push runner (`InProcessTransport`,\n * `ChannelTransport`) rather than POSTing the `messages[]` array (`HttpTransport`). One definition (G12).\n */\nexport function extractLastUserText(messages: readonly UIMessage[]): string {\n for (let i = messages.length - 1; i >= 0; i--) {\n const message = messages[i]\n if (message.role !== 'user') continue\n const text = message.parts\n .filter((part): part is { type: 'text'; text: string } => part.type === 'text')\n .map((part) => part.text)\n .join('')\n if (text.length > 0) return text\n }\n return ''\n}\n","import type { ChatTransport, UIMessage, UIMessageChunk } from 'ai'\n\nimport { extractLastUserText } from './last-user-text.js'\nimport type { AgentTransport, ApprovalDecision } from './transport.js'\n\n/** Handlers the transport hands to the injected push source for one turn. */\nexport interface ChannelTurnHandlers {\n /** One pushed JSONL line (a serialized `UIMessageChunk`). */\n onLine: (line: string) => void\n /** The turn ended — no more lines. */\n onClose: () => void\n /** The push source failed. */\n onError?: (err: unknown) => void\n}\n\n/**\n * The injected push source — a Tauri `Channel`/`invoke` bridge, kept STRUCTURAL so core adds no\n * `@tauri-apps/*` dependency and the transport is testable with a fake (ADR-0051 D2). The Tauri app\n * wires it: `new Channel()`, `channel.onmessage = onLine`, `invoke('run_agent', { message, channel })`,\n * returning a teardown that aborts the sidecar turn.\n */\nexport interface ChannelPushSource {\n /**\n * Start a turn; deliver each JSONL `UIMessageChunk` line to `onLine`, then `onClose`. Return a teardown.\n * `turn.context` (M43) is the per-request context (from the seam's `metadata`) — the Tauri `invoke`\n * forwards it to the sidecar. When no context is set it is present as `context: undefined` (the key is\n * NOT absent) — a sidecar that checks `'context' in turn` should treat `undefined` as \"no context\".\n */\n start(turn: { message: string; context?: unknown }, handlers: ChannelTurnHandlers): () => void\n /** Optional HITL settle (another Tauri `invoke`). */\n settle?(approvalId: string, decision: ApprovalDecision): Promise<void>\n}\n\nexport interface ChannelTransportOptions {\n source: ChannelPushSource\n}\n\n/**\n * M42 (ADR-0051) — `ChatTransport` over a Tauri-`Channel`-shaped push source, for the desktop webview.\n *\n * - `sendMessages`: start the turn via the injected source and bridge its pushed JSONL frames into a\n * `ReadableStream<UIMessageChunk>` (built in `start` — a Channel is push, so the stream's queue buffers\n * frames; ADR-0051 D3). A malformed JSONL line is SKIPPED, never fatal (ADR-0051 D4, Rule 8). `abortSignal`\n * tears down the source and closes the stream.\n * - `reconnectToStream`: always `null` — the M36 sidecar runs the turn directly (no durable server stream);\n * this is the honest parity for a single-process push surface (ADR-0051 D5; mirrors `InProcessTransport`).\n * - `approve`: routes to the injected `settle` (another Tauri `invoke`); absent `settle` → a typed error.\n *\n * The push source is INJECTED — core stays Tauri-agnostic and this transport is unit-tested with a fake.\n */\nexport class ChannelTransport implements AgentTransport {\n readonly #source: ChannelPushSource\n\n constructor(options: ChannelTransportOptions) {\n this.#source = options.source\n }\n\n sendMessages(\n options: Parameters<ChatTransport<UIMessage>['sendMessages']>[0],\n ): Promise<ReadableStream<UIMessageChunk>> {\n const { messages, abortSignal, metadata } = options\n const message = extractLastUserText(messages)\n const source = this.#source\n\n // Per-stream teardown/abort-detach, hoisted so `cancel` (consumer stops reading) can also tear the\n // source down. `closed` makes every terminal transition idempotent (no enqueue/close after close).\n let closed = false\n let teardown: () => void = () => undefined\n let detachAbort: () => void = () => undefined\n\n const stream = new ReadableStream<UIMessageChunk>({\n start(controller) {\n const finish = (settle: () => void): void => {\n if (closed) return\n closed = true\n detachAbort()\n settle()\n }\n teardown = source.start(\n { message, context: metadata },\n {\n onLine: (line) => {\n if (closed) return\n // Skip a malformed pushed line — one bad frame must never crash the webview (ADR-0051 D4).\n let parsed: unknown\n try {\n parsed = JSON.parse(line)\n } catch {\n return\n }\n // Discriminant guard: a `UIMessageChunk` is an object with a string `type`. The trust\n // boundary here is the LOCAL sidecar (not the network), so a discriminant check — not the\n // full `ai` schema (which isn't exposed standalone) — is proportionate: it rejects\n // structureless / wrong-shape payloads before they reach `readUIMessageStream`. Same\n // skip-not-crash policy as a parse error.\n if (\n typeof parsed !== 'object' ||\n parsed === null ||\n typeof (parsed as { type?: unknown }).type !== 'string'\n ) {\n return\n }\n controller.enqueue(parsed as UIMessageChunk)\n },\n onClose: () => {\n finish(() => {\n controller.close()\n })\n },\n onError: (err) => {\n finish(() => {\n controller.error(err)\n })\n },\n },\n )\n if (abortSignal !== undefined) {\n const onAbort = (): void => {\n finish(() => {\n teardown()\n controller.close()\n })\n }\n if (abortSignal.aborted) onAbort()\n else {\n abortSignal.addEventListener('abort', onAbort)\n detachAbort = () => {\n abortSignal.removeEventListener('abort', onAbort)\n }\n }\n }\n },\n cancel() {\n // The consumer stopped reading (reader.cancel) — abort the source turn + drop the abort listener.\n if (closed) return\n closed = true\n detachAbort()\n teardown()\n },\n })\n return Promise.resolve(stream)\n }\n\n reconnectToStream(): Promise<ReadableStream<UIMessageChunk> | null> {\n return Promise.resolve(null)\n }\n\n async approve(approvalId: string, decision: ApprovalDecision): Promise<void> {\n if (this.#source.settle === undefined) {\n throw new Error(`This channel source has no HITL settle — cannot approve '${approvalId}'.`)\n }\n await this.#source.settle(approvalId, decision)\n }\n}\n","import type { ChatTransport, UIMessage, UIMessageChunk } from 'ai'\n\nimport { extractLastUserText } from './last-user-text.js'\nimport type { AgentTransport, ApprovalDecision } from './transport.js'\n\n/** An inline approval request handed to the transport's resolver (structural — no server import). */\nexport interface InProcessApprovalRequestLike {\n approvalId: string\n toolName: string\n opts: unknown\n}\n\n/** Resolve one gated-tool approval inline (mirrors the SDK's `boolean | HitlDecision` return). */\nexport type InProcessAwaitApproval = (\n req: InProcessApprovalRequestLike,\n) => Promise<boolean | ApprovalDecision>\n\n/** The input the injected runner receives (structurally compatible with `StreamAgentTurnInProcessInput`). */\nexport interface InProcessRunInput {\n message: string\n sessionId?: string\n signal?: AbortSignal\n awaitApproval?: InProcessAwaitApproval\n /** M43 — per-request context (from `sendMessages`'s `metadata`) — tenant / provider / auth for the runner. */\n context?: unknown\n}\n\n/**\n * The in-process turn runner. The consumer binds `streamAgentTurnInProcess(mod, apiKey, …)`:\n * `new InProcessTransport({ run: (input) => streamAgentTurnInProcess(mod, apiKey, input) })`.\n * Injecting it keeps this client module decoupled from `server/` and makes the transport testable.\n */\nexport type InProcessRunner = (input: InProcessRunInput) => AsyncGenerator<UIMessageChunk>\n\nexport interface InProcessTransportOptions {\n run: InProcessRunner\n}\n\n/** Bridge an `AsyncGenerator<UIMessageChunk>` into a pull-based `ReadableStream<UIMessageChunk>`. */\nfunction generatorToStream(gen: AsyncGenerator<UIMessageChunk>): ReadableStream<UIMessageChunk> {\n return new ReadableStream<UIMessageChunk>({\n async pull(controller) {\n try {\n const result = await gen.next()\n if (result.done === true) {\n controller.close()\n return\n }\n // After the done-guard, `result` is an IteratorYieldResult<UIMessageChunk> — value is typed.\n controller.enqueue(result.value)\n } catch (err) {\n controller.error(err)\n }\n },\n async cancel() {\n await gen.return(undefined)\n },\n })\n}\n\n/**\n * M41 (ADR-0050 D4) — `ChatTransport` over the in-process seam (`streamAgentTurnInProcess`), for the\n * terminal/desktop surfaces that run client + server in ONE process (no HTTP loopback).\n *\n * - `sendMessages`: bridge the injected runner's `AsyncGenerator<UIMessageChunk>` into a\n * `ReadableStream<UIMessageChunk>` (honoring `abortSignal`, which the runner forwards to the SDK).\n * - `reconnectToStream`: always `null` — a single process has no dropped server-side stream to resume\n * (mirrors `ai`'s `DirectChatTransport`).\n * - `approve`: resolve the pending inline approval by id (the run parks on `awaitApproval`). An unknown\n * id rejects (fail-fast, Rule 8 — never a silent resolve).\n *\n * Error asymmetry vs `HttpTransport` (by design, matching the `ChatTransport` contract): a runner that\n * throws SYNCHRONOUSLY surfaces the error when the stream is READ (via `controller.error`), not from the\n * `sendMessages` promise — whereas `HttpTransport` throws from `sendMessages` on a non-2xx response.\n */\nexport class InProcessTransport implements AgentTransport {\n readonly #run: InProcessRunner\n /** Pending inline approvals: approvalId → resolver of the parked `awaitApproval` promise. */\n readonly #pending = new Map<string, (decision: boolean | ApprovalDecision) => void>()\n\n constructor(options: InProcessTransportOptions) {\n this.#run = options.run\n }\n\n #awaitApproval: InProcessAwaitApproval = (req) =>\n new Promise<boolean | ApprovalDecision>((resolve, reject) => {\n // Approval ids are server-minted UUIDs — a collision means a real bug (two turns reusing an id).\n // Fail fast rather than silently overwrite the earlier turn's parked resolver (Rule 8).\n if (this.#pending.has(req.approvalId)) {\n reject(new Error(`Duplicate pending approval id '${req.approvalId}' — ids must be unique.`))\n return\n }\n this.#pending.set(req.approvalId, resolve)\n })\n\n sendMessages(\n options: Parameters<ChatTransport<UIMessage>['sendMessages']>[0],\n ): Promise<ReadableStream<UIMessageChunk>> {\n const { messages, abortSignal, metadata } = options\n const generator = this.#run({\n message: extractLastUserText(messages),\n signal: abortSignal ?? undefined,\n awaitApproval: this.#awaitApproval,\n // M43 — forward per-request context (the seam's `metadata`) to the runner.\n context: metadata,\n })\n return Promise.resolve(generatorToStream(generator))\n }\n\n reconnectToStream(): Promise<ReadableStream<UIMessageChunk> | null> {\n return Promise.resolve(null)\n }\n\n approve(approvalId: string, decision: ApprovalDecision): Promise<void> {\n const resolve = this.#pending.get(approvalId)\n if (resolve === undefined) {\n return Promise.reject(\n new Error(`No pending approval '${approvalId}' (unknown or already settled).`),\n )\n }\n this.#pending.delete(approvalId)\n resolve(decision)\n return Promise.resolve()\n }\n}\n","import type { ChatTransport, UIMessage, UIMessageChunk } from 'ai'\n\nimport { responseToChunkStream } from './consume-ui-message-stream.js'\nimport type { AgentTransport, ApprovalDecision } from './transport.js'\n\n/** Extra request headers — a static record OR a resolver called per request (for dynamic auth). */\nexport type HeadersResolver = Record<string, string> | (() => Record<string, string> | undefined)\n\nexport interface HttpTransportOptions {\n /** Agent endpoint path or URL, e.g. `/api/agents/support`. */\n api: string\n /**\n * Extra request headers (e.g. auth). Static record OR a resolver evaluated on EVERY request — pass a\n * resolver when the value is dynamic (a rotating JWT), so a stale header is never sent. Merged UNDER\n * per-request headers.\n */\n headers?: HeadersResolver\n /** Override fetch (primarily for tests / non-browser hosts) — static; resolved once at construction. */\n fetch?: typeof fetch\n}\n\n/** Normalize `ai`'s `Record | Headers | undefined` header option into a plain record. */\nfunction toRecord(headers: Record<string, string> | Headers | undefined): Record<string, string> {\n if (headers === undefined) return {}\n if (headers instanceof Headers) return Object.fromEntries(headers.entries())\n return headers\n}\n\n/**\n * M41 (ADR-0050 D3) — `ChatTransport` over the web agent path.\n *\n * - `sendMessages`: `POST <api>` with the UIMessageStream `accept` + the `X-Theo-Action` CSRF header\n * (HTTP method + headers are identical to the pre-M41 `useAgent` fetch; the body shape is a superset —\n * `{ ...input, messages: [UIMessage] }` — which the server's dual-path parser accepts, so no\n * regression), captures the server-minted `x-theokit-run-id`, and returns `ReadableStream<UIMessageChunk>`\n * via `ai`'s own SSE parser (`responseToChunkStream`).\n * - `reconnectToStream`: `GET <api>/runs/<runId>/stream` (M37 durable transport); 404 → `null` (the run\n * completed / was evicted). A caller may pass a `Last-Event-ID` header to resume only the tail; by\n * default the server replays the run from the start and the client upserts by message id (idempotent).\n * - `approve`: `POST <api>/approve/<id>` (out-of-band HITL settle).\n *\n * Implemented directly (not by subclassing `DefaultChatTransport`) because reconnect keys on our\n * server-minted `runId` captured from a response header, which the base class does not expose — see\n * ADR-0050 D3.\n */\nexport class HttpTransport implements AgentTransport {\n readonly #api: string\n readonly #headers: HeadersResolver\n readonly #fetch: typeof fetch\n /** Server-minted id of the last run (from `x-theokit-run-id`) — the reconnect key. */\n #lastRunId: string | undefined\n\n constructor(options: HttpTransportOptions) {\n this.#api = options.api\n this.#headers = options.headers ?? {}\n // BIND the default fetch to globalThis — the native `fetch` throws `TypeError: Illegal invocation` when\n // invoked as a method (`this.#fetch(...)` would set `this` to this transport instance, not the window).\n // An injected fetch (tests / non-browser hosts) is a plain function and is used as-is.\n this.#fetch = options.fetch ?? globalThis.fetch.bind(globalThis)\n }\n\n /** Resolve the configured headers per request (a resolver evaluates now — dynamic auth is never stale). */\n #resolveHeaders(): Record<string, string> {\n return (typeof this.#headers === 'function' ? this.#headers() : this.#headers) ?? {}\n }\n\n async sendMessages(\n options: Parameters<ChatTransport<UIMessage>['sendMessages']>[0],\n ): Promise<ReadableStream<UIMessageChunk>> {\n const { messages, abortSignal, headers, body, chatId } = options\n // Only spread an object body (never a primitive — that would emit char-indexed keys). `body` is typed\n // `object | undefined` (ai's `ChatRequestOptions`), so no cast is needed — the guard narrows to\n // `object`. (A runtime-`null` body, which the type forbids, spreads to `{}` — harmless.) The server\n // accepts `{ messages }` (ai shape) AND `{ ...input }` (typed input), preferring the turn text.\n const extra = typeof body === 'object' ? body : undefined\n const response = await this.#fetch(this.#api, {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n accept: 'text/event-stream',\n 'X-Theo-Action': '1',\n ...this.#resolveHeaders(),\n ...toRecord(headers),\n },\n // Send the stable `chatId` as the top-level `id` — the server reads it as the sessionId, so ONE\n // conversation (SDK history + session-scoped tools like `todolist`) persists across turns instead of\n // resetting on a fresh random session each request. Placed last so a session is never shadowed by an\n // `id` field inside the typed input. Undefined chatId ⇒ key omitted ⇒ server falls back (unchanged).\n body: JSON.stringify({ ...extra, messages, id: chatId }),\n signal: abortSignal,\n })\n if (!response.ok) {\n throw new Error(\n `Agent request to ${this.#api} failed: ${response.status} ${response.statusText}`,\n )\n }\n this.#lastRunId = response.headers.get('x-theokit-run-id') ?? undefined\n return responseToChunkStream(response)\n }\n\n async reconnectToStream(\n options: Parameters<ChatTransport<UIMessage>['reconnectToStream']>[0],\n ): Promise<ReadableStream<UIMessageChunk> | null> {\n if (this.#lastRunId === undefined) return null\n const response = await this.#fetch(`${this.#api}/runs/${this.#lastRunId}/stream`, {\n method: 'GET',\n headers: { ...this.#resolveHeaders(), ...toRecord(options.headers) },\n })\n if (response.status === 404) return null\n if (!response.ok) {\n throw new Error(\n `Agent reconnect to run ${this.#lastRunId} failed: ${response.status} ${response.statusText}`,\n )\n }\n return responseToChunkStream(response)\n }\n\n async approve(approvalId: string, decision: ApprovalDecision): Promise<void> {\n const response = await this.#fetch(`${this.#api}/approve/${approvalId}`, {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n 'X-Theo-Action': '1',\n ...this.#resolveHeaders(),\n },\n body: JSON.stringify(decision),\n })\n if (!response.ok) {\n throw new Error(`Approve ${approvalId} failed: ${response.status} ${response.statusText}`)\n }\n }\n}\n","import type { UIMessage } from 'ai'\n\nimport { AgentClient, type AgentClientState } from './agent-client.js'\nimport type { AgentTransport, ApprovalDecision, RequestContext } from './transport.js'\n\nexport interface CreateAgentClientOptions {\n /**\n * M43 — per-request context attached to every turn (`headers` → HTTP; `metadata` → in-process/channel).\n * A value OR a resolver. **To keep context live across sends, pass a RESOLVER** (`() => ({...})`) — a\n * plain value is captured at construction and not re-read, so mutating `options.context` afterwards has\n * no effect (unlike `useAgent`, which reads a live ref). A resolver is re-evaluated every send/reconnect.\n */\n context?: RequestContext | (() => RequestContext | undefined)\n}\n\n/**\n * M44 (ADR-0053) — the standalone (no-React) agent client. A plain handle over the framework-agnostic\n * {@link AgentClient} store: drive a turn, observe state, settle HITL — from a node script, a CLI, a\n * test, or a non-React UI, over ANY {@link AgentTransport}.\n */\nexport interface AgentClientHandle<TInput = unknown> {\n /** Send a typed input; opens a fresh stream (replaces prior messages). */\n send(input: TInput): void\n /** Abort an in-flight stream. */\n abort(): void\n /** Clear messages + error, back to idle. */\n reset(): void\n /** Settle a paused HITL approval via the transport's HITL path. */\n approve(approvalId: string, decision: ApprovalDecision): Promise<void>\n /** Resume an interrupted stream (no-op when the transport can't). */\n reconnect(): void\n /** Subscribe to state changes; returns an unsubscribe fn. */\n subscribe(listener: () => void): () => void\n /**\n * The current immutable snapshot: per-turn `messages` (back-compat) + the full conversation `thread`\n * (M46 — committed history + in-flight, accumulated across sends) + `status` / `error`. A TUI / vanilla\n * consumer renders `thread` directly without hand-rolling a transcript.\n */\n getState(): AgentClientState\n /**\n * Drive one turn and yield the assistant `UIMessage` as it streams (progressive snapshots); iteration\n * ends when the turn settles. A `for await` script takes the last yielded value as the final result;\n * a failed turn rejects the iterator with the run error.\n */\n stream(input: TInput): AsyncIterable<UIMessage>\n}\n\n/** Drive a turn on the store and yield the latest assistant message on each streaming update. */\nfunction streamTurn<TInput>(client: AgentClient<TInput>, input: TInput): AsyncIterable<UIMessage> {\n return {\n async *[Symbol.asyncIterator]() {\n const pending: UIMessage[] = []\n let finished = false\n let streamError: Error | undefined\n let notify: (() => void) | null = null\n // Read via functions — `finished`/`streamError` flip ASYNC in the subscribe callback, so a direct\n // read would be wrongly narrowed to its initial literal by control-flow analysis (M41 pattern).\n const isFinished = (): boolean => finished\n const takeError = (): Error | undefined => streamError\n\n const unsubscribe = client.subscribe(() => {\n const snapshot = client.getSnapshot()\n if (snapshot.status === 'streaming') {\n // Push the growing assistant message ONLY on streaming updates. The terminal ('done'/'error')\n // emit carries the SAME final message already yielded on the last streaming update — pushing it\n // again would yield a duplicate. So the terminal emit only flips `finished` (+ captures error).\n const last = snapshot.messages.at(-1)\n if (last !== undefined) pending.push(last)\n } else {\n if (snapshot.status === 'error') streamError = snapshot.error\n finished = true\n }\n notify?.()\n })\n\n client.send(input)\n try {\n for (;;) {\n while (pending.length > 0) {\n const message = pending.shift()\n if (message !== undefined) yield message\n }\n if (isFinished()) break\n await new Promise<void>((resolve) => {\n notify = resolve\n // Close the lost-wakeup window: an emit may have fired during the drain, BEFORE `notify` was\n // set (its `notify?.()` was then a no-op). If work already arrived, resolve now, don't sleep.\n if (pending.length > 0 || isFinished()) resolve()\n })\n notify = null\n }\n const err = takeError()\n if (err !== undefined) throw err\n } finally {\n // Runs on normal completion AND on early `break` (the for-await calls the generator's return()):\n // stop observing, and abort the turn so an early-exiting consumer doesn't leave it running (abort\n // is a no-op once the turn has settled).\n unsubscribe()\n client.abort()\n }\n },\n }\n}\n\n/**\n * Create a standalone (no-React) agent client over a transport. `useAgent` is the React binding over the\n * same store; this is the equivalent for any JS runtime — one agent, consumable everywhere on the seam.\n */\nexport function createAgentClient<TInput = unknown>(\n transport: AgentTransport,\n options: CreateAgentClientOptions = {},\n): AgentClientHandle<TInput> {\n const { context } = options\n const contextResolver =\n context === undefined\n ? undefined\n : (): RequestContext | undefined => (typeof context === 'function' ? context() : context)\n const client = new AgentClient<TInput>(transport, contextResolver)\n\n return {\n send: client.send,\n abort: client.abort,\n reset: client.reset,\n approve: client.approve,\n reconnect: client.reconnect,\n subscribe: client.subscribe,\n getState: client.getSnapshot,\n stream: (input: TInput) => streamTurn(client, input),\n }\n}\n"],"mappings":"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","names":[]}
|