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
|
@@ -1 +0,0 @@
|
|
|
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 (mirrors define-agent-endpoint.ts).\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/chunk-ZSTZXR2D.js
DELETED
|
@@ -1,208 +0,0 @@
|
|
|
1
|
-
// src/server/define/define-route.ts
|
|
2
|
-
function defineRoute(config) {
|
|
3
|
-
return config;
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
// src/server/define/define-action.ts
|
|
7
|
-
function defineAction(config) {
|
|
8
|
-
return config;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
// src/server/define/define-middleware.ts
|
|
12
|
-
function defineMiddleware(handler) {
|
|
13
|
-
return handler;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
// src/server/define/define-agent-endpoint.ts
|
|
17
|
-
var SSE_HEADERS = {
|
|
18
|
-
"content-type": "text/event-stream",
|
|
19
|
-
"cache-control": "no-cache, no-transform",
|
|
20
|
-
connection: "keep-alive"
|
|
21
|
-
};
|
|
22
|
-
function encodeSSE(event) {
|
|
23
|
-
return new TextEncoder().encode(`data: ${JSON.stringify(event)}
|
|
24
|
-
|
|
25
|
-
`);
|
|
26
|
-
}
|
|
27
|
-
function resolveAbortSignal(request) {
|
|
28
|
-
if (typeof request !== "object" || request === null) {
|
|
29
|
-
return new AbortController().signal;
|
|
30
|
-
}
|
|
31
|
-
const r = request;
|
|
32
|
-
const isNodeRequest = typeof r.on === "function";
|
|
33
|
-
if (!isNodeRequest && typeof r.signal === "object" && r.signal !== null && "aborted" in r.signal && typeof r.signal.addEventListener === "function") {
|
|
34
|
-
return r.signal;
|
|
35
|
-
}
|
|
36
|
-
const controller = new AbortController();
|
|
37
|
-
const abort = () => {
|
|
38
|
-
if (!controller.signal.aborted) controller.abort();
|
|
39
|
-
};
|
|
40
|
-
if (r.aborted === true) controller.abort();
|
|
41
|
-
if (typeof r.on === "function") {
|
|
42
|
-
r.on("aborted", abort);
|
|
43
|
-
r.on("close", () => {
|
|
44
|
-
if (r.complete !== true) abort();
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
|
-
const socket = r.socket;
|
|
48
|
-
if (socket && typeof socket.on === "function") {
|
|
49
|
-
socket.on("close", abort);
|
|
50
|
-
}
|
|
51
|
-
return controller.signal;
|
|
52
|
-
}
|
|
53
|
-
function defineAgentEndpoint(config) {
|
|
54
|
-
return {
|
|
55
|
-
params: config.params,
|
|
56
|
-
handler: async ({ request, ctx, body, query, params }) => {
|
|
57
|
-
const cookieHeaders = new Headers();
|
|
58
|
-
const signal = resolveAbortSignal(request);
|
|
59
|
-
const generator = config.handler({
|
|
60
|
-
request,
|
|
61
|
-
ctx,
|
|
62
|
-
body,
|
|
63
|
-
query,
|
|
64
|
-
params,
|
|
65
|
-
cookieHeaders,
|
|
66
|
-
signal
|
|
67
|
-
});
|
|
68
|
-
let firstResult;
|
|
69
|
-
let primeError;
|
|
70
|
-
try {
|
|
71
|
-
firstResult = await generator.next();
|
|
72
|
-
} catch (err) {
|
|
73
|
-
primeError = err;
|
|
74
|
-
firstResult = { value: void 0, done: true };
|
|
75
|
-
}
|
|
76
|
-
const responseHeaders = new Headers(SSE_HEADERS);
|
|
77
|
-
for (const value of cookieHeaders.getSetCookie()) {
|
|
78
|
-
responseHeaders.append("set-cookie", value);
|
|
79
|
-
}
|
|
80
|
-
const stream = new ReadableStream({
|
|
81
|
-
async start(controller) {
|
|
82
|
-
const onAbort = () => {
|
|
83
|
-
void generator.return(void 0);
|
|
84
|
-
};
|
|
85
|
-
if (signal.aborted) {
|
|
86
|
-
controller.close();
|
|
87
|
-
return;
|
|
88
|
-
}
|
|
89
|
-
signal.addEventListener("abort", onAbort, { once: true });
|
|
90
|
-
try {
|
|
91
|
-
if (primeError !== void 0) {
|
|
92
|
-
let message;
|
|
93
|
-
if (primeError instanceof Error) {
|
|
94
|
-
message = primeError.message;
|
|
95
|
-
} else if (typeof primeError === "string") {
|
|
96
|
-
message = primeError;
|
|
97
|
-
} else {
|
|
98
|
-
message = "agent handler failed during setup";
|
|
99
|
-
}
|
|
100
|
-
controller.enqueue(encodeSSE({ type: "error", message }));
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
103
|
-
if (firstResult.done !== true) {
|
|
104
|
-
controller.enqueue(encodeSSE(firstResult.value));
|
|
105
|
-
} else {
|
|
106
|
-
return;
|
|
107
|
-
}
|
|
108
|
-
for await (const event of generator) {
|
|
109
|
-
if (signal.aborted) break;
|
|
110
|
-
controller.enqueue(encodeSSE(event));
|
|
111
|
-
}
|
|
112
|
-
} catch (err) {
|
|
113
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
114
|
-
controller.enqueue(encodeSSE({ type: "error", message }));
|
|
115
|
-
} finally {
|
|
116
|
-
signal.removeEventListener("abort", onAbort);
|
|
117
|
-
controller.close();
|
|
118
|
-
}
|
|
119
|
-
},
|
|
120
|
-
cancel() {
|
|
121
|
-
void generator.return(void 0);
|
|
122
|
-
}
|
|
123
|
-
});
|
|
124
|
-
return new Response(stream, { headers: responseHeaders });
|
|
125
|
-
}
|
|
126
|
-
};
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// src/server/define/define-agent-tool.ts
|
|
130
|
-
import { z } from "zod";
|
|
131
|
-
var TOOL_NAME_REGEX = /^[a-zA-Z][a-zA-Z0-9_-]{0,63}$/;
|
|
132
|
-
function isZodObject(schema) {
|
|
133
|
-
let current = schema;
|
|
134
|
-
for (let depth = 0; depth < 10; depth++) {
|
|
135
|
-
if (current instanceof z.ZodObject) return true;
|
|
136
|
-
const z4 = current.def;
|
|
137
|
-
if (z4?.type === "object") return true;
|
|
138
|
-
const z3 = current._def;
|
|
139
|
-
if (z3?.typeName === "ZodObject") return true;
|
|
140
|
-
const next = z4?.innerType ?? z4?.in ?? z3?.schema ?? z3?.innerType;
|
|
141
|
-
if (next !== void 0) {
|
|
142
|
-
current = next;
|
|
143
|
-
continue;
|
|
144
|
-
}
|
|
145
|
-
return false;
|
|
146
|
-
}
|
|
147
|
-
return false;
|
|
148
|
-
}
|
|
149
|
-
function defineAgentTool(spec) {
|
|
150
|
-
if (!TOOL_NAME_REGEX.test(spec.name)) {
|
|
151
|
-
throw new Error(
|
|
152
|
-
`defineAgentTool: name must match ${TOOL_NAME_REGEX.source}. Got: ${JSON.stringify(spec.name)}`
|
|
153
|
-
);
|
|
154
|
-
}
|
|
155
|
-
if (!isZodObject(spec.inputSchema)) {
|
|
156
|
-
throw new Error("defineAgentTool: inputSchema must be a ZodObject (z.object({...}))");
|
|
157
|
-
}
|
|
158
|
-
if (spec.description.length === 0) {
|
|
159
|
-
console.warn(
|
|
160
|
-
`defineAgentTool(${JSON.stringify(spec.name)}): empty description degrades LLM tool selection \u2014 provide a one-sentence summary.`
|
|
161
|
-
);
|
|
162
|
-
}
|
|
163
|
-
const { $schema: _$schema, ...inputSchema } = z.toJSONSchema(spec.inputSchema);
|
|
164
|
-
return {
|
|
165
|
-
name: spec.name,
|
|
166
|
-
description: spec.description,
|
|
167
|
-
inputSchema,
|
|
168
|
-
handler: async (input) => {
|
|
169
|
-
const parsed = spec.inputSchema.parse(input);
|
|
170
|
-
return await spec.handler(parsed);
|
|
171
|
-
}
|
|
172
|
-
};
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
// src/server/define/define-websocket.ts
|
|
176
|
-
function defineWebSocket(handler) {
|
|
177
|
-
return handler;
|
|
178
|
-
}
|
|
179
|
-
function defineWebSocketWeb(handler) {
|
|
180
|
-
return handler;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
// src/server/define/define-channel.ts
|
|
184
|
-
function defineChannel(handler) {
|
|
185
|
-
return handler;
|
|
186
|
-
}
|
|
187
|
-
function defineWebChannel(handler) {
|
|
188
|
-
return handler;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
// src/server/define/define-plugin.ts
|
|
192
|
-
function defineTheoPlugin(plugin) {
|
|
193
|
-
return plugin;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
export {
|
|
197
|
-
defineRoute,
|
|
198
|
-
defineAction,
|
|
199
|
-
defineMiddleware,
|
|
200
|
-
defineAgentEndpoint,
|
|
201
|
-
defineAgentTool,
|
|
202
|
-
defineWebSocket,
|
|
203
|
-
defineWebSocketWeb,
|
|
204
|
-
defineChannel,
|
|
205
|
-
defineWebChannel,
|
|
206
|
-
defineTheoPlugin
|
|
207
|
-
};
|
|
208
|
-
//# sourceMappingURL=chunk-ZSTZXR2D.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/server/define/define-route.ts","../src/server/define/define-action.ts","../src/server/define/define-middleware.ts","../src/server/define/define-agent-endpoint.ts","../src/server/define/define-agent-tool.ts","../src/server/define/define-websocket.ts","../src/server/define/define-channel.ts","../src/server/define/define-plugin.ts"],"sourcesContent":["import type { z } from 'zod'\n\n// T2.2 (architecture-cleanup) — RouteConfig type moved to core/contracts/\n// (canonical home per ADR-0001 v3). Re-export preserves the public path\n// `import { type RouteConfig } from 'theokit/server'`.\nexport type { RouteConfig } from '../../core/contracts/route-config.js'\n\nimport type { RouteConfig } from '../../core/contracts/route-config.js'\n\n/**\n * Define a typed HTTP route.\n * Identity function — provides type inference for route handlers.\n */\nexport function defineRoute<\n TQuery extends z.ZodType = z.ZodUndefined,\n TBody extends z.ZodType = z.ZodUndefined,\n TParams extends z.ZodType = z.ZodUndefined,\n TCtx = unknown,\n TResponse = unknown,\n>(\n config: RouteConfig<TQuery, TBody, TParams, TCtx, TResponse>,\n): RouteConfig<TQuery, TBody, TParams, TCtx, TResponse> {\n return config\n}\n","import type { z } from 'zod'\n\n/**\n * Action wire-protocol accept mode per plan g3-server-actions-and-useaction\n * v1.2 ADR D1. Default behavior (when omitted) is `'json'`. `'form'` opts the\n * action into FormData multipart parsing for progressive-enhancement forms;\n * the runtime in `server/http/action-execute.ts` will coerce FormData entries\n * against the `input` schema via `formDataToObject` (Astro pattern).\n */\nexport type ActionAccept = 'form' | 'json'\n\nexport interface ActionConfig<TInput extends z.ZodType, TCtx = unknown> {\n /**\n * Zod input schema. Required: every action declares its input contract via\n * Zod (architecture rule: zod-is-SSOT). The shape becomes the handler's\n * typed `input` parameter via `z.infer<TInput>`.\n */\n input: TInput\n /**\n * Wire-protocol accept mode. Defaults to `'json'` when omitted. Setting\n * `'form'` switches the runtime to FormData multipart parsing — the input\n * schema MUST be `z.object(...)` so field-by-field coercion can drive\n * boolean string / number / array coercion (Astro pattern).\n */\n accept?: ActionAccept\n /**\n * Opt OUT of CSRF enforcement for this action. Default (omitted) keeps the\n * multi-header CSRF gate active. Set `false` for endpoints intentionally\n * callable without the `X-Theo-Action` header (e.g. public webhooks). The\n * runtime in `server/http/action-execute.ts` reads this flag.\n */\n csrf?: false\n handler: (ctx: { input: z.infer<TInput>; ctx: TCtx }) => unknown\n}\n\n/**\n * Define a typed server action.\n *\n * Identity function — provides type inference for action handlers. The\n * runtime that consumes the config (validation + invocation + serialization)\n * lives in `server/http/action-execute.ts`.\n *\n * Per plan g3-server-actions-and-useaction v1.2 § Phase 1 / T1.2: the new\n * `accept?: 'form' | 'json'` field is the only contract change vs the\n * pre-G3 identity. Existing callsites (`defineAction({input, handler})`)\n * continue to compile — `accept` is opt-in.\n */\nexport function defineAction<TInput extends z.ZodType, TCtx = unknown>(\n config: ActionConfig<TInput, TCtx>,\n): ActionConfig<TInput, TCtx> {\n return config\n}\n","export type MiddlewareHandler = (\n request: Request,\n next: (request: Request) => Promise<Response>,\n) => Response | Promise<Response>\n\n/**\n * Define a middleware handler.\n * Identity function — provides type annotation for middleware.\n */\nexport function defineMiddleware(handler: MiddlewareHandler): MiddlewareHandler {\n return handler\n}\n","import type { z } from 'zod'\n\nimport type { AgentEvent } from '../agent/agent-types.js'\n\nimport type { RouteConfig } from './define-route.js'\n\n/**\n * T5.1 — defineAgentEndpoint\n *\n * Sugar over defineRoute (ADR D4). Accepts an async generator that yields\n * AgentEvents and produces a RouteConfig whose handler returns a Response\n * streaming Server-Sent Events (SSE).\n *\n * Wire format: `data: <JSON>\\n\\n` per event. Standards-compliant.\n *\n * The generator may throw — the wrapper catches and emits a final\n * `{ type: 'error', message }` event before closing the stream.\n *\n * The wrapper observes `request.signal` (EC-7) — when aborted, the\n * underlying generator is told to `return()` and the stream closes\n * promptly.\n *\n * Note (EC-12, Out of Scope): SSE backpressure (slow consumer) is not\n * handled here. For high-frequency token streaming consider a different\n * transport (WS) or a buffer policy. This MVP enqueues each event\n * immediately.\n */\n\nexport interface AgentEndpointHandlerArgs<\n TCtx = unknown,\n TBody = unknown,\n TParams extends z.ZodType = z.ZodUndefined,\n> {\n query: undefined\n body: TBody\n params: z.infer<TParams>\n request: Request\n ctx: TCtx\n /**\n * Mutable headers bag that the wrapper merges into the SSE response BEFORE\n * the stream starts. Used by `createConversationHistory` to issue a\n * conversation-id cookie on first request. Append entries via\n * `cookieHeaders.append('set-cookie', '<cookie-string>')`.\n *\n * Cookies appended AFTER the first yield are NOT applied — the response\n * headers commit when the wrapper constructs the Response, before the\n * async generator runs. This is per HTTP semantics, not a wrapper choice.\n */\n cookieHeaders: Headers\n /**\n * Phase 3 (Production-Readiness #5) — request abort signal.\n *\n * Fires when the SSE client disconnects (browser closes tab, abort fetch,\n * etc.). Thread this to `agent.send(msg, { signal })` so the SDK cancels\n * the in-flight provider call — STOPS TOKENS FROM CHARGING for output\n * the user will never receive.\n *\n * EC-1 (MUST FIX): the signal is derived via duck-type detection\n * (`'aborted' in r.signal && typeof r.signal.addEventListener === 'function'`)\n * to survive cross-realm scenarios (Node 18 polyfills, undici, Edge\n * runtimes with their own AbortSignal globals). `instanceof AbortSignal`\n * would fail in those cases.\n */\n signal: AbortSignal\n}\n\nexport interface AgentEndpointConfig<\n TCtx = unknown,\n TBody = unknown,\n TParams extends z.ZodType = z.ZodUndefined,\n> {\n /**\n * Optional Zod schema for path params (e.g. `z.object({ id: z.string() })`).\n * When present, the runner validates path params and returns 400 on mismatch\n * BEFORE the generator runs; the validated params are threaded to the\n * generator typed as `z.infer<TParams>` (D4).\n */\n params?: TParams\n handler: (\n args: AgentEndpointHandlerArgs<TCtx, TBody, TParams>,\n ) => AsyncGenerator<AgentEvent, void, unknown>\n}\n\nconst SSE_HEADERS = {\n 'content-type': 'text/event-stream',\n 'cache-control': 'no-cache, no-transform',\n connection: 'keep-alive',\n} as const\n\nfunction encodeSSE(event: AgentEvent): Uint8Array {\n return new TextEncoder().encode(`data: ${JSON.stringify(event)}\\n\\n`)\n}\n\n/**\n * Resolve an AbortSignal from either a Web `Request` (`.signal`) or a\n * Node `IncomingMessage` (`.socket` close + `'aborted'`/'close' events).\n *\n * The framework's `executeRoute` passes IncomingMessage to route handlers\n * today, but `defineAgentEndpoint` was designed for the Web Standards\n * `Request` shape. This helper bridges both — preventing a runtime crash\n * (`Cannot read properties of undefined (reading 'aborted')`) that would\n * otherwise abort the SSE stream silently before the first yield.\n *\n * Node ≥23 regression (empty-SSE-stream): Node 23 added\n * `http.IncomingMessage.prototype.signal` — a Web `AbortSignal` that fires\n * `abort` the instant the request body is fully received (`req.complete ===\n * true`), NOT when the client disconnects. The earlier duck-type\n * (\"has `.signal` with `aborted` + `addEventListener`\") matched a genuine Web\n * `Request` AND, on Node ≥23, the Node `IncomingMessage` — so the helper\n * returned the request-lifecycle signal, already aborted by the time the\n * handler primes, and EVERY agent stream produced 0 bytes on Node 24.\n *\n * Discriminator: a Node `IncomingMessage` is an `EventEmitter` (`typeof\n * r.on === 'function'`); a Web `Request` is not. So `r.signal` is trusted\n * directly ONLY when this is not a Node request. For the Node path the only\n * lifecycle event that means \"client disconnected\" (rather than \"request body\n * finished\") is the underlying SOCKET closing — `req`'s own `.signal`/`'close'`\n * fire at body-end on Node ≥23 and would kill a long-lived SSE response.\n */\nfunction resolveAbortSignal(request: unknown): AbortSignal {\n if (typeof request !== 'object' || request === null) {\n return new AbortController().signal\n }\n const r = request as {\n signal?: unknown\n aborted?: boolean\n complete?: boolean\n on?: (event: string, cb: () => void) => void\n socket?: { on?: (event: string, cb: () => void) => void }\n }\n\n // A genuine Web `Request` exposes `.signal` and is NOT a Node EventEmitter.\n const isNodeRequest = typeof r.on === 'function'\n if (\n !isNodeRequest &&\n typeof r.signal === 'object' &&\n r.signal !== null &&\n 'aborted' in r.signal &&\n typeof (r.signal as AbortSignal).addEventListener === 'function'\n ) {\n return r.signal as AbortSignal\n }\n\n const controller = new AbortController()\n const abort = () => {\n if (!controller.signal.aborted) controller.abort()\n }\n if (r.aborted === true) controller.abort()\n if (typeof r.on === 'function') {\n // Explicit client abort (request reset mid-flight).\n r.on('aborted', abort)\n // `req`'s own 'close' fires at request-completion on Node ≥23 (body fully\n // received), which is NOT a disconnect — guard with `complete`. A close\n // while the request already completed normally is body-end noise.\n r.on('close', () => {\n if (r.complete !== true) abort()\n })\n }\n // The underlying socket closes only on real connection teardown (client\n // gone), never at request-body-end — the correct disconnect signal for a\n // long-lived SSE response.\n const socket = r.socket\n if (socket && typeof socket.on === 'function') {\n socket.on('close', abort)\n }\n return controller.signal\n}\n\nexport function defineAgentEndpoint<\n TBody = unknown,\n TCtx = unknown,\n TParams extends z.ZodType = z.ZodUndefined,\n>(\n config: AgentEndpointConfig<TCtx, TBody, TParams>,\n): RouteConfig<z.ZodUndefined, z.ZodUndefined, TParams, TCtx, Response> {\n return {\n params: config.params,\n handler: async ({ request, ctx, body, query, params }) => {\n const cookieHeaders = new Headers()\n const signal = resolveAbortSignal(request)\n const generator = config.handler({\n request,\n ctx: ctx,\n body: body as TBody,\n query: query,\n params: params,\n cookieHeaders,\n signal,\n })\n\n // Prime the generator to its first yield. This forces the handler's\n // synchronous + async setup (including `createConversationHistory` if\n // used) to run BEFORE the Response headers commit, so any Set-Cookie\n // lines appended to `cookieHeaders` land in the actual response.\n //\n // First-byte latency cost: bounded by the work up to the first yield\n // (typically 100-500ms for an LLM chat with `agent.send`). Acceptable\n // for the use case; alternative (heuristic detection of cookie writes)\n // is brittle.\n let firstResult: IteratorResult<AgentEvent, void>\n let primeError: unknown\n try {\n firstResult = await generator.next()\n } catch (err) {\n primeError = err\n firstResult = { value: undefined, done: true }\n }\n\n // Merge any cookies the handler issued into the response headers.\n const responseHeaders = new Headers(SSE_HEADERS)\n for (const value of cookieHeaders.getSetCookie()) {\n responseHeaders.append('set-cookie', value)\n }\n\n const stream = new ReadableStream<Uint8Array>({\n async start(controller) {\n const onAbort = () => {\n void generator.return(undefined)\n }\n\n if (signal.aborted) {\n controller.close()\n return\n }\n signal.addEventListener('abort', onAbort, { once: true })\n\n try {\n // If priming threw, yield a final error event and close.\n if (primeError !== undefined) {\n let message: string\n if (primeError instanceof Error) {\n message = primeError.message\n } else if (typeof primeError === 'string') {\n message = primeError\n } else {\n message = 'agent handler failed during setup'\n }\n controller.enqueue(encodeSSE({ type: 'error', message }))\n return\n }\n // Enqueue the primed first value (if any).\n if (firstResult.done !== true) {\n controller.enqueue(encodeSSE(firstResult.value))\n } else {\n return\n }\n // Continue iterating remaining events.\n for await (const event of generator) {\n // `signal.aborted` can flip true asynchronously via the\n // listener registered above. ESLint's narrowing only sees\n // the false assertion at line 114; the dynamic abort is\n // legitimate and the guard prevents enqueue-after-close.\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n if (signal.aborted) break\n controller.enqueue(encodeSSE(event))\n }\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n controller.enqueue(encodeSSE({ type: 'error', message }))\n } finally {\n signal.removeEventListener('abort', onAbort)\n controller.close()\n }\n },\n cancel() {\n void generator.return(undefined)\n },\n })\n\n return new Response(stream, { headers: responseHeaders })\n },\n }\n}\n","import { z } from 'zod'\n\n/**\n * Item #4 — `defineAgentTool`\n *\n * Sugar over the `@theokit/sdk` `CustomTool` contract. Takes a Zod schema +\n * handler and produces a structurally-compatible `CustomTool` that\n * `Agent.create({ tools: [...] })` accepts.\n *\n * Uses Zod v4's native `z.toJSONSchema()` to convert the input schema to\n * JSON Schema for LLM providers.\n *\n * Handler error propagation:\n * `defineAgentTool` parses the input via the Zod schema BEFORE calling the\n * user handler. Invalid input throws a `ZodError`, which the SDK's tool-\n * dispatcher (or the `streamAgentRun` adapter) sees as a tool failure and\n * surfaces as an `error` AgentEvent on the SSE wire (ADR D3).\n */\n\n/**\n * Local mirror of the SDK's `CustomTool` interface. We don't `import type`\n * from `@theokit/sdk` because the SDK is an optional peer (consumers who\n * never call `defineAgentTool` shouldn't need it installed). The shape is\n * the wire contract; any structurally-matching object is accepted by\n * `Agent.create({ tools })`.\n *\n * @public\n */\nexport interface CustomTool {\n name: string\n description: string\n inputSchema: Record<string, unknown>\n handler: (input: Record<string, unknown>) => string | Promise<string>\n}\n\n/**\n * Spec accepted by {@link defineAgentTool}. `inputSchema` is a Zod 3 schema\n * rooted in `z.object(...)`. The `handler` argument type is inferred via\n * `z.infer<T>`.\n *\n * @public\n */\nexport interface DefineAgentToolSpec<T extends z.ZodType> {\n /** Tool name surfaced to the LLM. Must match `^[a-zA-Z][a-zA-Z0-9_-]{0,63}$`. */\n name: string\n /** Description surfaced to the LLM. Required — drives tool-selection accuracy. */\n description: string\n /** Zod schema describing the input. Must be `z.object(...)` at the root. */\n inputSchema: T\n /** Handler invoked with the parsed input. */\n handler: (input: z.infer<T>) => string | Promise<string>\n}\n\nconst TOOL_NAME_REGEX = /^[a-zA-Z][a-zA-Z0-9_-]{0,63}$/\n\nfunction isZodObject(schema: z.ZodType): boolean {\n // Refinements (`.refine`), transforms (`.transform`), and defaults wrap the\n // underlying schema — walk the chain until we hit a ZodObject (or give up).\n // Supports both zod 4 (`instanceof z.ZodObject`, `def.type === 'object'`,\n // wrappers via `def.innerType` / pipe via `def.in`) and zod 3\n // (`_def.typeName === 'ZodObject'`, wrappers via `_def.schema`/`_def.innerType`).\n let current: unknown = schema\n for (let depth = 0; depth < 10; depth++) {\n if (current instanceof z.ZodObject) return true\n const z4 = (current as { def?: { type?: string; innerType?: unknown; in?: unknown } }).def\n if (z4?.type === 'object') return true\n const z3 = (current as { _def?: { typeName?: string; schema?: unknown; innerType?: unknown } })\n ._def\n if (z3?.typeName === 'ZodObject') return true\n const next = z4?.innerType ?? z4?.in ?? z3?.schema ?? z3?.innerType\n if (next !== undefined) {\n current = next\n continue\n }\n return false\n }\n return false\n}\n\n/**\n * Build a {@link CustomTool} from a Zod 3 schema + handler.\n *\n * Behavior:\n * - Validates `name` matches the LLM tool-name regex.\n * - Requires `inputSchema` to be a `ZodObject` (Anthropic + SDK contract).\n * - Warns (not throws) if `description` is empty — empty descriptions\n * degrade LLM tool selection.\n * - Converts the Zod schema to JSON Schema 7 inline (no `$ref`s — LLMs handle\n * inline schemas more reliably).\n * - Strips the top-level `$schema` field (Anthropic rejects schemas with\n * `$schema` at root in some provider modes).\n * - Wraps the handler to parse the input via the Zod schema BEFORE invoking\n * the user code — bad LLM-supplied input throws `ZodError`, which the SDK\n * converts to `tool_result(isError)`.\n *\n * @public\n */\nexport function defineAgentTool<T extends z.ZodType>(spec: DefineAgentToolSpec<T>): CustomTool {\n if (!TOOL_NAME_REGEX.test(spec.name)) {\n throw new Error(\n `defineAgentTool: name must match ${TOOL_NAME_REGEX.source}. Got: ${JSON.stringify(spec.name)}`,\n )\n }\n if (!isZodObject(spec.inputSchema)) {\n throw new Error('defineAgentTool: inputSchema must be a ZodObject (z.object({...}))')\n }\n if (spec.description.length === 0) {\n console.warn(\n `defineAgentTool(${JSON.stringify(spec.name)}): empty description degrades LLM tool selection — provide a one-sentence summary.`,\n )\n }\n\n // Zod v4 native JSON Schema conversion — replaces zod-to-json-schema dep.\n // Strip $schema (Anthropic + some providers reject it).\n const { $schema: _$schema, ...inputSchema } = z.toJSONSchema(spec.inputSchema) as Record<\n string,\n unknown\n > & {\n $schema?: unknown\n }\n\n return {\n name: spec.name,\n description: spec.description,\n inputSchema,\n handler: async (input: Record<string, unknown>): Promise<string> => {\n const parsed = spec.inputSchema.parse(input)\n return await spec.handler(parsed)\n },\n }\n}\n","import type { IncomingMessage } from 'node:http'\n\nexport interface WebSocketLike {\n send(data: string | Buffer): void\n close(code?: number, reason?: string): void\n}\n\nexport interface WebSocketHandler {\n onOpen?: (ws: WebSocketLike, req: IncomingMessage) => void\n onMessage?: (ws: WebSocketLike, data: string | Buffer) => void\n onClose?: (ws: WebSocketLike, code: number, reason: Buffer) => void\n onError?: (ws: WebSocketLike, error: Error) => void\n}\n\n/**\n * Define a WebSocket endpoint handler.\n * Identity function — provides type inference for WebSocket handlers.\n */\nexport function defineWebSocket(handler: WebSocketHandler): WebSocketHandler {\n return handler\n}\n\n/**\n * T5a.2 Phase F slice 3/3 — Web-Standards WebSocket endpoint handler.\n *\n * Mirror of `WebSocketHandler` for the Web `Request` shape. `onOpen`\n * receives `request: Request` instead of `req: IncomingMessage`. The\n * rest of the lifecycle (onMessage, onClose, onError) is shape-agnostic\n * (`WebSocketLike` is already Web-standards-compatible per the existing\n * design — `send(string | Buffer)` works on both Node `ws` and Web\n * `WebSocket` instances; CF Workers / Bun / Deno coerce as needed at\n * the adapter boundary).\n *\n * Per `docs/plans/t5a2-incoming-message-to-request-shape-refactor-plan.md`\n * v1.0 § Phase F (closes Phase F).\n *\n * **Architectural note — WebSocket upgrade semantics differ across runtimes:**\n * - Node + `ws`: `WebSocketServer.handleUpgrade(req, socket, head, cb)` —\n * `req` is `IncomingMessage`. Use `WebSocketHandler`.\n * - CF Workers: `new WebSocketPair()` + `request.headers` (the upgrade\n * handshake IS a Web Request). Use `WebSocketHandlerWeb`.\n * - Bun: `server.upgrade(request, { data })` — same Web Request shape.\n * - Deno: `Deno.upgradeWebSocket(request)` — same Web Request shape.\n *\n * Cross-runtime WebSocket endpoints ship BOTH `WebSocketHandler` +\n * `WebSocketHandlerWeb` exports; the runtime adapter picks the matching\n * one. This is the canonical Hono / Nitric pattern.\n */\nexport interface WebSocketHandlerWeb {\n onOpen?: (ws: WebSocketLike, request: Request) => void\n onMessage?: (ws: WebSocketLike, data: string | Uint8Array) => void\n onClose?: (ws: WebSocketLike, code: number, reason: string) => void\n onError?: (ws: WebSocketLike, error: Error) => void\n}\n\n/**\n * Web-Standards `defineWebSocket` sibling. Identity function — provides\n * type inference for Web WebSocket handlers.\n *\n * **Type difference note vs Node path:**\n * - `onMessage` data is `string | Uint8Array` instead of `string | Buffer`\n * (Web standards have no `Buffer`; Node's Buffer is a Uint8Array\n * subclass so the Node path's Buffer values flow through unchanged\n * when adapters wrap them).\n * - `onClose` reason is `string` instead of `Buffer` (Web `CloseEvent`\n * exposes the reason as a UTF-8 string natively).\n */\nexport function defineWebSocketWeb(handler: WebSocketHandlerWeb): WebSocketHandlerWeb {\n return handler\n}\n","import type { IncomingMessage } from 'node:http'\n\nimport type { WebSocketLike } from './define-websocket.js'\n\nexport interface ChannelHandler<TMessage = unknown> {\n onSubscribe?: (ws: WebSocketLike, room: string, req: IncomingMessage) => void\n onMessage?: (ws: WebSocketLike, room: string, data: TMessage) => void\n onUnsubscribe?: (ws: WebSocketLike, room: string) => void\n}\n\n/**\n * Define a channel handler for WebSocket rooms.\n * Identity function — provides type inference for channel handlers.\n */\nexport function defineChannel<TMessage = unknown>(\n handler: ChannelHandler<TMessage>,\n): ChannelHandler<TMessage> {\n return handler\n}\n\n/**\n * T5a.2 Phase F slice 2/3 — Web-Standards channel handler.\n *\n * Mirror of `ChannelHandler<TMessage>` for the Web `Request` shape.\n * `onSubscribe` receives `request: Request` instead of `req: IncomingMessage`\n * — the rest of the surface (onMessage, onUnsubscribe) is shape-agnostic\n * (WebSocketLike is already Web-standards-compatible per `define-websocket.ts`).\n *\n * Per `docs/plans/t5a2-incoming-message-to-request-shape-refactor-plan.md`\n * v1.0 § Phase F.\n *\n * **Architectural note:** WebSocket upgrade semantics differ across\n * runtimes:\n * - Node: `WebSocketServer.handleUpgrade(req, socket, head, cb)` —\n * hands you `req: IncomingMessage` at the upgrade handshake.\n * - CF Workers: `new WebSocketPair()` + `request.headers` (the upgrade\n * handshake IS a Web Request) — hands you `request: Request`.\n * - Bun: `server.upgrade(request, { data })` — same Web Request shape.\n * - Deno: `Deno.upgradeWebSocket(request)` — same Web Request shape.\n *\n * Channel handlers targeting CF/Bun/Deno use `WebChannelHandler`; legacy\n * Node consumers stay on `ChannelHandler`. Cross-runtime channels ship\n * both shapes.\n */\nexport interface WebChannelHandler<TMessage = unknown> {\n onSubscribe?: (ws: WebSocketLike, room: string, request: Request) => void\n onMessage?: (ws: WebSocketLike, room: string, data: TMessage) => void\n onUnsubscribe?: (ws: WebSocketLike, room: string) => void\n}\n\n/**\n * Web-Standards `defineChannel` sibling. Identity function — provides\n * type inference for Web channel handlers.\n */\nexport function defineWebChannel<TMessage = unknown>(\n handler: WebChannelHandler<TMessage>,\n): WebChannelHandler<TMessage> {\n return handler\n}\n","import type { TheoPlugin } from '../plugin-types.js'\n\n/**\n * Identity function for defining a Theo plugin.\n *\n * **Note:** Prefer `definePlugin` (shorter, canonical name per ADR-0008 D6).\n * Both functions are identical — `defineTheoPlugin` is kept as an alias for\n * existing in-tree consumers without forcing a migration sweep.\n */\nexport function defineTheoPlugin(plugin: TheoPlugin): TheoPlugin {\n return plugin\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,229 +0,0 @@
|
|
|
1
|
-
import { a as AgentErrorEvent, A as AgentEvent } from '../../agent-events-DosDXkSV.js';
|
|
2
|
-
export { b as AgentMessageEvent, f as AgentRunErrorCode, c as AgentThinkingEvent, d as AgentToolCallEvent, e as AgentToolResultEvent } from '../../agent-events-DosDXkSV.js';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Item #4 — `streamAgentRun`
|
|
6
|
-
*
|
|
7
|
-
* Adapter that consumes the `@theokit/sdk` `Run.stream()` async generator
|
|
8
|
-
* (SDKMessage variants) and yields TheoKit `AgentEvent`s suitable for SSE
|
|
9
|
-
* via `defineAgentEndpoint`. One line at the consumer side:
|
|
10
|
-
*
|
|
11
|
-
* ```ts
|
|
12
|
-
* const run = await agent.send(message)
|
|
13
|
-
* yield* streamAgentRun(run)
|
|
14
|
-
* ```
|
|
15
|
-
*
|
|
16
|
-
* Mapping table (SDK → AgentEvent):
|
|
17
|
-
*
|
|
18
|
-
* | SDK message | AgentEvent yielded |
|
|
19
|
-
* |---|---|
|
|
20
|
-
* | assistant.content[].type==='text' | { type: 'message', content } |
|
|
21
|
-
* | assistant.content[].type==='tool_use'| (none — covered by tool_call below) |
|
|
22
|
-
* | tool_call(status='running') | { type: 'tool_call', name, args, id } |
|
|
23
|
-
* | tool_call(status='completed') | { type: 'tool_result', name, data, id }|
|
|
24
|
-
* | tool_call(status='error') | { type: 'error', message, id } |
|
|
25
|
-
* | run.wait() status==='error' | { type: 'error', message } |
|
|
26
|
-
* | run.wait() status==='cancelled' | (none — cancel ≠ error) |
|
|
27
|
-
* | system / user / thinking / status / | (none — internal SDK telemetry) |
|
|
28
|
-
* | task / request / object_delta | |
|
|
29
|
-
*
|
|
30
|
-
* Abort semantics: when the consumer calls `generator.return()` on the
|
|
31
|
-
* outer `defineAgentEndpoint` generator, `streamAgentRun` exits the
|
|
32
|
-
* `for await` loop and does NOT call `run.wait()`. Cleanup of in-flight
|
|
33
|
-
* SDK resources is the SDK consumer's responsibility (`run.cancel()`).
|
|
34
|
-
*/
|
|
35
|
-
/**
|
|
36
|
-
* Local mirror of the SDK's `Run` interface — only the surfaces we consume.
|
|
37
|
-
* The message contract is minimal (just `{ type: string }`) so the SDK's
|
|
38
|
-
* `SDKMessage` discriminated union IS structurally assignable without any
|
|
39
|
-
* cast at the consumer site (covariant — TS accepts the SDK's typed message
|
|
40
|
-
* as the wider `{ type: string }`). Property access inside `streamAgentRun`
|
|
41
|
-
* narrows via runtime type guards.
|
|
42
|
-
*
|
|
43
|
-
* `import type` from `@theokit/sdk` would couple TheoKit to the SDK at type-
|
|
44
|
-
* resolution time even for consumers who never use the agent surface.
|
|
45
|
-
*/
|
|
46
|
-
interface AgentRunLike {
|
|
47
|
-
stream: () => AsyncIterable<{
|
|
48
|
-
type: string;
|
|
49
|
-
}>;
|
|
50
|
-
wait: () => Promise<AgentRunResult>;
|
|
51
|
-
}
|
|
52
|
-
/**
|
|
53
|
-
* Re-exported as a convenience for test fixtures. Production code typically
|
|
54
|
-
* passes an SDK `Run` directly (structural match via `{ type: string }`).
|
|
55
|
-
*/
|
|
56
|
-
type AgentRunStreamMessage = {
|
|
57
|
-
type: 'assistant';
|
|
58
|
-
message: {
|
|
59
|
-
role: 'assistant';
|
|
60
|
-
content: {
|
|
61
|
-
type: string;
|
|
62
|
-
text?: string;
|
|
63
|
-
}[];
|
|
64
|
-
};
|
|
65
|
-
} | {
|
|
66
|
-
type: 'tool_call';
|
|
67
|
-
name: string;
|
|
68
|
-
status: 'running' | 'completed' | 'error';
|
|
69
|
-
args?: unknown;
|
|
70
|
-
result?: unknown;
|
|
71
|
-
call_id: string;
|
|
72
|
-
} | {
|
|
73
|
-
type: string;
|
|
74
|
-
[k: string]: unknown;
|
|
75
|
-
};
|
|
76
|
-
/** Subset of the SDK `RunResult` we consume. */
|
|
77
|
-
interface AgentRunResult {
|
|
78
|
-
status: 'finished' | 'error' | 'cancelled';
|
|
79
|
-
error?: {
|
|
80
|
-
message: string;
|
|
81
|
-
code?: string;
|
|
82
|
-
cause?: unknown;
|
|
83
|
-
};
|
|
84
|
-
}
|
|
85
|
-
/**
|
|
86
|
-
* Map an SDK error to the AgentErrorEvent shape. Pure function — easy to test.
|
|
87
|
-
*
|
|
88
|
-
* Backward compat (D4): non-AgentRunError throws yield only `message` field
|
|
89
|
-
* (legacy shape); discriminated fields stay `undefined`.
|
|
90
|
-
*
|
|
91
|
-
* Return type is the specific `AgentErrorEvent` (not the union) for ergonomic
|
|
92
|
-
* call-site access — `errorToEvent(err).code` works without narrowing.
|
|
93
|
-
*/
|
|
94
|
-
declare function errorToEvent(err: unknown, id?: string): AgentErrorEvent;
|
|
95
|
-
declare function streamAgentRun(run: AgentRunLike): AsyncGenerator<AgentEvent, void, unknown>;
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Item #5 — `createConversationHistory`
|
|
99
|
-
*
|
|
100
|
-
* Orchestrator that resolves a stable `agentId` from (explicit → session →
|
|
101
|
-
* cookie → fresh UUID), then returns an `@theokit/sdk` `Agent` via
|
|
102
|
-
* `Agent.getOrCreate(agentId, options)`. Conversation turns auto-persist in
|
|
103
|
-
* `<cwd>/.theokit/agents/<agentId>/messages.jsonl` — SDK owns the storage
|
|
104
|
-
* (ADR D1). `MemorySettings` (facts recall layer) is opt-in passthrough via
|
|
105
|
-
* `options.memory` (ADR D2).
|
|
106
|
-
*
|
|
107
|
-
* Security: agentId from cookie/explicit is attacker-controlled. The SDK
|
|
108
|
-
* uses it as a filesystem path component AND we serialize it into Set-Cookie.
|
|
109
|
-
* EC-1 enforces a strict regex `^[a-zA-Z0-9_-]{1,128}$` at every entry point;
|
|
110
|
-
* invalid values fall through (treated as "missing") rather than throw.
|
|
111
|
-
*/
|
|
112
|
-
/**
|
|
113
|
-
* Minimum surface of an SDK Agent that consumers care about post-creation.
|
|
114
|
-
* Structural match — any object with `send` + `dispose` of compatible
|
|
115
|
-
* shape works. `send` returns a `SdkRunLike` (a Run-shaped object) that
|
|
116
|
-
* `streamAgentRun` can consume. Permissive `unknown` for now; consumers
|
|
117
|
-
* who want stricter types can cast to the SDK's own types.
|
|
118
|
-
*
|
|
119
|
-
* **T5.2 (architecture-cleanup) — DP-7 decision: KEEP (Opt B).** The 5 duck-typed
|
|
120
|
-
* mirrors below were flagged as "over-engineered" by the 2026-05-27 architecture
|
|
121
|
-
* review. Decision: KEEP them because `@theokit/sdk` is `devDependency` (not
|
|
122
|
-
* required at runtime for consumers that don't use agent features). Removing
|
|
123
|
-
* the mirrors would force a hard runtime dep on the SDK, breaking consumers
|
|
124
|
-
* who build TheoKit apps without the agent layer.
|
|
125
|
-
*
|
|
126
|
-
* @kept Defensive duck-type. Do NOT replace with direct SDK type imports unless
|
|
127
|
-
* `@theokit/sdk` is promoted from devDependency to dependency. If promoting,
|
|
128
|
-
* ALSO drop the mirrors (Opt A from the architecture-cleanup plan T5.2).
|
|
129
|
-
*/
|
|
130
|
-
interface SdkRunLike {
|
|
131
|
-
stream: () => AsyncIterable<{
|
|
132
|
-
type: string;
|
|
133
|
-
}>;
|
|
134
|
-
wait: () => Promise<{
|
|
135
|
-
status: 'finished' | 'error' | 'cancelled';
|
|
136
|
-
error?: {
|
|
137
|
-
message: string;
|
|
138
|
-
};
|
|
139
|
-
}>;
|
|
140
|
-
}
|
|
141
|
-
interface SdkAgent {
|
|
142
|
-
send: (message: string, options?: unknown) => Promise<SdkRunLike>;
|
|
143
|
-
dispose: () => Promise<void>;
|
|
144
|
-
}
|
|
145
|
-
/**
|
|
146
|
-
* Phase 2 — structural duck-type of `@theokit/sdk`'s `ConversationStorageAdapter`.
|
|
147
|
-
*
|
|
148
|
-
* D2 (decoupling): we mirror the SDK's shape locally rather than hard-import
|
|
149
|
-
* the SDK type. This lets consumers pass any object matching the structural
|
|
150
|
-
* contract (own implementation, SDK's `FileSystemConversationStorage`,
|
|
151
|
-
* `InMemoryConversationStorage`, or a Postgres/Redis recipe).
|
|
152
|
-
*
|
|
153
|
-
* EC-5 (SHOULD TEST — sync drift detection): the SDK type MUST be assignable
|
|
154
|
-
* to this interface AND vice-versa. A contract test asserts both directions.
|
|
155
|
-
*
|
|
156
|
-
* `unknown` for the message payload avoids coupling to the SDK's `SDKMessage`
|
|
157
|
-
* shape. Real consumers cast at the call site if they need stricter types.
|
|
158
|
-
*/
|
|
159
|
-
interface ConversationStorageLike {
|
|
160
|
-
getMessages(conversationId: string): Promise<readonly unknown[]>;
|
|
161
|
-
appendMessage(conversationId: string, message: unknown): Promise<void>;
|
|
162
|
-
deleteConversation(conversationId: string): Promise<void>;
|
|
163
|
-
listConversationIds?(opts?: {
|
|
164
|
-
limit?: number;
|
|
165
|
-
}): Promise<readonly string[] | undefined>;
|
|
166
|
-
dispose?(): Promise<void>;
|
|
167
|
-
}
|
|
168
|
-
/**
|
|
169
|
-
* Minimum surface of `AgentOptions` accepted by `Agent.getOrCreate`. Forward-
|
|
170
|
-
* compatible: callers pass whatever the SDK supports (memory, tools, etc.).
|
|
171
|
-
*
|
|
172
|
-
* Phase 2 adds typed `conversationStorage` slot. The index signature still
|
|
173
|
-
* passes everything else opaquely.
|
|
174
|
-
*/
|
|
175
|
-
interface SdkAgentOptions {
|
|
176
|
-
apiKey?: string;
|
|
177
|
-
model?: {
|
|
178
|
-
id: string;
|
|
179
|
-
};
|
|
180
|
-
tools?: readonly unknown[];
|
|
181
|
-
memory?: Record<string, unknown>;
|
|
182
|
-
/**
|
|
183
|
-
* Phase 2 (Production-Readiness #1) — pluggable conversation persistence.
|
|
184
|
-
* Default (when omitted): SDK falls back to `FileSystemConversationStorage`.
|
|
185
|
-
* Required for serverless / multi-host deploys.
|
|
186
|
-
*/
|
|
187
|
-
conversationStorage?: ConversationStorageLike;
|
|
188
|
-
[key: string]: unknown;
|
|
189
|
-
}
|
|
190
|
-
interface ConversationHistoryArgs {
|
|
191
|
-
/** Request — for reading the conversation cookie. */
|
|
192
|
-
request: Request | {
|
|
193
|
-
headers?: {
|
|
194
|
-
cookie?: string;
|
|
195
|
-
} | Headers;
|
|
196
|
-
};
|
|
197
|
-
/**
|
|
198
|
-
* Response-like surface that accepts a `Set-Cookie` header. The primitive
|
|
199
|
-
* appends a Set-Cookie line when issuing a new conversation id. If absent,
|
|
200
|
-
* the primitive still reads the existing cookie but cannot issue a new
|
|
201
|
-
* one — useful for read-only contexts.
|
|
202
|
-
*/
|
|
203
|
-
response?: {
|
|
204
|
-
headers: Headers;
|
|
205
|
-
};
|
|
206
|
-
/** Explicit override — wins over session/cookie/uuid (ADR D3 step 1). */
|
|
207
|
-
agentId?: string;
|
|
208
|
-
/** Auth session containing a `conversationId` field — ADR D3 step 2. */
|
|
209
|
-
session?: {
|
|
210
|
-
conversationId?: string;
|
|
211
|
-
} | null;
|
|
212
|
-
/** SDK AgentOptions forwarded to Agent.getOrCreate. apiKey + model required. */
|
|
213
|
-
options: SdkAgentOptions;
|
|
214
|
-
/** Cookie name override. Default: 'theo_conversation'. */
|
|
215
|
-
cookieName?: string;
|
|
216
|
-
/** Cookie max-age in seconds. Default + min: 30 days. Non-positive coerced to default (EC-4). */
|
|
217
|
-
cookieMaxAge?: number;
|
|
218
|
-
}
|
|
219
|
-
interface ConversationHistoryResult {
|
|
220
|
-
/** The SDK Agent, ready to receive `agent.send(message)`. */
|
|
221
|
-
agent: SdkAgent;
|
|
222
|
-
/** The resolved conversation id (useful for logging / debugging). */
|
|
223
|
-
conversationId: string;
|
|
224
|
-
/** True when the id was newly generated (no prior cookie / session). */
|
|
225
|
-
isNew: boolean;
|
|
226
|
-
}
|
|
227
|
-
declare function createConversationHistory(args: ConversationHistoryArgs): Promise<ConversationHistoryResult>;
|
|
228
|
-
|
|
229
|
-
export { AgentErrorEvent, AgentEvent, type AgentRunLike, type AgentRunResult, type AgentRunStreamMessage, type ConversationHistoryArgs, type ConversationHistoryResult, type ConversationStorageLike, type SdkAgent, type SdkAgentOptions, type SdkRunLike, createConversationHistory, errorToEvent, streamAgentRun };
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
__resetSdkForTests,
|
|
3
|
-
__setSdkForTests,
|
|
4
|
-
createConversationHistory,
|
|
5
|
-
errorToEvent,
|
|
6
|
-
streamAgentRun
|
|
7
|
-
} from "../../chunk-2KZQPDYR.js";
|
|
8
|
-
import "../../chunk-EXP56GFQ.js";
|
|
9
|
-
import "../../chunk-DGUM43GV.js";
|
|
10
|
-
export {
|
|
11
|
-
__resetSdkForTests,
|
|
12
|
-
__setSdkForTests,
|
|
13
|
-
createConversationHistory,
|
|
14
|
-
errorToEvent,
|
|
15
|
-
streamAgentRun
|
|
16
|
-
};
|
|
17
|
-
//# sourceMappingURL=index.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
/package/dist/{actions-virtual-module-EIPXX4ZB.js.map → actions-virtual-module-RM7JRY5N.js.map}
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|