theokit 0.15.2 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. package/dist/actions-virtual-module-G4BANOLW.js +0 -0
  2. package/dist/add-W6FNTAFS.js +0 -0
  3. package/dist/{agent-MN7XGJR3.js → agent-7DBAK65H.js} +3 -3
  4. package/dist/{agents-typed-client-SAWAAH7K.js → agents-typed-client-D3LXWGCF.js} +6 -4
  5. package/dist/agents-typed-client-D3LXWGCF.js.map +1 -0
  6. package/dist/{agents-typed-client-UTEQUA63.js → agents-typed-client-LQ36PCHL.js} +6 -4
  7. package/dist/agents-typed-client-LQ36PCHL.js.map +1 -0
  8. package/dist/app-typed-client-Z6BHD4MF.js +0 -0
  9. package/dist/aws-lambda-7GSCNLPY.js +0 -0
  10. package/dist/body-parser-web-FV5HWCY3.js +0 -0
  11. package/dist/broadcast-LUMOJIJT.js +0 -0
  12. package/dist/{build-QDAFSKKW.js → build-MTI4RAI3.js} +2 -2
  13. package/dist/build-request-body-preview-QMWD2IXK.js +0 -0
  14. package/dist/bun-KP2KES6S.js +0 -0
  15. package/dist/check-PD2FTKMM.js +0 -0
  16. package/dist/chunk-34YQOXGM.js +0 -0
  17. package/dist/{chunk-5UUOGAJH.js → chunk-3FNZG6Q3.js} +36 -1
  18. package/dist/chunk-3FNZG6Q3.js.map +1 -0
  19. package/dist/chunk-45C3WUQ7.js +0 -0
  20. package/dist/{chunk-ZJDKAD3L.js → chunk-4FOPDXKT.js} +270 -37
  21. package/dist/chunk-4FOPDXKT.js.map +1 -0
  22. package/dist/{chunk-OTFIRP6S.js → chunk-4JDYVNSY.js} +28 -6
  23. package/dist/chunk-4JDYVNSY.js.map +1 -0
  24. package/dist/chunk-567NA7Y6.js +0 -0
  25. package/dist/chunk-5ODOE6EF.js +0 -0
  26. package/dist/{chunk-KGFNWIMS.js → chunk-CWVBDUDC.js} +18 -4
  27. package/dist/chunk-CWVBDUDC.js.map +1 -0
  28. package/dist/chunk-FOZIR3TG.js +0 -0
  29. package/dist/chunk-GBXLKYIA.js +0 -0
  30. package/dist/chunk-GDN3PXFH.js +0 -0
  31. package/dist/chunk-HGZL5EOI.js +0 -0
  32. package/dist/chunk-HNBWZKIQ.js +0 -0
  33. package/dist/chunk-IEES3CHD.js +0 -0
  34. package/dist/chunk-JAIKGP3Q.js +0 -0
  35. package/dist/chunk-JQSFBJR5.js +0 -0
  36. package/dist/chunk-KXA37ONC.js +0 -0
  37. package/dist/{chunk-NBWB4S46.js → chunk-M6NMS5LR.js} +17 -7
  38. package/dist/{chunk-NBWB4S46.js.map → chunk-M6NMS5LR.js.map} +1 -1
  39. package/dist/chunk-NXTF5PPW.js +0 -0
  40. package/dist/chunk-P37RZRFV.js +0 -0
  41. package/dist/chunk-WR4F4EEZ.js +0 -0
  42. package/dist/{chunk-UOR6JTCI.js → chunk-X6COGWHN.js} +296 -48
  43. package/dist/chunk-X6COGWHN.js.map +1 -0
  44. package/dist/chunk-YJAUJXZS.js +0 -0
  45. package/dist/cli/index.js +4 -4
  46. package/dist/client/index.d.ts +59 -2
  47. package/dist/client/index.js +43 -0
  48. package/dist/client/index.js.map +1 -1
  49. package/dist/cloudflare-C6E5SPAE.js +0 -0
  50. package/dist/configure-agent-registry-ZOBVU3MV.js +0 -0
  51. package/dist/db-3UNAMSFK.js +0 -0
  52. package/dist/deno-deploy-RFZN56X4.js +0 -0
  53. package/dist/{dev-TEE4T6ZB.js → dev-SVHKR2VR.js} +4 -4
  54. package/dist/dev-emit-VJ5CFMPY.js +0 -0
  55. package/dist/dispatcher-EJHL6JMJ.js +0 -0
  56. package/dist/docker-LZZB4D5E.js +0 -0
  57. package/dist/generate-OMKHQ7OM.js +0 -0
  58. package/dist/index.js +2 -2
  59. package/dist/info-7PE2PZJI.js +0 -0
  60. package/dist/internal-api-J27TYE2I.js +0 -0
  61. package/dist/load-config-JKYO5RFK.js +0 -0
  62. package/dist/netlify-PMLHVPN4.js +0 -0
  63. package/dist/node-BPJ3Z4DT.js +0 -0
  64. package/dist/openapi-MXMLZCXC.js +0 -0
  65. package/dist/registry-XJUYD2OU.js +0 -0
  66. package/dist/router-TLEAOFID.js +0 -0
  67. package/dist/routes-NNBEZSGN.js +0 -0
  68. package/dist/scan-7MJC6PYU.js +0 -0
  69. package/dist/schema-7CAY6IZR.js +0 -0
  70. package/dist/server/define/index.d.ts +42 -6
  71. package/dist/server/define/index.js +3 -1
  72. package/dist/server/index.d.ts +278 -4
  73. package/dist/server/index.js +232 -2
  74. package/dist/server/index.js.map +1 -1
  75. package/dist/server-error-to-envelope-UUXDSLAZ.js +0 -0
  76. package/dist/server-routes-hmr-VZXOP5YT.js +0 -0
  77. package/dist/services-json-Y3XODNB5.js +0 -0
  78. package/dist/services-typed-client-32KMTTXR.js +0 -0
  79. package/dist/{start-7MQEEQH4.js → start-SDZTFJNQ.js} +3 -3
  80. package/dist/static-7ARBVDJF.js +0 -0
  81. package/dist/storage-manager-ZSQFLEYT.js +0 -0
  82. package/dist/theo-cloud-DQRP6S6M.js +0 -0
  83. package/dist/upgrade-readiness-ACGW44JC.js +0 -0
  84. package/dist/vercel-J6G7ZHYQ.js +0 -0
  85. package/dist/vite-plugin/index.js +2 -2
  86. package/dist/{vite-plugin-GC6WCU4P.js → vite-plugin-ZZYTMWZY.js} +4 -4
  87. package/package.json +12 -11
  88. package/LICENSE +0 -201
  89. package/dist/agents-typed-client-SAWAAH7K.js.map +0 -1
  90. package/dist/agents-typed-client-UTEQUA63.js.map +0 -1
  91. package/dist/chunk-5UUOGAJH.js.map +0 -1
  92. package/dist/chunk-KGFNWIMS.js.map +0 -1
  93. package/dist/chunk-OTFIRP6S.js.map +0 -1
  94. package/dist/chunk-UOR6JTCI.js.map +0 -1
  95. package/dist/chunk-ZJDKAD3L.js.map +0 -1
  96. /package/dist/{agent-MN7XGJR3.js.map → agent-7DBAK65H.js.map} +0 -0
  97. /package/dist/{build-QDAFSKKW.js.map → build-MTI4RAI3.js.map} +0 -0
  98. /package/dist/{dev-TEE4T6ZB.js.map → dev-SVHKR2VR.js.map} +0 -0
  99. /package/dist/{start-7MQEEQH4.js.map → start-SDZTFJNQ.js.map} +0 -0
  100. /package/dist/{vite-plugin-GC6WCU4P.js.map → vite-plugin-ZZYTMWZY.js.map} +0 -0
File without changes
package/dist/cli/index.js CHANGED
@@ -5,12 +5,12 @@ import "tsx/esm";
5
5
  import cac from "cac";
6
6
  var cli = cac("theokit");
7
7
  cli.command("dev", "Start development server").option("--port <port>", "Port number").action(async (options) => {
8
- const { devCommand } = await import("../dev-TEE4T6ZB.js");
8
+ const { devCommand } = await import("../dev-SVHKR2VR.js");
9
9
  await devCommand({ port: options.port ? Number(options.port) : void 0 });
10
10
  });
