theokit 0.13.0 → 0.14.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 (62) hide show
  1. package/dist/{actions-virtual-module-EIPXX4ZB.js → actions-virtual-module-RM7JRY5N.js} +7 -7
  2. package/dist/{app-typed-client-7PBFWZUE.js → app-typed-client-5VQE6DNF.js} +7 -7
  3. package/dist/boot/index.js +3 -3
  4. package/dist/{build-HXND27XG.js → build-5K7LK77K.js} +2 -2
  5. package/dist/{chunk-3S3BNW5K.js → chunk-5UUOGAJH.js} +2 -2
  6. package/dist/{chunk-O62MW4MT.js → chunk-637WJB7Z.js} +2 -2
  7. package/dist/{chunk-TGTNRUH3.js → chunk-H243MR3F.js} +2 -2
  8. package/dist/{chunk-NHJMZCAS.js → chunk-JZHT3LW7.js} +1 -1
  9. package/dist/chunk-JZHT3LW7.js.map +1 -0
  10. package/dist/chunk-KGFNWIMS.js +94 -0
  11. package/dist/chunk-KGFNWIMS.js.map +1 -0
  12. package/dist/{chunk-RYTZYFSD.js → chunk-RBHCJHRR.js} +65 -28
  13. package/dist/chunk-RBHCJHRR.js.map +1 -0
  14. package/dist/{chunk-UNLA45FY.js → chunk-S7Y5WLZY.js} +1 -1
  15. package/dist/chunk-S7Y5WLZY.js.map +1 -0
  16. package/dist/{chunk-WFNLNIJX.js → chunk-Z5IGLBWE.js} +1 -1
  17. package/dist/cli/index.js +3 -3
  18. package/dist/client/index.d.ts +3 -152
  19. package/dist/client/index.js +13 -236
  20. package/dist/client/index.js.map +1 -1
  21. package/dist/{dev-OWW4XVIH.js → dev-QOEVYNBG.js} +3 -3
  22. package/dist/index.js +12 -13
  23. package/dist/index.js.map +1 -1
  24. package/dist/{internal-api-4YTJDITC.js → internal-api-5BDI6N5U.js} +20 -20
  25. package/dist/server/auth/index.js +1 -1
  26. package/dist/server/define/index.d.ts +3 -70
  27. package/dist/server/define/index.js +2 -4
  28. package/dist/server/http/index.js +2 -2
  29. package/dist/server/index.d.ts +1 -3
  30. package/dist/server/index.js +90 -105
  31. package/dist/server/index.js.map +1 -1
  32. package/dist/server/jobs/index.js +4 -4
  33. package/dist/server/observability/index.js +1 -1
  34. package/dist/server/scan/index.js +1 -1
  35. package/dist/{start-KIQ5TTLR.js → start-2KG4JSXM.js} +2 -2
  36. package/dist/vite-plugin/index.js +12 -13
  37. package/dist/{vite-plugin-RK66K26Z.js → vite-plugin-TDIDZ5U7.js} +3 -3
  38. package/package.json +1 -5
  39. package/dist/agent-events-DosDXkSV.d.ts +0 -94
  40. package/dist/chunk-2KZQPDYR.js +0 -226
  41. package/dist/chunk-2KZQPDYR.js.map +0 -1
  42. package/dist/chunk-EXP56GFQ.js +0 -52
  43. package/dist/chunk-EXP56GFQ.js.map +0 -1
  44. package/dist/chunk-NHJMZCAS.js.map +0 -1
  45. package/dist/chunk-RYTZYFSD.js.map +0 -1
  46. package/dist/chunk-UNLA45FY.js.map +0 -1
  47. package/dist/chunk-ZSTZXR2D.js +0 -208
  48. package/dist/chunk-ZSTZXR2D.js.map +0 -1
  49. package/dist/server/agent/index.d.ts +0 -229
  50. package/dist/server/agent/index.js +0 -17
  51. package/dist/vite-plugin-RK66K26Z.js.map +0 -1
  52. /package/dist/{actions-virtual-module-EIPXX4ZB.js.map → actions-virtual-module-RM7JRY5N.js.map} +0 -0
  53. /package/dist/{app-typed-client-7PBFWZUE.js.map → app-typed-client-5VQE6DNF.js.map} +0 -0
  54. /package/dist/{build-HXND27XG.js.map → build-5K7LK77K.js.map} +0 -0
  55. /package/dist/{chunk-3S3BNW5K.js.map → chunk-5UUOGAJH.js.map} +0 -0
  56. /package/dist/{chunk-O62MW4MT.js.map → chunk-637WJB7Z.js.map} +0 -0
  57. /package/dist/{chunk-TGTNRUH3.js.map → chunk-H243MR3F.js.map} +0 -0
  58. /package/dist/{chunk-WFNLNIJX.js.map → chunk-Z5IGLBWE.js.map} +0 -0
  59. /package/dist/{dev-OWW4XVIH.js.map → dev-QOEVYNBG.js.map} +0 -0
  60. /package/dist/{internal-api-4YTJDITC.js.map → internal-api-5BDI6N5U.js.map} +0 -0
  61. /package/dist/{start-KIQ5TTLR.js.map → start-2KG4JSXM.js.map} +0 -0
  62. /package/dist/{server/agent/index.js.map → vite-plugin-TDIDZ5U7.js.map} +0 -0
