theokit 0.40.0 → 0.43.1
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-RNPMHJOM.js → actions-virtual-module-LD3YKWIX.js} +2 -2
- package/dist/{actions-virtual-module-YGKSEIOJ.js → actions-virtual-module-N7GKUS3D.js} +3 -3
- package/dist/{agent-ST6GB3VX.js → agent-U35G3WNE.js} +2 -2
- package/dist/{app-typed-client-YSLQU5HT.js → app-typed-client-6YZRDAPY.js} +3 -3
- package/dist/{app-typed-client-FQPUOL6A.js → app-typed-client-V77SVU2O.js} +2 -2
- package/dist/{build-VAR5EFOU.js → build-BYLQZJT2.js} +2 -2
- package/dist/{chunk-L4CYLM2V.js → chunk-2ZX4J4SX.js} +11 -7
- package/dist/chunk-2ZX4J4SX.js.map +1 -0
- package/dist/{chunk-KLFCJQXU.js → chunk-AXVFVMLM.js} +17 -3
- package/dist/chunk-AXVFVMLM.js.map +1 -0
- package/dist/{chunk-63BUBV5L.js → chunk-E25FBYJP.js} +1 -1
- package/dist/chunk-E25FBYJP.js.map +1 -0
- package/dist/{chunk-YMS6KMKA.js → chunk-FEDSPOY6.js} +2 -2
- package/dist/{chunk-MYDS52BG.js → chunk-IPC5UREF.js} +13 -9
- package/dist/chunk-IPC5UREF.js.map +1 -0
- package/dist/{chunk-EV33DOG6.js → chunk-JQJN6LFK.js} +2 -2
- package/dist/{chunk-HQQADLLD.js → chunk-SPJNVWOK.js} +2 -2
- package/dist/{chunk-SPFMJAFW.js → chunk-UI6FWWOF.js} +2 -2
- package/dist/{chunk-HEDLJJDF.js → chunk-UNLG36Z5.js} +17 -3
- package/dist/{chunk-HEDLJJDF.js.map → chunk-UNLG36Z5.js.map} +1 -1
- package/dist/{chunk-BEQQ6WCK.js → chunk-UO7WS32S.js} +2 -2
- package/dist/{chunk-32GOXYA7.js → chunk-XZ4QD5IN.js} +7 -3
- package/dist/{chunk-32GOXYA7.js.map → chunk-XZ4QD5IN.js.map} +1 -1
- package/dist/cli/index.js +6 -6
- package/dist/client/core.js +1 -1
- package/dist/client/index.js +1 -1
- package/dist/{define-agent-tool-ChaGymol.d.ts → define-agent-tool-cRuSpidb.d.ts} +29 -9
- package/dist/{dev-VAIYPLV6.js → dev-PS2NGT6I.js} +5 -5
- package/dist/{generate-ATHMC4ET.js → generate-5X4LG77M.js} +12 -17
- package/dist/generate-5X4LG77M.js.map +1 -0
- package/dist/index.js +2 -2
- package/dist/{internal-api-ORXPQTEI.js → internal-api-FBFV265J.js} +3 -3
- package/dist/{internal-api-MRTUTAM7.js → internal-api-LFW7R65K.js} +2 -2
- package/dist/{mcp-SMDPAW7T.js → mcp-ML4QK5ON.js} +2 -2
- package/dist/server/agent/index.d.ts +1 -1
- package/dist/server/agent/index.js +2 -2
- package/dist/server/define/index.d.ts +2 -2
- package/dist/server/define/index.js +2 -2
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.js +3 -3
- package/dist/{start-MK3MNSFB.js → start-YLR2SFHU.js} +91 -10
- package/dist/start-YLR2SFHU.js.map +1 -0
- package/dist/vite-plugin/index.js +2 -2
- package/dist/{vite-plugin-WB57H4AT.js → vite-plugin-6VVU6PPC.js} +5 -5
- package/package.json +7 -6
- package/dist/chunk-63BUBV5L.js.map +0 -1
- package/dist/chunk-KLFCJQXU.js.map +0 -1
- package/dist/chunk-L4CYLM2V.js.map +0 -1
- package/dist/chunk-MYDS52BG.js.map +0 -1
- package/dist/generate-ATHMC4ET.js.map +0 -1
- package/dist/start-MK3MNSFB.js.map +0 -1
- /package/dist/{actions-virtual-module-RNPMHJOM.js.map → actions-virtual-module-LD3YKWIX.js.map} +0 -0
- /package/dist/{actions-virtual-module-YGKSEIOJ.js.map → actions-virtual-module-N7GKUS3D.js.map} +0 -0
- /package/dist/{agent-ST6GB3VX.js.map → agent-U35G3WNE.js.map} +0 -0
- /package/dist/{app-typed-client-YSLQU5HT.js.map → app-typed-client-6YZRDAPY.js.map} +0 -0
- /package/dist/{app-typed-client-FQPUOL6A.js.map → app-typed-client-V77SVU2O.js.map} +0 -0
- /package/dist/{build-VAR5EFOU.js.map → build-BYLQZJT2.js.map} +0 -0
- /package/dist/{chunk-YMS6KMKA.js.map → chunk-FEDSPOY6.js.map} +0 -0
- /package/dist/{chunk-EV33DOG6.js.map → chunk-JQJN6LFK.js.map} +0 -0
- /package/dist/{chunk-HQQADLLD.js.map → chunk-SPJNVWOK.js.map} +0 -0
- /package/dist/{chunk-SPFMJAFW.js.map → chunk-UI6FWWOF.js.map} +0 -0
- /package/dist/{chunk-BEQQ6WCK.js.map → chunk-UO7WS32S.js.map} +0 -0
- /package/dist/{dev-VAIYPLV6.js.map → dev-PS2NGT6I.js.map} +0 -0
- /package/dist/{internal-api-MRTUTAM7.js.map → internal-api-FBFV265J.js.map} +0 -0
- /package/dist/{internal-api-ORXPQTEI.js.map → internal-api-LFW7R65K.js.map} +0 -0
- /package/dist/{mcp-SMDPAW7T.js.map → mcp-ML4QK5ON.js.map} +0 -0
- /package/dist/{vite-plugin-WB57H4AT.js.map → vite-plugin-6VVU6PPC.js.map} +0 -0
|
@@ -336,7 +336,7 @@ function parseAgentRequestBody(body) {
|
|
|
336
336
|
}
|
|
337
337
|
return null;
|
|
338
338
|
}
|
|
339
|
-
async function mountAgent(mod, request, apiKey, source = "agent module", csrfMode = "strict") {
|
|
339
|
+
async function mountAgent(mod, request, apiKey, { source = "agent module", csrfMode = "strict", projectRoot } = {}) {
|
|
340
340
|
if (csrfMode !== "off") {
|
|
341
341
|
const csrf = validateCsrfRequest(request);
|
|
342
342
|
if (!csrf.valid && csrfMode === "strict") {
|
|
@@ -360,10 +360,24 @@ async function mountAgent(mod, request, apiKey, source = "agent module", csrfMod
|
|
|
360
360
|
const hitl = buildAgentHitl(compiled);
|
|
361
361
|
const runId = mintRunId();
|
|
362
362
|
return durableUiMessageStreamResponse(
|
|
363
|
-
streamAgentUIMessages2(compiled, apiKey, {
|
|
363
|
+
streamAgentUIMessages2(compiled, apiKey, {
|
|
364
|
+
...input,
|
|
365
|
+
hitl,
|
|
366
|
+
signal: request.signal,
|
|
367
|
+
cwd: resolveDiscoveryCwd(compiled, projectRoot),
|
|
368
|
+
baseDir: resolveSessionBaseDir(projectRoot)
|
|
369
|
+
}),
|
|
364
370
|
{ runId, cache: getRunEventCache() }
|
|
365
371
|
);
|
|
366
372
|
}
|
|
373
|
+
function resolveDiscoveryCwd(compiled, projectRoot) {
|
|
374
|
+
const optedIn = (compiled.settingSources?.length ?? 0) > 0;
|
|
375
|
+
return projectRoot !== void 0 && optedIn ? projectRoot : void 0;
|
|
376
|
+
}
|
|
377
|
+
function resolveSessionBaseDir(projectRoot) {
|
|
378
|
+
if (projectRoot === void 0) return void 0;
|
|
379
|
+
return `${projectRoot.replace(/\/+$/, "")}/.data/agent-sessions`;
|
|
380
|
+
}
|
|
367
381
|
|
|
368
382
|
// src/server/agent/provider-resolver.ts
|
|
369
383
|
var DEFAULT_REGISTRY = [
|
|
@@ -422,4 +436,4 @@ export {
|
|
|
422
436
|
mountAgent,
|
|
423
437
|
resolveProvider
|
|
424
438
|
};
|
|
425
|
-
//# sourceMappingURL=chunk-
|
|
439
|
+
//# sourceMappingURL=chunk-AXVFVMLM.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/server/http/controller-dispatch.ts","../src/server/agent/mount-agent.ts","../src/server/agent/build-agent-streamer.ts","../src/server/agent/approval-registry.ts","../src/server/agent/durable-ui-message-stream-response.ts","../src/server/agent/run-event-cache.ts","../src/server/agent/provider-resolver.ts"],"sourcesContent":["/* eslint-disable security/detect-non-literal-fs-filename --\n * Controller files are walked from the developer's `serverDir/controllers`\n * (a build-time config path), never from HTTP input. No injection vector.\n */\nimport { readdirSync, type Dirent } from 'node:fs'\nimport type { IncomingMessage, ServerResponse } from 'node:http'\nimport { join } from 'node:path'\n\nimport { createDecoratorHandler, isControllerClass, type ServeAgent } from '@theokit/http'\n\nimport { dispatchCsrfWarn } from '../security/csrf-warn-dispatch.js'\nimport { enforceCsrf, type DisallowedConfig } from '../security/csrf.js'\n\nimport { incomingMessageToWebRequest } from './node-request.js'\nimport { sendError } from './send-response.js'\n\n/** A decorator controller constructor (`@Controller` class). */\ntype ControllerClass = new (...args: never[]) => object\n\n/** Loads a controller module by absolute path. In dev this is Vite's `ssrLoadModule`\n * (the Task 1.1 swc transform has already compiled the parameter decorators); tests\n * inject `@theokit/http`'s `loadControllerWithSwc`. */\nexport type ControllerModuleLoader = (absPath: string) => Promise<Record<string, unknown>>\n\n/** A built controller route table exposed as a pure Web-Standard handler. */\nexport interface ControllerDispatcher {\n /** `null` = no controller route matched — the host owns the miss (404 / fall-through). */\n dispatch(request: Request): Promise<Response | null>\n /** Non-executing route probe — true when a controller route owns `method` + `pathname`. */\n matches(method: string, pathname: string): boolean\n}\n\n// State-mutating methods get CSRF, mirroring the file-route pipeline (execute.ts).\nconst CSRF_PROTECTED_METHODS = new Set(['POST', 'PUT', 'PATCH', 'DELETE'])\n\n/** Recursively collect `*.controller.ts` files under `dir` (absolute paths). */\nfunction findControllerFiles(dir: string): string[] {\n const found: string[] = []\n const walk = (current: string): void => {\n let entries: Dirent[]\n try {\n entries = readdirSync(current, { withFileTypes: true })\n } catch {\n return // dir doesn't exist — no controllers\n }\n for (const entry of entries) {\n const full = join(current, entry.name)\n if (entry.isDirectory()) walk(full)\n else if (entry.name.endsWith('.controller.ts')) found.push(full)\n }\n }\n walk(dir)\n return found\n}\n\n/** A discovered controller: its source file + the loaded `@Controller` class. */\nexport interface ControllerModule {\n filePath: string\n cls: ControllerClass\n}\n\n/**\n * Load every `@Controller` class under `controllersDir` via the injected loader,\n * keeping each class paired with its source file (needed by the typed-client\n * codegen to emit `import type { X } from '<file>'`). Non-controller exports are\n * ignored (`isControllerClass` — reused from @theokit/http).\n */\nexport async function scanControllerModules(\n controllersDir: string,\n loadModule: ControllerModuleLoader,\n): Promise<ControllerModule[]> {\n const files = findControllerFiles(controllersDir)\n const modules: ControllerModule[] = []\n for (const filePath of files) {\n const mod = await loadModule(filePath)\n for (const exported of Object.values(mod)) {\n if (typeof exported === 'function' && isControllerClass(exported)) {\n modules.push({ filePath, cls: exported as ControllerClass })\n }\n }\n }\n return modules\n}\n\n/** Load every `@Controller` class under `controllersDir` (classes only). */\nexport async function scanControllers(\n controllersDir: string,\n loadModule: ControllerModuleLoader,\n): Promise<ControllerClass[]> {\n const modules = await scanControllerModules(controllersDir, loadModule)\n return modules.map((m) => m.cls)\n}\n\n/**\n * Scan `controllersDir` and build a Web-Standard dispatcher over the decorator\n * controllers found. Returns `null` when the directory has no controllers, so\n * the host can skip the controller path entirely (zero cost for routes-only apps).\n *\n * Dispatch REUSES @theokit/http's `createDecoratorHandler` (match + `@Param`\n * binding + `@Body` validation + Response building) — never re-implemented (ADR-1).\n */\nexport async function createControllerDispatcher(opts: {\n controllersDir: string\n loadModule: ControllerModuleLoader\n /** M47 — serves `@Expose`-bound agent routes (theo supplies a `mountAgent`-backed impl). */\n serveAgent?: ServeAgent\n}): Promise<ControllerDispatcher | null> {\n const classes = await scanControllers(opts.controllersDir, opts.loadModule)\n if (classes.length === 0) return null\n const handle = createDecoratorHandler({ controllers: classes, serveAgent: opts.serveAgent })\n return {\n dispatch: (request) => handle(request),\n matches: (method, pathname) => handle.matches(method, pathname),\n }\n}\n\n/** Write a buffered Web `Response` (controllers never stream) to a Node response. */\nasync function writeControllerResponse(res: ServerResponse, response: Response): Promise<void> {\n const headersBag: Record<string, string> = {}\n for (const [k, v] of response.headers) {\n if (k.toLowerCase() !== 'set-cookie') headersBag[k] = v\n }\n const setCookies = response.headers.getSetCookie()\n if (setCookies.length > 0) res.setHeader('Set-Cookie', setCookies)\n res.writeHead(response.status, headersBag)\n const body = response.body ? await response.text() : ''\n res.end(body || undefined)\n}\n\n/**\n * The `api-middleware` fall-through in one call: scan `controllersDir`, build the\n * dispatcher, and serve the request. Builds a body-ful Web `Request` (`@Body`\n * needs the body; the raw stream is undrained at a route miss) and enforces CSRF\n * with the SAME gate file routes use (parity). Returns `true` when a controller\n * handled it (or CSRF blocked it), `false` when there are no controllers OR none\n * matched (the host continues to its own 404). Built per-miss so controller edits\n * reflect via HMR.\n */\nexport async function dispatchControllerRequest(args: {\n controllersDir: string\n loadModule: ControllerModuleLoader\n req: IncomingMessage\n res: ServerResponse\n csrfMode: 'off' | 'warn' | 'strict'\n disallowed?: DisallowedConfig\n requestId: string\n /** M47 — serves `@Expose`-bound agent routes (mountAgent-backed); omit for routes-only apps. */\n serveAgent?: ServeAgent\n}): Promise<boolean> {\n const { req, res, csrfMode, disallowed, requestId } = args\n const dispatcher = await createControllerDispatcher({\n controllersDir: args.controllersDir,\n loadModule: args.loadModule,\n serveAgent: args.serveAgent,\n })\n if (!dispatcher) return false\n\n const method = (req.method ?? 'GET').toUpperCase()\n const webRequest = incomingMessageToWebRequest(req)\n const pathname = new URL(webRequest.url).pathname\n\n // CSRF parity: enforce ONLY when a protected-method controller route actually\n // owns this path (probe with the non-executing matcher — never double-dispatch,\n // which would run the handler + its side effects). An unrouted path falls\n // through to the host's own 404, not a 403.\n if (CSRF_PROTECTED_METHODS.has(method) && dispatcher.matches(method, pathname)) {\n const decision = enforceCsrf(\n req,\n csrfMode,\n { warn: dispatchCsrfWarn, path: req.url },\n disallowed,\n )\n if (!decision.allow) {\n sendError(\n res,\n 'CSRF_INVALID',\n decision.reason ?? 'CSRF check failed',\n 403,\n undefined,\n requestId,\n )\n return true\n }\n }\n\n const response = await dispatcher.dispatch(webRequest)\n if (response === null) return false\n await writeControllerResponse(res, response)\n return true\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, resolveEnabledSkills, streamAgentUIMessages } from '@theokit/agents'\n\nimport { validateCsrfRequest, type CsrfMode } from '../security/csrf.js'\n\nimport { buildAgentHitl } from './build-agent-streamer.js'\nimport { durableUiMessageStreamResponse } from './durable-ui-message-stream-response.js'\nimport { getRunEventCache, mintRunId } from './run-event-cache.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/** Non-runtime options for {@link mountAgent} (grouped so the call stays ≤ 5 params). */\nexport interface MountAgentOptions {\n /** Labels a fail-fast `AgentDefinitionError` (the agent file path). Default `'agent module'`. */\n source?: string\n /** CSRF posture at this boundary. Default `'strict'`; `'off'` when a controller already gated (G5). */\n csrfMode?: CsrfMode\n /**\n * theokit-file-based-config (EC-1) — the framework-resolved app root. When the agent opted into\n * `.theokit/` file-based config (`.settingSources([...])`), discovery points here, NOT `process.cwd()`.\n */\n projectRoot?: string\n}\n\n/**\n * Mount a loaded agent module as a `Response`. `apiKey` is resolved by the caller\n * (`resolveProvider`). See {@link MountAgentOptions} for the labeling / CSRF / app-root knobs.\n */\nexport async function mountAgent(\n mod: unknown,\n request: Request,\n apiKey: string,\n { source = 'agent module', csrfMode = 'strict', projectRoot }: MountAgentOptions = {},\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 // M13 — resolve a per-request skills selector (from `defineAgent({ skills: (ctx) => [...] })`)\n // against the M7 run-context, setting `skills.enabled` before the SDK runs. `undefined` ⇒ the\n // SDK enables every discovered skill. `compiled` is fresh per request, so mutation is safe.\n if (compiled.skillsResolver) {\n const enabled = await resolveEnabledSkills(compiled.skillsResolver, compiled.runContext ?? {})\n if (enabled !== undefined) compiled.skills = { enabled, autoInject: true }\n }\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 // When the agent has @HumanInTheLoop-gated tools (M4), wire the pause: the plugin's `awaitApproval`\n // registers a pending approval in the shared registry (the Promise that PAUSES the run); the\n // approve route (`handleAgentApproval`) resolves it. No gated tools ⇒ the M2 stream path unchanged.\n // Extracted to `build-agent-streamer.ts` (M39 / DRY — the thread routes reuse the same wiring).\n const hitl = buildAgentHitl(compiled)\n\n // M37 (ADR-0046) — mint a transport runId + stream through the durable layer:\n // each SSE frame is cached + `id:`-tagged so a dropped client can reconnect\n // (or a second client observe) via `GET /api/agents/<name>/runs/<runId>/stream`.\n // The runId is surfaced in the `x-theokit-run-id` response header.\n const runId = mintRunId()\n return durableUiMessageStreamResponse(\n streamAgentUIMessages(compiled, apiKey, {\n ...input,\n hitl,\n signal: request.signal,\n cwd: resolveDiscoveryCwd(compiled, projectRoot),\n baseDir: resolveSessionBaseDir(projectRoot),\n }),\n { runId, cache: getRunEventCache() },\n )\n}\n\n/**\n * theokit-file-based-config (EC-1) — resolve the `.theokit/` discovery cwd. When the agent opted into\n * file-based config (`.settingSources([...])`), point discovery at the framework-resolved app root,\n * NOT `process.cwd()` (which is not guaranteed to be the app root). No opt-in ⇒ `undefined` (byte-unchanged).\n */\nfunction resolveDiscoveryCwd(\n compiled: { settingSources?: readonly unknown[] },\n projectRoot: string | undefined,\n): string | undefined {\n const optedIn = (compiled.settingSources?.length ?? 0) > 0\n return projectRoot !== undefined && optedIn ? projectRoot : undefined\n}\n\n/**\n * SDK 4.0 (SE40) — resolve the root of the native `.jsonl` session transcript. Unlike `.theokit/`\n * discovery, persistence is NOT gated on a file-based-config opt-in — every agent session persists.\n * Root it under the app's `.data/` (git-ignored, EC-2), kept OUT of the `.theokit/` config dir so a\n * `projects/` transcript subtree never collides with `settingSources` discovery. Absent `projectRoot`\n * ⇒ `undefined` (the SDK default `~/.theokit` applies).\n */\nexport function resolveSessionBaseDir(projectRoot: string | undefined): string | undefined {\n if (projectRoot === undefined) return undefined\n // Web-Standards discipline (R3a/G8): no `node:path` in `server/` — append with `/` (the SDK\n // normalizes the separator). Strip a trailing slash on `projectRoot` so we never emit `//`.\n return `${projectRoot.replace(/\\/+$/, '')}/.data/agent-sessions`\n}\n","/**\n * M39 (ADR-0048) — shared agent-run streamer builders, extracted from\n * `mount-agent.ts` (DRY / G12) so the thread routes drive the SAME SDK run path\n * as the plain POST. Reuses SDK primitives (`compileAgentModule` +\n * `streamAgentUIMessages`) — it does NOT reimplement the loop (G2).\n */\n\nimport {\n compileAgentModule,\n type HumanInTheLoopOptions,\n resolveEnabledSkills,\n streamAgentUIMessages,\n} from '@theokit/agents'\nimport type { UIMessageChunk } from 'ai'\n\nimport { getApprovalRegistry } from './approval-registry.js'\n\ntype Compiled = ReturnType<typeof compileAgentModule>\n\n/**\n * Build the HITL wiring for a compiled agent: gated tools register a pending\n * approval in the shared registry (the Promise that PAUSES the run). `undefined`\n * when the agent has no `@HumanInTheLoop`-gated tools. Extracted verbatim from\n * `mountAgent` so both the plain POST and the thread routes wire HITL identically.\n */\nexport function buildAgentHitl(compiled: Compiled) {\n const gated = compiled.hitl\n if (gated === undefined || gated.size === 0) return undefined\n const registry = getApprovalRegistry()\n return {\n gated,\n awaitApproval: (approvalId: string, opts: HumanInTheLoopOptions, toolName: string) =>\n registry.register(approvalId, {\n timeoutMs: opts.timeout ?? 300_000,\n onTimeout: opts.onTimeout ?? 'abort',\n toolName,\n question: opts.question,\n ...(opts.payloadSchema !== undefined ? { payloadSchema: opts.payloadSchema } : {}),\n }),\n }\n}\n\n/**\n * A `startRun(sessionId, message)` closure for the thread dispatcher. Per run it\n * compiles the module fresh (skills resolution mutates `compiled`), resolves\n * per-request skills, builds HITL, and streams. Headless — no request signal (a\n * thread continuation runs to completion into the durable cache, not to a client).\n */\nexport function makeThreadStartRun(\n mod: unknown,\n apiKey: string,\n source: string,\n): (sessionId: string, message: string) => AsyncIterable<UIMessageChunk> {\n return (sessionId, message) =>\n (async function* () {\n const compiled = compileAgentModule(mod, source)\n if (compiled.skillsResolver) {\n const enabled = await resolveEnabledSkills(\n compiled.skillsResolver,\n compiled.runContext ?? {},\n )\n if (enabled !== undefined) compiled.skills = { enabled, autoInject: true }\n }\n const hitl = buildAgentHitl(compiled)\n yield* streamAgentUIMessages(compiled, apiKey, { message, sessionId, hitl })\n })()\n}\n","/**\n * M4 (theokit-ai-first) — the in-process HITL approval registry.\n *\n * The HITL plugin's `awaitApproval` calls `register(approvalId)` and awaits the returned Promise\n * (this is what genuinely PAUSES the SDK run — the SDK `pre_tool_call` hook is awaited). The\n * approve route calls `resolve(approvalId, approved)` to settle it. A per-approval timeout settles\n * the Promise deterministically per the `@HumanInTheLoop` `onTimeout` policy so a hung approval\n * never leaks the paused stream.\n *\n * Single-process contract (ADR 0038 / plan Drawback 2): a multi-instance deploy needs a shared\n * registry — the interface is injectable so a durable impl (Redis, etc.) slots in without touching\n * the harness. We do NOT build a durable store now (YAGNI).\n */\n// The timeout policy vocabulary is owned by the `@HumanInTheLoop` decorator (DRY / G12) — reuse it\n// rather than re-declaring the union, so the two can never drift.\nimport type { TimeoutAction } from '@theokit/agents'\n\nexport type { TimeoutAction }\n\nexport interface RegisterOptions {\n /** Milliseconds before the approval auto-settles per `onTimeout`. */\n timeoutMs: number\n /**\n * What a timeout means. Only `'proceed'` auto-approves; `'abort'` and `'retry'` both deny — the\n * registry does NOT implement retry semantics (a timed-out `'retry'` is a deny, not a re-prompt).\n */\n onTimeout: TimeoutAction\n /** M14 — the gated tool name, surfaced by `list()` (optional; absent for legacy callers). */\n toolName?: string\n /** M14 — the approval question, surfaced by `list()` (optional). */\n question?: string\n /**\n * M20 — an optional JSON-schema descriptor of the custom payload the approver may attach. Carried\n * verbatim into `list()` + the `approval_required` event so the UI knows what to collect. Kept as\n * a plain JSON object (not a live Zod schema) so the registry stays serializable and SDK-free.\n */\n payloadSchema?: Record<string, unknown>\n}\n\n/**\n * M20 — a settled HITL decision. `approved` is the allow/deny bit (backward-compatible with the\n * legacy boolean); `reason` + `payload` are the optional approver-attached extras that surface to\n * the model (on denial, via the veto message) and to the app/UI.\n */\nexport interface ApprovalDecision {\n approved: boolean\n reason?: string\n payload?: unknown\n}\n\n/** M14 — a pending approval as surfaced by {@link ApprovalRegistry.list}. */\nexport interface PendingApproval {\n approvalId: string\n toolName?: string\n question?: string\n /** Epoch millis when the pending approval auto-settles (registeredAt + timeoutMs). */\n expiresAt: number\n /** M20 — the declared custom-payload schema, if the gated tool declares one. */\n payloadSchema?: Record<string, unknown>\n}\n\nexport interface ApprovalRegistry {\n /**\n * Register a pending approval; the returned Promise settles with the full {@link ApprovalDecision}\n * on `resolve` or timeout (M20 — was a bare boolean pre-M20).\n */\n register(approvalId: string, opts: RegisterOptions): Promise<ApprovalDecision>\n /**\n * Settle a pending approval. Accepts a full {@link ApprovalDecision} OR a bare boolean (coerced to\n * `{ approved }` — backward-compatible). Returns false if the id is unknown or already settled.\n */\n resolve(approvalId: string, decision: boolean | ApprovalDecision): boolean\n /** M14 — list the currently-pending approvals (process-wide; single-process contract). */\n list(): PendingApproval[]\n}\n\ninterface Pending {\n settle: (decision: ApprovalDecision) => void\n timer: ReturnType<typeof setTimeout>\n info: PendingApproval\n}\n\n/**\n * The one process-wide registry the stream mount (`mountAgent`) and the approve route share.\n *\n * The in-process impl holds LIVE Promise resolvers in memory — the approval a request awaits and\n * the approval the route resolves MUST be the same object, so a single instance per process is not\n * a convenience but a correctness requirement. Lazily created; a durable/multi-instance deploy\n * swaps this accessor for a shared-store impl (ADR 0038 / plan Drawback 2) without touching callers.\n * Tests use {@link createInProcessApprovalRegistry} directly — never this singleton.\n */\nlet serverRegistry: ApprovalRegistry | undefined\nexport function getApprovalRegistry(): ApprovalRegistry {\n serverRegistry ??= createInProcessApprovalRegistry()\n return serverRegistry\n}\n\nexport function createInProcessApprovalRegistry(): ApprovalRegistry {\n const pending = new Map<string, Pending>()\n\n return {\n register(approvalId, opts) {\n return new Promise<ApprovalDecision>((resolve) => {\n const settle = (decision: ApprovalDecision): void => {\n const entry = pending.get(approvalId)\n if (!entry) return\n clearTimeout(entry.timer)\n pending.delete(approvalId)\n resolve(decision)\n }\n // 'proceed' → allow on timeout; 'abort'/'retry' → deny on timeout.\n const timer = setTimeout(() => {\n settle({ approved: opts.onTimeout === 'proceed' })\n }, opts.timeoutMs)\n const info: PendingApproval = {\n approvalId,\n toolName: opts.toolName,\n question: opts.question,\n expiresAt: Date.now() + opts.timeoutMs,\n ...(opts.payloadSchema !== undefined ? { payloadSchema: opts.payloadSchema } : {}),\n }\n pending.set(approvalId, { settle, timer, info })\n })\n },\n resolve(approvalId, decision) {\n const entry = pending.get(approvalId)\n if (!entry) return false\n // M20 — coerce the legacy bare-boolean form to a decision object.\n entry.settle(typeof decision === 'boolean' ? { approved: decision } : decision)\n return true\n },\n list() {\n return [...pending.values()].map((p) => p.info)\n },\n }\n}\n","import type { UIMessageChunk } from 'ai'\n\nimport type { RunEventCache } from './run-event-cache.js'\n\n/**\n * M37 (ADR-0046) — the DURABLE variant of `uiMessageStreamResponse`. Same\n * `UIMessageStream` wire (`useChat` still consumes it), plus three additions so\n * a dropped client can reconnect without missing chunks:\n *\n * 1. each SSE frame gains a monotonic `id: <seq>\\n` line (SSE-native — the\n * browser echoes it as `Last-Event-ID` on reconnect, ADR-0046 D3);\n * 2. every frame is `append`ed to the {@link RunEventCache} keyed by `runId`\n * so the reconnect endpoint can replay it;\n * 3. the response carries `x-theokit-run-id: <runId>` so the client learns the\n * reconnect key (ADR-0046 D2).\n *\n * Fail-clear (error-handling.md): if the source aborts mid-stream, the run is\n * still `end`ed in the cache and `[DONE]` is flushed — never left hanging.\n */\n\n/** The UIMessageStream SSE base headers — shared by the encoder + reconnect handler (DRY). */\nexport const SSE_BASE_HEADERS = {\n 'content-type': 'text/event-stream',\n 'x-vercel-ai-ui-message-stream': 'v1',\n} as const\n\n/** Header the client reads to obtain the reconnect key. */\nexport const RUN_ID_HEADER = 'x-theokit-run-id'\n\nexport const SSE_DONE_FRAME = 'data: [DONE]\\n\\n'\n\n/** Format one SSE frame with its sequence id (shared by the encoder + reconnect replay). */\nexport function formatSseFrame(seq: number, data: string): string {\n return `id: ${seq}\\ndata: ${data}\\n\\n`\n}\n\n/** UTF-8 encode an SSE frame for a `ReadableStream<Uint8Array>` (shared). */\nexport function encodeSse(text: string): Uint8Array {\n return new TextEncoder().encode(text)\n}\n\nexport interface DurableStreamDeps {\n readonly runId: string\n readonly cache: RunEventCache\n}\n\nexport function durableUiMessageStreamResponse(\n chunks: AsyncIterable<UIMessageChunk>,\n deps: DurableStreamDeps,\n): Response {\n const { runId, cache } = deps\n const stream = new ReadableStream<Uint8Array>({\n async start(controller) {\n try {\n for await (const chunk of chunks) {\n const data = JSON.stringify(chunk)\n const seq = cache.append(runId, data)\n controller.enqueue(encodeSse(formatSseFrame(seq, data)))\n }\n } catch {\n // Source aborted mid-stream. The upstream translator owns error\n // semantics (surfaces failures as chunks + closes gracefully); this\n // transport guarantees only a terminated, cache-ended stream.\n } finally {\n cache.end(runId)\n controller.enqueue(encodeSse(SSE_DONE_FRAME))\n controller.close()\n }\n },\n })\n return new Response(stream, {\n headers: { ...SSE_BASE_HEADERS, [RUN_ID_HEADER]: runId },\n })\n}\n","/**\n * M37 (ADR-0046) — the durable event cache behind resumable agent streams.\n *\n * As an agent turn streams over SSE, each `UIMessageChunk` frame is `append`ed\n * here keyed by a transport `runId`. A client that drops and reconnects (or a\n * second client that observes) `attach`es to the run: it replays the frames it\n * missed (`seq > Last-Event-ID`) and then follows the live tail. This is the\n * TRANSPORT half of durable agents (ADR-0040/0044) — it caches serialized\n * frames, it does NOT run the agent loop.\n *\n * Single-process by default (an in-memory `Map`, same contract as the HITL\n * `approval-registry`). A persistent backend (Redis / `ConversationStorageAdapter`)\n * plugs in behind {@link RunEventCache} for multi-instance deploys — it is NOT\n * shipped in core (ADR-0046 D4: no broker in core).\n */\n\n/** One recorded SSE frame: its monotonic sequence id + the serialized `data:` payload. */\nexport interface CachedFrame {\n readonly seq: number\n readonly data: string\n}\n\n/** The result of {@link RunEventCache.attach}. */\nexport interface AttachResult {\n /** False when `runId` is unknown (never started, or evicted). */\n readonly known: boolean\n /** Frames with `seq > afterSeq`, in order — replay these before the live tail. */\n readonly replay: readonly CachedFrame[]\n /** True when the run already terminated: `replay` is complete, no live frames follow. */\n readonly ended: boolean\n /** Detach the live listener (idempotent). */\n readonly unsubscribe: () => void\n}\n\n/**\n * A per-`runId` ordered frame buffer with live fan-out. Implemented in-memory by\n * default; swap a persistent backend for multi-instance deploys.\n */\nexport interface RunEventCache {\n /**\n * M39 — synchronously register `runId` as a live (frame-less) run so `has()` is\n * true and a subscriber can `attach()` BEFORE the first frame. Idempotent.\n * Closes the race where a thread's active run is resolvable in the registry but\n * not yet in the cache (a headless pump appends its first frame asynchronously).\n */\n begin(runId: string): void\n /** Record a frame for `runId`, returning its assigned monotonic `seq`. Notifies live listeners. */\n append(runId: string, data: string): number\n /** Mark `runId` terminated: notify listeners, then schedule bounded eviction. Idempotent. */\n end(runId: string): void\n /** Whether `runId` is currently cached (started + not yet evicted). */\n has(runId: string): boolean\n /**\n * Atomically snapshot the frames after `afterSeq` AND (when the run is still\n * live) register `onFrame`/`onEnd` in the SAME synchronous tick — so no frame\n * slips between replay and subscribe, and none is duplicated.\n */\n attach(\n runId: string,\n afterSeq: number,\n onFrame: (frame: CachedFrame) => void,\n onEnd: () => void,\n ): AttachResult\n}\n\ninterface Listener {\n readonly onFrame: (frame: CachedFrame) => void\n readonly onEnd: () => void\n}\n\ninterface RunBuffer {\n readonly frames: CachedFrame[]\n ended: boolean\n readonly listeners: Set<Listener>\n evictTimer?: ReturnType<typeof setTimeout>\n}\n\nconst DEFAULT_EVICT_AFTER_MS = 5 * 60_000 // 5 min after end (parity with approval-registry)\n\nconst NOOP_UNSUBSCRIBE = (): void => {\n // No live listener was registered (unknown or already-ended run).\n}\n\nexport interface RunEventCacheOptions {\n /** Milliseconds after `end()` before a run's buffer is evicted. Default 5 min. */\n readonly evictAfterMs?: number\n}\n\nexport function createInMemoryRunEventCache(opts: RunEventCacheOptions = {}): RunEventCache {\n const evictAfterMs = opts.evictAfterMs ?? DEFAULT_EVICT_AFTER_MS\n const runs = new Map<string, RunBuffer>()\n\n function getOrCreate(runId: string): RunBuffer {\n let buf = runs.get(runId)\n if (buf === undefined) {\n buf = { frames: [], ended: false, listeners: new Set() }\n runs.set(runId, buf)\n }\n return buf\n }\n\n return {\n begin(runId) {\n getOrCreate(runId)\n },\n\n append(runId, data) {\n const buf = getOrCreate(runId)\n const seq = buf.frames.length\n const frame: CachedFrame = { seq, data }\n buf.frames.push(frame)\n // Notify live listeners (a throwing listener must not break the others).\n for (const l of buf.listeners) {\n try {\n l.onFrame(frame)\n } catch {\n /* a broken subscriber never breaks the producer or its peers */\n }\n }\n return seq\n },\n\n has(runId) {\n return runs.has(runId)\n },\n\n end(runId) {\n const buf = runs.get(runId)\n if (buf === undefined || buf.ended) return\n buf.ended = true\n for (const l of buf.listeners) {\n try {\n l.onEnd()\n } catch {\n /* isolate a broken subscriber */\n }\n }\n buf.listeners.clear()\n buf.evictTimer = setTimeout(() => runs.delete(runId), evictAfterMs)\n // Never keep the process alive just for an eviction timer.\n buf.evictTimer.unref()\n },\n\n attach(runId, afterSeq, onFrame, onEnd) {\n const buf = runs.get(runId)\n if (buf === undefined) {\n return { known: false, replay: [], ended: false, unsubscribe: NOOP_UNSUBSCRIBE }\n }\n // Synchronous snapshot — no `await` before the subscribe below, so no\n // append can interleave (single-threaded): no gap, no dup.\n const replay = buf.frames.filter((f) => f.seq > afterSeq)\n if (buf.ended) {\n return { known: true, replay, ended: true, unsubscribe: NOOP_UNSUBSCRIBE }\n }\n const listener: Listener = { onFrame, onEnd }\n buf.listeners.add(listener)\n return {\n known: true,\n replay,\n ended: false,\n unsubscribe: () => {\n buf.listeners.delete(listener)\n },\n }\n },\n }\n}\n\n/**\n * Process-wide singleton — the SSE encoder (`durableUiMessageStreamResponse`) and\n * the reconnect endpoint MUST share ONE cache. Lazily created; a multi-instance\n * deploy swaps this accessor for a shared-store impl (ADR-0046 D4) without\n * touching callers. Tests use {@link createInMemoryRunEventCache} directly.\n */\nlet serverCache: RunEventCache | undefined\nexport function getRunEventCache(): RunEventCache {\n serverCache ??= createInMemoryRunEventCache()\n return serverCache\n}\n\n/** Mint a stable transport `runId` (the reconnect key, ADR-0046 D2). */\nexport function mintRunId(): string {\n return `run-${crypto.randomUUID()}`\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"],"mappings":"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","names":["compileAgentModule","resolveEnabledSkills","streamAgentUIMessages","registry","compileAgentModule","resolveEnabledSkills","streamAgentUIMessages"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/server/define/define-agent-tool.ts"],"sourcesContent":["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 treats as a tool failure and surfaces to the model as a tool\n * error (the SDK owns the 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 */\n/**\n * M48 — the tool-handler run context, a standalone mirror of `@theokit/sdk`'s tool `ctx` (kept in\n * sync by the `tests/type/custom-tool-mirror.test-d.ts` type gate). `context` (M7) is the object\n * supplied once at agent/run level; `messages` (SE12) is a read-only transcript projection of the\n * current turn; `threadId` (#119) is the run's session identity — so a stateful tool shared across\n * sessions can scope its state per session instead of leaking it. Standalone (no `@theokit/sdk`\n * import) because the SDK is an optional peer.\n */\nexport interface ToolHandlerContext {\n signal?: AbortSignal\n context?: unknown\n messages?: readonly ToolContextMessage[]\n threadId?: string\n}\n\n/** SE12 — a read-only, text-only projection of a turn message (mirror of the SDK type). */\nexport interface ToolContextMessage {\n role: 'system' | 'user' | 'assistant'\n content: string\n}\n\nexport interface CustomTool {\n name: string\n description: string\n inputSchema: Record<string, unknown>\n /**\n * Returns `string` (theokit's `defineAgentTool` maps every result to the model-visible string).\n * The SDK's `CustomTool` handler return is WIDER (`string | ToolResultContentBlock[]`, SE7); a\n * `string`-returning handler is a valid subtype the SDK accepts — the type gate asserts\n * `mirror ⊆ SDK` for the return (covariant) and `mirror === SDK` for the `ctx` param (contravariant,\n * to catch #119 `ctx.threadId` drift). See `tests/type/custom-tool-mirror.test-d.ts`.\n */\n handler: (input: Record<string, unknown>, ctx?: ToolHandlerContext) => string | Promise<string>\n /** M18 — optional per-target formatters for the app's UI/transcript (ignored by the SDK wire). */\n transform?: ToolTransform\n}\n\n/**\n * M18 — per-target formatters. `display` shapes the rich handler result for the UI; `transcript`\n * shapes it for a saved transcript. Applied by {@link applyTransform}, never by the model wire.\n */\nexport interface ToolTransform<R = unknown> {\n display?: (result: R) => unknown\n transcript?: (result: R) => unknown\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, R = string> {\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 /**\n * Handler invoked with the parsed input and, optionally, the run `ctx` (M7). `ctx.context`\n * is the object supplied once at the agent level (`defineAgent({ context })`) or per-run —\n * read it for shared config like `projectRoot` instead of baking it into the factory.\n * `ctx.signal` is the abort signal. Optional so existing one-arg handlers keep working.\n *\n * M18 — the handler may return RICH data `R` (not just a string) when `toModelOutput` is\n * provided to map it to the model-visible string.\n */\n handler: (input: z.infer<T>, ctx?: ToolHandlerContext) => R | Promise<R>\n /**\n * M18 — map the rich handler result `R` to the string the model sees. Required (in practice)\n * when `handler` returns a non-string; absent ⇒ the handler must return a string.\n */\n toModelOutput?: (result: R) => string\n /** M18 — per-target formatters (`display` / `transcript`) for the app, applied by {@link applyTransform}. */\n transform?: ToolTransform<R>\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, R = string>(\n spec: DefineAgentToolSpec<T, R>,\n): 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>, ctx?: ToolHandlerContext): Promise<string> => {\n const parsed = spec.inputSchema.parse(input)\n // M7 — forward the run ctx so the handler can read `ctx.context` (e.g. projectRoot).\n const result = await spec.handler(parsed, ctx)\n // M18 — shape the (possibly rich) result into the model-visible string.\n if (spec.toModelOutput) return spec.toModelOutput(result)\n if (typeof result !== 'string') {\n throw new Error(\n `defineAgentTool(${JSON.stringify(spec.name)}): handler returned a non-string; provide toModelOutput to map it to a string for the model.`,\n )\n }\n return result\n },\n // M18 — carry the per-target formatters for the app (ignored by the SDK wire).\n ...(spec.transform !== undefined ? { transform: spec.transform as ToolTransform } : {}),\n }\n}\n\n/**\n * M18 — apply a tool's `transform` for a target (`display` / `transcript`). Returns the formatted\n * value, or the raw `result` when the tool declares no transform for that target.\n */\nexport function applyTransform(\n tool: CustomTool,\n result: unknown,\n target: 'display' | 'transcript',\n): unknown {\n const fn = tool.transform?.[target]\n return fn ? fn(result) : result\n}\n"],"mappings":";AAAA,SAAS,SAAS;AA2GlB,IAAM,kBAAkB;AAExB,SAAS,YAAY,QAA4B;AAM/C,MAAI,UAAmB;AACvB,WAAS,QAAQ,GAAG,QAAQ,IAAI,SAAS;AACvC,QAAI,mBAAmB,EAAE,UAAW,QAAO;AAC3C,UAAM,KAAM,QAA2E;AACvF,QAAI,IAAI,SAAS,SAAU,QAAO;AAClC,UAAM,KAAM,QACT;AACH,QAAI,IAAI,aAAa,YAAa,QAAO;AACzC,UAAM,OAAO,IAAI,aAAa,IAAI,MAAM,IAAI,UAAU,IAAI;AAC1D,QAAI,SAAS,QAAW;AACtB,gBAAU;AACV;AAAA,IACF;AACA,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAoBO,SAAS,gBACd,MACY;AACZ,MAAI,CAAC,gBAAgB,KAAK,KAAK,IAAI,GAAG;AACpC,UAAM,IAAI;AAAA,MACR,oCAAoC,gBAAgB,MAAM,UAAU,KAAK,UAAU,KAAK,IAAI,CAAC;AAAA,IAC/F;AAAA,EACF;AACA,MAAI,CAAC,YAAY,KAAK,WAAW,GAAG;AAClC,UAAM,IAAI,MAAM,oEAAoE;AAAA,EACtF;AACA,MAAI,KAAK,YAAY,WAAW,GAAG;AACjC,YAAQ;AAAA,MACN,mBAAmB,KAAK,UAAU,KAAK,IAAI,CAAC;AAAA,IAC9C;AAAA,EACF;AAIA,QAAM,EAAE,SAAS,UAAU,GAAG,YAAY,IAAI,EAAE,aAAa,KAAK,WAAW;AAO7E,SAAO;AAAA,IACL,MAAM,KAAK;AAAA,IACX,aAAa,KAAK;AAAA,IAClB;AAAA,IACA,SAAS,OAAO,OAAgC,QAA8C;AAC5F,YAAM,SAAS,KAAK,YAAY,MAAM,KAAK;AAE3C,YAAM,SAAS,MAAM,KAAK,QAAQ,QAAQ,GAAG;AAE7C,UAAI,KAAK,cAAe,QAAO,KAAK,cAAc,MAAM;AACxD,UAAI,OAAO,WAAW,UAAU;AAC9B,cAAM,IAAI;AAAA,UACR,mBAAmB,KAAK,UAAU,KAAK,IAAI,CAAC;AAAA,QAC9C;AAAA,MACF;AACA,aAAO;AAAA,IACT;AAAA;AAAA,IAEA,GAAI,KAAK,cAAc,SAAY,EAAE,WAAW,KAAK,UAA2B,IAAI,CAAC;AAAA,EACvF;AACF;AAMO,SAAS,eACd,MACA,QACA,QACS;AACT,QAAM,KAAK,KAAK,YAAY,MAAM;AAClC,SAAO,KAAK,GAAG,MAAM,IAAI;AAC3B;","names":[]}
|
|
@@ -178,7 +178,7 @@ async function agentCommand(name, message, deps = {}) {
|
|
|
178
178
|
async function createAgentSsrLoader(projectRoot) {
|
|
179
179
|
const { createServer } = await import("vite");
|
|
180
180
|
const react = (await import("@vitejs/plugin-react")).default;
|
|
181
|
-
const { theoPluginAsync } = await import("./vite-plugin-
|
|
181
|
+
const { theoPluginAsync } = await import("./vite-plugin-6VVU6PPC.js");
|
|
182
182
|
const { loadConfig } = await import("./load-config-3EAQTXPH.js");
|
|
183
183
|
const config = await loadConfig(projectRoot);
|
|
184
184
|
const theoPlugins = await theoPluginAsync({
|
|
@@ -212,4 +212,4 @@ export {
|
|
|
212
212
|
agentCommand,
|
|
213
213
|
createAgentSsrLoader
|
|
214
214
|
};
|
|
215
|
-
//# sourceMappingURL=chunk-
|
|
215
|
+
//# sourceMappingURL=chunk-FEDSPOY6.js.map
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
resolveTransformer,
|
|
9
9
|
serveAgentAuxRoute,
|
|
10
10
|
writeWebResponseToServerResponse
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-UO7WS32S.js";
|
|
12
12
|
import {
|
|
13
13
|
isMcpPath
|
|
14
14
|
} from "./chunk-2CVV6CNN.js";
|
|
@@ -36,7 +36,7 @@ import {
|
|
|
36
36
|
handleBatchRequest,
|
|
37
37
|
handleCspReport,
|
|
38
38
|
handleCsrfReadiness
|
|
39
|
-
} from "./chunk-
|
|
39
|
+
} from "./chunk-SPJNVWOK.js";
|
|
40
40
|
import {
|
|
41
41
|
applySecurityHeaders,
|
|
42
42
|
createPluginRunnerFromConfig,
|
|
@@ -51,7 +51,7 @@ import {
|
|
|
51
51
|
logRequest,
|
|
52
52
|
mountAgent,
|
|
53
53
|
sendError
|
|
54
|
-
} from "./chunk-
|
|
54
|
+
} from "./chunk-UNLG36Z5.js";
|
|
55
55
|
import {
|
|
56
56
|
getApprovalRegistry,
|
|
57
57
|
resolveProvider
|
|
@@ -545,7 +545,11 @@ function createAgentMiddleware(vite, projectRoot, csrfMode = "strict", agentsDir
|
|
|
545
545
|
const mod = await loadModule(agent.filePath);
|
|
546
546
|
const apiKey = resolveProvider().apiKey;
|
|
547
547
|
const request = incomingMessageToWebRequest(req);
|
|
548
|
-
const response = await mountAgent(mod, request, apiKey,
|
|
548
|
+
const response = await mountAgent(mod, request, apiKey, {
|
|
549
|
+
source: agent.filePath,
|
|
550
|
+
csrfMode,
|
|
551
|
+
projectRoot
|
|
552
|
+
});
|
|
549
553
|
await writeWebResponseToServerResponse(response, res);
|
|
550
554
|
} catch (err) {
|
|
551
555
|
sendError(
|
|
@@ -679,7 +683,7 @@ async function tryControllerFallthrough(ctx) {
|
|
|
679
683
|
requestId: ctx.requestId,
|
|
680
684
|
// M47 — serve @Expose-bound agent routes via the ONE runtime (mountAgent). CSRF is already enforced
|
|
681
685
|
// once at the controller-dispatch boundary (G5), so mountAgent runs with csrfMode 'off' (no double gate).
|
|
682
|
-
serveAgent: (agent, request) => mountAgent(agent, request, resolveProvider().apiKey, "expose", "off")
|
|
686
|
+
serveAgent: (agent, request) => mountAgent(agent, request, resolveProvider().apiKey, { source: "expose", csrfMode: "off" })
|
|
683
687
|
});
|
|
684
688
|
if (!handled) return false;
|
|
685
689
|
logRequest(
|
|
@@ -1969,19 +1973,19 @@ async function theoPluginAsync(rootOrOptions) {
|
|
|
1969
1973
|
})
|
|
1970
1974
|
);
|
|
1971
1975
|
}
|
|
1972
|
-
const { appTypedClientPlugin } = await import("./app-typed-client-
|
|
1976
|
+
const { appTypedClientPlugin } = await import("./app-typed-client-6YZRDAPY.js");
|
|
1973
1977
|
const appClientPlugin = appTypedClientPlugin({
|
|
1974
1978
|
cwd: projectRoot,
|
|
1975
1979
|
serverDir: serverDirAbs,
|
|
1976
1980
|
distDir: resolve8(projectRoot, ".theokit")
|
|
1977
1981
|
});
|
|
1978
|
-
const { actionsVirtualModule } = await import("./actions-virtual-module-
|
|
1982
|
+
const { actionsVirtualModule } = await import("./actions-virtual-module-N7GKUS3D.js");
|
|
1979
1983
|
const actionsPlugin = actionsVirtualModule({
|
|
1980
1984
|
serverDir: serverDirAbs,
|
|
1981
1985
|
distDir: resolve8(projectRoot, ".theokit")
|
|
1982
1986
|
});
|
|
1983
1987
|
const { agentsTypedClientPlugin } = await import("./agents-typed-client-NJ73Z6XY.js");
|
|
1984
|
-
const { generateManifest } = await import("./internal-api-
|
|
1988
|
+
const { generateManifest } = await import("./internal-api-FBFV265J.js");
|
|
1985
1989
|
const agentsClientPlugin = agentsTypedClientPlugin({
|
|
1986
1990
|
projectRoot,
|
|
1987
1991
|
agentsDir: agentsDirName,
|
|
@@ -2162,4 +2166,4 @@ export {
|
|
|
2162
2166
|
theoPluginAsync,
|
|
2163
2167
|
theoPlugin
|
|
2164
2168
|
};
|
|
2165
|
-
//# sourceMappingURL=chunk-
|
|
2169
|
+
//# sourceMappingURL=chunk-IPC5UREF.js.map
|