theokit 0.12.0 → 0.13.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 (131) hide show
  1. package/dist/{actions-virtual-module-SQDY3V5X.js → actions-virtual-module-3CDQTWOC.js} +6 -6
  2. package/dist/{actions-virtual-module-PNPRCEOS.js → actions-virtual-module-EIPXX4ZB.js} +3 -3
  3. package/dist/adapters/web-shim.d.ts +67 -0
  4. package/dist/adapters/ws-shim.d.ts +55 -0
  5. package/dist/agent-events-DosDXkSV.d.ts +94 -0
  6. package/dist/agents-typed-client-SAWAAH7K.js +142 -0
  7. package/dist/agents-typed-client-SAWAAH7K.js.map +1 -0
  8. package/dist/agents-typed-client-UTEQUA63.js +143 -0
  9. package/dist/agents-typed-client-UTEQUA63.js.map +1 -0
  10. package/dist/{app-typed-client-5GYEOYP3.js → app-typed-client-7PBFWZUE.js} +3 -3
  11. package/dist/{app-typed-client-QG7BVZYW.js → app-typed-client-CSOK7NPC.js} +6 -6
  12. package/dist/audit-log-BQWM5YLG.d.ts +60 -0
  13. package/dist/body-parser-web-FV5HWCY3.js +71 -0
  14. package/dist/body-parser-web-FV5HWCY3.js.map +1 -0
  15. package/dist/boot/index.d.ts +39 -0
  16. package/dist/{build-QFRLSEZ4.js → build-HXND27XG.js} +11 -11
  17. package/dist/{chunk-223EFY5X.js → chunk-2J7XU3PW.js} +68 -27
  18. package/dist/chunk-2J7XU3PW.js.map +1 -0
  19. package/dist/{chunk-RESN62GB.js → chunk-2KZQPDYR.js} +5 -48
  20. package/dist/chunk-2KZQPDYR.js.map +1 -0
  21. package/dist/chunk-3S3BNW5K.js +445 -0
  22. package/dist/chunk-3S3BNW5K.js.map +1 -0
  23. package/dist/{chunk-6FYD34NX.js → chunk-BQDGES7C.js} +28 -28
  24. package/dist/{chunk-6FYD34NX.js.map → chunk-BQDGES7C.js.map} +1 -1
  25. package/dist/chunk-EXP56GFQ.js +52 -0
  26. package/dist/chunk-EXP56GFQ.js.map +1 -0
  27. package/dist/chunk-F4YUPDJ2.js +115 -0
  28. package/dist/chunk-F4YUPDJ2.js.map +1 -0
  29. package/dist/{chunk-NAZ4E2GT.js → chunk-KXA37ONC.js} +2 -2
  30. package/dist/chunk-NHJMZCAS.js +32 -0
  31. package/dist/chunk-NHJMZCAS.js.map +1 -0
  32. package/dist/{chunk-43D6XNDR.js → chunk-O62MW4MT.js} +91 -18
  33. package/dist/chunk-O62MW4MT.js.map +1 -0
  34. package/dist/chunk-RSVN727G.js +1 -0
  35. package/dist/{chunk-7CBRKNQA.js → chunk-RYTZYFSD.js} +198 -6
  36. package/dist/chunk-RYTZYFSD.js.map +1 -0
  37. package/dist/chunk-UNLA45FY.js +235 -0
  38. package/dist/chunk-UNLA45FY.js.map +1 -0
  39. package/dist/{chunk-GFMQJHXX.js → chunk-WR4F4EEZ.js} +1082 -1074
  40. package/dist/chunk-WR4F4EEZ.js.map +1 -0
  41. package/dist/{chunk-AD74EAK3.js → chunk-ZSTZXR2D.js} +1 -30
  42. package/dist/chunk-ZSTZXR2D.js.map +1 -0
  43. package/dist/cli/index.js +5 -5
  44. package/dist/client/index.d.ts +418 -0
  45. package/dist/client/index.js +84 -3
  46. package/dist/client/index.js.map +1 -1
  47. package/dist/csrf-BBrEZSBW.d.ts +107 -0
  48. package/dist/csrf-readiness-store-CjIoub3U.d.ts +43 -0
  49. package/dist/define-websocket-CdK94O-D.d.ts +64 -0
  50. package/dist/{dev-GBXOTXUP.js → dev-OWW4XVIH.js} +10 -10
  51. package/dist/{dev-emit-FEFEDLZF.js → dev-emit-5MDSBP5D.js} +3 -3
  52. package/dist/{dev-emit-O4EGOSNV.js → dev-emit-QH2YGZXN.js} +2 -2
  53. package/dist/devtools/entry.d.ts +5 -0
  54. package/dist/error-envelope-BsNzzAV5.d.ts +62 -0
  55. package/dist/health-route-C0hk64_U.d.ts +57 -0
  56. package/dist/index-B40qUSrQ.d.ts +575 -0
  57. package/dist/index.d.ts +361 -0
  58. package/dist/index.js +6 -4
  59. package/dist/index.js.map +1 -1
  60. package/dist/internal-api-4YTJDITC.js +83 -0
  61. package/dist/internal-api-EFKZWIYZ.js +66 -0
  62. package/dist/internal-api-EFKZWIYZ.js.map +1 -0
  63. package/dist/job-backend-CgC8Xf33.d.ts +68 -0
  64. package/dist/match-CfbEFRG4.d.ts +26 -0
  65. package/dist/{openapi-VR6AFBLJ.js → openapi-FHY6HC6I.js} +7 -7
  66. package/dist/plugin-runner-BGBkzgi0.d.ts +95 -0
  67. package/dist/plugin-types-DNJGxr4Z.d.ts +79 -0
  68. package/dist/rate-limit-BdNDZ3vt.d.ts +58 -0
  69. package/dist/rate-limit-store-BEJnhWdw.d.ts +72 -0
  70. package/dist/react-query/index.d.ts +33 -0
  71. package/dist/{registry-Q2TZQLUH.js → registry-34LL7NF4.js} +1 -1
  72. package/dist/{routes-LRYOIIAI.js → routes-EW7TP7NJ.js} +2 -2
  73. package/dist/schema-BpH6ivDY.d.ts +74 -0
  74. package/dist/server/agent/index.d.ts +229 -0
  75. package/dist/server/agent/index.js +2 -1
  76. package/dist/server/auth/index.d.ts +419 -0
  77. package/dist/server/cost/index.d.ts +177 -0
  78. package/dist/server/cron/index.d.ts +208 -0
  79. package/dist/server/define/index.d.ts +313 -0
  80. package/dist/server/define/index.js +4 -2
  81. package/dist/server/http/index.d.ts +11 -0
  82. package/dist/server/index.d.ts +848 -0
  83. package/dist/server/index.js +9 -294
  84. package/dist/server/index.js.map +1 -1
  85. package/dist/server/jobs/index.d.ts +348 -0
  86. package/dist/server/observability/index.d.ts +324 -0
  87. package/dist/server/plugins/index.d.ts +17 -0
  88. package/dist/server/rate-limit/index.d.ts +105 -0
  89. package/dist/server/realtime/index.d.ts +15 -0
  90. package/dist/server/scan/index.d.ts +126 -0
  91. package/dist/server/scan/index.js +1 -1
  92. package/dist/server/security/index.d.ts +193 -0
  93. package/dist/server/storage/index.d.ts +22 -0
  94. package/dist/server/webhook/index.d.ts +148 -0
  95. package/dist/{start-3ZHAXSJE.js → start-KIQ5TTLR.js} +76 -13
  96. package/dist/start-KIQ5TTLR.js.map +1 -0
  97. package/dist/storage-manager-C4jsO0Tp.d.ts +89 -0
  98. package/dist/storage-types-DsDTCPbp.d.ts +96 -0
  99. package/dist/vite-plugin/index.d.ts +115 -0
  100. package/dist/vite-plugin/index.js +6 -4
  101. package/dist/{vite-plugin-WO72VLYR.js → vite-plugin-RK66K26Z.js} +7 -7
  102. package/dist/vite-plugin-RK66K26Z.js.map +1 -0
  103. package/package.json +4 -4
  104. package/dist/chunk-223EFY5X.js.map +0 -1
  105. package/dist/chunk-3LVRAGAZ.js +0 -73
  106. package/dist/chunk-3LVRAGAZ.js.map +0 -1
  107. package/dist/chunk-43D6XNDR.js.map +0 -1
  108. package/dist/chunk-7CBRKNQA.js.map +0 -1
  109. package/dist/chunk-AD74EAK3.js.map +0 -1
  110. package/dist/chunk-GFMQJHXX.js.map +0 -1
  111. package/dist/chunk-PBEH6NXR.js +0 -44
  112. package/dist/chunk-PBEH6NXR.js.map +0 -1
  113. package/dist/chunk-PIVX3DYW.js +0 -142
  114. package/dist/chunk-PIVX3DYW.js.map +0 -1
  115. package/dist/chunk-PPPR5DGR.js +0 -1
  116. package/dist/chunk-RESN62GB.js.map +0 -1
  117. package/dist/start-3ZHAXSJE.js.map +0 -1
  118. /package/dist/{actions-virtual-module-SQDY3V5X.js.map → actions-virtual-module-3CDQTWOC.js.map} +0 -0
  119. /package/dist/{actions-virtual-module-PNPRCEOS.js.map → actions-virtual-module-EIPXX4ZB.js.map} +0 -0
  120. /package/dist/{app-typed-client-5GYEOYP3.js.map → app-typed-client-7PBFWZUE.js.map} +0 -0
  121. /package/dist/{app-typed-client-QG7BVZYW.js.map → app-typed-client-CSOK7NPC.js.map} +0 -0
  122. /package/dist/{build-QFRLSEZ4.js.map → build-HXND27XG.js.map} +0 -0
  123. /package/dist/{chunk-NAZ4E2GT.js.map → chunk-KXA37ONC.js.map} +0 -0
  124. /package/dist/{chunk-PPPR5DGR.js.map → chunk-RSVN727G.js.map} +0 -0
  125. /package/dist/{dev-GBXOTXUP.js.map → dev-OWW4XVIH.js.map} +0 -0
  126. /package/dist/{dev-emit-FEFEDLZF.js.map → dev-emit-5MDSBP5D.js.map} +0 -0
  127. /package/dist/{dev-emit-O4EGOSNV.js.map → dev-emit-QH2YGZXN.js.map} +0 -0
  128. /package/dist/{vite-plugin-WO72VLYR.js.map → internal-api-4YTJDITC.js.map} +0 -0
  129. /package/dist/{openapi-VR6AFBLJ.js.map → openapi-FHY6HC6I.js.map} +0 -0
  130. /package/dist/{registry-Q2TZQLUH.js.map → registry-34LL7NF4.js.map} +0 -0
  131. /package/dist/{routes-LRYOIIAI.js.map → routes-EW7TP7NJ.js.map} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/server/define/define-route.ts","../src/server/define/define-action.ts","../src/server/define/define-middleware.ts","../src/server/define/define-agent-endpoint.ts","../src/server/define/define-agent-tool.ts","../src/server/define/define-websocket.ts","../src/server/define/define-channel.ts","../src/server/define/define-plugin.ts"],"sourcesContent":["import type { z } from 'zod'\n\n// T2.2 (architecture-cleanup) — RouteConfig type moved to core/contracts/\n// (canonical home per ADR-0001 v3). Re-export preserves the public path\n// `import { type RouteConfig } from 'theokit/server'`.\nexport type { RouteConfig } from '../../core/contracts/route-config.js'\n\nimport type { RouteConfig } from '../../core/contracts/route-config.js'\n\n/**\n * Define a typed HTTP route.\n * Identity function — provides type inference for route handlers.\n */\nexport function defineRoute<\n TQuery extends z.ZodType = z.ZodUndefined,\n TBody extends z.ZodType = z.ZodUndefined,\n TParams extends z.ZodType = z.ZodUndefined,\n TCtx = unknown,\n TResponse = unknown,\n>(\n config: RouteConfig<TQuery, TBody, TParams, TCtx, TResponse>,\n): RouteConfig<TQuery, TBody, TParams, TCtx, TResponse> {\n return config\n}\n","import type { z } from 'zod'\n\n/**\n * Action wire-protocol accept mode per plan g3-server-actions-and-useaction\n * v1.2 ADR D1. Default behavior (when omitted) is `'json'`. `'form'` opts the\n * action into FormData multipart parsing for progressive-enhancement forms;\n * the runtime in `server/http/action-execute.ts` will coerce FormData entries\n * against the `input` schema via `formDataToObject` (Astro pattern).\n */\nexport type ActionAccept = 'form' | 'json'\n\nexport interface ActionConfig<TInput extends z.ZodType, TCtx = unknown> {\n /**\n * Zod input schema. Required: every action declares its input contract via\n * Zod (architecture rule: zod-is-SSOT). The shape becomes the handler's\n * typed `input` parameter via `z.infer<TInput>`.\n */\n input: TInput\n /**\n * Wire-protocol accept mode. Defaults to `'json'` when omitted. Setting\n * `'form'` switches the runtime to FormData multipart parsing — the input\n * schema MUST be `z.object(...)` so field-by-field coercion can drive\n * boolean string / number / array coercion (Astro pattern).\n */\n accept?: ActionAccept\n /**\n * Opt OUT of CSRF enforcement for this action. Default (omitted) keeps the\n * multi-header CSRF gate active. Set `false` for endpoints intentionally\n * callable without the `X-Theo-Action` header (e.g. public webhooks). The\n * runtime in `server/http/action-execute.ts` reads this flag.\n */\n csrf?: false\n handler: (ctx: { input: z.infer<TInput>; ctx: TCtx }) => unknown\n}\n\n/**\n * Define a typed server action.\n *\n * Identity function — provides type inference for action handlers. The\n * runtime that consumes the config (validation + invocation + serialization)\n * lives in `server/http/action-execute.ts`.\n *\n * Per plan g3-server-actions-and-useaction v1.2 § Phase 1 / T1.2: the new\n * `accept?: 'form' | 'json'` field is the only contract change vs the\n * pre-G3 identity. Existing callsites (`defineAction({input, handler})`)\n * continue to compile — `accept` is opt-in.\n */\nexport function defineAction<TInput extends z.ZodType, TCtx = unknown>(\n config: ActionConfig<TInput, TCtx>,\n): ActionConfig<TInput, TCtx> {\n return config\n}\n","export type MiddlewareHandler = (\n request: Request,\n next: (request: Request) => Promise<Response>,\n) => Response | Promise<Response>\n\n/**\n * Define a middleware handler.\n * Identity function — provides type annotation for middleware.\n */\nexport function defineMiddleware(handler: MiddlewareHandler): MiddlewareHandler {\n return handler\n}\n","import type { z } from 'zod'\n\nimport type { AgentEvent } from '../agent/agent-types.js'\n\nimport type { RouteConfig } from './define-route.js'\n\n/**\n * T5.1 — defineAgentEndpoint\n *\n * Sugar over defineRoute (ADR D4). Accepts an async generator that yields\n * AgentEvents and produces a RouteConfig whose handler returns a Response\n * streaming Server-Sent Events (SSE).\n *\n * Wire format: `data: <JSON>\\n\\n` per event. Standards-compliant.\n *\n * The generator may throw — the wrapper catches and emits a final\n * `{ type: 'error', message }` event before closing the stream.\n *\n * The wrapper observes `request.signal` (EC-7) — when aborted, the\n * underlying generator is told to `return()` and the stream closes\n * promptly.\n *\n * Note (EC-12, Out of Scope): SSE backpressure (slow consumer) is not\n * handled here. For high-frequency token streaming consider a different\n * transport (WS) or a buffer policy. This MVP enqueues each event\n * immediately.\n */\n\nexport interface AgentEndpointHandlerArgs<\n TCtx = unknown,\n TBody = unknown,\n TParams extends z.ZodType = z.ZodUndefined,\n> {\n query: undefined\n body: TBody\n params: z.infer<TParams>\n request: Request\n ctx: TCtx\n /**\n * Mutable headers bag that the wrapper merges into the SSE response BEFORE\n * the stream starts. Used by `createConversationHistory` to issue a\n * conversation-id cookie on first request. Append entries via\n * `cookieHeaders.append('set-cookie', '<cookie-string>')`.\n *\n * Cookies appended AFTER the first yield are NOT applied — the response\n * headers commit when the wrapper constructs the Response, before the\n * async generator runs. This is per HTTP semantics, not a wrapper choice.\n */\n cookieHeaders: Headers\n /**\n * Phase 3 (Production-Readiness #5) — request abort signal.\n *\n * Fires when the SSE client disconnects (browser closes tab, abort fetch,\n * etc.). Thread this to `agent.send(msg, { signal })` so the SDK cancels\n * the in-flight provider call — STOPS TOKENS FROM CHARGING for output\n * the user will never receive.\n *\n * EC-1 (MUST FIX): the signal is derived via duck-type detection\n * (`'aborted' in r.signal && typeof r.signal.addEventListener === 'function'`)\n * to survive cross-realm scenarios (Node 18 polyfills, undici, Edge\n * runtimes with their own AbortSignal globals). `instanceof AbortSignal`\n * would fail in those cases.\n */\n signal: AbortSignal\n}\n\nexport interface AgentEndpointConfig<\n TCtx = unknown,\n TBody = unknown,\n TParams extends z.ZodType = z.ZodUndefined,\n> {\n /**\n * Optional Zod schema for path params (e.g. `z.object({ id: z.string() })`).\n * When present, the runner validates path params and returns 400 on mismatch\n * BEFORE the generator runs; the validated params are threaded to the\n * generator typed as `z.infer<TParams>` (D4).\n */\n params?: TParams\n handler: (\n args: AgentEndpointHandlerArgs<TCtx, TBody, TParams>,\n ) => AsyncGenerator<AgentEvent, void, unknown>\n}\n\nconst SSE_HEADERS = {\n 'content-type': 'text/event-stream',\n 'cache-control': 'no-cache, no-transform',\n connection: 'keep-alive',\n} as const\n\nfunction encodeSSE(event: AgentEvent): Uint8Array {\n return new TextEncoder().encode(`data: ${JSON.stringify(event)}\\n\\n`)\n}\n\n/**\n * Resolve an AbortSignal from either a Web `Request` (`.signal`) or a\n * Node `IncomingMessage` (`.socket` close + `'aborted'`/'close' events).\n *\n * The framework's `executeRoute` passes IncomingMessage to route handlers\n * today, but `defineAgentEndpoint` was designed for the Web Standards\n * `Request` shape. This helper bridges both — preventing a runtime crash\n * (`Cannot read properties of undefined (reading 'aborted')`) that would\n * otherwise abort the SSE stream silently before the first yield.\n *\n * Node ≥23 regression (empty-SSE-stream): Node 23 added\n * `http.IncomingMessage.prototype.signal` — a Web `AbortSignal` that fires\n * `abort` the instant the request body is fully received (`req.complete ===\n * true`), NOT when the client disconnects. The earlier duck-type\n * (\"has `.signal` with `aborted` + `addEventListener`\") matched a genuine Web\n * `Request` AND, on Node ≥23, the Node `IncomingMessage` — so the helper\n * returned the request-lifecycle signal, already aborted by the time the\n * handler primes, and EVERY agent stream produced 0 bytes on Node 24.\n *\n * Discriminator: a Node `IncomingMessage` is an `EventEmitter` (`typeof\n * r.on === 'function'`); a Web `Request` is not. So `r.signal` is trusted\n * directly ONLY when this is not a Node request. For the Node path the only\n * lifecycle event that means \"client disconnected\" (rather than \"request body\n * finished\") is the underlying SOCKET closing — `req`'s own `.signal`/`'close'`\n * fire at body-end on Node ≥23 and would kill a long-lived SSE response.\n */\nfunction resolveAbortSignal(request: unknown): AbortSignal {\n if (typeof request !== 'object' || request === null) {\n return new AbortController().signal\n }\n const r = request as {\n signal?: unknown\n aborted?: boolean\n complete?: boolean\n on?: (event: string, cb: () => void) => void\n socket?: { on?: (event: string, cb: () => void) => void }\n }\n\n // A genuine Web `Request` exposes `.signal` and is NOT a Node EventEmitter.\n const isNodeRequest = typeof r.on === 'function'\n if (\n !isNodeRequest &&\n typeof r.signal === 'object' &&\n r.signal !== null &&\n 'aborted' in r.signal &&\n typeof (r.signal as AbortSignal).addEventListener === 'function'\n ) {\n return r.signal as AbortSignal\n }\n\n const controller = new AbortController()\n const abort = () => {\n if (!controller.signal.aborted) controller.abort()\n }\n if (r.aborted === true) controller.abort()\n if (typeof r.on === 'function') {\n // Explicit client abort (request reset mid-flight).\n r.on('aborted', abort)\n // `req`'s own 'close' fires at request-completion on Node ≥23 (body fully\n // received), which is NOT a disconnect — guard with `complete`. A close\n // while the request already completed normally is body-end noise.\n r.on('close', () => {\n if (r.complete !== true) abort()\n })\n }\n // The underlying socket closes only on real connection teardown (client\n // gone), never at request-body-end — the correct disconnect signal for a\n // long-lived SSE response.\n const socket = r.socket\n if (socket && typeof socket.on === 'function') {\n socket.on('close', abort)\n }\n return controller.signal\n}\n\nexport function defineAgentEndpoint<\n TBody = unknown,\n TCtx = unknown,\n TParams extends z.ZodType = z.ZodUndefined,\n>(\n config: AgentEndpointConfig<TCtx, TBody, TParams>,\n): RouteConfig<z.ZodUndefined, z.ZodUndefined, TParams, TCtx, Response> {\n return {\n params: config.params,\n handler: async ({ request, ctx, body, query, params }) => {\n const cookieHeaders = new Headers()\n const signal = resolveAbortSignal(request)\n const generator = config.handler({\n request,\n ctx: ctx,\n body: body as TBody,\n query: query,\n params: params,\n cookieHeaders,\n signal,\n })\n\n // Prime the generator to its first yield. This forces the handler's\n // synchronous + async setup (including `createConversationHistory` if\n // used) to run BEFORE the Response headers commit, so any Set-Cookie\n // lines appended to `cookieHeaders` land in the actual response.\n //\n // First-byte latency cost: bounded by the work up to the first yield\n // (typically 100-500ms for an LLM chat with `agent.send`). Acceptable\n // for the use case; alternative (heuristic detection of cookie writes)\n // is brittle.\n let firstResult: IteratorResult<AgentEvent, void>\n let primeError: unknown\n try {\n firstResult = await generator.next()\n } catch (err) {\n primeError = err\n firstResult = { value: undefined, done: true }\n }\n\n // Merge any cookies the handler issued into the response headers.\n const responseHeaders = new Headers(SSE_HEADERS)\n for (const value of cookieHeaders.getSetCookie()) {\n responseHeaders.append('set-cookie', value)\n }\n\n const stream = new ReadableStream<Uint8Array>({\n async start(controller) {\n const onAbort = () => {\n void generator.return(undefined)\n }\n\n if (signal.aborted) {\n controller.close()\n return\n }\n signal.addEventListener('abort', onAbort, { once: true })\n\n try {\n // If priming threw, yield a final error event and close.\n if (primeError !== undefined) {\n let message: string\n if (primeError instanceof Error) {\n message = primeError.message\n } else if (typeof primeError === 'string') {\n message = primeError\n } else {\n message = 'agent handler failed during setup'\n }\n controller.enqueue(encodeSSE({ type: 'error', message }))\n return\n }\n // Enqueue the primed first value (if any).\n if (firstResult.done !== true) {\n controller.enqueue(encodeSSE(firstResult.value))\n } else {\n return\n }\n // Continue iterating remaining events.\n for await (const event of generator) {\n // `signal.aborted` can flip true asynchronously via the\n // listener registered above. ESLint's narrowing only sees\n // the false assertion at line 114; the dynamic abort is\n // legitimate and the guard prevents enqueue-after-close.\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n if (signal.aborted) break\n controller.enqueue(encodeSSE(event))\n }\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n controller.enqueue(encodeSSE({ type: 'error', message }))\n } finally {\n signal.removeEventListener('abort', onAbort)\n controller.close()\n }\n },\n cancel() {\n void generator.return(undefined)\n },\n })\n\n return new Response(stream, { headers: responseHeaders })\n },\n }\n}\n","import { z } from 'zod'\n\n/**\n * Item #4 — `defineAgentTool`\n *\n * Sugar over the `@theokit/sdk` `CustomTool` contract. Takes a Zod schema +\n * handler and produces a structurally-compatible `CustomTool` that\n * `Agent.create({ tools: [...] })` accepts.\n *\n * Uses Zod v4's native `z.toJSONSchema()` to convert the input schema to\n * JSON Schema for LLM providers.\n *\n * Handler error propagation:\n * `defineAgentTool` parses the input via the Zod schema BEFORE calling the\n * user handler. Invalid input throws a `ZodError`, which the SDK's tool-\n * dispatcher (or the `streamAgentRun` adapter) sees as a tool failure and\n * surfaces as an `error` AgentEvent on the SSE wire (ADR D3).\n */\n\n/**\n * Local mirror of the SDK's `CustomTool` interface. We don't `import type`\n * from `@theokit/sdk` because the SDK is an optional peer (consumers who\n * never call `defineAgentTool` shouldn't need it installed). The shape is\n * the wire contract; any structurally-matching object is accepted by\n * `Agent.create({ tools })`.\n *\n * @public\n */\nexport interface CustomTool {\n name: string\n description: string\n inputSchema: Record<string, unknown>\n handler: (input: Record<string, unknown>) => string | Promise<string>\n}\n\n/**\n * Spec accepted by {@link defineAgentTool}. `inputSchema` is a Zod 3 schema\n * rooted in `z.object(...)`. The `handler` argument type is inferred via\n * `z.infer<T>`.\n *\n * @public\n */\nexport interface DefineAgentToolSpec<T extends z.ZodType> {\n /** Tool name surfaced to the LLM. Must match `^[a-zA-Z][a-zA-Z0-9_-]{0,63}$`. */\n name: string\n /** Description surfaced to the LLM. Required — drives tool-selection accuracy. */\n description: string\n /** Zod schema describing the input. Must be `z.object(...)` at the root. */\n inputSchema: T\n /** Handler invoked with the parsed input. */\n handler: (input: z.infer<T>) => string | Promise<string>\n}\n\nconst TOOL_NAME_REGEX = /^[a-zA-Z][a-zA-Z0-9_-]{0,63}$/\n\nfunction isZodObject(schema: z.ZodType): boolean {\n // Refinements (`.refine`), transforms (`.transform`), and defaults wrap the\n // underlying schema — walk the chain until we hit a ZodObject (or give up).\n // Supports both zod 4 (`instanceof z.ZodObject`, `def.type === 'object'`,\n // wrappers via `def.innerType` / pipe via `def.in`) and zod 3\n // (`_def.typeName === 'ZodObject'`, wrappers via `_def.schema`/`_def.innerType`).\n let current: unknown = schema\n for (let depth = 0; depth < 10; depth++) {\n if (current instanceof z.ZodObject) return true\n const z4 = (current as { def?: { type?: string; innerType?: unknown; in?: unknown } }).def\n if (z4?.type === 'object') return true\n const z3 = (current as { _def?: { typeName?: string; schema?: unknown; innerType?: unknown } })\n ._def\n if (z3?.typeName === 'ZodObject') return true\n const next = z4?.innerType ?? z4?.in ?? z3?.schema ?? z3?.innerType\n if (next !== undefined) {\n current = next\n continue\n }\n return false\n }\n return false\n}\n\n/**\n * Build a {@link CustomTool} from a Zod 3 schema + handler.\n *\n * Behavior:\n * - Validates `name` matches the LLM tool-name regex.\n * - Requires `inputSchema` to be a `ZodObject` (Anthropic + SDK contract).\n * - Warns (not throws) if `description` is empty — empty descriptions\n * degrade LLM tool selection.\n * - Converts the Zod schema to JSON Schema 7 inline (no `$ref`s — LLMs handle\n * inline schemas more reliably).\n * - Strips the top-level `$schema` field (Anthropic rejects schemas with\n * `$schema` at root in some provider modes).\n * - Wraps the handler to parse the input via the Zod schema BEFORE invoking\n * the user code — bad LLM-supplied input throws `ZodError`, which the SDK\n * converts to `tool_result(isError)`.\n *\n * @public\n */\nexport function defineAgentTool<T extends z.ZodType>(spec: DefineAgentToolSpec<T>): CustomTool {\n if (!TOOL_NAME_REGEX.test(spec.name)) {\n throw new Error(\n `defineAgentTool: name must match ${TOOL_NAME_REGEX.source}. Got: ${JSON.stringify(spec.name)}`,\n )\n }\n if (!isZodObject(spec.inputSchema)) {\n throw new Error('defineAgentTool: inputSchema must be a ZodObject (z.object({...}))')\n }\n if (spec.description.length === 0) {\n console.warn(\n `defineAgentTool(${JSON.stringify(spec.name)}): empty description degrades LLM tool selection — provide a one-sentence summary.`,\n )\n }\n\n // Zod v4 native JSON Schema conversion — replaces zod-to-json-schema dep.\n // Strip $schema (Anthropic + some providers reject it).\n const { $schema: _$schema, ...inputSchema } = z.toJSONSchema(spec.inputSchema) as Record<\n string,\n unknown\n > & {\n $schema?: unknown\n }\n\n return {\n name: spec.name,\n description: spec.description,\n inputSchema,\n handler: async (input: Record<string, unknown>): Promise<string> => {\n const parsed = spec.inputSchema.parse(input)\n return await spec.handler(parsed)\n },\n }\n}\n","import type { IncomingMessage } from 'node:http'\n\nexport interface WebSocketLike {\n send(data: string | Buffer): void\n close(code?: number, reason?: string): void\n}\n\nexport interface WebSocketHandler {\n onOpen?: (ws: WebSocketLike, req: IncomingMessage) => void\n onMessage?: (ws: WebSocketLike, data: string | Buffer) => void\n onClose?: (ws: WebSocketLike, code: number, reason: Buffer) => void\n onError?: (ws: WebSocketLike, error: Error) => void\n}\n\n/**\n * Define a WebSocket endpoint handler.\n * Identity function — provides type inference for WebSocket handlers.\n */\nexport function defineWebSocket(handler: WebSocketHandler): WebSocketHandler {\n return handler\n}\n\n/**\n * T5a.2 Phase F slice 3/3 — Web-Standards WebSocket endpoint handler.\n *\n * Mirror of `WebSocketHandler` for the Web `Request` shape. `onOpen`\n * receives `request: Request` instead of `req: IncomingMessage`. The\n * rest of the lifecycle (onMessage, onClose, onError) is shape-agnostic\n * (`WebSocketLike` is already Web-standards-compatible per the existing\n * design — `send(string | Buffer)` works on both Node `ws` and Web\n * `WebSocket` instances; CF Workers / Bun / Deno coerce as needed at\n * the adapter boundary).\n *\n * Per `docs/plans/t5a2-incoming-message-to-request-shape-refactor-plan.md`\n * v1.0 § Phase F (closes Phase F).\n *\n * **Architectural note — WebSocket upgrade semantics differ across runtimes:**\n * - Node + `ws`: `WebSocketServer.handleUpgrade(req, socket, head, cb)` —\n * `req` is `IncomingMessage`. Use `WebSocketHandler`.\n * - CF Workers: `new WebSocketPair()` + `request.headers` (the upgrade\n * handshake IS a Web Request). Use `WebSocketHandlerWeb`.\n * - Bun: `server.upgrade(request, { data })` — same Web Request shape.\n * - Deno: `Deno.upgradeWebSocket(request)` — same Web Request shape.\n *\n * Cross-runtime WebSocket endpoints ship BOTH `WebSocketHandler` +\n * `WebSocketHandlerWeb` exports; the runtime adapter picks the matching\n * one. This is the canonical Hono / Nitric pattern.\n */\nexport interface WebSocketHandlerWeb {\n onOpen?: (ws: WebSocketLike, request: Request) => void\n onMessage?: (ws: WebSocketLike, data: string | Uint8Array) => void\n onClose?: (ws: WebSocketLike, code: number, reason: string) => void\n onError?: (ws: WebSocketLike, error: Error) => void\n}\n\n/**\n * Web-Standards `defineWebSocket` sibling. Identity function — provides\n * type inference for Web WebSocket handlers.\n *\n * **Type difference note vs Node path:**\n * - `onMessage` data is `string | Uint8Array` instead of `string | Buffer`\n * (Web standards have no `Buffer`; Node's Buffer is a Uint8Array\n * subclass so the Node path's Buffer values flow through unchanged\n * when adapters wrap them).\n * - `onClose` reason is `string` instead of `Buffer` (Web `CloseEvent`\n * exposes the reason as a UTF-8 string natively).\n */\nexport function defineWebSocketWeb(handler: WebSocketHandlerWeb): WebSocketHandlerWeb {\n return handler\n}\n","import type { IncomingMessage } from 'node:http'\n\nimport type { WebSocketLike } from './define-websocket.js'\n\nexport interface ChannelHandler<TMessage = unknown> {\n onSubscribe?: (ws: WebSocketLike, room: string, req: IncomingMessage) => void\n onMessage?: (ws: WebSocketLike, room: string, data: TMessage) => void\n onUnsubscribe?: (ws: WebSocketLike, room: string) => void\n}\n\n/**\n * Define a channel handler for WebSocket rooms.\n * Identity function — provides type inference for channel handlers.\n */\nexport function defineChannel<TMessage = unknown>(\n handler: ChannelHandler<TMessage>,\n): ChannelHandler<TMessage> {\n return handler\n}\n\n/**\n * T5a.2 Phase F slice 2/3 — Web-Standards channel handler.\n *\n * Mirror of `ChannelHandler<TMessage>` for the Web `Request` shape.\n * `onSubscribe` receives `request: Request` instead of `req: IncomingMessage`\n * — the rest of the surface (onMessage, onUnsubscribe) is shape-agnostic\n * (WebSocketLike is already Web-standards-compatible per `define-websocket.ts`).\n *\n * Per `docs/plans/t5a2-incoming-message-to-request-shape-refactor-plan.md`\n * v1.0 § Phase F.\n *\n * **Architectural note:** WebSocket upgrade semantics differ across\n * runtimes:\n * - Node: `WebSocketServer.handleUpgrade(req, socket, head, cb)` —\n * hands you `req: IncomingMessage` at the upgrade handshake.\n * - CF Workers: `new WebSocketPair()` + `request.headers` (the upgrade\n * handshake IS a Web Request) — hands you `request: Request`.\n * - Bun: `server.upgrade(request, { data })` — same Web Request shape.\n * - Deno: `Deno.upgradeWebSocket(request)` — same Web Request shape.\n *\n * Channel handlers targeting CF/Bun/Deno use `WebChannelHandler`; legacy\n * Node consumers stay on `ChannelHandler`. Cross-runtime channels ship\n * both shapes.\n */\nexport interface WebChannelHandler<TMessage = unknown> {\n onSubscribe?: (ws: WebSocketLike, room: string, request: Request) => void\n onMessage?: (ws: WebSocketLike, room: string, data: TMessage) => void\n onUnsubscribe?: (ws: WebSocketLike, room: string) => void\n}\n\n/**\n * Web-Standards `defineChannel` sibling. Identity function — provides\n * type inference for Web channel handlers.\n */\nexport function defineWebChannel<TMessage = unknown>(\n handler: WebChannelHandler<TMessage>,\n): WebChannelHandler<TMessage> {\n return handler\n}\n","import type { TheoPlugin } from '../plugin-types.js'\n\n/**\n * Identity function for defining a Theo plugin.\n *\n * **Note:** Prefer `definePlugin` (shorter, canonical name per ADR-0008 D6).\n * Both functions are identical — `defineTheoPlugin` is kept as an alias for\n * existing in-tree consumers without forcing a migration sweep.\n */\nexport function defineTheoPlugin(plugin: TheoPlugin): TheoPlugin {\n return plugin\n}\n"],"mappings":"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","names":[]}
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-GBXOTXUP.js");
8
+ const { devCommand } = await import("../dev-OWW4XVIH.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-QFRLSEZ4.js");
13
+ const { buildCommand } = await import("../build-HXND27XG.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-3ZHAXSJE.js");
25
+ const { startCommand } = await import("../start-KIQ5TTLR.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);
@@ -49,7 +49,7 @@ cli.command(
49
49
  });