@@ -232,4 +232,4 @@ export {
232
232
  incomingMessageToWebRequest,
233
233
  writeWebResponseToServerResponse
234
234
  };
235
- //# sourceMappingURL=chunk-UNLA45FY.js.map
235
+ //# sourceMappingURL=chunk-S7Y5WLZY.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/server/transformer.ts","../src/server/agent/mount-agent.ts","../src/server/define/ui-message-stream-response.ts","../src/server/agent/provider-resolver.ts","../src/server/http/node-web-adapter.ts"],"sourcesContent":["import superjson from 'superjson'\n\n/**\n * T5.2 — pluggable response/request transformer.\n *\n * `superjson` is the default, preserving Date/Map/Set/BigInt/etc.\n * `json` is the lightweight option (plain JSON.stringify/parse).\n * Users can supply a custom object implementing this contract.\n */\nexport interface TheoTransformer {\n name: string\n serialize: (value: unknown) => string\n deserialize: (raw: string) => unknown\n}\n\nexport const superjsonTransformer: TheoTransformer = {\n name: 'superjson',\n serialize: (v) => JSON.stringify(superjson.serialize(v)),\n deserialize: (raw) => {\n const parsed = JSON.parse(raw) as Parameters<typeof superjson.deserialize>[0]\n return superjson.deserialize(parsed)\n },\n}\n\nexport const jsonTransformer: TheoTransformer = {\n name: 'json',\n serialize: (v) => JSON.stringify(v),\n deserialize: (raw) => JSON.parse(raw) as unknown,\n}\n\nconst BUILT_INS: Record<string, TheoTransformer> = {\n superjson: superjsonTransformer,\n json: jsonTransformer,\n}\n\nexport function resolveTransformer(\n selector: 'json' | 'superjson' | TheoTransformer,\n): TheoTransformer {\n if (typeof selector === 'string') {\n // selector is 'json' | 'superjson' literal — both keys exist in\n // BUILT_INS by construction. Type system guarantees a hit; we keep\n // a defensive fallback that the compiler cannot see is unreachable\n // at runtime, just in case someone adds a new literal to the union\n // but forgets to register the built-in.\n const built = BUILT_INS[selector]\n // Defensive: the public union ensures `built` is defined, but if a\n // future contributor extends the union without registering the impl,\n // the cast keeps the failure mode loud.\n if ((built as TheoTransformer | undefined) === undefined) {\n throw new Error(\n `Unknown transformer \"${selector}\". Built-in options: ${Object.keys(BUILT_INS).join(', ')}.`,\n )\n }\n return built\n }\n if (\n typeof selector !== 'object' ||\n typeof selector.serialize !== 'function' ||\n typeof selector.deserialize !== 'function'\n ) {\n throw new Error(\n `Custom transformer must have serialize and deserialize functions. Got: ${JSON.stringify(selector)}`,\n )\n }\n return selector\n}\n","/**\n * M2 (theokit-ai-first) — mount a scanned `agents/<name>.ts` module as an SSE endpoint.\n *\n * The SINGLE wiring point shared by dev (vite middleware) and prod (built server), so the\n * two never drift (EC-4). Web-Standard `Request` → `Response`: parse the chat body, compile\n * the module (`compileAgentModule`, converges both surfaces), stream via the M0/M1 canonical\n * protocol (`streamAgentUIMessages` → `uiMessageStreamResponse`).\n *\n * Request body — accepts the `@ai-sdk/react` `useChat` shape (`{ id, messages: UIMessage[] }`,\n * the typed-client path) AND a simple `{ message, sessionId? }` shape (M0/M1-style clients).\n */\nimport { compileAgentModule, streamAgentUIMessages } from '@theokit/agents'\n\nimport { uiMessageStreamResponse } from '../define/ui-message-stream-response.js'\nimport { validateCsrfRequest, type CsrfMode } from '../security/csrf.js'\n\n/** The message + session extracted from a chat request, or `null` when the body is invalid. */\nexport interface AgentRequestInput {\n message: string\n sessionId: string\n}\n\n/** Extract the text of a `{ type: 'text', text: string }` part from an untrusted value. */\nfunction partText(part: unknown): string {\n if (typeof part !== 'object' || part === null) return ''\n const p = part as Record<string, unknown>\n return p.type === 'text' && typeof p.text === 'string' ? p.text : ''\n}\n\nfunction jsonError(status: number, code: string, message: string): Response {\n return new Response(JSON.stringify({ error: { code, message } }), {\n status,\n headers: { 'content-type': 'application/json' },\n })\n}\n\n/**\n * Extract `{ message, sessionId }` from a chat request body. Returns `null` when neither the\n * ai-sdk `messages` shape nor the simple `message` shape yields non-empty user text.\n */\nexport function parseAgentRequestBody(body: unknown): AgentRequestInput | null {\n if (typeof body !== 'object' || body === null) return null\n const b = body as Record<string, unknown>\n\n // ai-sdk useChat shape: { id, messages: UIMessage[] } — take the last message's text parts.\n if (Array.isArray(b.messages) && b.messages.length > 0) {\n const last = b.messages[b.messages.length - 1] as Record<string, unknown>\n const parts = Array.isArray(last.parts) ? last.parts : []\n const text = parts.map(partText).join('')\n if (text.length === 0) return null\n const sessionId = typeof b.id === 'string' && b.id.length > 0 ? b.id : crypto.randomUUID()\n return { message: text, sessionId }\n }\n\n // Simple shape: { message, sessionId? }.\n if (typeof b.message === 'string' && b.message.length > 0) {\n const sessionId =\n typeof b.sessionId === 'string' && b.sessionId.length > 0 ? b.sessionId : crypto.randomUUID()\n return { message: b.message, sessionId }\n }\n\n return null\n}\n\n/**\n * Mount a loaded agent module as a `Response`. `apiKey` is resolved by the caller\n * (`resolveProvider`). `source` labels a fail-fast `AgentDefinitionError` (the file path).\n */\nexport async function mountAgent(\n mod: unknown,\n request: Request,\n apiKey: string,\n source = 'agent module',\n csrfMode: CsrfMode = 'strict',\n): Promise<Response> {\n // Enforce CSRF BEFORE any work — an agent run spends real LLM tokens, so a cross-origin\n // POST must be rejected before it reaches the SDK (parity with actions/routes). The custom\n // `X-Theo-Action` header + Origin match is the same defense `executeRoute`/`executeAction`\n // apply; the `useAgent` client sends the header. `off` skips; `warn` never blocks.\n if (csrfMode !== 'off') {\n const csrf = validateCsrfRequest(request)\n if (!csrf.valid && csrfMode === 'strict') {\n return jsonError(403, 'CSRF_FAILED', `CSRF check failed: ${csrf.reason}`)\n }\n }\n\n const compiled = compileAgentModule(mod, source)\n\n let body: unknown = null\n try {\n body = await request.json()\n } catch {\n /* invalid/empty JSON → handled below as a 400 */\n }\n\n const input = parseAgentRequestBody(body)\n if (input === null) {\n return jsonError(400, 'BAD_REQUEST', 'Request must contain a non-empty message or messages[].')\n }\n\n return uiMessageStreamResponse(streamAgentUIMessages(compiled, apiKey, input))\n}\n","import type { UIMessageChunk } from 'ai'\n\n/**\n * M0 (theokit-ai-first) — serialize a stream of ai-sdk `UIMessageChunk`s into a\n * Web-Standards `Response` on the UIMessageStream wire so `@ai-sdk/react`'s\n * `useChat` consumes it WITHOUT a custom adapter.\n *\n * Wire contract (must match ai-sdk's consumer transport exactly):\n * - `content-type: text/event-stream`\n * - `x-vercel-ai-ui-message-stream: v1` — the version marker `useChat` checks\n * - each chunk framed as `data: ${JSON.stringify(chunk)}\\n\\n`\n * - a terminal `data: [DONE]\\n\\n` after the last chunk (ignored by the parser)\n *\n * Web Standards only — `Response` + `ReadableStream` (G8, no node:http). The\n * headers commit before the stream starts.\n *\n * Fail-clear (error-handling.md): if the source iterable throws mid-stream, the\n * `[DONE]` terminal is still flushed and the stream is closed — never left\n * hanging. (The translator upstream already closes gracefully; this is defense\n * in depth for any other chunk source.)\n */\nconst UI_MESSAGE_STREAM_HEADERS = {\n 'content-type': 'text/event-stream',\n 'x-vercel-ai-ui-message-stream': 'v1',\n} as const\n\nconst DONE_FRAME = 'data: [DONE]\\n\\n'\n\nfunction encode(text: string): Uint8Array {\n return new TextEncoder().encode(text)\n}\n\nexport function uiMessageStreamResponse(chunks: AsyncIterable<UIMessageChunk>): Response {\n const stream = new ReadableStream<Uint8Array>({\n async start(controller) {\n try {\n for await (const chunk of chunks) {\n controller.enqueue(encode(`data: ${JSON.stringify(chunk)}\\n\\n`))\n }\n } catch {\n // The source iterable aborted mid-stream. The upstream translator owns\n // error semantics (it surfaces failures as chunks + closes gracefully);\n // this transport's sole guarantee is a terminated stream — fall through\n // to the DONE terminal rather than re-throw and error an open stream.\n } finally {\n controller.enqueue(encode(DONE_FRAME))\n controller.close()\n }\n },\n })\n return new Response(stream, { headers: UI_MESSAGE_STREAM_HEADERS })\n}\n","/**\n * Provider Resolver — Strategy + Registry pattern (FAANG-grade).\n *\n * Inspiração: Dapr Conversation Registry (`dapr/pkg/components/conversation/registry.go`)\n * + Encore Manager provider array (`encore/runtimes/go/pubsub/manager_internal.go`).\n *\n * Princípio: provider routing é responsabilidade do FRAMEWORK, não do consumer.\n * Consumer template usa `model: { id: 'gpt-4o-mini' }` puro — sem conditionals.\n *\n * Wire protocol: OpenAI Chat Completions (universal — implementado por todos\n * os providers: OpenRouter, Groq, Mistral, Together, Anthropic via proxy, etc).\n *\n * Resolução por prioridade (FIRST match wins):\n * 1. OPENROUTER_API_KEY → baseUrl=openrouter.ai (gateway multi-modelo)\n * 2. OPENAI_API_KEY → baseUrl=api.openai.com\n * 3. ANTHROPIC_API_KEY → direct Anthropic (Messages API, não OpenAI-compat)\n *\n * Escape hatch: `options.apiKey` explícito SOBREPÕE auto-resolution\n * (consumer pode forçar provider específico se quiser).\n */\n\n/**\n * Provider configuration descriptor — Registry entry shape.\n *\n * @public\n */\nexport interface ProviderDescriptor {\n /** Stable name used internally — não exposto no wire. */\n name: string\n /** Environment variable that holds the API key for this provider. */\n envKey: string\n /** Base URL for the provider's OpenAI-compatible (or native) API. */\n baseUrl: string\n /** Resolution priority (lower = higher priority). FIRST match wins. */\n priority: number\n}\n\n/**\n * Resolved provider configuration — output of `resolveProvider()`.\n *\n * @public\n */\nexport interface ResolvedProvider {\n name: string\n apiKey: string\n baseUrl: string\n}\n\n/**\n * Default provider registry. Order = priority (first = highest).\n *\n * Adding a new provider:\n * 1. Append entry below (or register via `registerProvider()`).\n * 2. Set `envKey` matching the user's env var convention.\n * 3. Set `baseUrl` to the OpenAI-compat endpoint (or native if not compat).\n * 4. Provider name used in telemetry/logs only — never wire-exposed.\n */\nconst DEFAULT_REGISTRY: ProviderDescriptor[] = [\n {\n name: 'openrouter',\n envKey: 'OPENROUTER_API_KEY',\n baseUrl: 'https://openrouter.ai/api/v1',\n priority: 1,\n },\n {\n name: 'openai',\n envKey: 'OPENAI_API_KEY',\n baseUrl: 'https://api.openai.com/v1',\n priority: 2,\n },\n {\n name: 'anthropic',\n envKey: 'ANTHROPIC_API_KEY',\n baseUrl: 'https://api.anthropic.com',\n priority: 3,\n },\n]\n\n/**\n * Runtime registry — copy of DEFAULT_REGISTRY mutable via registerProvider().\n * Sorted by priority on every resolve (stable, O(n log n) — n <= ~10 providers).\n */\nconst registry: ProviderDescriptor[] = [...DEFAULT_REGISTRY]\n\n/**\n * Register a new provider (Registry pattern — runtime extension point).\n * Useful for self-hosted endpoints or custom providers without touching theokit src.\n *\n * @example\n * registerProvider({\n * name: 'self-hosted',\n * envKey: 'SELF_HOSTED_API_KEY',\n * baseUrl: 'https://llm.internal.acme.com/v1',\n * priority: 0, // highest priority\n * })\n *\n * @public\n */\nexport function registerProvider(descriptor: ProviderDescriptor): void {\n // Idempotent — replace existing by name.\n const idx = registry.findIndex((p) => p.name === descriptor.name)\n if (idx >= 0) registry[idx] = descriptor\n else registry.push(descriptor)\n}\n\n/**\n * Reset registry to DEFAULT_REGISTRY (test-only / dev escape hatch).\n *\n * @public\n */\nexport function resetProviderRegistry(): void {\n registry.length = 0\n registry.push(...DEFAULT_REGISTRY)\n}\n\n/**\n * Get current registry snapshot (read-only — inspection).\n *\n * @public\n */\nexport function listProviders(): readonly ProviderDescriptor[] {\n return [...registry].sort((a, b) => a.priority - b.priority)\n}\n\n/**\n * Resolve provider from env vars by priority. FIRST env var found wins.\n *\n * @returns ResolvedProvider with apiKey + baseUrl + name\n * @throws Error if NO provider env var is set (actionable message)\n *\n * @public\n */\nexport function resolveProvider(): ResolvedProvider {\n const sorted = [...registry].sort((a, b) => a.priority - b.priority)\n for (const desc of sorted) {\n const apiKey = process.env[desc.envKey]\n if (apiKey && apiKey.length > 0) {\n return {\n name: desc.name,\n apiKey,\n baseUrl: desc.baseUrl,\n }\n }\n }\n // No env var found — emit actionable error.\n const envKeys = sorted.map((p) => p.envKey).join(' OR ')\n throw new Error(\n `No LLM provider API key found in environment. Set one of: ${envKeys}. ` +\n `Get a free OpenRouter key at https://openrouter.ai/keys (recommended — one key, many models).`,\n )\n}\n\n/**\n * Try to resolve — does NOT throw. Returns null if no provider available.\n * Useful for graceful degradation (e.g., mock mode).\n *\n * @public\n */\nexport function tryResolveProvider(): ResolvedProvider | null {\n try {\n return resolveProvider()\n } catch {\n return null\n }\n}\n","/**\n * T5a.2 Phase G slice 5/N — Node adapter shim for the Web request handler.\n *\n * Bridges Node's `IncomingMessage` + `ServerResponse` shape to the\n * Web-Standards `executeWebRequest` (Phase A → G slices 1-4). Per\n * ADR-0028 R3a, the Node adapter is the ONLY place IncomingMessage ↔\n * Request conversion happens — every other layer of `server/` flows\n * through Web `Request`/`Response`.\n *\n * **Two conversions:**\n *\n * 1. `incomingMessageToWebRequest(req)` — Node → Web. Reads\n * `req.method`, `req.url`, `req.headers`, and (for POST/PUT/etc.)\n * drains the Node Readable body into a Web `ReadableStream`. The\n * request URL is resolved to absolute form using `req.headers.host`\n * (Web Request guarantees absolute URL).\n *\n * 2. `writeWebResponseToServerResponse(response, res)` — Web → Node.\n * Sets status code + status text, copies headers (including all\n * `Set-Cookie` values via `getSetCookie()`), then drains the Web\n * `ReadableStream` body into the Node ServerResponse.\n *\n * Plus the convenience composer `executeWebRequestFromNode(req, res,\n * routeModule, opts?)` that wires both ends — exactly what api-middleware\n * (and the prod CLI start path) need to migrate from the legacy\n * `executeRoute` to `executeWebRequest` without touching call sites.\n *\n * Per `docs/plans/t5a2-incoming-message-to-request-shape-refactor-plan.md`\n * v1.0 § Phase G slice 5/N (closes the executor bridge surface).\n */\nimport type { IncomingMessage, ServerResponse } from 'node:http'\nimport { Readable } from 'node:stream'\n\nimport { executeWebRequest, type ExecuteWebRequestOptions } from '../web-handler.js'\n\n/**\n * Build a Web `Request` from a Node `IncomingMessage`. The Web Request\n * spec requires an absolute URL; we synthesize one from\n * `req.headers.host` (or fall back to `localhost` for test doubles).\n *\n * For methods with a body (POST/PUT/PATCH/DELETE), the Node Readable\n * stream is wrapped as a Web ReadableStream via `Readable.toWeb()` so\n * downstream consumers can call `request.json()` / `request.formData()`\n * / `request.text()` natively.\n *\n * EC-1: Node sometimes provides headers as `string | string[]`. Web\n * `Headers` collapses to single-comma-joined strings — we do the join\n * manually for repeated headers because `Headers.append` would create\n * multi-value entries which behave differently on `.get()`.\n */\nexport function incomingMessageToWebRequest(req: IncomingMessage): Request {\n const host = pickHeaderString(req.headers.host) ?? 'localhost'\n const url = `http://${host}${req.url ?? '/'}`\n\n const headers = new Headers()\n for (const [key, value] of Object.entries(req.headers)) {\n if (value === undefined) continue\n if (Array.isArray(value)) {\n headers.set(key, value.join(', '))\n } else {\n headers.set(key, value)\n }\n }\n\n const method = (req.method ?? 'GET').toUpperCase()\n const hasBody = method !== 'GET' && method !== 'HEAD'\n\n if (!hasBody) {\n return new Request(url, { method, headers })\n }\n\n // Drain Node's Readable into a Web ReadableStream. `Readable.toWeb` is\n // available in Node 18+ (theokit's engines.node floor is 22+, so safe).\n const webStream = Readable.toWeb(req) as ReadableStream\n return new Request(url, {\n method,\n headers,\n body: webStream,\n // EC-2: Node 18+ requires `duplex: 'half'` when body is a stream.\n // The `RequestInit` type omits it (Web spec gap); cast accordingly.\n ...({ duplex: 'half' } as { duplex: 'half' }),\n })\n}\n\n/**\n * Write a Web `Response` into a Node `ServerResponse`. Mirrors the Node\n * `res.writeHead` + `res.end` pattern.\n *\n * Set-Cookie is the only multi-value header the Web spec exposes via\n * `getSetCookie()`. We append each entry individually so Node emits\n * separate `Set-Cookie:` lines per the HTTP spec.\n *\n * If the Response body is a `ReadableStream`, it's piped chunk-by-chunk\n * to `res`. Empty body (null) → just close.\n */\nexport async function writeWebResponseToServerResponse(\n response: Response,\n res: ServerResponse,\n): Promise<void> {\n // Status + headers FIRST (writeHead locks them).\n // EC-3: Set-Cookie needs special handling (writeHead's plain object\n // shape conflicts with multi-value headers; we set them via setHeader\n // BEFORE writeHead so the array form is preserved).\n const setCookies = response.headers.getSetCookie()\n if (setCookies.length > 0) {\n res.setHeader('Set-Cookie', setCookies)\n }\n const otherHeaders: Record<string, string> = {}\n for (const [key, value] of response.headers.entries()) {\n if (key.toLowerCase() === 'set-cookie') continue\n otherHeaders[key] = value\n }\n res.writeHead(response.status, response.statusText, otherHeaders)\n\n // Body — drain ReadableStream OR just end() for null body.\n if (response.body === null) {\n res.end()\n return\n }\n const reader = response.body.getReader()\n try {\n for (;;) {\n const { done, value } = await reader.read()\n if (done) break\n // Node's res.write accepts Uint8Array natively (no conversion needed).\n res.write(value)\n }\n res.end()\n } finally {\n reader.releaseLock()\n }\n}\n\n/**\n * Convenience composer — full request lifecycle Node → Web → Node.\n *\n * Use case: existing api-middleware (and the prod CLI start path)\n * receive Node `(req, res)` from `http.createServer`. To migrate to the\n * Web-Standards executor without rewriting every call site, wrap with\n * this composer:\n *\n * import * as users from './app/users/route.js'\n * await executeWebRequestFromNode(req, res, users, { csrfMode: 'strict' })\n *\n * The Web request is built, dispatched through executeWebRequest, and\n * the Response is drained back into `res`. Caller does NOT need to call\n * `res.end()` afterwards — this composer handles it.\n */\nexport async function executeWebRequestFromNode(\n req: IncomingMessage,\n res: ServerResponse,\n routeModule: Parameters<typeof executeWebRequest>[1],\n opts?: ExecuteWebRequestOptions,\n): Promise<void> {\n const webRequest = incomingMessageToWebRequest(req)\n const webResponse = await executeWebRequest(webRequest, routeModule, opts)\n await writeWebResponseToServerResponse(webResponse, res)\n}\n\n/** Pick the first usable string from Node's `string | string[] | undefined` headers. */\nfunction pickHeaderString(value: string | string[] | undefined): string | undefined {\n if (typeof value === 'string') return value\n if (Array.isArray(value)) {\n for (const v of value) if (typeof v === 'string' && v.length > 0) return v\n }\n return undefined\n}\n"],"mappings":"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","names":[]}
@@ -326,4 +326,4 @@ export {
326
326
  _resetWarnOnceForTests,
327
327
  warnOnce
328
328
  };