11
11
  cli.command("build", "Build for production").option("--target <target>", "Deploy target (node, vercel, cloudflare)").action(async (options) => {
12
12
  try {
13
- const { buildCommand } = await import("../build-QDAFSKKW.js");
13
+ const { buildCommand } = await import("../build-MTI4RAI3.js");
14
14
  await buildCommand({ target: options.target });
15
15
  } catch (err) {
16
16
  const msg = err instanceof Error ? err.message : String(err);
@@ -22,7 +22,7 @@ cli.command("build", "Build for production").option("--target <target>", "Deploy
22
22
  });
23
23
  cli.command("start", "Start production server").option("--port <port>", "Port number").action(async (options) => {
24
24
  try {
25
- const { startCommand } = await import("../start-7MQEEQH4.js");
25
+ const { startCommand } = await import("../start-SDZTFJNQ.js");
26
26
  await startCommand({ port: options.port ? Number(options.port) : void 0 });
27
27
  } catch (err) {
28
28
  const msg = err instanceof Error ? err.message : String(err);
@@ -52,7 +52,7 @@ cli.command(
52
52
  "Run an agent in the terminal (stream + tool calls + approval)"
53
53
  ).action(async (name, message) => {
54
54
  try {
55
- const { agentCommand } = await import("../agent-MN7XGJR3.js");
55
+ const { agentCommand } = await import("../agent-7DBAK65H.js");
56
56
  const { sawError } = await agentCommand(name, message);
57
57
  if (sawError) process.exit(1);
58
58
  } catch (err) {
@@ -144,7 +144,7 @@ declare function consumeUIMessageStream(response: Response, onMessage: (message:
144
144
  * loaded lazily by `consumeUIMessageStream` so non-agent apps never pay for it.
145
145
  */
146
146
  type UseAgentStatus = 'idle' | 'streaming' | 'done' | 'error';
147
- interface UseAgentReturn<TInput = unknown> {
147
+ interface UseAgentReturn<TInput = unknown, TToolNames extends string = string> {
148
148
  /** Reconstructed assistant messages so far (ai `UIMessage[]`). */
149
149
  messages: UIMessage[];
150
150
  status: UseAgentStatus;
@@ -156,6 +156,13 @@ interface UseAgentReturn<TInput = unknown> {
156
156
  abort: () => void;
157
157
  /** Clear messages + error, back to idle. */
158
158
  reset: () => void;
159
+ /**
160
+ * The union of tool names this agent can emit (M8), carried end-to-end from the `agent()`
161
+ * builder's accumulated tool-name type through the generated `@theo/agents` client. Type-only
162
+ * witness (never populated at runtime) — narrow a streamed `tool-<name>` part against it. Resolves
163
+ * to the literal union for builder agents (`'read_file' | 'count_lines'`), `string` otherwise.
164
+ */
165
+ readonly __toolNames?: TToolNames;
159
166
  }
160
167
  interface UseAgentOptions {
161
168
  /** Extra request headers (e.g., auth). */
@@ -266,4 +273,54 @@ interface ImageProps extends React.ImgHTMLAttributes<HTMLImageElement> {
266
273
  }
267
274
  declare function Image({ priority, loading, decoding, ...props }: ImageProps): react.JSX.Element;
268
275
 
269
- export { type BatchRequest, type BatchResponse, type BatchTransport, type Batcher, type BatcherOptions, type CreateAppClientOptions, Image, type ImageProps, type InferBody, type InferQuery, type InferResponse, Link, type LinkProps, Metadata, type MetadataProps, type PrefetchBehavior, TheoFetchError, type TheoFetchOptions, type UseAgentOptions, type UseAgentReturn, type UseAgentStatus, consumeUIMessageStream, createAppClient, createBatcher, theoFetch, useAgent };
276
+ /**
277
+ * M30 (ADR-0041) — client host for MCP App `ui://` resources.
278
+ *
279
+ * Renders a tool's `ui://` HTML in a SANDBOXED iframe and bridges a capability-scoped guest API over
280
+ * `postMessage`. Security is load-bearing:
281
+ * - the iframe is `sandbox="allow-scripts"` ONLY — NOT `allow-same-origin`, so the guest runs at a
282
+ * null origin and cannot touch the parent DOM, cookies, or storage;
283
+ * - the host only honors messages whose `source` is the iframe's own `contentWindow`;
284
+ * - the guest API is a fixed, capability-scoped vocabulary (`callServerTool`, `sendMessage`) — any
285
+ * other message type is ignored.
286
+ */
287
+ /** The guest → host message vocabulary (capability-scoped). */
288
+ type GuestMessage = {
289
+ type: 'callServerTool';
290
+ id: string;
291
+ tool: string;
292
+ args?: unknown;
293
+ } | {
294
+ type: 'sendMessage';
295
+ text: string;
296
+ };
297
+ /** Callbacks the host wires for the guest API. */
298
+ interface McpAppHostOptions {
299
+ /** Guest asked to call a server tool — return its result (posted back to the guest by id). */
300
+ onCallServerTool: (tool: string, args: unknown) => unknown;
301
+ /** Guest emitted a chat message to the host app. */
302
+ onSendMessage?: (text: string) => void;
303
+ }
304
+ /**
305
+ * Pure bridge: interpret one guest message and (for `callServerTool`) post the result back via
306
+ * `post`. Exported for unit testing without a DOM. Unknown message shapes are ignored — the guest
307
+ * API is capability-scoped, not an open RPC surface.
308
+ */
309
+ declare function createGuestMessageHandler(opts: McpAppHostOptions, post: (message: unknown) => void): (data: unknown) => Promise<void>;
310
+ /** Handle returned by {@link mountMcpApp}. */
311
+ interface McpAppHandle {
312
+ iframe: HTMLIFrameElement;
313
+ /** Remove the message listener and the iframe. */
314
+ dispose: () => void;
315
+ }
316
+ /** The sandbox tokens the guest iframe is allowed — scripts only, NEVER `allow-same-origin`. */
317
+ declare const MCP_APP_SANDBOX = "allow-scripts";
318
+ /**
319
+ * Mount an MCP App resource's HTML in a sandboxed iframe inside `container`, wiring the guest API.
320
+ * The HTML comes from `resources/read` (see `mcp-app-resources.ts`). Returns a handle to dispose.
321
+ */
322
+ declare function mountMcpApp(container: HTMLElement, resource: {
323
+ html: string;
324
+ }, opts: McpAppHostOptions): McpAppHandle;
325
+
326
+ export { type BatchRequest, type BatchResponse, type BatchTransport, type Batcher, type BatcherOptions, type CreateAppClientOptions, type GuestMessage, Image, type ImageProps, type InferBody, type InferQuery, type InferResponse, Link, type LinkProps, MCP_APP_SANDBOX, type McpAppHandle, type McpAppHostOptions, Metadata, type MetadataProps, type PrefetchBehavior, TheoFetchError, type TheoFetchOptions, type UseAgentOptions, type UseAgentReturn, type UseAgentStatus, consumeUIMessageStream, createAppClient, createBatcher, createGuestMessageHandler, mountMcpApp, theoFetch, useAgent };
@@ -560,15 +560,58 @@ function Image({ priority, loading, decoding, ...props }) {
560
560
  }
561
561
  );
562
562
  }
563
+
564
+ // src/client/mcp-app-host.ts
565
+ function createGuestMessageHandler(opts, post) {
566
+ return async (data) => {
567
+ if (typeof data !== "object" || data === null) return;
568
+ const msg = data;
569
+ if (msg.type === "callServerTool" && typeof msg.id === "string" && typeof msg.tool === "string") {
570
+ const result = await opts.onCallServerTool(msg.tool, msg.args);
571
+ post({ type: "callServerTool:result", id: msg.id, result });
572
+ return;
573
+ }
574
+ if (msg.type === "sendMessage" && typeof msg.text === "string") {
575
+ opts.onSendMessage?.(msg.text);
576
+ }
577
+ };
578
+ }
579
+ var MCP_APP_SANDBOX = "allow-scripts";
580
+ function mountMcpApp(container, resource, opts) {
581
+ const iframe = container.ownerDocument.createElement("iframe");
582
+ iframe.setAttribute("sandbox", MCP_APP_SANDBOX);
583
+ iframe.srcdoc = resource.html;
584
+ container.appendChild(iframe);
585
+ const post = (message) => {
586
+ iframe.contentWindow?.postMessage(message, "*");
587
+ };
588
+ const handle = createGuestMessageHandler(opts, post);
589
+ const onMessage = (event) => {
590
+ if (event.source !== iframe.contentWindow) return;
591
+ void handle(event.data);
592
+ };
593
+ const view = container.ownerDocument.defaultView;
594
+ view?.addEventListener("message", onMessage);
595
+ return {
596
+ iframe,
597
+ dispose: () => {
598
+ view?.removeEventListener("message", onMessage);
599
+ iframe.remove();
600
+ }
601
+ };
602
+ }
563
603
  export {
564
604
  Image,
565
605
  Link,
606
+ MCP_APP_SANDBOX,
566
607
  Metadata,
567
608
  TheoFetchError,
568
609
  buildUseTheoQueryConfig,
569
610
  consumeUIMessageStream,
570
611
  createAppClient,
571
612
  createBatcher,
613
+ createGuestMessageHandler,
614
+ mountMcpApp,
572
615
  stableQueryKey,
573
616
  theoFetch,
574
617
  useAgent
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/client/theo-fetch.ts","../../src/client/batch.ts","../../src/client/batch-transport.ts","../../src/client/app-client.ts","../../src/client/consume-ui-message-stream.ts","../../src/client/use-agent.ts","../../src/client/link.tsx","../../src/client/metadata.tsx","../../src/client/image.tsx"],"sourcesContent":["import superjson from 'superjson'\nimport type { z } from 'zod'\n\nimport type { TheoErrorCode, TheoErrorEnvelope } from '../core/contracts/error-envelope.js'\n\nimport { getGlobalBatcher } from './batch-transport.js'\n\n// --- Transformer (T1.3) ---\n\n/**\n * Module-scoped flag preventing the transformer-mismatch warning from\n * firing more than once per session (EC-6).\n */\nlet mismatchWarned = false\n\n/** Test-only helper to reset the warned flag between assertions. */\nexport function __resetMismatchWarningForTests(): void {\n mismatchWarned = false\n}\n\n/**\n * Deserialize a fetch response body according to the negotiated transformer.\n *\n * `serverTransformerName` comes from the `x-theo-transformer` response header\n * (null when absent — server is using the default JSON path).\n * `clientTransformerName` is the transformer the client was built with\n * (typically injected via a Vite virtual module; falls back to `'json'`).\n *\n * Mismatch fires a single console.warn and falls back to JSON.parse (EC-5/EC-6).\n */\nexport function deserializeFetchResponse(\n raw: string,\n serverTransformerName: string | null,\n clientTransformerName: string,\n): unknown {\n // `raw` is typed `string`, but stay defensive: callers pass `await response.text()`\n // which can be empty.\n if (raw === '') {\n return null\n }\n\n const serverEffective = serverTransformerName ?? 'json'\n if (serverEffective !== clientTransformerName && !mismatchWarned) {\n mismatchWarned = true\n console.warn(\n `[theokit] transformer mismatch: server=${serverEffective}, client=${clientTransformerName}. Falling back to JSON.parse.`,\n )\n }\n\n if (serverEffective === 'superjson' && clientTransformerName === 'superjson') {\n const wrapped = JSON.parse(raw) as Parameters<typeof superjson.deserialize>[0]\n return superjson.deserialize(wrapped)\n }\n\n // Default path (json) or mismatch fallback\n return JSON.parse(raw)\n}\n\n// --- Utility Types ---\n\n/** Infer the response type from a route's handler return */\nexport type InferResponse<T> = T extends { handler: (...args: never[]) => infer R }\n ? Awaited<R>\n : unknown\n\n/** Extract the query Zod schema type, handling optional properties */\ntype ExtractQuery<T> = T extends { query?: infer Q } ? (Q extends z.ZodType ? Q : never) : never\n\n/** Extract the body Zod schema type, handling optional properties */\ntype ExtractBody<T> = T extends { body?: infer B } ? (B extends z.ZodType ? B : never) : never\n\n/** Infer query type from a route's query Zod schema */\nexport type InferQuery<T> = [ExtractQuery<T>] extends [never]\n ? undefined\n : ExtractQuery<T> extends z.ZodUndefined\n ? undefined\n : z.infer<ExtractQuery<T>>\n\n/** Infer body type from a route's body Zod schema */\nexport type InferBody<T> = [ExtractBody<T>] extends [never]\n ? undefined\n : ExtractBody<T> extends z.ZodUndefined\n ? undefined\n : z.infer<ExtractBody<T>>\n\n/** Build the options type based on what schemas the route has */\nexport type TheoFetchOptions<T> = Omit<RequestInit, 'body' | 'method'> &\n (InferQuery<T> extends undefined ? { query?: never } : { query: InferQuery<T> }) &\n (InferBody<T> extends undefined ? { body?: never } : { body: InferBody<T> })\n\n// --- Error Class ---\n\n/**\n * G5 T2.1 — extract a TheoErrorEnvelope from a response body. Supports both\n * the new envelope-at-root shape (`{ code, message, ext?, ... }`, blueprint\n * Recommendations § concrete shape) and the legacy nested shape\n * (`{ error: { code, message, issues? } }`, G3 SerializedActionResult).\n *\n * Falls back to `INTERNAL_SERVER_ERROR` with a synthetic message when the\n * body shape is unrecognized. Pure; no I/O.\n */\nfunction extractEnvelope(status: number, body: unknown): TheoErrorEnvelope {\n if (!body || typeof body !== 'object') {\n return { code: 'INTERNAL_SERVER_ERROR', message: `HTTP ${String(status)}` }\n }\n const obj = body as Record<string, unknown>\n // New shape — envelope at root\n if (typeof obj.code === 'string' && typeof obj.message === 'string') {\n return {\n code: obj.code as TheoErrorCode,\n message: obj.message,\n cause: obj.cause,\n meta: obj.meta as Record<string, unknown> | undefined,\n ext: obj.ext,\n }\n }\n // Legacy shape — { error: { code, message, ... } }\n if (obj.error && typeof obj.error === 'object') {\n const nested = obj.error as Record<string, unknown>\n return {\n code:\n typeof nested.code === 'string' ? (nested.code as TheoErrorCode) : 'INTERNAL_SERVER_ERROR',\n message: typeof nested.message === 'string' ? nested.message : `HTTP ${String(status)}`,\n }\n }\n return { code: 'INTERNAL_SERVER_ERROR', message: `HTTP ${String(status)}` }\n}\n\n/**\n * Pull a single string field from an object, returning undefined if missing\n * or non-string. Helper for `extractLegacyFields` to stay under the\n * complexity ceiling.\n */\nfunction strField(obj: Record<string, unknown> | null, key: string): string | undefined {\n const v = obj?.[key]\n return typeof v === 'string' ? v : undefined\n}\n\n/**\n * Pull the canonical {message, code, issues} from either an envelope-at-root\n * body or the legacy { error: {...} } body. Pure.\n */\nfunction extractLegacyFields(\n status: number,\n body: unknown,\n): { message: string; code?: string; issues?: unknown[] } {\n if (!body || typeof body !== 'object') {\n return { message: `HTTP ${String(status)}` }\n }\n const obj = body as Record<string, unknown>\n const nested =\n obj.error && typeof obj.error === 'object' ? (obj.error as Record<string, unknown>) : null\n const issues = Array.isArray(nested?.issues) ? nested.issues : undefined\n return {\n message: strField(obj, 'message') ?? strField(nested, 'message') ?? `HTTP ${String(status)}`,\n code: strField(obj, 'code') ?? strField(nested, 'code'),\n issues,\n }\n}\n\nexport class TheoFetchError extends Error {\n status: number\n code?: string\n issues?: unknown[]\n /**\n * G5 T2.1 — canonical envelope view of the server-side error. Use this in\n * consumer code that wants to switch on a typed TheoErrorCode instead of\n * coupling to the legacy `.status` / `.code` flat fields.\n */\n readonly envelope: TheoErrorEnvelope\n\n constructor(status: number, body?: unknown) {\n const legacy = extractLegacyFields(status, body)\n super(legacy.message)\n this.name = 'TheoFetchError'\n this.status = status\n this.code = legacy.code\n this.issues = legacy.issues\n this.envelope = extractEnvelope(status, body)\n }\n}\n\n/**\n * Serialize a query-param value to a string. Handles primitives, dates,\n * and falls back to JSON for arrays/objects. Avoids the\n * `[object Object]` foot-gun that `no-base-to-string` warns against.\n */\nfunction stringifyQueryValue(value: unknown): string {\n if (value === null) return 'null'\n if (value instanceof Date) return value.toISOString()\n switch (typeof value) {\n case 'string':\n return value\n case 'number':\n case 'boolean':\n case 'bigint':\n return String(value)\n default:\n return JSON.stringify(value)\n }\n}\n\n// --- Main Function ---\n\ninterface TheoFetchInternalOptions {\n method?: string\n query?: Record<string, unknown>\n body?: unknown\n headers?: HeadersInit\n signal?: AbortSignal\n}\n\n/**\n * Resolve the request origin per the documented fallback hierarchy. Pure\n * helper extracted to keep `theoFetch` under the complexity ceiling.\n * 1. `globalThis.location.origin` (browser)\n * 2. `globalThis.__THEO_ORIGIN__` (build-time literal)\n * 3. `process.env.THEO_ORIGIN` (escape hatch)\n * 4. `http://localhost` (placeholder for URL parsing — the URL is built\n * relative so only pathname+search ever flows to the wire)\n */\nfunction resolveRequestOrigin(): string {\n const g = globalThis as { location?: { origin?: string }; __THEO_ORIGIN__?: string }\n const fromEnv = typeof process !== 'undefined' ? process.env.THEO_ORIGIN : undefined\n return g.location?.origin ?? g.__THEO_ORIGIN__ ?? fromEnv ?? 'http://localhost'\n}\n\nfunction buildFetchUrl(path: string, query: Record<string, unknown> | undefined): URL {\n const fetchUrl = new URL(path, resolveRequestOrigin())\n if (!query) return fetchUrl\n for (const [k, v] of Object.entries(query)) {\n if (v !== undefined) {\n fetchUrl.searchParams.set(k, stringifyQueryValue(v))\n }\n }\n return fetchUrl\n}\n\n/**\n * Normalize `HeadersInit` (Headers / `[string, string][]` / Record) into a\n * single plain `Record<string, string>`. Plain-object output is required\n * by callers that introspect headers via index access (test fixtures use\n * `(init.headers as Record<string, string>)['Content-Type']`).\n */\nfunction normalizeHeaders(input: HeadersInit | undefined): Record<string, string> {\n const out: Record<string, string> = {}\n if (!input) return out\n if (input instanceof Headers) {\n input.forEach((value, key) => {\n out[key] = value\n })\n return out\n }\n if (Array.isArray(input)) {\n for (const [key, value] of input) out[key] = value\n return out\n }\n for (const [key, value] of Object.entries(input)) {\n out[key] = value\n }\n return out\n}\n\nfunction buildRequestInit(opts: TheoFetchInternalOptions): RequestInit {\n const init: RequestInit = {}\n if (opts.method !== undefined) init.method = opts.method\n if (opts.signal !== undefined) init.signal = opts.signal\n\n const headers = normalizeHeaders(opts.headers)\n init.headers = headers\n\n if (opts.body !== undefined) {\n init.body = JSON.stringify(opts.body)\n headers['Content-Type'] = 'application/json'\n }\n\n // Phase 5 — Auto-attach `X-Theo-Action: 1` for state-mutating methods so\n // the framework's CSRF check passes when servers run in `strict` mode.\n // Safe methods (GET/HEAD/OPTIONS) skip the header to keep them cacheable.\n const method = (opts.method ?? 'GET').toUpperCase()\n if (method !== 'GET' && method !== 'HEAD' && method !== 'OPTIONS') {\n headers['X-Theo-Action'] = '1'\n }\n return init\n}\n\nasync function tryBatcher(\n url: string,\n options: TheoFetchInternalOptions,\n): Promise<{ matched: true; result: unknown } | { matched: false }> {\n const batcher = getGlobalBatcher()\n if (!batcher) return { matched: false }\n try {\n const result = await batcher.dispatch({\n path: url,\n method: options.method ?? 'GET',\n query: options.query,\n body: options.body,\n })\n return { matched: true, result }\n } catch {\n // Batcher failure: fall through to direct fetch (graceful degrade).\n return { matched: false }\n }\n}\n\nexport async function theoFetch<T>(\n url: string,\n options?: TheoFetchOptions<T>,\n): Promise<InferResponse<T>> {\n const internal = (options ?? {}) as unknown as TheoFetchInternalOptions\n\n // T1.5 — Transparent batching when globalThis.__THEO_BATCHING__ is truthy.\n const batchAttempt = await tryBatcher(url, internal)\n if (batchAttempt.matched) {\n return batchAttempt.result as InferResponse<T>\n }\n\n const fetchUrl = buildFetchUrl(url, internal.query)\n const init = buildRequestInit(internal)\n const response = await fetch(fetchUrl.toString(), init)\n\n if (!response.ok) {\n let errorBody: unknown\n try {\n errorBody = await response.json()\n } catch {\n // Non-JSON error response\n }\n throw new TheoFetchError(response.status, errorBody)\n }\n\n // Handle 204 No Content (EC-1)\n if (response.status === 204) {\n return null as InferResponse<T>\n }\n\n // Check for empty body\n const contentLength = response.headers.get('content-length')\n if (contentLength === '0') {\n return null as InferResponse<T>\n }\n\n // T1.3 — transformer-aware deserialization\n const serverTransformerName = response.headers.get('x-theo-transformer')\n const clientTransformerName = resolveClientTransformerName()\n const text = await response.text()\n return deserializeFetchResponse(\n text,\n serverTransformerName,\n clientTransformerName,\n ) as InferResponse<T>\n}\n\n/**\n * Read the client-configured transformer name. Default `'json'`.\n *\n * In a Vite build, this is overridden by virtual module\n * `/@theo/runtime-config` which sets `globalThis.__THEO_TRANSFORMER__`.\n * Outside Vite (Node SSR, tests) the default applies.\n */\nfunction resolveClientTransformerName(): string {\n const g = globalThis as { __THEO_TRANSFORMER__?: string }\n return g.__THEO_TRANSFORMER__ ?? 'json'\n}\n","/**\n * T5.1 — client-side microtask batching.\n *\n * Collects all `dispatch` calls within the same microtask and sends them as a\n * single HTTP POST to the configured transport. Each caller's promise resolves\n * with its own result; one failed item does not break the others (per-item\n * error isolation).\n *\n * Designed as a transport-agnostic primitive so unit tests do not require\n * network access. The default transport (fetch to `/api/__theo_batch__`)\n * lives alongside this module but is created by the consumer (e.g., theoFetch).\n */\n\nexport interface BatchRequest {\n path: string\n method: string\n query?: Record<string, unknown>\n body?: unknown\n headers?: Record<string, string>\n}\n\nexport type BatchResponse =\n | { index: number; data: unknown }\n | { index: number; error: { message: string; code?: string } }\n\nexport type BatchTransport = (requests: BatchRequest[]) => Promise<BatchResponse[]>\n\nexport interface BatcherOptions {\n transport: BatchTransport\n /** Maximum batch size before flushing into multiple parallel batches. */\n max?: number\n}\n\nexport interface Batcher {\n dispatch(req: BatchRequest): Promise<unknown>\n}\n\ninterface PendingCall {\n req: BatchRequest\n resolve: (value: unknown) => void\n reject: (reason: unknown) => void\n}\n\nexport function createBatcher(options: BatcherOptions): Batcher {\n const max = options.max ?? 32\n let queue: PendingCall[] = []\n let flushScheduled = false\n\n function flush(): void {\n flushScheduled = false\n const current = queue\n queue = []\n if (current.length === 0) return\n\n // Split into chunks respecting max\n const chunks: PendingCall[][] = []\n for (let i = 0; i < current.length; i += max) {\n chunks.push(current.slice(i, i + max))\n }\n\n for (const chunk of chunks) {\n const payload: BatchRequest[] = chunk.map((p) => p.req)\n options\n .transport(payload)\n .then((results) => {\n for (let i = 0; i < chunk.length; i++) {\n // `results[i]` could be undefined at runtime when the transport\n // returns fewer entries than the chunk; TypeScript's strict\n // typing without `noUncheckedIndexedAccess` does not surface\n // this, so we hand-narrow.\n const result = results.length > i ? results[i] : undefined\n if (result === undefined) {\n chunk[i].reject(new Error(`Batch transport returned no result for index ${i}`))\n continue\n }\n if ('error' in result) {\n const err = new Error(result.error.message)\n ;(err as { code?: string }).code = result.error.code\n chunk[i].reject(err)\n } else {\n chunk[i].resolve(result.data)\n }\n }\n })\n .catch((err: unknown) => {\n for (const item of chunk) item.reject(err)\n })\n }\n }\n\n return {\n dispatch(req: BatchRequest): Promise<unknown> {\n return new Promise<unknown>((resolve, reject) => {\n queue.push({ req, resolve, reject })\n if (!flushScheduled) {\n flushScheduled = true\n queueMicrotask(flush)\n }\n })\n },\n }\n}\n","/**\n * T1.5 — Default HTTP transport for the client batcher.\n *\n * Wraps `fetch` to POST `/api/__theo_batch__` with the collected requests\n * and return the array of per-item results.\n */\n\nimport {\n createBatcher,\n type BatchTransport,\n type Batcher,\n type BatchRequest,\n type BatchResponse,\n} from './batch.js'\n\nconst BATCH_ENDPOINT = '/api/__theo_batch__'\n\nexport interface CreateBatchTransportOptions {\n /** Override fetch (default: globalThis.fetch). Used by tests. */\n fetchImpl?: typeof fetch\n /** Override endpoint (default '/api/__theo_batch__'). */\n endpoint?: string\n}\n\nexport function createBatchTransport(options: CreateBatchTransportOptions = {}): BatchTransport {\n const fetchImpl = options.fetchImpl ?? fetch\n const endpoint = options.endpoint ?? BATCH_ENDPOINT\n return async (requests: BatchRequest[]): Promise<BatchResponse[]> => {\n const response = await fetchImpl(endpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ requests }),\n })\n if (!response.ok) {\n throw new Error(`Batch transport returned ${response.status}`)\n }\n const payload = (await response.json()) as { results?: BatchResponse[] }\n if (!Array.isArray(payload.results)) {\n throw new Error('Batch response missing results array')\n }\n // Map results to indexed BatchResponse shape (server already returns the right form)\n return payload.results.map((item, index) => {\n if ('error' in item) {\n return { index, error: (item as { error: { message: string; code?: string } }).error }\n }\n return { index, data: (item as { data: unknown }).data }\n })\n }\n}\n\n// --- EC-7: singleton batcher per page, lazy-instantiated ---\n\nlet globalBatcher: Batcher | undefined\n\n/** Test-only — reset the module-scope singleton between assertions. */\nexport function __resetGlobalBatcherForTests(): void {\n globalBatcher = undefined\n}\n\n/**\n * Returns the global batcher singleton when `globalThis.__THEO_BATCHING__` is\n * truthy; undefined otherwise. Lazy-instantiated on first call.\n */\nexport function getGlobalBatcher(): Batcher | undefined {\n const g = globalThis as { __THEO_BATCHING__?: boolean }\n if (!g.__THEO_BATCHING__) return undefined\n globalBatcher ??= createBatcher({ transport: createBatchTransport() })\n return globalBatcher\n}\n","/**\n * Phase 3 of G1 (g1-client-codegen-plan.md):\n *\n * `createAppClient(baseUrl?, fetchImpl?)` returns a Proxy that walks property\n * access (`client.posts.id.get(...)`) into a `theoFetch` invocation against\n * `{baseUrl}/posts/{params.id}` with method `GET`.\n *\n * Type safety is delivered by the `.d.ts` file emitted in Phase 2 — this\n * runtime is structurally untyped. Power users may use `theoFetch` directly\n * when they need to escape the Proxy facade.\n *\n * Edge cases absorbed:\n * - EC-1 (thenable trap): `then` / `catch` / `finally` / `toJSON` / `Symbol.*`\n * return `undefined` so that accidental `await client.posts` resolves to a\n * plain Proxy value (it is NOT thenable) instead of looping.\n * - EC-7 (abort signal): `opts.signal` is spread through to the underlying\n * fetchImpl unchanged — verified in unit tests.\n *\n * Bundle target: ≤ 2KB gzipped.\n */\n\nimport { theoFetch, TheoFetchError } from './theo-fetch.js'\nimport type { TheoFetchOptions } from './theo-fetch.js'\nimport { HTTP_METHOD_LOWERCASE } from '../core/contracts/http-methods.js'\n\nconst HTTP_METHODS_SET = new Set(HTTP_METHOD_LOWERCASE)\n\n/**\n * Keys that JavaScript runtime / inspectors / Promise resolution probe\n * unconditionally. Returning `undefined` for these keeps the Proxy from\n * pretending to be a thenable / iterable / serializable thing.\n */\nconst NON_INTERCEPT_KEYS = new Set([\n 'then',\n 'catch',\n 'finally',\n 'toJSON',\n 'toString',\n 'valueOf',\n 'constructor',\n 'prototype',\n])\n\ntype FetchImpl = typeof theoFetch\n\ninterface CallOptions {\n params?: Record<string, string | number>\n query?: Record<string, unknown>\n body?: unknown\n signal?: AbortSignal\n headers?: HeadersInit\n // arbitrary RequestInit fields (mode, credentials, cache, etc.)\n [k: string]: unknown\n}\n\nfunction injectParams(path: string, params?: Record<string, string | number>): string {\n if (!params) {\n if (/:(?:\\.\\.\\.)?[A-Za-z_]/.test(path)) {\n throw new TheoFetchError(0, {\n error: {\n code: 'MISSING_PARAM',\n message: `Route ${path} requires params but none were provided. Pass them via opts.params, e.g. client.posts.get({ params: { id: '123' } }).`,\n },\n })\n }\n return path\n }\n return path.replace(/:(?:\\.\\.\\.)?([A-Za-z_][A-Za-z0-9_]*)/g, (_match, key: string) => {\n const value = params[key]\n if (value === undefined || value === null || value === '') {\n throw new TheoFetchError(0, {\n error: {\n code: 'MISSING_PARAM',\n message: `Param '${key}' is required for path '${path}' but was missing/empty.`,\n },\n })\n }\n return encodeURIComponent(String(value))\n })\n}\n\ninterface ProxyContext {\n segments: string[]\n baseUrl: string\n fetchImpl: FetchImpl\n}\n\nfunction makeProxy(ctx: ProxyContext): unknown {\n const handler: ProxyHandler<() => void> = {\n get(_target, key) {\n if (typeof key !== 'string') return undefined\n if (NON_INTERCEPT_KEYS.has(key)) return undefined\n if (HTTP_METHODS_SET.has(key)) {\n return async (opts?: CallOptions) => {\n if (ctx.segments.length === 0) {\n throw new TheoFetchError(0, {\n error: {\n code: 'INVALID_PATH',\n message: `client.${key}() called without a route segment. Use client.<resource>.${key}(...) instead.`,\n },\n })\n }\n let finalPath = `${ctx.baseUrl}/${ctx.segments.join('/')}`\n if (opts?.params) {\n // Build a path with `:name` placeholders re-inserted from segments\n // whose name matches a key in opts.params (treat as dynamic).\n const segmentsWithDynamic = ctx.segments.map((seg) =>\n Object.prototype.hasOwnProperty.call(opts.params, seg) ? `:${seg}` : seg,\n )\n finalPath = injectParams(\n `${ctx.baseUrl}/${segmentsWithDynamic.join('/')}`,\n opts.params,\n )\n }\n const { params: _ignored, ...rest } = opts ?? {}\n const init = { method: key.toUpperCase(), ...rest } as unknown as TheoFetchOptions<unknown>\n return ctx.fetchImpl(finalPath, init)\n }\n }\n // Continue traversal — append segment to the path.\n return makeProxy({ ...ctx, segments: [...ctx.segments, key] })\n },\n apply() {\n return Promise.reject(\n new TheoFetchError(0, {\n error: {\n code: 'INVALID_CALL',\n message:\n 'client(...) is not callable. Use client.<resource>.<method>(opts?) — e.g. client.posts.get().',\n },\n }),\n )\n },\n }\n return new Proxy(() => undefined, handler)\n}\n\nexport interface CreateAppClientOptions {\n /** Base URL for the API. Defaults to `/api`. */\n baseUrl?: string\n /** Test-only fetch override. Production callers should not pass this. */\n fetchImpl?: FetchImpl\n}\n\nexport function createAppClient<TAppClient = unknown>(\n baseUrlOrOptions?: string | CreateAppClientOptions,\n legacyFetchImpl?: FetchImpl,\n): TAppClient {\n let baseUrl = '/api'\n let fetchImpl: FetchImpl = theoFetch\n if (typeof baseUrlOrOptions === 'string') {\n baseUrl = baseUrlOrOptions || '/api'\n } else if (baseUrlOrOptions && typeof baseUrlOrOptions === 'object') {\n if (baseUrlOrOptions.baseUrl) baseUrl = baseUrlOrOptions.baseUrl\n if (baseUrlOrOptions.fetchImpl) fetchImpl = baseUrlOrOptions.fetchImpl\n }\n // Legacy 2nd-arg fetchImpl overrides regardless of 1st-arg shape (test seam).\n if (legacyFetchImpl) fetchImpl = legacyFetchImpl\n // Strip trailing slash for predictable joining.\n if (baseUrl.length > 1 && baseUrl.endsWith('/')) baseUrl = baseUrl.slice(0, -1)\n return makeProxy({ segments: [], baseUrl, fetchImpl }) as TAppClient\n}\n","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 if (response.body === null) return\n\n const { parseJsonEventStream, readUIMessageStream, uiMessageChunkSchema } = await import('ai')\n\n // ai validates each SSE JSON frame against its own strict chunk schema (the exact\n // gate `useChat` runs), then yields `{ success, value }`; forward the valid chunks.\n const parsed = parseJsonEventStream({ stream: response.body, schema: uiMessageChunkSchema })\n const chunkStream = 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 for await (const message of readUIMessageStream({ stream: chunkStream })) {\n onMessage(message)\n }\n}\n","import type { UIMessage } from 'ai'\nimport { useCallback, useRef, useState } from 'react'\n\nimport { consumeUIMessageStream } from './consume-ui-message-stream.js'\n\n/**\n * M2 (theokit-ai-first) — `useAgent`, the typed client hook for the `agents/*.ts` convention.\n *\n * A thin React hook over `consumeUIMessageStream` (which reuses `ai`'s own UIMessageStream\n * reader). `useAgent('support')` binds to `POST /api/agents/support`; the generated\n * `@theo/agents` module (`.theokit/agents.d.ts`) types `send` to the agent's `input` schema,\n * so the request shape is inferred end-to-end from the server `defineAgent({ input })` with\n * ZERO manual wiring (DoD line 2).\n *\n * Transport: fetch + ReadableStream (POST needs a body; EventSource is GET-only). `ai` is\n * loaded lazily by `consumeUIMessageStream` so non-agent apps never pay for it.\n */\nexport type UseAgentStatus = 'idle' | 'streaming' | 'done' | 'error'\n\nexport interface UseAgentReturn<TInput = unknown> {\n /** Reconstructed assistant messages so far (ai `UIMessage[]`). */\n messages: UIMessage[]\n status: UseAgentStatus\n /** The last error, or `undefined`. */\n error: Error | undefined\n /** Send a request; opens a new stream. Typed to the agent's `input` schema. */\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}\n\nexport interface UseAgentOptions {\n /** Extra request headers (e.g., auth). */\n headers?: Record<string, string>\n /** Override fetch (primarily for tests). */\n fetch?: typeof fetch\n}\n\n/**\n * Bind to the agent endpoint at `path` (`/api/agents/<name>`). Prefer the generated\n * `useAgent` from `@theo/agents` (typed by agent name); this base accepts an explicit path.\n */\nexport function useAgent<TInput = unknown>(\n path: string,\n options: UseAgentOptions = {},\n): UseAgentReturn<TInput> {\n const [messages, setMessages] = useState<UIMessage[]>([])\n const [status, setStatus] = useState<UseAgentStatus>('idle')\n const [error, setError] = useState<Error | undefined>(undefined)\n const controllerRef = useRef<AbortController | null>(null)\n\n const abort = useCallback(() => {\n controllerRef.current?.abort()\n controllerRef.current = null\n }, [])\n\n const reset = useCallback(() => {\n abort()\n setMessages([])\n setError(undefined)\n setStatus('idle')\n }, [abort])\n\n const send = useCallback(\n (input: TInput) => {\n abort()\n const controller = new AbortController()\n controllerRef.current = controller\n setMessages([])\n setError(undefined)\n setStatus('streaming')\n\n const fetchImpl = options.fetch ?? globalThis.fetch\n void (async () => {\n try {\n const response = await fetchImpl(path, {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n accept: 'text/event-stream',\n 'X-Theo-Action': '1',\n ...options.headers,\n },\n body: JSON.stringify(input),\n signal: controller.signal,\n })\n await consumeUIMessageStream(response, (message) => {\n setMessages((prev) => {\n const next = [...prev]\n const idx = next.findIndex((m) => m.id === message.id)\n if (idx >= 0) next[idx] = message\n else next.push(message)\n return next\n })\n })\n setStatus('done')\n } catch (err) {\n if (controller.signal.aborted) return\n setError(err instanceof Error ? err : new Error(String(err)))\n setStatus('error')\n }\n })()\n },\n [abort, options.fetch, options.headers, path],\n )\n\n return { messages, status, error, send, abort, reset }\n}\n","/**\n * <Link> — React Router Link with route prefetching.\n *\n * Wraps react-router's <Link> with prefetch behavior:\n * - `intent` (default): prefetch on hover + focus (~200ms before click)\n * - `viewport`: prefetch when visible (IntersectionObserver)\n * - `none`: no prefetch (same as plain react-router Link)\n *\n * Uses `<link rel=\"prefetch\">` (not modulepreload) because route paths\n * can be prefetched directly — no Vite manifest resolution needed (EC-1).\n */\nimport {\n Link as RouterLink,\n type LinkProps as RouterLinkProps,\n} from 'react-router'\nimport { useRef, useCallback, useEffect } from 'react'\n\nexport type PrefetchBehavior = 'none' | 'intent' | 'viewport'\n\nexport interface LinkProps extends RouterLinkProps {\n /** Prefetch strategy. Default: 'intent' (on hover + focus). */\n prefetch?: PrefetchBehavior\n}\n\n/** Deduplication — each URL prefetched at most once per session. */\nconst prefetched = new Set<string>()\n\nfunction injectPrefetch(href: string): void {\n // EC-2: SSR guard — document unavailable on server\n if (typeof document === 'undefined') return\n if (prefetched.has(href)) return\n prefetched.add(href)\n\n const link = document.createElement('link')\n link.rel = 'prefetch'\n link.href = href\n document.head.appendChild(link)\n}\n\nfunction resolveTo(to: LinkProps['to']): string {\n if (typeof to === 'string') return to\n return to?.pathname ?? ''\n}\n\n/**\n * TheoKit Link — drop-in replacement for react-router Link with prefetch.\n *\n * @example\n * ```tsx\n * import { Link } from 'theokit/client'\n *\n * <Link to=\"/contacts\">Contacts</Link>\n * <Link to=\"/dashboard\" prefetch=\"viewport\">Dashboard</Link>\n * <Link to=\"/settings\" prefetch=\"none\">Settings</Link>\n * ```\n */\nexport function Link({ prefetch = 'intent', to, onMouseEnter, onFocus, ...rest }: LinkProps) {\n const ref = useRef<HTMLAnchorElement>(null)\n\n const handleIntent = useCallback(\n (event: React.MouseEvent<HTMLAnchorElement> | React.FocusEvent<HTMLAnchorElement>) => {\n if (prefetch === 'intent') {\n injectPrefetch(resolveTo(to))\n }\n // Forward original handlers\n if (event.type === 'mouseenter' && onMouseEnter) {\n ;(onMouseEnter as React.MouseEventHandler<HTMLAnchorElement>)(event as React.MouseEvent<HTMLAnchorElement>)\n }\n if (event.type === 'focus' && onFocus) {\n ;(onFocus as React.FocusEventHandler<HTMLAnchorElement>)(event as React.FocusEvent<HTMLAnchorElement>)\n }\n },\n [prefetch, to, onMouseEnter, onFocus],\n )\n\n // Viewport mode: IntersectionObserver\n useEffect(() => {\n if (prefetch !== 'viewport' || !ref.current) return\n if (typeof IntersectionObserver === 'undefined') return // SSR guard\n\n const observer = new IntersectionObserver(\n ([entry]) => {\n if (entry.isIntersecting) {\n injectPrefetch(resolveTo(to))\n observer.disconnect()\n }\n },\n { rootMargin: '200px' }, // prefetch slightly before visible\n )\n observer.observe(ref.current)\n return () => observer.disconnect()\n }, [prefetch, to])\n\n return (\n <RouterLink\n ref={ref}\n to={to}\n onMouseEnter={handleIntent}\n onFocus={handleIntent}\n {...rest}\n />\n )\n}\n","/**\n * <Metadata> — SEO-ready head tags from a single component.\n *\n * Uses React 19's native <title>/<meta>/<link> hoisting to <head>.\n * No build-time extraction needed — works in SSR and client.\n *\n * @example\n * ```tsx\n * import { Metadata } from 'theokit/client'\n *\n * export default function ContactsPage() {\n * return (\n * <>\n * <Metadata\n * title=\"Contacts | My CRM\"\n * description=\"Manage your contacts\"\n * ogImage=\"/og/contacts.png\"\n * canonical=\"https://mycrm.com/contacts\"\n * />\n * <h1>Contacts</h1>\n * </>\n * )\n * }\n * ```\n */\n\nexport interface MetadataProps {\n title?: string\n description?: string\n canonical?: string\n ogTitle?: string\n ogDescription?: string\n ogImage?: string\n ogType?: string\n ogUrl?: string\n twitterCard?: 'summary' | 'summary_large_image'\n /** Custom meta tags rendered as children */\n children?: React.ReactNode\n}\n\nexport function Metadata(props: MetadataProps) {\n const ogTitle = props.ogTitle ?? props.title\n const ogDesc = props.ogDescription ?? props.description\n\n return (\n <>\n {props.title && <title>{props.title}</title>}\n {props.description && <meta name=\"description\" content={props.description} />}\n {props.canonical && <link rel=\"canonical\" href={props.canonical} />}\n {ogTitle && <meta property=\"og:title\" content={ogTitle} />}\n {ogDesc && <meta property=\"og:description\" content={ogDesc} />}\n {props.ogImage && <meta property=\"og:image\" content={props.ogImage} />}\n {props.ogType && <meta property=\"og:type\" content={props.ogType} />}\n {props.ogUrl && <meta property=\"og:url\" content={props.ogUrl} />}\n {props.twitterCard && <meta name=\"twitter:card\" content={props.twitterCard} />}\n {props.children}\n </>\n )\n}\n","/**\n * <Image> — optimized image component with lazy loading.\n *\n * Renders a standard <img> with:\n * - loading=\"lazy\" by default (eager with priority={true})\n * - decoding=\"async\" for non-blocking decode\n * - width/height for CLS prevention\n * - srcSet/sizes forwarded for responsive images\n *\n * No CDN, no Sharp, no build-time optimization — pure HTML attributes\n * that deliver 80% of the performance value at zero complexity.\n *\n * @example\n * ```tsx\n * import { Image } from 'theokit/client'\n *\n * <Image src=\"/team.jpg\" alt=\"Team photo\" width={800} height={600} />\n * <Image src=\"/hero.jpg\" alt=\"Hero\" priority />\n * <Image\n * src=\"/product.jpg\"\n * alt=\"Product\"\n * width={400}\n * height={300}\n * srcSet=\"/product-400.jpg 400w, /product-800.jpg 800w\"\n * sizes=\"(max-width: 768px) 100vw, 400px\"\n * />\n * ```\n */\n\nexport interface ImageProps extends React.ImgHTMLAttributes<HTMLImageElement> {\n /** Image source URL (required). */\n src: string\n /** Alt text for accessibility (required). */\n alt: string\n /** If true, loading=\"eager\" — use for above-the-fold images. */\n priority?: boolean\n}\n\nexport function Image({ priority, loading, decoding, ...props }: ImageProps) {\n return (\n <img\n loading={loading ?? (priority ? 'eager' : 'lazy')}\n decoding={decoding ?? 'async'}\n {...props}\n />\n )\n}\n"],"mappings":"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","names":["useRef","useCallback","jsx","jsx"]}
1
+ {"version":3,"sources":["../../src/client/theo-fetch.ts","../../src/client/batch.ts","../../src/client/batch-transport.ts","../../src/client/app-client.ts","../../src/client/consume-ui-message-stream.ts","../../src/client/use-agent.ts","../../src/client/link.tsx","../../src/client/metadata.tsx","../../src/client/image.tsx","../../src/client/mcp-app-host.ts"],"sourcesContent":["import superjson from 'superjson'\nimport type { z } from 'zod'\n\nimport type { TheoErrorCode, TheoErrorEnvelope } from '../core/contracts/error-envelope.js'\n\nimport { getGlobalBatcher } from './batch-transport.js'\n\n// --- Transformer (T1.3) ---\n\n/**\n * Module-scoped flag preventing the transformer-mismatch warning from\n * firing more than once per session (EC-6).\n */\nlet mismatchWarned = false\n\n/** Test-only helper to reset the warned flag between assertions. */\nexport function __resetMismatchWarningForTests(): void {\n mismatchWarned = false\n}\n\n/**\n * Deserialize a fetch response body according to the negotiated transformer.\n *\n * `serverTransformerName` comes from the `x-theo-transformer` response header\n * (null when absent — server is using the default JSON path).\n * `clientTransformerName` is the transformer the client was built with\n * (typically injected via a Vite virtual module; falls back to `'json'`).\n *\n * Mismatch fires a single console.warn and falls back to JSON.parse (EC-5/EC-6).\n */\nexport function deserializeFetchResponse(\n raw: string,\n serverTransformerName: string | null,\n clientTransformerName: string,\n): unknown {\n // `raw` is typed `string`, but stay defensive: callers pass `await response.text()`\n // which can be empty.\n if (raw === '') {\n return null\n }\n\n const serverEffective = serverTransformerName ?? 'json'\n if (serverEffective !== clientTransformerName && !mismatchWarned) {\n mismatchWarned = true\n console.warn(\n `[theokit] transformer mismatch: server=${serverEffective}, client=${clientTransformerName}. Falling back to JSON.parse.`,\n )\n }\n\n if (serverEffective === 'superjson' && clientTransformerName === 'superjson') {\n const wrapped = JSON.parse(raw) as Parameters<typeof superjson.deserialize>[0]\n return superjson.deserialize(wrapped)\n }\n\n // Default path (json) or mismatch fallback\n return JSON.parse(raw)\n}\n\n// --- Utility Types ---\n\n/** Infer the response type from a route's handler return */\nexport type InferResponse<T> = T extends { handler: (...args: never[]) => infer R }\n ? Awaited<R>\n : unknown\n\n/** Extract the query Zod schema type, handling optional properties */\ntype ExtractQuery<T> = T extends { query?: infer Q } ? (Q extends z.ZodType ? Q : never) : never\n\n/** Extract the body Zod schema type, handling optional properties */\ntype ExtractBody<T> = T extends { body?: infer B } ? (B extends z.ZodType ? B : never) : never\n\n/** Infer query type from a route's query Zod schema */\nexport type InferQuery<T> = [ExtractQuery<T>] extends [never]\n ? undefined\n : ExtractQuery<T> extends z.ZodUndefined\n ? undefined\n : z.infer<ExtractQuery<T>>\n\n/** Infer body type from a route's body Zod schema */\nexport type InferBody<T> = [ExtractBody<T>] extends [never]\n ? undefined\n : ExtractBody<T> extends z.ZodUndefined\n ? undefined\n : z.infer<ExtractBody<T>>\n\n/** Build the options type based on what schemas the route has */\nexport type TheoFetchOptions<T> = Omit<RequestInit, 'body' | 'method'> &\n (InferQuery<T> extends undefined ? { query?: never } : { query: InferQuery<T> }) &\n (InferBody<T> extends undefined ? { body?: never } : { body: InferBody<T> })\n\n// --- Error Class ---\n\n/**\n * G5 T2.1 — extract a TheoErrorEnvelope from a response body. Supports both\n * the new envelope-at-root shape (`{ code, message, ext?, ... }`, blueprint\n * Recommendations § concrete shape) and the legacy nested shape\n * (`{ error: { code, message, issues? } }`, G3 SerializedActionResult).\n *\n * Falls back to `INTERNAL_SERVER_ERROR` with a synthetic message when the\n * body shape is unrecognized. Pure; no I/O.\n */\nfunction extractEnvelope(status: number, body: unknown): TheoErrorEnvelope {\n if (!body || typeof body !== 'object') {\n return { code: 'INTERNAL_SERVER_ERROR', message: `HTTP ${String(status)}` }\n }\n const obj = body as Record<string, unknown>\n // New shape — envelope at root\n if (typeof obj.code === 'string' && typeof obj.message === 'string') {\n return {\n code: obj.code as TheoErrorCode,\n message: obj.message,\n cause: obj.cause,\n meta: obj.meta as Record<string, unknown> | undefined,\n ext: obj.ext,\n }\n }\n // Legacy shape — { error: { code, message, ... } }\n if (obj.error && typeof obj.error === 'object') {\n const nested = obj.error as Record<string, unknown>\n return {\n code:\n typeof nested.code === 'string' ? (nested.code as TheoErrorCode) : 'INTERNAL_SERVER_ERROR',\n message: typeof nested.message === 'string' ? nested.message : `HTTP ${String(status)}`,\n }\n }\n return { code: 'INTERNAL_SERVER_ERROR', message: `HTTP ${String(status)}` }\n}\n\n/**\n * Pull a single string field from an object, returning undefined if missing\n * or non-string. Helper for `extractLegacyFields` to stay under the\n * complexity ceiling.\n */\nfunction strField(obj: Record<string, unknown> | null, key: string): string | undefined {\n const v = obj?.[key]\n return typeof v === 'string' ? v : undefined\n}\n\n/**\n * Pull the canonical {message, code, issues} from either an envelope-at-root\n * body or the legacy { error: {...} } body. Pure.\n */\nfunction extractLegacyFields(\n status: number,\n body: unknown,\n): { message: string; code?: string; issues?: unknown[] } {\n if (!body || typeof body !== 'object') {\n return { message: `HTTP ${String(status)}` }\n }\n const obj = body as Record<string, unknown>\n const nested =\n obj.error && typeof obj.error === 'object' ? (obj.error as Record<string, unknown>) : null\n const issues = Array.isArray(nested?.issues) ? nested.issues : undefined\n return {\n message: strField(obj, 'message') ?? strField(nested, 'message') ?? `HTTP ${String(status)}`,\n code: strField(obj, 'code') ?? strField(nested, 'code'),\n issues,\n }\n}\n\nexport class TheoFetchError extends Error {\n status: number\n code?: string\n issues?: unknown[]\n /**\n * G5 T2.1 — canonical envelope view of the server-side error. Use this in\n * consumer code that wants to switch on a typed TheoErrorCode instead of\n * coupling to the legacy `.status` / `.code` flat fields.\n */\n readonly envelope: TheoErrorEnvelope\n\n constructor(status: number, body?: unknown) {\n const legacy = extractLegacyFields(status, body)\n super(legacy.message)\n this.name = 'TheoFetchError'\n this.status = status\n this.code = legacy.code\n this.issues = legacy.issues\n this.envelope = extractEnvelope(status, body)\n }\n}\n\n/**\n * Serialize a query-param value to a string. Handles primitives, dates,\n * and falls back to JSON for arrays/objects. Avoids the\n * `[object Object]` foot-gun that `no-base-to-string` warns against.\n */\nfunction stringifyQueryValue(value: unknown): string {\n if (value === null) return 'null'\n if (value instanceof Date) return value.toISOString()\n switch (typeof value) {\n case 'string':\n return value\n case 'number':\n case 'boolean':\n case 'bigint':\n return String(value)\n default:\n return JSON.stringify(value)\n }\n}\n\n// --- Main Function ---\n\ninterface TheoFetchInternalOptions {\n method?: string\n query?: Record<string, unknown>\n body?: unknown\n headers?: HeadersInit\n signal?: AbortSignal\n}\n\n/**\n * Resolve the request origin per the documented fallback hierarchy. Pure\n * helper extracted to keep `theoFetch` under the complexity ceiling.\n * 1. `globalThis.location.origin` (browser)\n * 2. `globalThis.__THEO_ORIGIN__` (build-time literal)\n * 3. `process.env.THEO_ORIGIN` (escape hatch)\n * 4. `http://localhost` (placeholder for URL parsing — the URL is built\n * relative so only pathname+search ever flows to the wire)\n */\nfunction resolveRequestOrigin(): string {\n const g = globalThis as { location?: { origin?: string }; __THEO_ORIGIN__?: string }\n const fromEnv = typeof process !== 'undefined' ? process.env.THEO_ORIGIN : undefined\n return g.location?.origin ?? g.__THEO_ORIGIN__ ?? fromEnv ?? 'http://localhost'\n}\n\nfunction buildFetchUrl(path: string, query: Record<string, unknown> | undefined): URL {\n const fetchUrl = new URL(path, resolveRequestOrigin())\n if (!query) return fetchUrl\n for (const [k, v] of Object.entries(query)) {\n if (v !== undefined) {\n fetchUrl.searchParams.set(k, stringifyQueryValue(v))\n }\n }\n return fetchUrl\n}\n\n/**\n * Normalize `HeadersInit` (Headers / `[string, string][]` / Record) into a\n * single plain `Record<string, string>`. Plain-object output is required\n * by callers that introspect headers via index access (test fixtures use\n * `(init.headers as Record<string, string>)['Content-Type']`).\n */\nfunction normalizeHeaders(input: HeadersInit | undefined): Record<string, string> {\n const out: Record<string, string> = {}\n if (!input) return out\n if (input instanceof Headers) {\n input.forEach((value, key) => {\n out[key] = value\n })\n return out\n }\n if (Array.isArray(input)) {\n for (const [key, value] of input) out[key] = value\n return out\n }\n for (const [key, value] of Object.entries(input)) {\n out[key] = value\n }\n return out\n}\n\nfunction buildRequestInit(opts: TheoFetchInternalOptions): RequestInit {\n const init: RequestInit = {}\n if (opts.method !== undefined) init.method = opts.method\n if (opts.signal !== undefined) init.signal = opts.signal\n\n const headers = normalizeHeaders(opts.headers)\n init.headers = headers\n\n if (opts.body !== undefined) {\n init.body = JSON.stringify(opts.body)\n headers['Content-Type'] = 'application/json'\n }\n\n // Phase 5 — Auto-attach `X-Theo-Action: 1` for state-mutating methods so\n // the framework's CSRF check passes when servers run in `strict` mode.\n // Safe methods (GET/HEAD/OPTIONS) skip the header to keep them cacheable.\n const method = (opts.method ?? 'GET').toUpperCase()\n if (method !== 'GET' && method !== 'HEAD' && method !== 'OPTIONS') {\n headers['X-Theo-Action'] = '1'\n }\n return init\n}\n\nasync function tryBatcher(\n url: string,\n options: TheoFetchInternalOptions,\n): Promise<{ matched: true; result: unknown } | { matched: false }> {\n const batcher = getGlobalBatcher()\n if (!batcher) return { matched: false }\n try {\n const result = await batcher.dispatch({\n path: url,\n method: options.method ?? 'GET',\n query: options.query,\n body: options.body,\n })\n return { matched: true, result }\n } catch {\n // Batcher failure: fall through to direct fetch (graceful degrade).\n return { matched: false }\n }\n}\n\nexport async function theoFetch<T>(\n url: string,\n options?: TheoFetchOptions<T>,\n): Promise<InferResponse<T>> {\n const internal = (options ?? {}) as unknown as TheoFetchInternalOptions\n\n // T1.5 — Transparent batching when globalThis.__THEO_BATCHING__ is truthy.\n const batchAttempt = await tryBatcher(url, internal)\n if (batchAttempt.matched) {\n return batchAttempt.result as InferResponse<T>\n }\n\n const fetchUrl = buildFetchUrl(url, internal.query)\n const init = buildRequestInit(internal)\n const response = await fetch(fetchUrl.toString(), init)\n\n if (!response.ok) {\n let errorBody: unknown\n try {\n errorBody = await response.json()\n } catch {\n // Non-JSON error response\n }\n throw new TheoFetchError(response.status, errorBody)\n }\n\n // Handle 204 No Content (EC-1)\n if (response.status === 204) {\n return null as InferResponse<T>\n }\n\n // Check for empty body\n const contentLength = response.headers.get('content-length')\n if (contentLength === '0') {\n return null as InferResponse<T>\n }\n\n // T1.3 — transformer-aware deserialization\n const serverTransformerName = response.headers.get('x-theo-transformer')\n const clientTransformerName = resolveClientTransformerName()\n const text = await response.text()\n return deserializeFetchResponse(\n text,\n serverTransformerName,\n clientTransformerName,\n ) as InferResponse<T>\n}\n\n/**\n * Read the client-configured transformer name. Default `'json'`.\n *\n * In a Vite build, this is overridden by virtual module\n * `/@theo/runtime-config` which sets `globalThis.__THEO_TRANSFORMER__`.\n * Outside Vite (Node SSR, tests) the default applies.\n */\nfunction resolveClientTransformerName(): string {\n const g = globalThis as { __THEO_TRANSFORMER__?: string }\n return g.__THEO_TRANSFORMER__ ?? 'json'\n}\n","/**\n * T5.1 — client-side microtask batching.\n *\n * Collects all `dispatch` calls within the same microtask and sends them as a\n * single HTTP POST to the configured transport. Each caller's promise resolves\n * with its own result; one failed item does not break the others (per-item\n * error isolation).\n *\n * Designed as a transport-agnostic primitive so unit tests do not require\n * network access. The default transport (fetch to `/api/__theo_batch__`)\n * lives alongside this module but is created by the consumer (e.g., theoFetch).\n */\n\nexport interface BatchRequest {\n path: string\n method: string\n query?: Record<string, unknown>\n body?: unknown\n headers?: Record<string, string>\n}\n\nexport type BatchResponse =\n | { index: number; data: unknown }\n | { index: number; error: { message: string; code?: string } }\n\nexport type BatchTransport = (requests: BatchRequest[]) => Promise<BatchResponse[]>\n\nexport interface BatcherOptions {\n transport: BatchTransport\n /** Maximum batch size before flushing into multiple parallel batches. */\n max?: number\n}\n\nexport interface Batcher {\n dispatch(req: BatchRequest): Promise<unknown>\n}\n\ninterface PendingCall {\n req: BatchRequest\n resolve: (value: unknown) => void\n reject: (reason: unknown) => void\n}\n\nexport function createBatcher(options: BatcherOptions): Batcher {\n const max = options.max ?? 32\n let queue: PendingCall[] = []\n let flushScheduled = false\n\n function flush(): void {\n flushScheduled = false\n const current = queue\n queue = []\n if (current.length === 0) return\n\n // Split into chunks respecting max\n const chunks: PendingCall[][] = []\n for (let i = 0; i < current.length; i += max) {\n chunks.push(current.slice(i, i + max))\n }\n\n for (const chunk of chunks) {\n const payload: BatchRequest[] = chunk.map((p) => p.req)\n options\n .transport(payload)\n .then((results) => {\n for (let i = 0; i < chunk.length; i++) {\n // `results[i]` could be undefined at runtime when the transport\n // returns fewer entries than the chunk; TypeScript's strict\n // typing without `noUncheckedIndexedAccess` does not surface\n // this, so we hand-narrow.\n const result = results.length > i ? results[i] : undefined\n if (result === undefined) {\n chunk[i].reject(new Error(`Batch transport returned no result for index ${i}`))\n continue\n }\n if ('error' in result) {\n const err = new Error(result.error.message)\n ;(err as { code?: string }).code = result.error.code\n chunk[i].reject(err)\n } else {\n chunk[i].resolve(result.data)\n }\n }\n })\n .catch((err: unknown) => {\n for (const item of chunk) item.reject(err)\n })\n }\n }\n\n return {\n dispatch(req: BatchRequest): Promise<unknown> {\n return new Promise<unknown>((resolve, reject) => {\n queue.push({ req, resolve, reject })\n if (!flushScheduled) {\n flushScheduled = true\n queueMicrotask(flush)\n }\n })\n },\n }\n}\n","/**\n * T1.5 — Default HTTP transport for the client batcher.\n *\n * Wraps `fetch` to POST `/api/__theo_batch__` with the collected requests\n * and return the array of per-item results.\n */\n\nimport {\n createBatcher,\n type BatchTransport,\n type Batcher,\n type BatchRequest,\n type BatchResponse,\n} from './batch.js'\n\nconst BATCH_ENDPOINT = '/api/__theo_batch__'\n\nexport interface CreateBatchTransportOptions {\n /** Override fetch (default: globalThis.fetch). Used by tests. */\n fetchImpl?: typeof fetch\n /** Override endpoint (default '/api/__theo_batch__'). */\n endpoint?: string\n}\n\nexport function createBatchTransport(options: CreateBatchTransportOptions = {}): BatchTransport {\n const fetchImpl = options.fetchImpl ?? fetch\n const endpoint = options.endpoint ?? BATCH_ENDPOINT\n return async (requests: BatchRequest[]): Promise<BatchResponse[]> => {\n const response = await fetchImpl(endpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ requests }),\n })\n if (!response.ok) {\n throw new Error(`Batch transport returned ${response.status}`)\n }\n const payload = (await response.json()) as { results?: BatchResponse[] }\n if (!Array.isArray(payload.results)) {\n throw new Error('Batch response missing results array')\n }\n // Map results to indexed BatchResponse shape (server already returns the right form)\n return payload.results.map((item, index) => {\n if ('error' in item) {\n return { index, error: (item as { error: { message: string; code?: string } }).error }\n }\n return { index, data: (item as { data: unknown }).data }\n })\n }\n}\n\n// --- EC-7: singleton batcher per page, lazy-instantiated ---\n\nlet globalBatcher: Batcher | undefined\n\n/** Test-only — reset the module-scope singleton between assertions. */\nexport function __resetGlobalBatcherForTests(): void {\n globalBatcher = undefined\n}\n\n/**\n * Returns the global batcher singleton when `globalThis.__THEO_BATCHING__` is\n * truthy; undefined otherwise. Lazy-instantiated on first call.\n */\nexport function getGlobalBatcher(): Batcher | undefined {\n const g = globalThis as { __THEO_BATCHING__?: boolean }\n if (!g.__THEO_BATCHING__) return undefined\n globalBatcher ??= createBatcher({ transport: createBatchTransport() })\n return globalBatcher\n}\n","/**\n * Phase 3 of G1 (g1-client-codegen-plan.md):\n *\n * `createAppClient(baseUrl?, fetchImpl?)` returns a Proxy that walks property\n * access (`client.posts.id.get(...)`) into a `theoFetch` invocation against\n * `{baseUrl}/posts/{params.id}` with method `GET`.\n *\n * Type safety is delivered by the `.d.ts` file emitted in Phase 2 — this\n * runtime is structurally untyped. Power users may use `theoFetch` directly\n * when they need to escape the Proxy facade.\n *\n * Edge cases absorbed:\n * - EC-1 (thenable trap): `then` / `catch` / `finally` / `toJSON` / `Symbol.*`\n * return `undefined` so that accidental `await client.posts` resolves to a\n * plain Proxy value (it is NOT thenable) instead of looping.\n * - EC-7 (abort signal): `opts.signal` is spread through to the underlying\n * fetchImpl unchanged — verified in unit tests.\n *\n * Bundle target: ≤ 2KB gzipped.\n */\n\nimport { theoFetch, TheoFetchError } from './theo-fetch.js'\nimport type { TheoFetchOptions } from './theo-fetch.js'\nimport { HTTP_METHOD_LOWERCASE } from '../core/contracts/http-methods.js'\n\nconst HTTP_METHODS_SET = new Set(HTTP_METHOD_LOWERCASE)\n\n/**\n * Keys that JavaScript runtime / inspectors / Promise resolution probe\n * unconditionally. Returning `undefined` for these keeps the Proxy from\n * pretending to be a thenable / iterable / serializable thing.\n */\nconst NON_INTERCEPT_KEYS = new Set([\n 'then',\n 'catch',\n 'finally',\n 'toJSON',\n 'toString',\n 'valueOf',\n 'constructor',\n 'prototype',\n])\n\ntype FetchImpl = typeof theoFetch\n\ninterface CallOptions {\n params?: Record<string, string | number>\n query?: Record<string, unknown>\n body?: unknown\n signal?: AbortSignal\n headers?: HeadersInit\n // arbitrary RequestInit fields (mode, credentials, cache, etc.)\n [k: string]: unknown\n}\n\nfunction injectParams(path: string, params?: Record<string, string | number>): string {\n if (!params) {\n if (/:(?:\\.\\.\\.)?[A-Za-z_]/.test(path)) {\n throw new TheoFetchError(0, {\n error: {\n code: 'MISSING_PARAM',\n message: `Route ${path} requires params but none were provided. Pass them via opts.params, e.g. client.posts.get({ params: { id: '123' } }).`,\n },\n })\n }\n return path\n }\n return path.replace(/:(?:\\.\\.\\.)?([A-Za-z_][A-Za-z0-9_]*)/g, (_match, key: string) => {\n const value = params[key]\n if (value === undefined || value === null || value === '') {\n throw new TheoFetchError(0, {\n error: {\n code: 'MISSING_PARAM',\n message: `Param '${key}' is required for path '${path}' but was missing/empty.`,\n },\n })\n }\n return encodeURIComponent(String(value))\n })\n}\n\ninterface ProxyContext {\n segments: string[]\n baseUrl: string\n fetchImpl: FetchImpl\n}\n\nfunction makeProxy(ctx: ProxyContext): unknown {\n const handler: ProxyHandler<() => void> = {\n get(_target, key) {\n if (typeof key !== 'string') return undefined\n if (NON_INTERCEPT_KEYS.has(key)) return undefined\n if (HTTP_METHODS_SET.has(key)) {\n return async (opts?: CallOptions) => {\n if (ctx.segments.length === 0) {\n throw new TheoFetchError(0, {\n error: {\n code: 'INVALID_PATH',\n message: `client.${key}() called without a route segment. Use client.<resource>.${key}(...) instead.`,\n },\n })\n }\n let finalPath = `${ctx.baseUrl}/${ctx.segments.join('/')}`\n if (opts?.params) {\n // Build a path with `:name` placeholders re-inserted from segments\n // whose name matches a key in opts.params (treat as dynamic).\n const segmentsWithDynamic = ctx.segments.map((seg) =>\n Object.prototype.hasOwnProperty.call(opts.params, seg) ? `:${seg}` : seg,\n )\n finalPath = injectParams(\n `${ctx.baseUrl}/${segmentsWithDynamic.join('/')}`,\n opts.params,\n )\n }\n const { params: _ignored, ...rest } = opts ?? {}\n const init = { method: key.toUpperCase(), ...rest } as unknown as TheoFetchOptions<unknown>\n return ctx.fetchImpl(finalPath, init)\n }\n }\n // Continue traversal — append segment to the path.\n return makeProxy({ ...ctx, segments: [...ctx.segments, key] })\n },\n apply() {\n return Promise.reject(\n new TheoFetchError(0, {\n error: {\n code: 'INVALID_CALL',\n message:\n 'client(...) is not callable. Use client.<resource>.<method>(opts?) — e.g. client.posts.get().',\n },\n }),\n )\n },\n }\n return new Proxy(() => undefined, handler)\n}\n\nexport interface CreateAppClientOptions {\n /** Base URL for the API. Defaults to `/api`. */\n baseUrl?: string\n /** Test-only fetch override. Production callers should not pass this. */\n fetchImpl?: FetchImpl\n}\n\nexport function createAppClient<TAppClient = unknown>(\n baseUrlOrOptions?: string | CreateAppClientOptions,\n legacyFetchImpl?: FetchImpl,\n): TAppClient {\n let baseUrl = '/api'\n let fetchImpl: FetchImpl = theoFetch\n if (typeof baseUrlOrOptions === 'string') {\n baseUrl = baseUrlOrOptions || '/api'\n } else if (baseUrlOrOptions && typeof baseUrlOrOptions === 'object') {\n if (baseUrlOrOptions.baseUrl) baseUrl = baseUrlOrOptions.baseUrl\n if (baseUrlOrOptions.fetchImpl) fetchImpl = baseUrlOrOptions.fetchImpl\n }\n // Legacy 2nd-arg fetchImpl overrides regardless of 1st-arg shape (test seam).\n if (legacyFetchImpl) fetchImpl = legacyFetchImpl\n // Strip trailing slash for predictable joining.\n if (baseUrl.length > 1 && baseUrl.endsWith('/')) baseUrl = baseUrl.slice(0, -1)\n return makeProxy({ segments: [], baseUrl, fetchImpl }) as TAppClient\n}\n","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 if (response.body === null) return\n\n const { parseJsonEventStream, readUIMessageStream, uiMessageChunkSchema } = await import('ai')\n\n // ai validates each SSE JSON frame against its own strict chunk schema (the exact\n // gate `useChat` runs), then yields `{ success, value }`; forward the valid chunks.\n const parsed = parseJsonEventStream({ stream: response.body, schema: uiMessageChunkSchema })\n const chunkStream = 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 for await (const message of readUIMessageStream({ stream: chunkStream })) {\n onMessage(message)\n }\n}\n","import type { UIMessage } from 'ai'\nimport { useCallback, useRef, useState } from 'react'\n\nimport { consumeUIMessageStream } from './consume-ui-message-stream.js'\n\n/**\n * M2 (theokit-ai-first) — `useAgent`, the typed client hook for the `agents/*.ts` convention.\n *\n * A thin React hook over `consumeUIMessageStream` (which reuses `ai`'s own UIMessageStream\n * reader). `useAgent('support')` binds to `POST /api/agents/support`; the generated\n * `@theo/agents` module (`.theokit/agents.d.ts`) types `send` to the agent's `input` schema,\n * so the request shape is inferred end-to-end from the server `defineAgent({ input })` with\n * ZERO manual wiring (DoD line 2).\n *\n * Transport: fetch + ReadableStream (POST needs a body; EventSource is GET-only). `ai` is\n * loaded lazily by `consumeUIMessageStream` so non-agent apps never pay for it.\n */\nexport type UseAgentStatus = 'idle' | 'streaming' | 'done' | 'error'\n\nexport interface UseAgentReturn<TInput = unknown, TToolNames extends string = string> {\n /** Reconstructed assistant messages so far (ai `UIMessage[]`). */\n messages: UIMessage[]\n status: UseAgentStatus\n /** The last error, or `undefined`. */\n error: Error | undefined\n /** Send a request; opens a new stream. Typed to the agent's `input` schema. */\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 /**\n * The union of tool names this agent can emit (M8), carried end-to-end from the `agent()`\n * builder's accumulated tool-name type through the generated `@theo/agents` client. Type-only\n * witness (never populated at runtime) — narrow a streamed `tool-<name>` part against it. Resolves\n * to the literal union for builder agents (`'read_file' | 'count_lines'`), `string` otherwise.\n */\n readonly __toolNames?: TToolNames\n}\n\nexport interface UseAgentOptions {\n /** Extra request headers (e.g., auth). */\n headers?: Record<string, string>\n /** Override fetch (primarily for tests). */\n fetch?: typeof fetch\n}\n\n/**\n * Bind to the agent endpoint at `path` (`/api/agents/<name>`). Prefer the generated\n * `useAgent` from `@theo/agents` (typed by agent name); this base accepts an explicit path.\n */\nexport function useAgent<TInput = unknown>(\n path: string,\n options: UseAgentOptions = {},\n): UseAgentReturn<TInput> {\n const [messages, setMessages] = useState<UIMessage[]>([])\n const [status, setStatus] = useState<UseAgentStatus>('idle')\n const [error, setError] = useState<Error | undefined>(undefined)\n const controllerRef = useRef<AbortController | null>(null)\n\n const abort = useCallback(() => {\n controllerRef.current?.abort()\n controllerRef.current = null\n }, [])\n\n const reset = useCallback(() => {\n abort()\n setMessages([])\n setError(undefined)\n setStatus('idle')\n }, [abort])\n\n const send = useCallback(\n (input: TInput) => {\n abort()\n const controller = new AbortController()\n controllerRef.current = controller\n setMessages([])\n setError(undefined)\n setStatus('streaming')\n\n const fetchImpl = options.fetch ?? globalThis.fetch\n void (async () => {\n try {\n const response = await fetchImpl(path, {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n accept: 'text/event-stream',\n 'X-Theo-Action': '1',\n ...options.headers,\n },\n body: JSON.stringify(input),\n signal: controller.signal,\n })\n await consumeUIMessageStream(response, (message) => {\n setMessages((prev) => {\n const next = [...prev]\n const idx = next.findIndex((m) => m.id === message.id)\n if (idx >= 0) next[idx] = message\n else next.push(message)\n return next\n })\n })\n setStatus('done')\n } catch (err) {\n if (controller.signal.aborted) return\n setError(err instanceof Error ? err : new Error(String(err)))\n setStatus('error')\n }\n })()\n },\n [abort, options.fetch, options.headers, path],\n )\n\n return { messages, status, error, send, abort, reset }\n}\n","/**\n * <Link> — React Router Link with route prefetching.\n *\n * Wraps react-router's <Link> with prefetch behavior:\n * - `intent` (default): prefetch on hover + focus (~200ms before click)\n * - `viewport`: prefetch when visible (IntersectionObserver)\n * - `none`: no prefetch (same as plain react-router Link)\n *\n * Uses `<link rel=\"prefetch\">` (not modulepreload) because route paths\n * can be prefetched directly — no Vite manifest resolution needed (EC-1).\n */\nimport {\n Link as RouterLink,\n type LinkProps as RouterLinkProps,\n} from 'react-router'\nimport { useRef, useCallback, useEffect } from 'react'\n\nexport type PrefetchBehavior = 'none' | 'intent' | 'viewport'\n\nexport interface LinkProps extends RouterLinkProps {\n /** Prefetch strategy. Default: 'intent' (on hover + focus). */\n prefetch?: PrefetchBehavior\n}\n\n/** Deduplication — each URL prefetched at most once per session. */\nconst prefetched = new Set<string>()\n\nfunction injectPrefetch(href: string): void {\n // EC-2: SSR guard — document unavailable on server\n if (typeof document === 'undefined') return\n if (prefetched.has(href)) return\n prefetched.add(href)\n\n const link = document.createElement('link')\n link.rel = 'prefetch'\n link.href = href\n document.head.appendChild(link)\n}\n\nfunction resolveTo(to: LinkProps['to']): string {\n if (typeof to === 'string') return to\n return to?.pathname ?? ''\n}\n\n/**\n * TheoKit Link — drop-in replacement for react-router Link with prefetch.\n *\n * @example\n * ```tsx\n * import { Link } from 'theokit/client'\n *\n * <Link to=\"/contacts\">Contacts</Link>\n * <Link to=\"/dashboard\" prefetch=\"viewport\">Dashboard</Link>\n * <Link to=\"/settings\" prefetch=\"none\">Settings</Link>\n * ```\n */\nexport function Link({ prefetch = 'intent', to, onMouseEnter, onFocus, ...rest }: LinkProps) {\n const ref = useRef<HTMLAnchorElement>(null)\n\n const handleIntent = useCallback(\n (event: React.MouseEvent<HTMLAnchorElement> | React.FocusEvent<HTMLAnchorElement>) => {\n if (prefetch === 'intent') {\n injectPrefetch(resolveTo(to))\n }\n // Forward original handlers\n if (event.type === 'mouseenter' && onMouseEnter) {\n ;(onMouseEnter as React.MouseEventHandler<HTMLAnchorElement>)(event as React.MouseEvent<HTMLAnchorElement>)\n }\n if (event.type === 'focus' && onFocus) {\n ;(onFocus as React.FocusEventHandler<HTMLAnchorElement>)(event as React.FocusEvent<HTMLAnchorElement>)\n }\n },\n [prefetch, to, onMouseEnter, onFocus],\n )\n\n // Viewport mode: IntersectionObserver\n useEffect(() => {\n if (prefetch !== 'viewport' || !ref.current) return\n if (typeof IntersectionObserver === 'undefined') return // SSR guard\n\n const observer = new IntersectionObserver(\n ([entry]) => {\n if (entry.isIntersecting) {\n injectPrefetch(resolveTo(to))\n observer.disconnect()\n }\n },\n { rootMargin: '200px' }, // prefetch slightly before visible\n )\n observer.observe(ref.current)\n return () => observer.disconnect()\n }, [prefetch, to])\n\n return (\n <RouterLink\n ref={ref}\n to={to}\n onMouseEnter={handleIntent}\n onFocus={handleIntent}\n {...rest}\n />\n )\n}\n","/**\n * <Metadata> — SEO-ready head tags from a single component.\n *\n * Uses React 19's native <title>/<meta>/<link> hoisting to <head>.\n * No build-time extraction needed — works in SSR and client.\n *\n * @example\n * ```tsx\n * import { Metadata } from 'theokit/client'\n *\n * export default function ContactsPage() {\n * return (\n * <>\n * <Metadata\n * title=\"Contacts | My CRM\"\n * description=\"Manage your contacts\"\n * ogImage=\"/og/contacts.png\"\n * canonical=\"https://mycrm.com/contacts\"\n * />\n * <h1>Contacts</h1>\n * </>\n * )\n * }\n * ```\n */\n\nexport interface MetadataProps {\n title?: string\n description?: string\n canonical?: string\n ogTitle?: string\n ogDescription?: string\n ogImage?: string\n ogType?: string\n ogUrl?: string\n twitterCard?: 'summary' | 'summary_large_image'\n /** Custom meta tags rendered as children */\n children?: React.ReactNode\n}\n\nexport function Metadata(props: MetadataProps) {\n const ogTitle = props.ogTitle ?? props.title\n const ogDesc = props.ogDescription ?? props.description\n\n return (\n <>\n {props.title && <title>{props.title}</title>}\n {props.description && <meta name=\"description\" content={props.description} />}\n {props.canonical && <link rel=\"canonical\" href={props.canonical} />}\n {ogTitle && <meta property=\"og:title\" content={ogTitle} />}\n {ogDesc && <meta property=\"og:description\" content={ogDesc} />}\n {props.ogImage && <meta property=\"og:image\" content={props.ogImage} />}\n {props.ogType && <meta property=\"og:type\" content={props.ogType} />}\n {props.ogUrl && <meta property=\"og:url\" content={props.ogUrl} />}\n {props.twitterCard && <meta name=\"twitter:card\" content={props.twitterCard} />}\n {props.children}\n </>\n )\n}\n","/**\n * <Image> — optimized image component with lazy loading.\n *\n * Renders a standard <img> with:\n * - loading=\"lazy\" by default (eager with priority={true})\n * - decoding=\"async\" for non-blocking decode\n * - width/height for CLS prevention\n * - srcSet/sizes forwarded for responsive images\n *\n * No CDN, no Sharp, no build-time optimization — pure HTML attributes\n * that deliver 80% of the performance value at zero complexity.\n *\n * @example\n * ```tsx\n * import { Image } from 'theokit/client'\n *\n * <Image src=\"/team.jpg\" alt=\"Team photo\" width={800} height={600} />\n * <Image src=\"/hero.jpg\" alt=\"Hero\" priority />\n * <Image\n * src=\"/product.jpg\"\n * alt=\"Product\"\n * width={400}\n * height={300}\n * srcSet=\"/product-400.jpg 400w, /product-800.jpg 800w\"\n * sizes=\"(max-width: 768px) 100vw, 400px\"\n * />\n * ```\n */\n\nexport interface ImageProps extends React.ImgHTMLAttributes<HTMLImageElement> {\n /** Image source URL (required). */\n src: string\n /** Alt text for accessibility (required). */\n alt: string\n /** If true, loading=\"eager\" — use for above-the-fold images. */\n priority?: boolean\n}\n\nexport function Image({ priority, loading, decoding, ...props }: ImageProps) {\n return (\n <img\n loading={loading ?? (priority ? 'eager' : 'lazy')}\n decoding={decoding ?? 'async'}\n {...props}\n />\n )\n}\n","/**\n * M30 (ADR-0041) — client host for MCP App `ui://` resources.\n *\n * Renders a tool's `ui://` HTML in a SANDBOXED iframe and bridges a capability-scoped guest API over\n * `postMessage`. Security is load-bearing:\n * - the iframe is `sandbox=\"allow-scripts\"` ONLY — NOT `allow-same-origin`, so the guest runs at a\n * null origin and cannot touch the parent DOM, cookies, or storage;\n * - the host only honors messages whose `source` is the iframe's own `contentWindow`;\n * - the guest API is a fixed, capability-scoped vocabulary (`callServerTool`, `sendMessage`) — any\n * other message type is ignored.\n */\n\n/** The guest → host message vocabulary (capability-scoped). */\nexport type GuestMessage =\n | { type: 'callServerTool'; id: string; tool: string; args?: unknown }\n | { type: 'sendMessage'; text: string }\n\n/** Callbacks the host wires for the guest API. */\nexport interface McpAppHostOptions {\n /** Guest asked to call a server tool — return its result (posted back to the guest by id). */\n onCallServerTool: (tool: string, args: unknown) => unknown\n /** Guest emitted a chat message to the host app. */\n onSendMessage?: (text: string) => void\n}\n\n/**\n * Pure bridge: interpret one guest message and (for `callServerTool`) post the result back via\n * `post`. Exported for unit testing without a DOM. Unknown message shapes are ignored — the guest\n * API is capability-scoped, not an open RPC surface.\n */\nexport function createGuestMessageHandler(\n opts: McpAppHostOptions,\n post: (message: unknown) => void,\n): (data: unknown) => Promise<void> {\n return async (data: unknown): Promise<void> => {\n if (typeof data !== 'object' || data === null) return\n const msg = data as Partial<GuestMessage> & { type?: string }\n if (\n msg.type === 'callServerTool' &&\n typeof msg.id === 'string' &&\n typeof msg.tool === 'string'\n ) {\n const result = await opts.onCallServerTool(msg.tool, msg.args)\n post({ type: 'callServerTool:result', id: msg.id, result })\n return\n }\n if (msg.type === 'sendMessage' && typeof (msg as { text?: unknown }).text === 'string') {\n opts.onSendMessage?.((msg as { text: string }).text)\n }\n }\n}\n\n/** Handle returned by {@link mountMcpApp}. */\nexport interface McpAppHandle {\n iframe: HTMLIFrameElement\n /** Remove the message listener and the iframe. */\n dispose: () => void\n}\n\n/** The sandbox tokens the guest iframe is allowed — scripts only, NEVER `allow-same-origin`. */\nexport const MCP_APP_SANDBOX = 'allow-scripts'\n\n/**\n * Mount an MCP App resource's HTML in a sandboxed iframe inside `container`, wiring the guest API.\n * The HTML comes from `resources/read` (see `mcp-app-resources.ts`). Returns a handle to dispose.\n */\nexport function mountMcpApp(\n container: HTMLElement,\n resource: { html: string },\n opts: McpAppHostOptions,\n): McpAppHandle {\n const iframe = container.ownerDocument.createElement('iframe')\n // Security: scripts only, null origin. Do NOT add allow-same-origin.\n iframe.setAttribute('sandbox', MCP_APP_SANDBOX)\n iframe.srcdoc = resource.html\n container.appendChild(iframe)\n\n const post = (message: unknown): void => {\n // The guest is a sandboxed srcdoc iframe (allow-scripts, NO allow-same-origin) → its origin is\n // the opaque string \"null\", which cannot be used as a reliable targetOrigin. '*' is the only\n // valid target for a null-origin sandboxed guest; the sandbox (no navigation, no same-origin)\n // is what prevents a cross-origin leak, not the targetOrigin string.\n // eslint-disable-next-line sonarjs/post-message -- sandboxed null-origin iframe: '*' is correct\n iframe.contentWindow?.postMessage(message, '*')\n }\n const handle = createGuestMessageHandler(opts, post)\n\n const onMessage = (event: MessageEvent): void => {\n // Only trust messages from THIS iframe's guest window.\n if (event.source !== iframe.contentWindow) return\n void handle(event.data)\n }\n const view = container.ownerDocument.defaultView\n view?.addEventListener('message', onMessage)\n\n return {\n iframe,\n dispose: () => {\n view?.removeEventListener('message', onMessage)\n iframe.remove()\n },\n }\n}\n"],"mappings":"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","names":["useRef","useCallback","jsx","jsx"]}
File without changes
File without changes
File without changes
File without changes
@@ -8,9 +8,9 @@ import {
8
8
  } from "./chunk-HNBWZKIQ.js";
9
9
  import {
10
10
  theoPluginAsync
11
- } from "./chunk-ZJDKAD3L.js";
12
- import "./chunk-OTFIRP6S.js";
13
- import "./chunk-NBWB4S46.js";
11
+ } from "./chunk-4FOPDXKT.js";
12
+ import "./chunk-4JDYVNSY.js";
13
+ import "./chunk-M6NMS5LR.js";
14
14
  import "./chunk-IEES3CHD.js";
15
15
  import {
16
16
  loadConfig
@@ -104,4 +104,4 @@ export {
104
104
  devCommand,
105
105
  startDevServer
106
106
  };
107
- //# sourceMappingURL=dev-TEE4T6ZB.js.map
107
+ //# sourceMappingURL=dev-SVHKR2VR.js.map
File without changes
File without changes
File without changes
File without changes
package/dist/index.js CHANGED
@@ -8,11 +8,11 @@ import {
8
8
  scanRoutes,
9
9
  theoConfigSchema,
10
10
  theoPlugin
11
- } from "./chunk-UOR6JTCI.js";
11
+ } from "./chunk-X6COGWHN.js";
12
12
  import "./chunk-D7ZH7DTG.js";
13
13
  import "./chunk-RSVN727G.js";
14
14
  import "./chunk-7BIM27LV.js";
15
- import "./chunk-5UUOGAJH.js";
15
+ import "./chunk-3FNZG6Q3.js";
16
16
  import "./chunk-5QW7IQQU.js";
17
17
  import "./chunk-IAJ2JVEH.js";
18
18
  import "./chunk-VMEWD57H.js";
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -150,7 +150,20 @@ interface CustomTool {
150
150
  name: string;
151
151
  description: string;
152
152
  inputSchema: Record<string, unknown>;
153
- handler: (input: Record<string, unknown>) => string | Promise<string>;
153
+ handler: (input: Record<string, unknown>, ctx?: {
154
+ signal?: AbortSignal;
155
+ context?: unknown;
156
+ }) => string | Promise<string>;
157
+ /** M18 — optional per-target formatters for the app's UI/transcript (ignored by the SDK wire). */
158
+ transform?: ToolTransform;
159
+ }
160
+ /**
161
+ * M18 — per-target formatters. `display` shapes the rich handler result for the UI; `transcript`
162
+ * shapes it for a saved transcript. Applied by {@link applyTransform}, never by the model wire.
163
+ */
164
+ interface ToolTransform<R = unknown> {
165
+ display?: (result: R) => unknown;
166
+ transcript?: (result: R) => unknown;
154
167
  }
155
168
  /**
156
169
  * Spec accepted by {@link defineAgentTool}. `inputSchema` is a Zod 3 schema
@@ -159,15 +172,33 @@ interface CustomTool {
159
172
  *
160
173
  * @public
161
174
  */
162
- interface DefineAgentToolSpec<T extends z.ZodType> {
175
+ interface DefineAgentToolSpec<T extends z.ZodType, R = string> {
163
176
  /** Tool name surfaced to the LLM. Must match `^[a-zA-Z][a-zA-Z0-9_-]{0,63}$`. */
164
177
  name: string;
165
178
  /** Description surfaced to the LLM. Required — drives tool-selection accuracy. */
166
179
  description: string;
167
180
  /** Zod schema describing the input. Must be `z.object(...)` at the root. */
168
181
  inputSchema: T;
169
- /** Handler invoked with the parsed input. */
170
- handler: (input: z.infer<T>) => string | Promise<string>;
182
+ /**
183
+ * Handler invoked with the parsed input and, optionally, the run `ctx` (M7). `ctx.context`
184
+ * is the object supplied once at the agent level (`defineAgent({ context })`) or per-run —
185
+ * read it for shared config like `projectRoot` instead of baking it into the factory.
186
+ * `ctx.signal` is the abort signal. Optional so existing one-arg handlers keep working.
187
+ *
188
+ * M18 — the handler may return RICH data `R` (not just a string) when `toModelOutput` is
189
+ * provided to map it to the model-visible string.
190
+ */
191
+ handler: (input: z.infer<T>, ctx?: {
192
+ signal?: AbortSignal;
193
+ context?: unknown;
194
+ }) => R | Promise<R>;
195
+ /**
196
+ * M18 — map the rich handler result `R` to the string the model sees. Required (in practice)
197
+ * when `handler` returns a non-string; absent ⇒ the handler must return a string.
198
+ */
199
+ toModelOutput?: (result: R) => string;
200
+ /** M18 — per-target formatters (`display` / `transcript`) for the app, applied by {@link applyTransform}. */
201
+ transform?: ToolTransform<R>;
171
202
  }
172
203
  /**
173
204
  * Build a {@link CustomTool} from a Zod 3 schema + handler.
@@ -187,7 +218,12 @@ interface DefineAgentToolSpec<T extends z.ZodType> {
187
218
  *
188
219
  * @public
189
220
  */
190
- declare function defineAgentTool<T extends z.ZodType>(spec: DefineAgentToolSpec<T>): CustomTool;
221
+ declare function defineAgentTool<T extends z.ZodType, R = string>(spec: DefineAgentToolSpec<T, R>): CustomTool;
222
+ /**
223
+ * M18 — apply a tool's `transform` for a target (`display` / `transcript`). Returns the formatted
224
+ * value, or the raw `result` when the tool declares no transform for that target.
225
+ */
226
+ declare function applyTransform(tool: CustomTool, result: unknown, target: 'display' | 'transcript'): unknown;
191
227
 
192
228
  interface ChannelHandler<TMessage = unknown> {
193
229
  onSubscribe?: (ws: WebSocketLike, room: string, req: IncomingMessage) => void;
@@ -243,4 +279,4 @@ declare function defineWebChannel<TMessage = unknown>(handler: WebChannelHandler
243
279
  */
244
280
  declare function defineTheoPlugin(plugin: TheoPlugin): TheoPlugin;
245
281
 
246
- export { type ActionAccept, type ActionConfig, type ChannelHandler, type CustomTool, type DefineAgentToolSpec, type MiddlewareHandler, type RouteConfig, type WebChannelHandler, WebSocketLike, defineAction, defineAgentTool, defineChannel, defineMiddleware, defineRoute, defineTheoPlugin, defineWebChannel, uiMessageStreamResponse };
282
+ export { type ActionAccept, type ActionConfig, type ChannelHandler, type CustomTool, type DefineAgentToolSpec, type MiddlewareHandler, type RouteConfig, type ToolTransform, type WebChannelHandler, WebSocketLike, applyTransform, defineAction, defineAgentTool, defineChannel, defineMiddleware, defineRoute, defineTheoPlugin, defineWebChannel, uiMessageStreamResponse };
@@ -1,4 +1,5 @@
1
1
  import {
2
+ applyTransform,
2
3
  defineAction,
3
4
  defineAgentTool,
4
5
  defineChannel,
@@ -8,7 +9,7 @@ import {
8
9
  defineWebChannel,
9
10
  defineWebSocket,
10
11
  defineWebSocketWeb
11
- } from "../../chunk-KGFNWIMS.js";
12
+ } from "../../chunk-CWVBDUDC.js";
12
13
  import {
13
14
  uiMessageStreamResponse
14
15
  } from "../../chunk-JZHT3LW7.js";
@@ -23,6 +24,7 @@ import "../../chunk-DGUM43GV.js";
23
24
  export {
24
25
  HEALTH_PATH,
25
26
  READY_PATH,
27
+ applyTransform,
26
28
  defineAction,
27
29
  defineAgentTool,
28
30
  defineChannel,