50
50
  cli.command("routes", "List all routes, actions, and WebSocket endpoints").action(async () => {
51
51
  try {
52
- const { routesCommand } = await import("../routes-LRYOIIAI.js");
52
+ const { routesCommand } = await import("../routes-EW7TP7NJ.js");
53
53
  await routesCommand();
54
54
  } catch (err) {
55
55
  const msg = err instanceof Error ? err.message : String(err);
@@ -98,7 +98,7 @@ cli.command(
98
98
  "Generate <distDir>/openapi.json from route schemas (opt-in via config.openapi)"
99
99
  ).option("--dry-run", "Print the document to stdout without writing to disk (EC-3)").action(async (options) => {
100
100
  try {
101
- const { openapiCommand } = await import("../openapi-VR6AFBLJ.js");
101
+ const { openapiCommand } = await import("../openapi-FHY6HC6I.js");
102
102
  await openapiCommand({ dryRun: Boolean(options.dryRun) });
103
103
  } catch (err) {
104
104
  const msg = err instanceof Error ? err.message : String(err);
@@ -0,0 +1,418 @@
1
+ import { z } from 'zod';
2
+ import { T as TheoErrorEnvelope } from '../error-envelope-BsNzzAV5.js';
3
+ export { FetchOptionsLike, Fetcher, QueryKey, UseTheoQueryConfig, buildUseTheoQueryConfig, stableQueryKey } from '../react-query/index.js';
4
+ import { A as AgentEvent, a as AgentErrorEvent } from '../agent-events-DosDXkSV.js';
5
+ export { b as AgentMessageEvent, c as AgentThinkingEvent, d as AgentToolCallEvent, e as AgentToolResultEvent } from '../agent-events-DosDXkSV.js';
6
+ import { UIMessage } from 'ai';
7
+ import * as react from 'react';
8
+ import { LinkProps as LinkProps$1 } from 'react-router';
9
+
10
+ /** Infer the response type from a route's handler return */
11
+ type InferResponse<T> = T extends {
12
+ handler: (...args: never[]) => infer R;
13
+ } ? Awaited<R> : unknown;
14
+ /** Extract the query Zod schema type, handling optional properties */
15
+ type ExtractQuery<T> = T extends {
16
+ query?: infer Q;
17
+ } ? (Q extends z.ZodType ? Q : never) : never;
18
+ /** Extract the body Zod schema type, handling optional properties */
19
+ type ExtractBody<T> = T extends {
20
+ body?: infer B;
21
+ } ? (B extends z.ZodType ? B : never) : never;
22
+ /** Infer query type from a route's query Zod schema */
23
+ type InferQuery<T> = [ExtractQuery<T>] extends [never] ? undefined : ExtractQuery<T> extends z.ZodUndefined ? undefined : z.infer<ExtractQuery<T>>;
24
+ /** Infer body type from a route's body Zod schema */
25
+ type InferBody<T> = [ExtractBody<T>] extends [never] ? undefined : ExtractBody<T> extends z.ZodUndefined ? undefined : z.infer<ExtractBody<T>>;
26
+ /** Build the options type based on what schemas the route has */
27
+ type TheoFetchOptions<T> = Omit<RequestInit, 'body' | 'method'> & (InferQuery<T> extends undefined ? {
28
+ query?: never;
29
+ } : {
30
+ query: InferQuery<T>;
31
+ }) & (InferBody<T> extends undefined ? {
32
+ body?: never;
33
+ } : {
34
+ body: InferBody<T>;
35
+ });
36
+ declare class TheoFetchError extends Error {
37
+ status: number;
38
+ code?: string;
39
+ issues?: unknown[];
40
+ /**
41
+ * G5 T2.1 — canonical envelope view of the server-side error. Use this in
42
+ * consumer code that wants to switch on a typed TheoErrorCode instead of
43
+ * coupling to the legacy `.status` / `.code` flat fields.
44
+ */
45
+ readonly envelope: TheoErrorEnvelope;
46
+ constructor(status: number, body?: unknown);
47
+ }
48
+ declare function theoFetch<T>(url: string, options?: TheoFetchOptions<T>): Promise<InferResponse<T>>;
49
+
50
+ /**
51
+ * Phase 3 of G1 (g1-client-codegen-plan.md):
52
+ *
53
+ * `createAppClient(baseUrl?, fetchImpl?)` returns a Proxy that walks property
54
+ * access (`client.posts.id.get(...)`) into a `theoFetch` invocation against
55
+ * `{baseUrl}/posts/{params.id}` with method `GET`.
56
+ *
57
+ * Type safety is delivered by the `.d.ts` file emitted in Phase 2 — this
58
+ * runtime is structurally untyped. Power users may use `theoFetch` directly
59
+ * when they need to escape the Proxy facade.
60
+ *
61
+ * Edge cases absorbed:
62
+ * - EC-1 (thenable trap): `then` / `catch` / `finally` / `toJSON` / `Symbol.*`
63
+ * return `undefined` so that accidental `await client.posts` resolves to a
64
+ * plain Proxy value (it is NOT thenable) instead of looping.
65
+ * - EC-7 (abort signal): `opts.signal` is spread through to the underlying
66
+ * fetchImpl unchanged — verified in unit tests.
67
+ *
68
+ * Bundle target: ≤ 2KB gzipped.
69
+ */
70
+
71
+ type FetchImpl = typeof theoFetch;
72
+ interface CreateAppClientOptions {
73
+ /** Base URL for the API. Defaults to `/api`. */
74
+ baseUrl?: string;
75
+ /** Test-only fetch override. Production callers should not pass this. */
76
+ fetchImpl?: FetchImpl;
77
+ }
78
+ declare function createAppClient<TAppClient = unknown>(baseUrlOrOptions?: string | CreateAppClientOptions, legacyFetchImpl?: FetchImpl): TAppClient;
79
+
80
+ /**
81
+ * T5.1 — client-side microtask batching.
82
+ *
83
+ * Collects all `dispatch` calls within the same microtask and sends them as a
84
+ * single HTTP POST to the configured transport. Each caller's promise resolves
85
+ * with its own result; one failed item does not break the others (per-item
86
+ * error isolation).
87
+ *
88
+ * Designed as a transport-agnostic primitive so unit tests do not require
89
+ * network access. The default transport (fetch to `/api/__theo_batch__`)
90
+ * lives alongside this module but is created by the consumer (e.g., theoFetch).
91
+ */
92
+ interface BatchRequest {
93
+ path: string;
94
+ method: string;
95
+ query?: Record<string, unknown>;
96
+ body?: unknown;
97
+ headers?: Record<string, string>;
98
+ }
99
+ type BatchResponse = {
100
+ index: number;
101
+ data: unknown;
102
+ } | {
103
+ index: number;
104
+ error: {
105
+ message: string;
106
+ code?: string;
107
+ };
108
+ };
109
+ type BatchTransport = (requests: BatchRequest[]) => Promise<BatchResponse[]>;
110
+ interface BatcherOptions {
111
+ transport: BatchTransport;
112
+ /** Maximum batch size before flushing into multiple parallel batches. */
113
+ max?: number;
114
+ }
115
+ interface Batcher {
116
+ dispatch(req: BatchRequest): Promise<unknown>;
117
+ }
118
+ declare function createBatcher(options: BatcherOptions): Batcher;
119
+
120
+ /**
121
+ * Accumulated assistant text (M5-1): the concatenation of every `message`
122
+ * event's `content`. Pure. Assumes append/delta semantics — a server that
123
+ * emits full-snapshot messages should read `events` directly.
124
+ */
125
+ declare function deriveLiveText(events: readonly AgentEvent[]): string;
126
+ /**
127
+ * The current error (M5-1): the last `error` event, or `undefined`. Surfaces
128
+ * the full `AgentErrorEvent` (with `code`/`retriable`) so the UI can branch.
129
+ * Pure.
130
+ */
131
+ declare function deriveError(events: readonly AgentEvent[]): AgentErrorEvent | undefined;
132
+ /**
133
+ * T5.2 — useAgentStream
134
+ *
135
+ * React hook to consume an agent endpoint defined with `defineAgentEndpoint`.
136
+ *
137
+ * Transport (ADR D7, EC-3): fetch + ReadableStream — NOT EventSource.
138
+ * EventSource is GET-only; agent endpoints need POST + body.
139
+ *
140
+ * Returns:
141
+ * events — array of AgentEvents accumulated so far
142
+ * send — call with a payload to (re)open a stream
143
+ * status — 'idle' | 'streaming' | 'done' | 'error'
144
+ * abort — manually cancel an in-flight stream
145
+ *
146
+ * Cleanup (EC-8): on unmount, the current AbortController fires .abort(),
147
+ * which propagates to the underlying fetch and the ReadableStream reader.
148
+ * Safe under React.StrictMode double-mount.
149
+ */
150
+ type AgentStreamStatus = 'idle' | 'streaming' | 'done' | 'error';
151
+ interface UseAgentStreamReturn<TBody = unknown> {
152
+ events: AgentEvent[];
153
+ status: AgentStreamStatus;
154
+ /** Accumulated assistant text — concatenation of all `message` contents (M5-1). */
155
+ liveText: string;
156
+ /** The last `error` event, or `undefined` (M5-1). */
157
+ error: AgentErrorEvent | undefined;
158
+ send: (body: TBody) => void;
159
+ abort: () => void;
160
+ reset: () => void;
161
+ }
162
+ interface UseAgentStreamOptions {
163
+ /** Extra headers (e.g., auth). */
164
+ headers?: Record<string, string>;
165
+ /** Override fetch (rare — primarily for tests). */
166
+ fetch?: typeof fetch;
167
+ }
168
+ declare function useAgentStream<TBody = unknown>(path: string, options?: UseAgentStreamOptions): UseAgentStreamReturn<TBody>;
169
+
170
+ /**
171
+ * A correlated, UI-facing view of one tool invocation (M5-2).
172
+ *
173
+ * `foldAgentToolCards` correlates a `tool_call` with its `tool_result` (by
174
+ * `id`, falling back to FIFO-by-name) into a single card so consumers stop
175
+ * hand-rolling `switch(event.type)`.
176
+ */
177
+ interface AgentToolCard {
178
+ /** Correlation id (the event `id`, or a synthesized `tool-<index>`). */
179
+ id: string;
180
+ name: string;
181
+ args: Record<string, unknown>;
182
+ status: 'running' | 'success' | 'error';
183
+ /** The `tool_result.data` payload, once the result arrives. */
184
+ result?: unknown;
185
+ }
186
+ /**
187
+ * Classifies a `tool_result.data` payload as success/error. Injectable so the
188
+ * correlator is decoupled from any specific tool-result envelope shape.
189
+ */
190
+ type ToolEnvelopeResolver = (data: unknown) => {
191
+ error: boolean;
192
+ };
193
+ interface FoldAgentToolCardsOptions {
194
+ /** Override the default envelope classifier. */
195
+ resolveEnvelope?: ToolEnvelopeResolver;
196
+ }
197
+ /**
198
+ * Default envelope resolver: an object (or a JSON STRING parsing to an object)
199
+ * with `ok === false` is an error; everything else is success (conservative).
200
+ * sdk-tools handlers return a JSON STRING, so string parsing is intentional.
201
+ */
202
+ declare function defaultResolveEnvelope(data: unknown): {
203
+ error: boolean;
204
+ };
205
+ /**
206
+ * Fold an `AgentEvent[]` into correlated {@link AgentToolCard}s (M5-2). Pure.
207
+ *
208
+ * Correlation: a `tool_result` matches its `tool_call` by `id` when both carry
209
+ * one; otherwise it matches the oldest still-`running` card with the same
210
+ * `name` (FIFO). An unmatched `tool_result` becomes its own finished card.
211
+ * A `tool_call` with no result stays `running`. The error status comes from
212
+ * `options.resolveEnvelope` (default {@link defaultResolveEnvelope}).
213
+ */
214
+ declare function foldAgentToolCards(events: readonly AgentEvent[], options?: FoldAgentToolCardsOptions): AgentToolCard[];
215
+
216
+ /** {@link useAgentToolCards} return — the stream result plus correlated tool cards. */
217
+ interface UseAgentToolCardsReturn<TBody = unknown> extends UseAgentStreamReturn<TBody> {
218
+ /** Correlated tool cards folded from the stream events (M5-2). */
219
+ toolCards: AgentToolCard[];
220
+ }
221
+ /**
222
+ * `useAgentStream` + correlated tool cards (M5-2). Composes the stream hook with
223
+ * the pure {@link foldAgentToolCards} reducer; pass `resolveEnvelope` to classify
224
+ * tool-result payloads (default flags `{ ok: false }` envelopes as errors).
225
+ */
226
+ declare function useAgentToolCards<TBody = unknown>(path: string, options?: UseAgentStreamOptions & FoldAgentToolCardsOptions): UseAgentToolCardsReturn<TBody>;
227
+
228
+ /**
229
+ * T5.2 — Pure SSE consumer used by useAgentStream.
230
+ *
231
+ * Split out from the React hook so the wire behavior can be tested
232
+ * without a DOM. The hook is glue: useState + useEffect + this primitive.
233
+ *
234
+ * Transport (ADR D7, EC-3): fetch + ReadableStream — NOT EventSource.
235
+ * EventSource is GET-only and cannot carry a body. Agent endpoints need
236
+ * POST + JSON body, so we use fetch + manual SSE chunk parsing.
237
+ */
238
+ interface ConsumeOptions<TBody = unknown> {
239
+ /** Request body — JSON-serialized into the POST. */
240
+ body: TBody;
241
+ /** Called once per SSE event parsed off the stream. */
242
+ onEvent: (event: AgentEvent) => void;
243
+ /** Optional fetch override (tests). */
244
+ fetch?: typeof fetch;
245
+ /** Optional abort signal — passed through to fetch. */
246
+ signal?: AbortSignal;
247
+ /** Extra headers (e.g., auth). */
248
+ headers?: Record<string, string>;
249
+ }
250
+ /**
251
+ * Parse a single SSE line of the form `data: <json>`.
252
+ * Returns null for non-data lines, comments, or malformed JSON.
253
+ */
254
+ declare function parseSSEChunk(line: string): AgentEvent | null;
255
+ /**
256
+ * POSTs to `path` with `body`, reads the SSE response, and invokes
257
+ * `onEvent` for each parsed AgentEvent. Resolves when the server
258
+ * closes the stream or the signal aborts.
259
+ *
260
+ * T1.1 — Attaches `X-Theo-Action: '1'` so 0.3.0 strict CSRF mode accepts
261
+ * the request. The user can override (or suppress) by passing the header
262
+ * in `options.headers` — spread order ensures their value wins.
263
+ */
264
+ declare function consumeAgentStream<TBody = unknown>(path: string, options: ConsumeOptions<TBody>): Promise<void>;
265
+
266
+ /**
267
+ * M2 (theokit-ai-first) — read a TheoKit agent endpoint's `UIMessageStream` SSE `Response`
268
+ * into reconstructed assistant `UIMessage`s, reusing the `ai` package's own consumer
269
+ * primitives (`parseJsonEventStream` + `readUIMessageStream`) — the exact path
270
+ * `@ai-sdk/react`'s `useChat` runs internally. No reinvented wire parser (Rule 9).
271
+ *
272
+ * `ai` is an OPTIONAL peer dependency, so it is imported dynamically: an app that never
273
+ * calls an agent never pays for it, and importing `theokit/client` does not hard-require
274
+ * `ai` (mirrors how the agent runtime dynamically imports `@theokit/sdk`). An agent app
275
+ * always has `ai` installed (it is the UIMessageStream consumer).
276
+ *
277
+ * `onMessage` is invoked on every reconstruction step with the latest snapshot of the
278
+ * assistant message, so a caller (the `useAgent` hook) can render streaming updates.
279
+ */
280
+ declare function consumeUIMessageStream(response: Response, onMessage: (message: UIMessage) => void): Promise<void>;
281
+
282
+ /**
283
+ * M2 (theokit-ai-first) — `useAgent`, the typed client hook for the `agents/*.ts` convention.
284
+ *
285
+ * A thin React hook over `consumeUIMessageStream` (which reuses `ai`'s own UIMessageStream
286
+ * reader). `useAgent('support')` binds to `POST /api/agents/support`; the generated
287
+ * `@theo/agents` module (`.theokit/agents.d.ts`) types `send` to the agent's `input` schema,
288
+ * so the request shape is inferred end-to-end from the server `defineAgent({ input })` with
289
+ * ZERO manual wiring (DoD line 2).
290
+ *
291
+ * Transport mirrors `useAgentStream`: fetch + ReadableStream (POST needs a body; EventSource
292
+ * is GET-only). `ai` is loaded lazily by `consumeUIMessageStream` so non-agent apps never
293
+ * pay for it.
294
+ */
295
+ type UseAgentStatus = 'idle' | 'streaming' | 'done' | 'error';
296
+ interface UseAgentReturn<TInput = unknown> {
297
+ /** Reconstructed assistant messages so far (ai `UIMessage[]`). */
298
+ messages: UIMessage[];
299
+ status: UseAgentStatus;
300
+ /** The last error, or `undefined`. */
301
+ error: Error | undefined;
302
+ /** Send a request; opens a new stream. Typed to the agent's `input` schema. */
303
+ send: (input: TInput) => void;
304
+ /** Abort an in-flight stream. */
305
+ abort: () => void;
306
+ /** Clear messages + error, back to idle. */
307
+ reset: () => void;
308
+ }
309
+ interface UseAgentOptions {
310
+ /** Extra request headers (e.g., auth). */
311
+ headers?: Record<string, string>;
312
+ /** Override fetch (primarily for tests). */
313
+ fetch?: typeof fetch;
314
+ }
315
+ /**
316
+ * Bind to the agent endpoint at `path` (`/api/agents/<name>`). Prefer the generated
317
+ * `useAgent` from `@theo/agents` (typed by agent name); this base accepts an explicit path.
318
+ */
319
+ declare function useAgent<TInput = unknown>(path: string, options?: UseAgentOptions): UseAgentReturn<TInput>;
320
+
321
+ type PrefetchBehavior = 'none' | 'intent' | 'viewport';
322
+ interface LinkProps extends LinkProps$1 {
323
+ /** Prefetch strategy. Default: 'intent' (on hover + focus). */
324
+ prefetch?: PrefetchBehavior;
325
+ }
326
+ /**
327
+ * TheoKit Link — drop-in replacement for react-router Link with prefetch.
328
+ *
329
+ * @example
330
+ * ```tsx
331
+ * import { Link } from 'theokit/client'
332
+ *
333
+ * <Link to="/contacts">Contacts</Link>
334
+ * <Link to="/dashboard" prefetch="viewport">Dashboard</Link>
335
+ * <Link to="/settings" prefetch="none">Settings</Link>
336
+ * ```
337
+ */
338
+ declare function Link({ prefetch, to, onMouseEnter, onFocus, ...rest }: LinkProps): react.JSX.Element;
339
+
340
+ /**
341
+ * <Metadata> — SEO-ready head tags from a single component.
342
+ *
343
+ * Uses React 19's native <title>/<meta>/<link> hoisting to <head>.
344
+ * No build-time extraction needed — works in SSR and client.
345
+ *
346
+ * @example
347
+ * ```tsx
348
+ * import { Metadata } from 'theokit/client'
349
+ *
350
+ * export default function ContactsPage() {
351
+ * return (
352
+ * <>
353
+ * <Metadata
354
+ * title="Contacts | My CRM"
355
+ * description="Manage your contacts"
356
+ * ogImage="/og/contacts.png"
357
+ * canonical="https://mycrm.com/contacts"
358
+ * />
359
+ * <h1>Contacts</h1>
360
+ * </>
361
+ * )
362
+ * }
363
+ * ```
364
+ */
365
+ interface MetadataProps {
366
+ title?: string;
367
+ description?: string;
368
+ canonical?: string;
369
+ ogTitle?: string;
370
+ ogDescription?: string;
371
+ ogImage?: string;
372
+ ogType?: string;
373
+ ogUrl?: string;
374
+ twitterCard?: 'summary' | 'summary_large_image';
375
+ /** Custom meta tags rendered as children */
376
+ children?: React.ReactNode;
377
+ }
378
+ declare function Metadata(props: MetadataProps): react.JSX.Element;
379
+
380
+ /**
381
+ * <Image> — optimized image component with lazy loading.
382
+ *
383
+ * Renders a standard <img> with:
384
+ * - loading="lazy" by default (eager with priority={true})
385
+ * - decoding="async" for non-blocking decode
386
+ * - width/height for CLS prevention
387
+ * - srcSet/sizes forwarded for responsive images
388
+ *
389
+ * No CDN, no Sharp, no build-time optimization — pure HTML attributes
390
+ * that deliver 80% of the performance value at zero complexity.
391
+ *
392
+ * @example
393
+ * ```tsx
394
+ * import { Image } from 'theokit/client'
395
+ *
396
+ * <Image src="/team.jpg" alt="Team photo" width={800} height={600} />
397
+ * <Image src="/hero.jpg" alt="Hero" priority />
398
+ * <Image
399
+ * src="/product.jpg"
400
+ * alt="Product"
401
+ * width={400}
402
+ * height={300}
403
+ * srcSet="/product-400.jpg 400w, /product-800.jpg 800w"
404
+ * sizes="(max-width: 768px) 100vw, 400px"
405
+ * />
406
+ * ```
407
+ */
408
+ interface ImageProps extends React.ImgHTMLAttributes<HTMLImageElement> {
409
+ /** Image source URL (required). */
410
+ src: string;
411
+ /** Alt text for accessibility (required). */
412
+ alt: string;
413
+ /** If true, loading="eager" — use for above-the-fold images. */
414
+ priority?: boolean;
415
+ }
416
+ declare function Image({ priority, loading, decoding, ...props }: ImageProps): react.JSX.Element;
417
+
418
+ export { AgentErrorEvent, AgentEvent, type AgentStreamStatus, type AgentToolCard, type BatchRequest, type BatchResponse, type BatchTransport, type Batcher, type BatcherOptions, type ConsumeOptions, type CreateAppClientOptions, type FoldAgentToolCardsOptions, Image, type ImageProps, type InferBody, type InferQuery, type InferResponse, Link, type LinkProps, Metadata, type MetadataProps, type PrefetchBehavior, TheoFetchError, type TheoFetchOptions, type ToolEnvelopeResolver, type UseAgentOptions, type UseAgentReturn, type UseAgentStatus, type UseAgentStreamOptions, type UseAgentStreamReturn, type UseAgentToolCardsReturn, consumeAgentStream, consumeUIMessageStream, createAppClient, createBatcher, defaultResolveEnvelope, deriveError, deriveLiveText, foldAgentToolCards, parseSSEChunk, theoFetch, useAgent, useAgentStream, useAgentToolCards };
@@ -599,11 +599,90 @@ function useAgentToolCards(path, options = {}) {
599
599
  return { ...stream, toolCards };
600
600
  }
601
601
 
602
+ // src/client/consume-ui-message-stream.ts
603
+ async function consumeUIMessageStream(response, onMessage) {
604
+ if (response.body === null) return;
605
+ const { parseJsonEventStream, readUIMessageStream, uiMessageChunkSchema } = await import("ai");
606
+ const parsed = parseJsonEventStream({ stream: response.body, schema: uiMessageChunkSchema });
607
+ const chunkStream = new ReadableStream({
608
+ async start(controller) {
609
+ for await (const result of parsed) {
610
+ if (result.success) controller.enqueue(result.value);
611
+ }
612
+ controller.close();
613
+ }
614
+ });
615
+ for await (const message of readUIMessageStream({ stream: chunkStream })) {
616
+ onMessage(message);
617
+ }
618
+ }
619
+
620
+ // src/client/use-agent.ts
621
+ import { useCallback as useCallback2, useRef as useRef2, useState as useState2 } from "react";
622
+ function useAgent(path, options = {}) {
623
+ const [messages, setMessages] = useState2([]);
624
+ const [status, setStatus] = useState2("idle");
625
+ const [error, setError] = useState2(void 0);
626
+ const controllerRef = useRef2(null);
627
+ const abort = useCallback2(() => {
628
+ controllerRef.current?.abort();
629
+ controllerRef.current = null;
630
+ }, []);
631
+ const reset = useCallback2(() => {
632
+ abort();
633
+ setMessages([]);
634
+ setError(void 0);
635
+ setStatus("idle");
636
+ }, [abort]);
637
+ const send = useCallback2(
638
+ (input) => {
639
+ abort();
640
+ const controller = new AbortController();
641
+ controllerRef.current = controller;
642
+ setMessages([]);
643
+ setError(void 0);
644
+ setStatus("streaming");
645
+ const fetchImpl = options.fetch ?? globalThis.fetch;
646
+ void (async () => {
647
+ try {
648
+ const response = await fetchImpl(path, {
649
+ method: "POST",
650
+ headers: {
651
+ "content-type": "application/json",
652
+ accept: "text/event-stream",
653
+ "X-Theo-Action": "1",
654
+ ...options.headers
655
+ },
656
+ body: JSON.stringify(input),
657
+ signal: controller.signal
658
+ });
659
+ await consumeUIMessageStream(response, (message) => {
660
+ setMessages((prev) => {
661
+ const next = [...prev];
662
+ const idx = next.findIndex((m) => m.id === message.id);
663
+ if (idx >= 0) next[idx] = message;
664
+ else next.push(message);
665
+ return next;
666
+ });
667
+ });
668
+ setStatus("done");
669
+ } catch (err) {
670
+ if (controller.signal.aborted) return;
671
+ setError(err instanceof Error ? err : new Error(String(err)));
672
+ setStatus("error");
673
+ }
674
+ })();
675
+ },
676
+ [abort, options.fetch, options.headers, path]
677
+ );
678
+ return { messages, status, error, send, abort, reset };
679
+ }
680
+
602
681
  // src/client/link.tsx
603
682
  import {
604
683
  Link as RouterLink
605
684
  } from "react-router";
606
- import { useRef as useRef2, useCallback as useCallback2, useEffect as useEffect2 } from "react";
685
+ import { useRef as useRef3, useCallback as useCallback3, useEffect as useEffect2 } from "react";
607
686
  import { jsx } from "react/jsx-runtime";
608
687
  var prefetched = /* @__PURE__ */ new Set();
609
688
  function injectPrefetch(href) {
@@ -620,8 +699,8 @@ function resolveTo(to) {
620
699
  return to?.pathname ?? "";
621
700
  }
622
701
  function Link({ prefetch = "intent", to, onMouseEnter, onFocus, ...rest }) {
623
- const ref = useRef2(null);
624
- const handleIntent = useCallback2(
702
+ const ref = useRef3(null);
703
+ const handleIntent = useCallback3(
625
704
  (event) => {
626
705
  if (prefetch === "intent") {
627
706
  injectPrefetch(resolveTo(to));
@@ -703,6 +782,7 @@ export {
703
782
  TheoFetchError,
704
783
  buildUseTheoQueryConfig,
705
784
  consumeAgentStream,
785
+ consumeUIMessageStream,
706
786
  createAppClient,
707
787
  createBatcher,
708
788
  defaultResolveEnvelope,
@@ -712,6 +792,7 @@ export {
712
792
  parseSSEChunk,
713
793
  stableQueryKey,
714
794
  theoFetch,
795
+ useAgent,
715
796
  useAgentStream,
716
797
  useAgentToolCards
717
798
  };