329
- //# sourceMappingURL=chunk-WFNLNIJX.js.map
329
+ //# sourceMappingURL=chunk-Z5IGLBWE.js.map
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-OWW4XVIH.js");
8
+ const { devCommand } = await import("../dev-QOEVYNBG.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-HXND27XG.js");
13
+ const { buildCommand } = await import("../build-5K7LK77K.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-KIQ5TTLR.js");
25
+ const { startCommand } = await import("../start-2KG4JSXM.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);
@@ -1,8 +1,6 @@
1
1
  import { z } from 'zod';
2
2
  import { T as TheoErrorEnvelope } from '../error-envelope-BsNzzAV5.js';
3
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
4
  import { UIMessage } from 'ai';
7
5
  import * as react from 'react';
8
6
  import { LinkProps as LinkProps$1 } from 'react-router';
@@ -117,152 +115,6 @@ interface Batcher {
117
115
  }
118
116
  declare function createBatcher(options: BatcherOptions): Batcher;
119
117
 
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
118
  /**
267
119
  * M2 (theokit-ai-first) — read a TheoKit agent endpoint's `UIMessageStream` SSE `Response`
268
120
  * into reconstructed assistant `UIMessage`s, reusing the `ai` package's own consumer
@@ -288,9 +140,8 @@ declare function consumeUIMessageStream(response: Response, onMessage: (message:
288
140
  * so the request shape is inferred end-to-end from the server `defineAgent({ input })` with
289
141
  * ZERO manual wiring (DoD line 2).
290
142
  *
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.
143
+ * Transport: fetch + ReadableStream (POST needs a body; EventSource is GET-only). `ai` is
144
+ * loaded lazily by `consumeUIMessageStream` so non-agent apps never pay for it.
294
145
  */
295
146
  type UseAgentStatus = 'idle' | 'streaming' | 'done' | 'error';
296
147
  interface UseAgentReturn<TInput = unknown> {
@@ -415,4 +266,4 @@ interface ImageProps extends React.ImgHTMLAttributes<HTMLImageElement> {
415
266
  }
416
267
  declare function Image({ priority, loading, decoding, ...props }: ImageProps): react.JSX.Element;
417
268
 
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 };
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 };
@@ -384,221 +384,6 @@ function createAppClient(baseUrlOrOptions, legacyFetchImpl) {
384
384
  return makeProxy({ segments: [], baseUrl, fetchImpl });
385
385
  }
