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.
- package/dist/{actions-virtual-module-EIPXX4ZB.js → actions-virtual-module-RM7JRY5N.js} +7 -7
- package/dist/{app-typed-client-7PBFWZUE.js → app-typed-client-5VQE6DNF.js} +7 -7
- package/dist/boot/index.js +3 -3
- package/dist/{build-HXND27XG.js → build-5K7LK77K.js} +2 -2
- package/dist/{chunk-3S3BNW5K.js → chunk-5UUOGAJH.js} +2 -2
- package/dist/{chunk-O62MW4MT.js → chunk-637WJB7Z.js} +2 -2
- package/dist/{chunk-TGTNRUH3.js → chunk-H243MR3F.js} +2 -2
- package/dist/{chunk-NHJMZCAS.js → chunk-JZHT3LW7.js} +1 -1
- package/dist/chunk-JZHT3LW7.js.map +1 -0
- package/dist/chunk-KGFNWIMS.js +94 -0
- package/dist/chunk-KGFNWIMS.js.map +1 -0
- package/dist/{chunk-RYTZYFSD.js → chunk-RBHCJHRR.js} +65 -28
- package/dist/chunk-RBHCJHRR.js.map +1 -0
- package/dist/{chunk-UNLA45FY.js → chunk-S7Y5WLZY.js} +1 -1
- package/dist/chunk-S7Y5WLZY.js.map +1 -0
- package/dist/{chunk-WFNLNIJX.js → chunk-Z5IGLBWE.js} +1 -1
- package/dist/cli/index.js +3 -3
- package/dist/client/index.d.ts +3 -152
- package/dist/client/index.js +13 -236
- package/dist/client/index.js.map +1 -1
- package/dist/{dev-OWW4XVIH.js → dev-QOEVYNBG.js} +3 -3
- package/dist/index.js +12 -13
- package/dist/index.js.map +1 -1
- package/dist/{internal-api-4YTJDITC.js → internal-api-5BDI6N5U.js} +20 -20
- package/dist/server/auth/index.js +1 -1
- package/dist/server/define/index.d.ts +3 -70
- package/dist/server/define/index.js +2 -4
- package/dist/server/http/index.js +2 -2
- package/dist/server/index.d.ts +1 -3
- package/dist/server/index.js +90 -105
- package/dist/server/index.js.map +1 -1
- package/dist/server/jobs/index.js +4 -4
- package/dist/server/observability/index.js +1 -1
- package/dist/server/scan/index.js +1 -1
- package/dist/{start-KIQ5TTLR.js → start-2KG4JSXM.js} +2 -2
- package/dist/vite-plugin/index.js +12 -13
- package/dist/{vite-plugin-RK66K26Z.js → vite-plugin-TDIDZ5U7.js} +3 -3
- package/package.json +1 -5
- package/dist/agent-events-DosDXkSV.d.ts +0 -94
- package/dist/chunk-2KZQPDYR.js +0 -226
- package/dist/chunk-2KZQPDYR.js.map +0 -1
- package/dist/chunk-EXP56GFQ.js +0 -52
- package/dist/chunk-EXP56GFQ.js.map +0 -1
- package/dist/chunk-NHJMZCAS.js.map +0 -1
- package/dist/chunk-RYTZYFSD.js.map +0 -1
- package/dist/chunk-UNLA45FY.js.map +0 -1
- package/dist/chunk-ZSTZXR2D.js +0 -208
- package/dist/chunk-ZSTZXR2D.js.map +0 -1
- package/dist/server/agent/index.d.ts +0 -229
- package/dist/server/agent/index.js +0 -17
- package/dist/vite-plugin-RK66K26Z.js.map +0 -1
- /package/dist/{actions-virtual-module-EIPXX4ZB.js.map → actions-virtual-module-RM7JRY5N.js.map} +0 -0
- /package/dist/{app-typed-client-7PBFWZUE.js.map → app-typed-client-5VQE6DNF.js.map} +0 -0
- /package/dist/{build-HXND27XG.js.map → build-5K7LK77K.js.map} +0 -0
- /package/dist/{chunk-3S3BNW5K.js.map → chunk-5UUOGAJH.js.map} +0 -0
- /package/dist/{chunk-O62MW4MT.js.map → chunk-637WJB7Z.js.map} +0 -0
- /package/dist/{chunk-TGTNRUH3.js.map → chunk-H243MR3F.js.map} +0 -0
- /package/dist/{chunk-WFNLNIJX.js.map → chunk-Z5IGLBWE.js.map} +0 -0
- /package/dist/{dev-OWW4XVIH.js.map → dev-QOEVYNBG.js.map} +0 -0
- /package/dist/{internal-api-4YTJDITC.js.map → internal-api-5BDI6N5U.js.map} +0 -0
- /package/dist/{start-KIQ5TTLR.js.map → start-2KG4JSXM.js.map} +0 -0
- /package/dist/{server/agent/index.js.map → vite-plugin-TDIDZ5U7.js.map} +0 -0
|
@@ -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":[]}
|
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-
|
|
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-
|
|
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-
|
|
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);
|
package/dist/client/index.d.ts
CHANGED
|
@@ -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
|
|
292
|
-
*
|
|
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 {
|
|
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 };
|
package/dist/client/index.js
CHANGED
|
@@ -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
|
|
406
|
+
import { useCallback, useRef, useState } from "react";
|
|
622
407
|
function useAgent(path, options = {}) {
|
|
623
|
-
const [messages, setMessages] =
|
|
624
|
-
const [status, setStatus] =
|
|
625
|
-
const [error, setError] =
|
|
626
|
-
const controllerRef =
|
|
627
|
-
const abort =
|
|
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 =
|
|
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 =
|
|
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
|
|
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 =
|
|
703
|
-
const handleIntent =
|
|
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
|
-
|
|
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
|