386
386
 
387
- // src/client/use-agent-stream.ts
388
- import { useCallback, useEffect, useMemo, useRef, useState } from "react";
389
-
390
- // src/client/agent-stream-core.ts
391
- function dispatchSseParts(parts, onEvent) {
392
- for (const part of parts) {
393
- for (const line of part.split("\n")) {
394
- const evt = parseSSEChunk(line);
395
- if (evt) onEvent(evt);
396
- }
397
- }
398
- }
399
- function parseSSEChunk(line) {
400
- if (!line.startsWith("data:")) return null;
401
- const raw = line.slice(5).trim();
402
- if (!raw) return null;
403
- try {
404
- return JSON.parse(raw);
405
- } catch {
406
- return null;
407
- }
408
- }
409
- async function consumeAgentStream(path, options) {
410
- const fetchImpl = options.fetch ?? globalThis.fetch;
411
- const response = await fetchImpl(path, {
412
- method: "POST",
413
- headers: {
414
- "content-type": "application/json",
415
- accept: "text/event-stream",
416
- "X-Theo-Action": "1",
417
- ...options.headers
418
- },
419
- body: JSON.stringify(options.body),
420
- signal: options.signal
421
- });
422
- if (!response.body) return;
423
- const reader = response.body.getReader();
424
- const decoder = new TextDecoder();
425
- let buf = "";
426
- try {
427
- let done = false;
428
- while (!done) {
429
- const chunk = await reader.read();
430
- done = chunk.done;
431
- if (done) continue;
432
- buf += decoder.decode(chunk.value, { stream: true });
433
- const parts = buf.split("\n\n");
434
- buf = parts.pop() ?? "";
435
- dispatchSseParts(parts, options.onEvent);
436
- }
437
- } finally {
438
- try {
439
- reader.releaseLock();
440
- } catch {
441
- }
442
- }
443
- }
444
-
445
- // src/client/use-agent-stream.ts
446
- function deriveLiveText(events) {
447
- let text = "";
448
- for (const event of events) {
449
- if (event.type === "message") text += event.content;
450
- }
451
- return text;
452
- }
453
- function deriveError(events) {
454
- let last;
455
- for (const event of events) {
456
- if (event.type === "error") last = event;
457
- }
458
- return last;
459
- }
460
- function useAgentStream(path, options = {}) {
461
- const [events, setEvents] = useState([]);
462
- const [status, setStatus] = useState("idle");
463
- const controllerRef = useRef(null);
464
- const abort = useCallback(() => {
465
- if (controllerRef.current) {
466
- controllerRef.current.abort();
467
- controllerRef.current = null;
468
- }
469
- }, []);
470
- const reset = useCallback(() => {
471
- abort();
472
- setEvents([]);
473
- setStatus("idle");
474
- }, [abort]);
475
- const send = useCallback(
476
- (body) => {
477
- abort();
478
- const controller = new AbortController();
479
- controllerRef.current = controller;
480
- setStatus("streaming");
481
- let sawError = false;
482
- consumeAgentStream(path, {
483
- body,
484
- fetch: options.fetch,
485
- headers: options.headers,
486
- signal: controller.signal,
487
- onEvent: (event) => {
488
- if (event.type === "error") sawError = true;
489
- setEvents((prev) => [...prev, event]);
490
- }
491
- }).then(() => {
492
- if (controllerRef.current !== controller) return;
493
- setStatus(sawError ? "error" : "done");
494
- }).catch(() => {
495
- if (controllerRef.current !== controller) return;
496
- if (controller.signal.aborted) return;
497
- setStatus("error");
498
- });
499
- },
500
- [abort, path, options.fetch, options.headers]
501
- );
502
- useEffect(() => {
503
- return () => {
504
- if (controllerRef.current) {
505
- controllerRef.current.abort();
506
- controllerRef.current = null;
507
- }
508
- };
509
- }, []);
510
- const liveText = useMemo(() => deriveLiveText(events), [events]);
511
- const error = useMemo(() => deriveError(events), [events]);
512
- return { events, status, liveText, error, send, abort, reset };
513
- }
514
-
515
- // src/client/agent-tool-cards.ts
516
- function defaultResolveEnvelope(data) {
517
- return { error: coerceObject(data)?.ok === false };
518
- }
519
- function coerceObject(data) {
520
- if (typeof data === "object" && data !== null) return data;
521
- if (typeof data === "string") {
522
- try {
523
- const parsed = JSON.parse(data);
524
- if (typeof parsed === "object" && parsed !== null) return parsed;
525
- } catch {
526
- return void 0;
527
- }
528
- }
529
- return void 0;
530
- }
531
- function foldAgentToolCards(events, options = {}) {
532
- const resolve = options.resolveEnvelope ?? defaultResolveEnvelope;
533
- const cards = [];
534
- const byId = /* @__PURE__ */ new Map();
535
- const runningByName = /* @__PURE__ */ new Map();
536
- events.forEach((event, index) => {
537
- if (event.type === "tool_call") {
538
- const card = {
539
- id: event.id ?? `tool-${index}`,
540
- name: event.name,
541
- args: event.args,
542
- status: "running"
543
- };
544
- cards.push(card);
545
- if (event.id !== void 0) byId.set(event.id, card);
546
- pushRunning(runningByName, event.name, card);
547
- } else if (event.type === "tool_result") {
548
- const card = matchResultCard(event, index, byId, runningByName, cards);
549
- card.result = event.data;
550
- card.status = resolve(event.data).error ? "error" : "success";
551
- }
552
- });
553
- return cards;
554
- }
555
- function pushRunning(runningByName, name, card) {
556
- const queue = runningByName.get(name);
557
- if (queue) queue.push(card);
558
- else runningByName.set(name, [card]);
559
- }
560
- function removeRunning(runningByName, name, card) {
561
- const queue = runningByName.get(name);
562
- if (!queue) return;
563
- const at = queue.indexOf(card);
564
- if (at >= 0) queue.splice(at, 1);
565
- }
566
- function matchResultCard(event, index, byId, runningByName, cards) {
567
- if (event.id !== void 0) {
568
- const byIdCard = byId.get(event.id);
569
- if (byIdCard) {
570
- byId.delete(event.id);
571
- removeRunning(runningByName, byIdCard.name, byIdCard);
572
- return byIdCard;
573
- }
574
- }
575
- const fifo = runningByName.get(event.name)?.shift();
576
- if (fifo) {
577
- byId.delete(fifo.id);
578
- return fifo;
579
- }
580
- const orphan = {
581
- id: event.id ?? `tool-${index}`,
582
- name: event.name,
583
- args: {},
584
- status: "running"
585
- };
586
- cards.push(orphan);
587
- return orphan;
588
- }
589
-
590
- // src/client/use-agent-tool-cards.ts
591
- import { useMemo as useMemo2 } from "react";
592
- function useAgentToolCards(path, options = {}) {
593
- const { resolveEnvelope, ...streamOptions } = options;
594
- const stream = useAgentStream(path, streamOptions);
595
- const toolCards = useMemo2(
596
- () => foldAgentToolCards(stream.events, { resolveEnvelope }),
597
- [stream.events, resolveEnvelope]
598
- );
599
- return { ...stream, toolCards };
600
- }
601
-
602
387
  // src/client/consume-ui-message-stream.ts
603
388
  async function consumeUIMessageStream(response, onMessage) {
604
389
  if (response.body === null) return;
@@ -618,23 +403,23 @@ async function consumeUIMessageStream(response, onMessage) {
618
403
  }
619
404
 
620
405
  // src/client/use-agent.ts
621
- import { useCallback as useCallback2, useRef as useRef2, useState as useState2 } from "react";
406
+ import { useCallback, useRef, useState } from "react";
622
407
  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(() => {
408
+ const [messages, setMessages] = useState([]);
409
+ const [status, setStatus] = useState("idle");
410
+ const [error, setError] = useState(void 0);
411
+ const controllerRef = useRef(null);
412
+ const abort = useCallback(() => {
628
413
  controllerRef.current?.abort();
629
414
  controllerRef.current = null;
630
415
  }, []);
631
- const reset = useCallback2(() => {
416
+ const reset = useCallback(() => {
632
417
  abort();
633
418
  setMessages([]);
634
419
  setError(void 0);
635
420
  setStatus("idle");
636
421
  }, [abort]);
637
- const send = useCallback2(
422
+ const send = useCallback(
638
423
  (input) => {
639
424
  abort();
640
425
  const controller = new AbortController();
@@ -682,7 +467,7 @@ function useAgent(path, options = {}) {
682
467
  import {
683
468
  Link as RouterLink
684
469
  } from "react-router";
685
- import { useRef as useRef3, useCallback as useCallback3, useEffect as useEffect2 } from "react";
470
+ import { useRef as useRef2, useCallback as useCallback2, useEffect } from "react";
686
471
  import { jsx } from "react/jsx-runtime";
687
472
  var prefetched = /* @__PURE__ */ new Set();
688
473
  function injectPrefetch(href) {
@@ -699,8 +484,8 @@ function resolveTo(to) {
699
484
  return to?.pathname ?? "";
700
485
  }
701
486
  function Link({ prefetch = "intent", to, onMouseEnter, onFocus, ...rest }) {
702
- const ref = useRef3(null);
703
- const handleIntent = useCallback3(
487
+ const ref = useRef2(null);
488
+ const handleIntent = useCallback2(
704
489
  (event) => {
705
490
  if (prefetch === "intent") {
706
491
  injectPrefetch(resolveTo(to));
@@ -716,7 +501,7 @@ function Link({ prefetch = "intent", to, onMouseEnter, onFocus, ...rest }) {
716
501
  },
717
502
  [prefetch, to, onMouseEnter, onFocus]
718
503
  );
719
- useEffect2(() => {
504
+ useEffect(() => {
720
505
  if (prefetch !== "viewport" || !ref.current) return;
721
506
  if (typeof IntersectionObserver === "undefined") return;
722
507
  const observer = new IntersectionObserver(
@@ -781,19 +566,11 @@ export {
781
566
  Metadata,
782
567
  TheoFetchError,
783
568
  buildUseTheoQueryConfig,
784
- consumeAgentStream,
785
569
  consumeUIMessageStream,
786
570
  createAppClient,
787
571
  createBatcher,
788
- defaultResolveEnvelope,
789
- deriveError,
790
- deriveLiveText,
791
- foldAgentToolCards,
792
- parseSSEChunk,
793
572
  stableQueryKey,
794
573
  theoFetch,
795
- useAgent,
796
- useAgentStream,
797
- useAgentToolCards
574
+ useAgent
798
575
  };
799
576
  //# sourceMappingURL=index.js.map