theokit 0.30.1 → 0.30.3
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-PBLLMJY4.js → actions-virtual-module-XHVLBFNN.js} +7 -7
- package/dist/{app-typed-client-EXHUWPOI.js → app-typed-client-4PAUHTO6.js} +7 -7
- package/dist/boot/index.js +3 -3
- package/dist/{chunk-UYFZ6DDW.js → chunk-4MTCKE5I.js} +1 -189
- package/dist/chunk-4MTCKE5I.js.map +1 -0
- package/dist/chunk-63BUBV5L.js +64 -0
- package/dist/chunk-63BUBV5L.js.map +1 -0
- package/dist/{chunk-RBRDTYOO.js → chunk-CEHXC5NI.js} +2 -2
- package/dist/chunk-CEHXC5NI.js.map +1 -0
- package/dist/{chunk-EHUT4FMG.js → chunk-HAOPVC47.js} +29 -27
- package/dist/{chunk-EHUT4FMG.js.map → chunk-HAOPVC47.js.map} +1 -1
- package/dist/chunk-HZ6USUHC.js +191 -0
- package/dist/chunk-HZ6USUHC.js.map +1 -0
- package/dist/chunk-SPFMJAFW.js +326 -0
- package/dist/chunk-SPFMJAFW.js.map +1 -0
- package/dist/{chunk-DMGVH3VG.js → chunk-V3X5FWJA.js} +5 -62
- package/dist/chunk-V3X5FWJA.js.map +1 -0
- package/dist/client/core.js +1 -1
- package/dist/client/index.js +1 -1
- package/dist/define-agent-tool-ChaGymol.d.ts +88 -0
- package/dist/index.js +11 -10
- package/dist/index.js.map +1 -1
- package/dist/{internal-api-JIR3HRKF.js → internal-api-QSP2HCUU.js} +20 -20
- package/dist/server/agent/index.d.ts +387 -0
- package/dist/server/agent/index.js +43 -0
- package/dist/server/agent/index.js.map +1 -0
- package/dist/server/auth/index.js +1 -1
- package/dist/server/define/index.d.ts +3 -86
- package/dist/server/define/index.js +4 -2
- package/dist/server/index.d.ts +10 -387
- package/dist/server/index.js +112 -398
- package/dist/server/index.js.map +1 -1
- package/dist/server/jobs/index.js +4 -4
- package/dist/server/scan/index.js +1 -1
- package/dist/server/webhook/index.d.ts +4 -45
- package/dist/vite-plugin/index.js +11 -10
- package/dist/webhook-types-CNUZY7o1.d.ts +45 -0
- package/package.json +6 -2
- package/dist/chunk-DMGVH3VG.js.map +0 -1
- package/dist/chunk-RBRDTYOO.js.map +0 -1
- package/dist/chunk-UYFZ6DDW.js.map +0 -1
- /package/dist/{actions-virtual-module-PBLLMJY4.js.map → actions-virtual-module-XHVLBFNN.js.map} +0 -0
- /package/dist/{app-typed-client-EXHUWPOI.js.map → app-typed-client-4PAUHTO6.js.map} +0 -0
- /package/dist/{internal-api-JIR3HRKF.js.map → internal-api-QSP2HCUU.js.map} +0 -0
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
// src/server/agent/mcp-app-resources.ts
|
|
2
|
+
function defineAppResource(input) {
|
|
3
|
+
if (!input.uri.startsWith("ui://")) {
|
|
4
|
+
throw new Error(
|
|
5
|
+
`defineAppResource: uri must use the ui:// scheme (an app UI resource). Got: ${JSON.stringify(input.uri)}`
|
|
6
|
+
);
|
|
7
|
+
}
|
|
8
|
+
if (input.html.length === 0) {
|
|
9
|
+
throw new Error(`defineAppResource(${JSON.stringify(input.uri)}): html must be non-empty.`);
|
|
10
|
+
}
|
|
11
|
+
return {
|
|
12
|
+
uri: input.uri,
|
|
13
|
+
name: input.name,
|
|
14
|
+
mimeType: "text/html",
|
|
15
|
+
html: input.html,
|
|
16
|
+
...input.description !== void 0 ? { description: input.description } : {}
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
function buildResourceDescriptors(resources) {
|
|
20
|
+
return resources.map((r) => ({
|
|
21
|
+
uri: r.uri,
|
|
22
|
+
name: r.name,
|
|
23
|
+
mimeType: r.mimeType,
|
|
24
|
+
...r.description !== void 0 ? { description: r.description } : {}
|
|
25
|
+
}));
|
|
26
|
+
}
|
|
27
|
+
function readAppResource(resources, uri) {
|
|
28
|
+
const found = resources.find((r) => r.uri === uri);
|
|
29
|
+
if (!found) return null;
|
|
30
|
+
return { contents: [{ uri: found.uri, mimeType: "text/html", text: found.html }] };
|
|
31
|
+
}
|
|
32
|
+
function isAppResource(v) {
|
|
33
|
+
if (typeof v !== "object" || v === null) return false;
|
|
34
|
+
const r = v;
|
|
35
|
+
return typeof r.uri === "string" && r.uri.startsWith("ui://") && r.mimeType === "text/html" && typeof r.html === "string";
|
|
36
|
+
}
|
|
37
|
+
function extractAppResources(mod) {
|
|
38
|
+
const raw = mod?.appResources;
|
|
39
|
+
if (!Array.isArray(raw)) return [];
|
|
40
|
+
return raw.filter(isAppResource);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// src/server/agent/mcp-handler.ts
|
|
44
|
+
import { compileAgentModule } from "@theokit/agents";
|
|
45
|
+
var MCP_PATH = /^\/api\/agents\/([^/]+)\/mcp$/;
|
|
46
|
+
var MCP_PROTOCOL_VERSION = "2025-06-18";
|
|
47
|
+
function toolDescriptors(tools) {
|
|
48
|
+
return tools.map((t) => ({
|
|
49
|
+
name: t.name,
|
|
50
|
+
description: t.description,
|
|
51
|
+
// The compiled tool already carries a JSON Schema (via `z.toJSONSchema()` in defineAgentTool).
|
|
52
|
+
// Fall back to a permissive object only when a tool genuinely declared no schema.
|
|
53
|
+
inputSchema: t.inputSchema && typeof t.inputSchema === "object" ? t.inputSchema : { type: "object", properties: {} }
|
|
54
|
+
}));
|
|
55
|
+
}
|
|
56
|
+
function isMcpPath(urlPath) {
|
|
57
|
+
const match = MCP_PATH.exec(urlPath);
|
|
58
|
+
return match ? decodeURIComponent(match[1]) : null;
|
|
59
|
+
}
|
|
60
|
+
function isJsonRpcRequest(body) {
|
|
61
|
+
return typeof body === "object" && body !== null && body.jsonrpc === "2.0" && typeof body.method === "string";
|
|
62
|
+
}
|
|
63
|
+
function jsonResponse(payload) {
|
|
64
|
+
return new Response(JSON.stringify(payload), {
|
|
65
|
+
status: 200,
|
|
66
|
+
headers: { "content-type": "application/json; charset=utf-8" }
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
async function callTool(tools, toolName, args, hitl) {
|
|
70
|
+
if (typeof toolName !== "string") {
|
|
71
|
+
return {
|
|
72
|
+
content: [{ type: "text", text: "tools/call requires a string `name`." }],
|
|
73
|
+
isError: true
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
const tool = tools.find((t) => t.name === toolName);
|
|
77
|
+
if (!tool) {
|
|
78
|
+
return { content: [{ type: "text", text: `Unknown tool: ${toolName}` }], isError: true };
|
|
79
|
+
}
|
|
80
|
+
if (hitl?.has(toolName)) {
|
|
81
|
+
return {
|
|
82
|
+
content: [
|
|
83
|
+
{
|
|
84
|
+
type: "text",
|
|
85
|
+
text: `Tool "${toolName}" requires human approval, which is not available over MCP. Refused.`
|
|
86
|
+
}
|
|
87
|
+
],
|
|
88
|
+
isError: true
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
try {
|
|
92
|
+
const result = await tool.handler(args);
|
|
93
|
+
const text = typeof result === "string" ? result : JSON.stringify(result);
|
|
94
|
+
return { content: [{ type: "text", text }] };
|
|
95
|
+
} catch (err) {
|
|
96
|
+
return {
|
|
97
|
+
content: [
|
|
98
|
+
{ type: "text", text: err instanceof Error ? err.message : "Tool execution failed" }
|
|
99
|
+
],
|
|
100
|
+
isError: true
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
function handleResourcesRead(id, params, appResources) {
|
|
105
|
+
const uri = params?.uri;
|
|
106
|
+
if (typeof uri !== "string") {
|
|
107
|
+
return jsonResponse({
|
|
108
|
+
jsonrpc: "2.0",
|
|
109
|
+
id,
|
|
110
|
+
error: { code: -32602, message: "resources/read requires a string `uri` param." }
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
const contents = readAppResource(appResources, uri);
|
|
114
|
+
if (contents === null) {
|
|
115
|
+
return jsonResponse({
|
|
116
|
+
jsonrpc: "2.0",
|
|
117
|
+
id,
|
|
118
|
+
error: { code: -32602, message: `Resource not found: ${uri}` }
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
return jsonResponse({ jsonrpc: "2.0", id, result: contents });
|
|
122
|
+
}
|
|
123
|
+
async function handleMcpJsonRpc(mod, name, body, appResources = []) {
|
|
124
|
+
if (!isJsonRpcRequest(body)) {
|
|
125
|
+
return jsonResponse({
|
|
126
|
+
jsonrpc: "2.0",
|
|
127
|
+
id: null,
|
|
128
|
+
error: { code: -32600, message: "Invalid Request" }
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
const { id, method, params } = body;
|
|
132
|
+
try {
|
|
133
|
+
const compiled = compileAgentModule(mod, `mcp server for "${name}"`);
|
|
134
|
+
if (method === "initialize") {
|
|
135
|
+
const capabilities = { tools: {} };
|
|
136
|
+
if (appResources.length > 0) capabilities.resources = {};
|
|
137
|
+
return jsonResponse({
|
|
138
|
+
jsonrpc: "2.0",
|
|
139
|
+
id,
|
|
140
|
+
result: {
|
|
141
|
+
protocolVersion: MCP_PROTOCOL_VERSION,
|
|
142
|
+
capabilities,
|
|
143
|
+
serverInfo: { name, version: "1.0" }
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
if (method === "tools/list") {
|
|
148
|
+
return jsonResponse({
|
|
149
|
+
jsonrpc: "2.0",
|
|
150
|
+
id,
|
|
151
|
+
result: { tools: toolDescriptors(compiled.tools) }
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
if (method === "tools/call") {
|
|
155
|
+
const p = params;
|
|
156
|
+
const result = await callTool(compiled.tools, p?.name, p?.arguments ?? {}, compiled.hitl);
|
|
157
|
+
return jsonResponse({ jsonrpc: "2.0", id, result });
|
|
158
|
+
}
|
|
159
|
+
if (method === "resources/list") {
|
|
160
|
+
return jsonResponse({
|
|
161
|
+
jsonrpc: "2.0",
|
|
162
|
+
id,
|
|
163
|
+
result: { resources: buildResourceDescriptors(appResources) }
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
if (method === "resources/read") {
|
|
167
|
+
return handleResourcesRead(id, params, appResources);
|
|
168
|
+
}
|
|
169
|
+
return jsonResponse({
|
|
170
|
+
jsonrpc: "2.0",
|
|
171
|
+
id,
|
|
172
|
+
error: { code: -32601, message: `Method not found: ${method}` }
|
|
173
|
+
});
|
|
174
|
+
} catch (err) {
|
|
175
|
+
return jsonResponse({
|
|
176
|
+
jsonrpc: "2.0",
|
|
177
|
+
id,
|
|
178
|
+
error: { code: -32603, message: err instanceof Error ? err.message : "Internal error" }
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export {
|
|
184
|
+
defineAppResource,
|
|
185
|
+
buildResourceDescriptors,
|
|
186
|
+
readAppResource,
|
|
187
|
+
extractAppResources,
|
|
188
|
+
isMcpPath,
|
|
189
|
+
handleMcpJsonRpc
|
|
190
|
+
};
|
|
191
|
+
//# sourceMappingURL=chunk-HZ6USUHC.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/server/agent/mcp-app-resources.ts","../src/server/agent/mcp-handler.ts"],"sourcesContent":["/**\n * M30 (ADR-0041) — MCP Apps: `ui://` HTML resources for the MCP server (M16).\n *\n * A tool can declare a `ui://` HTML resource; the MCP server advertises it via `resources/list` and\n * serves the HTML via `resources/read`. The client renders it in a SANDBOXED iframe (see\n * `mcp-app-host.ts`). Pure data transforms here — no LLM, no runtime (ADR-0040 § D2 home concern).\n *\n * Security: only the `ui://` scheme is accepted (an app UI resource), never `http(s)://` — the HTML\n * is rendered sandboxed on the client, and the scheme gate keeps a tool from smuggling a remote URL\n * into the app surface.\n */\n\n/** A declared `ui://` app resource. */\nexport interface AppResource {\n uri: string\n name: string\n mimeType: 'text/html'\n html: string\n description?: string\n}\n\n/** Input to {@link defineAppResource}. */\nexport interface AppResourceInput {\n /** MUST start with `ui://`. */\n uri: string\n name: string\n html: string\n description?: string\n}\n\n/** An MCP `resources/list` descriptor (no HTML body — that comes from `resources/read`). */\nexport interface McpResourceDescriptor {\n uri: string\n name: string\n mimeType: 'text/html'\n description?: string\n}\n\n/** An MCP `resources/read` result. */\nexport interface McpResourceContents {\n contents: { uri: string; mimeType: 'text/html'; text: string }[]\n}\n\n/**\n * Declare a `ui://` HTML app resource. Fails fast if the uri is not a `ui://` scheme or the HTML is\n * empty (error-handling.md) — a misconfigured resource is caught at definition time.\n */\nexport function defineAppResource(input: AppResourceInput): AppResource {\n if (!input.uri.startsWith('ui://')) {\n throw new Error(\n `defineAppResource: uri must use the ui:// scheme (an app UI resource). Got: ${JSON.stringify(input.uri)}`,\n )\n }\n if (input.html.length === 0) {\n throw new Error(`defineAppResource(${JSON.stringify(input.uri)}): html must be non-empty.`)\n }\n return {\n uri: input.uri,\n name: input.name,\n mimeType: 'text/html',\n html: input.html,\n ...(input.description !== undefined ? { description: input.description } : {}),\n }\n}\n\n/** Map app resources to MCP `resources/list` descriptors (the HTML body is omitted from the list). */\nexport function buildResourceDescriptors(\n resources: readonly AppResource[],\n): McpResourceDescriptor[] {\n return resources.map((r) => ({\n uri: r.uri,\n name: r.name,\n mimeType: r.mimeType,\n ...(r.description !== undefined ? { description: r.description } : {}),\n }))\n}\n\n/** Serve the HTML for `uri` as an MCP `resources/read` result, or `null` when unknown. */\nexport function readAppResource(\n resources: readonly AppResource[],\n uri: string,\n): McpResourceContents | null {\n const found = resources.find((r) => r.uri === uri)\n if (!found) return null\n return { contents: [{ uri: found.uri, mimeType: 'text/html', text: found.html }] }\n}\n\n/** True when `v` is a well-formed {@link AppResource} (from `defineAppResource`). */\nfunction isAppResource(v: unknown): v is AppResource {\n if (typeof v !== 'object' || v === null) return false\n const r = v as Record<string, unknown>\n return (\n typeof r.uri === 'string' &&\n r.uri.startsWith('ui://') &&\n r.mimeType === 'text/html' &&\n typeof r.html === 'string'\n )\n}\n\n/**\n * M30 wiring — extract the App resources an agent module declares via a named `appResources` export\n * (`export const appResources = [defineAppResource(...)]`). Returns only the well-formed entries; a\n * module without the export (or with a malformed one) yields `[]`. This is how per-agent `ui://`\n * resources reach the MCP server's `resources/list` + `resources/read` without a runtime dependency\n * from `@theokit/agents` on this theo-side type (the module exports them; the serving path reads them).\n */\nexport function extractAppResources(mod: unknown): AppResource[] {\n const raw = (mod as { appResources?: unknown } | null | undefined)?.appResources\n if (!Array.isArray(raw)) return []\n return raw.filter(isAppResource)\n}\n","/**\n * M16 (theokit-ai-first) — serve an agent as an MCP server over HTTP at `/api/agents/<name>/mcp`.\n *\n * Answers the two core MCP methods over JSON-RPC 2.0: `initialize` (server info + capabilities) and\n * `tools/list` (the agent's tools as MCP descriptors, via `buildMcpToolDescriptors`). M30 adds\n * `resources/list` + `resources/read` for `ui://` App resources. Unknown methods return `-32601`\n * (method not found). Web Standards Response (G8). The stdio transport + full method set stay\n * SDK-side (sdk-runtime.md); this exposes the agent over the app's own HTTP route.\n */\nimport { type CompiledTool, compileAgentModule } from '@theokit/agents'\n\nimport { type AppResource, buildResourceDescriptors, readAppResource } from './mcp-app-resources.js'\n\nconst MCP_PATH = /^\\/api\\/agents\\/([^/]+)\\/mcp$/\n\n/**\n * The MCP protocol revision THIS server transport implements (M34). The protocol version is a\n * property of the server that speaks it (this handler), not of the manifest data generator — so the\n * handler owns it. Current MCP revision (`2025-06-18`); replaces the stale `2024-11-05` the manifest\n * generator carried.\n */\nconst MCP_PROTOCOL_VERSION = '2025-06-18'\n\n/** An MCP `tools/list` descriptor with the tool's REAL input schema retained (M34 — no longer dropped). */\ninterface McpToolDescriptor {\n name: string\n description: string\n inputSchema: Record<string, unknown>\n}\n\n/** Build tool descriptors from the compiled tools, retaining each tool's real JSON-Schema input (M34). */\nfunction toolDescriptors(tools: readonly CompiledTool[]): McpToolDescriptor[] {\n return tools.map((t) => ({\n name: t.name,\n description: t.description,\n // The compiled tool already carries a JSON Schema (via `z.toJSONSchema()` in defineAgentTool).\n // Fall back to a permissive object only when a tool genuinely declared no schema.\n inputSchema:\n t.inputSchema && typeof t.inputSchema === 'object'\n ? (t.inputSchema as Record<string, unknown>)\n : { type: 'object', properties: {} },\n }))\n}\n\n/** An MCP `CallToolResult` — `content[]` + `isError` (the shape MCP clients expect). */\ninterface CallToolResult {\n content: { type: 'text'; text: string }[]\n isError?: boolean\n}\n\n/** Return the agent name when `urlPath` is the MCP endpoint, else `null`. */\nexport function isMcpPath(urlPath: string): string | null {\n const match = MCP_PATH.exec(urlPath)\n return match ? decodeURIComponent(match[1]) : null\n}\n\ninterface JsonRpcRequest {\n jsonrpc: '2.0'\n id: number | string | null\n method: string\n params?: unknown\n}\n\nfunction isJsonRpcRequest(body: unknown): body is JsonRpcRequest {\n return (\n typeof body === 'object' &&\n body !== null &&\n (body as { jsonrpc?: unknown }).jsonrpc === '2.0' &&\n typeof (body as { method?: unknown }).method === 'string'\n )\n}\n\nfunction jsonResponse(payload: unknown): Response {\n return new Response(JSON.stringify(payload), {\n status: 200,\n headers: { 'content-type': 'application/json; charset=utf-8' },\n })\n}\n\n/**\n * Execute an MCP `tools/call` against the compiled tools (M34). Finds the tool by name, runs its\n * handler with the supplied `arguments`, and shapes the result into a `CallToolResult`. An unknown\n * tool or a throwing handler yields `{ isError: true }` (MCP convention — the model sees the failure)\n * rather than a JSON-RPC crash.\n */\nasync function callTool(\n tools: readonly CompiledTool[],\n toolName: unknown,\n args: unknown,\n hitl?: ReadonlyMap<string, unknown>,\n): Promise<CallToolResult> {\n if (typeof toolName !== 'string') {\n return {\n content: [{ type: 'text', text: 'tools/call requires a string `name`.' }],\n isError: true,\n }\n }\n const tool = tools.find((t) => t.name === toolName)\n if (!tool) {\n return { content: [{ type: 'text', text: `Unknown tool: ${toolName}` }], isError: true }\n }\n // #99 — REFUSE a HITL-gated tool. The approval gate (`compiled.hitl`) lives in the SDK run-loop,\n // not in the raw tool handler; executing the handler here would BYPASS the human approval the web/\n // TUI surfaces enforce. Over MCP there is no approval mechanism, so a gated tool is not callable —\n // return an error result instead of running it unguarded (fail-closed, Rule 8).\n if (hitl?.has(toolName)) {\n return {\n content: [\n {\n type: 'text',\n text: `Tool \"${toolName}\" requires human approval, which is not available over MCP. Refused.`,\n },\n ],\n isError: true,\n }\n }\n try {\n const result = await tool.handler(args)\n const text = typeof result === 'string' ? result : JSON.stringify(result)\n return { content: [{ type: 'text', text }] }\n } catch (err) {\n return {\n content: [\n { type: 'text', text: err instanceof Error ? err.message : 'Tool execution failed' },\n ],\n isError: true,\n }\n }\n}\n\n/** `resources/read` (M30) — extracted to keep `handleMcpJsonRpc` within the complexity budget. */\nfunction handleResourcesRead(\n id: number | string | null,\n params: unknown,\n appResources: readonly AppResource[],\n): Response {\n const uri = (params as { uri?: unknown } | undefined)?.uri\n if (typeof uri !== 'string') {\n return jsonResponse({\n jsonrpc: '2.0',\n id,\n error: { code: -32602, message: 'resources/read requires a string `uri` param.' },\n })\n }\n const contents = readAppResource(appResources, uri)\n if (contents === null) {\n return jsonResponse({\n jsonrpc: '2.0',\n id,\n error: { code: -32602, message: `Resource not found: ${uri}` },\n })\n }\n return jsonResponse({ jsonrpc: '2.0', id, result: contents })\n}\n\n/**\n * Handle one MCP JSON-RPC request for an agent module. Always returns a 200 JSON-RPC envelope.\n *\n * M30 — `appResources` (optional) are the agent's declared `ui://` App resources; when present the\n * server advertises `capabilities.resources` and answers `resources/list` + `resources/read`.\n */\nexport async function handleMcpJsonRpc(\n mod: unknown,\n name: string,\n body: unknown,\n appResources: readonly AppResource[] = [],\n): Promise<Response> {\n if (!isJsonRpcRequest(body)) {\n return jsonResponse({\n jsonrpc: '2.0',\n id: null,\n error: { code: -32600, message: 'Invalid Request' },\n })\n }\n const { id, method, params } = body\n try {\n const compiled = compileAgentModule(mod, `mcp server for \"${name}\"`)\n if (method === 'initialize') {\n const capabilities: Record<string, unknown> = { tools: {} }\n if (appResources.length > 0) capabilities.resources = {}\n return jsonResponse({\n jsonrpc: '2.0',\n id,\n result: {\n protocolVersion: MCP_PROTOCOL_VERSION,\n capabilities,\n serverInfo: { name, version: '1.0' },\n },\n })\n }\n if (method === 'tools/list') {\n return jsonResponse({\n jsonrpc: '2.0',\n id,\n result: { tools: toolDescriptors(compiled.tools) },\n })\n }\n if (method === 'tools/call') {\n const p = params as { name?: unknown; arguments?: unknown } | undefined\n const result = await callTool(compiled.tools, p?.name, p?.arguments ?? {}, compiled.hitl)\n return jsonResponse({ jsonrpc: '2.0', id, result })\n }\n if (method === 'resources/list') {\n return jsonResponse({\n jsonrpc: '2.0',\n id,\n result: { resources: buildResourceDescriptors(appResources) },\n })\n }\n if (method === 'resources/read') {\n return handleResourcesRead(id, params, appResources)\n }\n return jsonResponse({\n jsonrpc: '2.0',\n id,\n error: { code: -32601, message: `Method not found: ${method}` },\n })\n } catch (err) {\n return jsonResponse({\n jsonrpc: '2.0',\n id,\n error: { code: -32603, message: err instanceof Error ? err.message : 'Internal error' },\n })\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
import {
|
|
2
|
+
handleMcpJsonRpc
|
|
3
|
+
} from "./chunk-HZ6USUHC.js";
|
|
4
|
+
import {
|
|
5
|
+
defineAgentTool
|
|
6
|
+
} from "./chunk-63BUBV5L.js";
|
|
7
|
+
|
|
8
|
+
// src/server/agent/workflow-tool.ts
|
|
9
|
+
import { z } from "zod";
|
|
10
|
+
var FAILURE_STATUSES = /* @__PURE__ */ new Set(["failed", "error", "cancelled", "canceled"]);
|
|
11
|
+
function createWorkflowTool(workflow, config) {
|
|
12
|
+
const runFn = workflow?.run;
|
|
13
|
+
if (typeof runFn !== "function") {
|
|
14
|
+
throw new Error(
|
|
15
|
+
"createWorkflowTool: the SDK does not expose a Workflow (expected an object with a run() method). Pass a `Workflow.create(...).\u2026build()` instance from @theokit/sdk."
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
const inputSchema = config.inputSchema ?? z.looseObject({});
|
|
19
|
+
return defineAgentTool({
|
|
20
|
+
name: config.name,
|
|
21
|
+
description: config.description,
|
|
22
|
+
inputSchema,
|
|
23
|
+
handler: async (input) => {
|
|
24
|
+
const run = await workflow.run(input);
|
|
25
|
+
if (FAILURE_STATUSES.has(run.status)) {
|
|
26
|
+
throw new Error(
|
|
27
|
+
`createWorkflowTool(${JSON.stringify(config.name)}): workflow run ${run.runId ? `'${run.runId}' ` : ""}failed with status '${run.status}'.`
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
return typeof run.output === "string" ? run.output : JSON.stringify(run.output);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// src/server/agent/acp-tool.ts
|
|
36
|
+
import { spawn } from "child_process";
|
|
37
|
+
import { AcpClient } from "@theokit/agents";
|
|
38
|
+
import { encodeAcpMessage } from "@theokit/agents";
|
|
39
|
+
var NodeAcpTransport = class {
|
|
40
|
+
// stdin=pipe, stdout=pipe, stderr=inherit → the third stream is null.
|
|
41
|
+
proc;
|
|
42
|
+
constructor(command, args = [], cwd) {
|
|
43
|
+
this.proc = spawn(command, args, { cwd, stdio: ["pipe", "pipe", "inherit"] });
|
|
44
|
+
}
|
|
45
|
+
send(line) {
|
|
46
|
+
this.proc.stdin.write(line);
|
|
47
|
+
}
|
|
48
|
+
subscribe(onData) {
|
|
49
|
+
this.proc.stdout.on("data", (buf) => {
|
|
50
|
+
onData(buf.toString("utf8"));
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
close() {
|
|
54
|
+
this.proc.kill();
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
function defaultTransport(config) {
|
|
58
|
+
return new NodeAcpTransport(config.command, config.args, config.cwd);
|
|
59
|
+
}
|
|
60
|
+
function createACPTool(config) {
|
|
61
|
+
if (typeof config.onPermissionRequest !== "function") {
|
|
62
|
+
throw new Error("[theokit] createACPTool requires onPermissionRequest (security by default \u2014 no default-allow)");
|
|
63
|
+
}
|
|
64
|
+
const makeTransport = config.transportFactory ?? defaultTransport;
|
|
65
|
+
return {
|
|
66
|
+
name: config.name,
|
|
67
|
+
description: config.description,
|
|
68
|
+
inputSchema: {
|
|
69
|
+
type: "object",
|
|
70
|
+
properties: { message: { type: "string", description: "The task/prompt for the coding agent." } },
|
|
71
|
+
required: ["message"]
|
|
72
|
+
},
|
|
73
|
+
handler: async (input) => {
|
|
74
|
+
const message = typeof input.message === "string" ? input.message : "";
|
|
75
|
+
const client = new AcpClient(makeTransport(config));
|
|
76
|
+
client.onRequest("session/request_permission", (params) => config.onPermissionRequest(params));
|
|
77
|
+
const result = await client.request("session/prompt", { message });
|
|
78
|
+
return result.text ?? "";
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// src/server/agent/vendor-agent-tool.ts
|
|
84
|
+
function createVendorAgentTool(config) {
|
|
85
|
+
if (!config.vendor || config.vendor.length === 0) {
|
|
86
|
+
throw new Error('createVendorAgentTool: `vendor` is required (e.g. "claude", "openai").');
|
|
87
|
+
}
|
|
88
|
+
const queryFn = config.client?.query;
|
|
89
|
+
if (typeof queryFn !== "function") {
|
|
90
|
+
throw new Error(
|
|
91
|
+
`createVendorAgentTool(${JSON.stringify(config.vendor)}): the vendor client does not expose a query() method. Pass the vendor SDK client (or a @theokit/agent-* wrapper).`
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
const name = config.name ?? `${config.vendor}_agent`;
|
|
95
|
+
const description = config.description ?? `Delegate a task to the ${config.vendor} agent and return its answer.`;
|
|
96
|
+
return {
|
|
97
|
+
name,
|
|
98
|
+
description,
|
|
99
|
+
inputSchema: {
|
|
100
|
+
type: "object",
|
|
101
|
+
properties: {
|
|
102
|
+
prompt: { type: "string", description: "The task/prompt for the vendor agent." },
|
|
103
|
+
resumeSessionId: {
|
|
104
|
+
type: "string",
|
|
105
|
+
description: "Optional vendor session id to resume a prior conversation."
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
required: ["prompt"]
|
|
109
|
+
},
|
|
110
|
+
handler: async (input) => {
|
|
111
|
+
const prompt = typeof input.prompt === "string" ? input.prompt : "";
|
|
112
|
+
const resumeSessionId = typeof input.resumeSessionId === "string" ? input.resumeSessionId : void 0;
|
|
113
|
+
const result = await config.client.query(
|
|
114
|
+
prompt,
|
|
115
|
+
resumeSessionId !== void 0 ? { resumeSessionId } : void 0
|
|
116
|
+
);
|
|
117
|
+
if (result.sessionId !== void 0 && config.onSession) config.onSession(result.sessionId);
|
|
118
|
+
return result.text;
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// src/server/agent/code-mode.ts
|
|
124
|
+
var CodeModePermissionDeniedError = class extends Error {
|
|
125
|
+
constructor(tool, reason) {
|
|
126
|
+
const suffix = reason ? `: ${reason}` : "";
|
|
127
|
+
super(`code-mode: tool '${tool}' denied by permission gate${suffix}`);
|
|
128
|
+
this.name = "CodeModePermissionDeniedError";
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
function describeToolInput(inputSchema) {
|
|
132
|
+
const props = inputSchema.properties ?? {};
|
|
133
|
+
const required = new Set(inputSchema.required ?? []);
|
|
134
|
+
const entries = Object.entries(props).map(([key, spec]) => {
|
|
135
|
+
const type = typeof spec.type === "string" ? spec.type : "unknown";
|
|
136
|
+
return `${key}${required.has(key) ? "" : "?"}: ${type}`;
|
|
137
|
+
});
|
|
138
|
+
return entries.length > 0 ? `{ ${entries.join(", ")} }` : "{}";
|
|
139
|
+
}
|
|
140
|
+
function generateCodeModeInstructions(tools, toolName) {
|
|
141
|
+
const calls = tools.map((t) => `- \`await api.${t.name}(${describeToolInput(t.inputSchema)})\` \u2014 ${t.description}`).join("\n");
|
|
142
|
+
return [
|
|
143
|
+
`The \`${toolName}\` tool runs your code in a sandbox. Your code may call ONLY these functions (each bridges to a real, validated tool on the host):`,
|
|
144
|
+
calls,
|
|
145
|
+
"Write an async function body that composes these calls and return exactly ONE structured result. Prefer `Promise.all` for independent calls; do arithmetic and aggregation in code, not in prose."
|
|
146
|
+
].join("\n\n");
|
|
147
|
+
}
|
|
148
|
+
function createCodeMode(config) {
|
|
149
|
+
if (typeof config.onPermissionRequest !== "function") {
|
|
150
|
+
throw new Error(
|
|
151
|
+
"createCodeMode requires onPermissionRequest (security by default \u2014 no default-allow for any tool)."
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
const sandboxRun = config.sandbox?.run;
|
|
155
|
+
if (typeof sandboxRun !== "function") {
|
|
156
|
+
throw new Error(
|
|
157
|
+
"createCodeMode requires an injected `sandbox` with a run() method (a vetted isolation boundary \u2014 never node:vm)."
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
if (config.tools.length === 0) {
|
|
161
|
+
throw new Error(
|
|
162
|
+
"createCodeMode requires a non-empty tools[] \u2014 the restricted API would be empty."
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
const api = {};
|
|
166
|
+
for (const tool2 of config.tools) {
|
|
167
|
+
api[tool2.name] = async (args) => {
|
|
168
|
+
const decision = await config.onPermissionRequest({ tool: tool2.name, args });
|
|
169
|
+
if (!decision.granted) throw new CodeModePermissionDeniedError(tool2.name, decision.reason);
|
|
170
|
+
return tool2.handler(args);
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
const name = config.name ?? "run_code";
|
|
174
|
+
const tool = {
|
|
175
|
+
name,
|
|
176
|
+
description: config.description ?? "Run code that composes the available tools. Only the declared tools are callable.",
|
|
177
|
+
inputSchema: {
|
|
178
|
+
type: "object",
|
|
179
|
+
properties: { code: { type: "string", description: "The code to run in the sandbox." } },
|
|
180
|
+
required: ["code"]
|
|
181
|
+
},
|
|
182
|
+
handler: async (input) => {
|
|
183
|
+
const code = typeof input.code === "string" ? input.code : "";
|
|
184
|
+
const result = await config.sandbox.run(code, api);
|
|
185
|
+
return typeof result === "string" ? result : JSON.stringify(result);
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
return { tool, instructions: generateCodeModeInstructions(config.tools, name) };
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// src/server/agent/channel-webhook.ts
|
|
192
|
+
var CHANNEL_PATH = /^\/api\/agents\/([^/]+)\/channels\/([^/]+)\/webhook$/;
|
|
193
|
+
function parseChannelPath(urlPath) {
|
|
194
|
+
const match = CHANNEL_PATH.exec(urlPath);
|
|
195
|
+
if (!match) return null;
|
|
196
|
+
return { agent: decodeURIComponent(match[1]), platform: decodeURIComponent(match[2]) };
|
|
197
|
+
}
|
|
198
|
+
function isChannelPath(urlPath) {
|
|
199
|
+
return CHANNEL_PATH.test(urlPath);
|
|
200
|
+
}
|
|
201
|
+
function jsonError(status, code, message) {
|
|
202
|
+
return new Response(JSON.stringify({ error: { code, message } }), {
|
|
203
|
+
status,
|
|
204
|
+
headers: { "content-type": "application/json" }
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
async function handleChannelWebhook(request, urlPath, config) {
|
|
208
|
+
const parsed = parseChannelPath(urlPath);
|
|
209
|
+
if (parsed === null) {
|
|
210
|
+
return jsonError(
|
|
211
|
+
400,
|
|
212
|
+
"BAD_REQUEST",
|
|
213
|
+
"Path must be /api/agents/<name>/channels/<platform>/webhook."
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
if (!Object.hasOwn(config.validators, parsed.platform)) {
|
|
217
|
+
return jsonError(
|
|
218
|
+
404,
|
|
219
|
+
"UNKNOWN_PLATFORM",
|
|
220
|
+
`No validator configured for platform '${parsed.platform}'.`
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
const verify = config.validators[parsed.platform];
|
|
224
|
+
const verifyResult = await verify(request.clone());
|
|
225
|
+
if (!verifyResult.ok) {
|
|
226
|
+
return jsonError(
|
|
227
|
+
401,
|
|
228
|
+
"INVALID_SIGNATURE",
|
|
229
|
+
`Signature validation failed: ${verifyResult.reason}`
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
let payload;
|
|
233
|
+
try {
|
|
234
|
+
payload = await request.json();
|
|
235
|
+
} catch {
|
|
236
|
+
return jsonError(400, "BAD_REQUEST", "Request body must be JSON.");
|
|
237
|
+
}
|
|
238
|
+
await config.onMessage({ agent: parsed.agent, platform: parsed.platform, payload });
|
|
239
|
+
return new Response(JSON.stringify({ ok: true }), {
|
|
240
|
+
status: 200,
|
|
241
|
+
headers: { "content-type": "application/json" }
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// src/server/agent/stream-agent-turn-in-process.ts
|
|
246
|
+
import {
|
|
247
|
+
compileAgentModule,
|
|
248
|
+
resolveEnabledSkills,
|
|
249
|
+
streamAgentUIMessages
|
|
250
|
+
} from "@theokit/agents";
|
|
251
|
+
var InProcessApprovalRequiredError = class extends Error {
|
|
252
|
+
constructor(toolNames) {
|
|
253
|
+
super(
|
|
254
|
+
`Agent has HITL-gated tool(s) [${toolNames.join(", ")}] but no \`awaitApproval\` resolver was supplied to streamAgentTurnInProcess. In-process runs must resolve approvals inline \u2014 pass awaitApproval, or remove the gate. Refused (fail-closed).`
|
|
255
|
+
);
|
|
256
|
+
this.name = "InProcessApprovalRequiredError";
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
function streamAgentTurnInProcess(mod, apiKey, input, deps = { stream: streamAgentUIMessages }) {
|
|
260
|
+
const compiled = compileAgentModule(mod, input.source);
|
|
261
|
+
const gated = compiled.hitl;
|
|
262
|
+
if (gated && gated.size > 0 && !input.awaitApproval) {
|
|
263
|
+
throw new InProcessApprovalRequiredError([...gated.keys()]);
|
|
264
|
+
}
|
|
265
|
+
const resolve = input.awaitApproval;
|
|
266
|
+
const hitl = gated && gated.size > 0 && resolve ? {
|
|
267
|
+
gated,
|
|
268
|
+
awaitApproval: (approvalId, opts, toolName) => resolve({ approvalId, toolName, opts })
|
|
269
|
+
} : void 0;
|
|
270
|
+
const sessionId = input.sessionId ?? crypto.randomUUID();
|
|
271
|
+
return (async function* () {
|
|
272
|
+
if (compiled.skillsResolver) {
|
|
273
|
+
const enabled = await resolveEnabledSkills(compiled.skillsResolver, compiled.runContext ?? {});
|
|
274
|
+
if (enabled !== void 0) compiled.skills = { enabled, autoInject: true };
|
|
275
|
+
}
|
|
276
|
+
yield* deps.stream(compiled, apiKey, {
|
|
277
|
+
message: input.message,
|
|
278
|
+
sessionId,
|
|
279
|
+
hitl,
|
|
280
|
+
signal: input.signal
|
|
281
|
+
});
|
|
282
|
+
})();
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// src/server/agent/mcp-stdio.ts
|
|
286
|
+
async function handleMcpStdioLine(line, mod, name, appResources = []) {
|
|
287
|
+
const trimmed = line.trim();
|
|
288
|
+
if (trimmed.length === 0) return null;
|
|
289
|
+
let body;
|
|
290
|
+
try {
|
|
291
|
+
body = JSON.parse(trimmed);
|
|
292
|
+
} catch {
|
|
293
|
+
return JSON.stringify({
|
|
294
|
+
jsonrpc: "2.0",
|
|
295
|
+
id: null,
|
|
296
|
+
error: { code: -32700, message: "Parse error" }
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
const response = await handleMcpJsonRpc(mod, name, body, appResources);
|
|
300
|
+
const payload = await response.json();
|
|
301
|
+
return JSON.stringify(payload);
|
|
302
|
+
}
|
|
303
|
+
async function serveMcpStdio(mod, name, appResources, streams) {
|
|
304
|
+
for await (const line of streams.lines) {
|
|
305
|
+
const out = await handleMcpStdioLine(line, mod, name, appResources);
|
|
306
|
+
if (out !== null) streams.write(`${out}
|
|
307
|
+
`);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
export {
|
|
312
|
+
createWorkflowTool,
|
|
313
|
+
NodeAcpTransport,
|
|
314
|
+
createACPTool,
|
|
315
|
+
createVendorAgentTool,
|
|
316
|
+
CodeModePermissionDeniedError,
|
|
317
|
+
createCodeMode,
|
|
318
|
+
parseChannelPath,
|
|
319
|
+
isChannelPath,
|
|
320
|
+
handleChannelWebhook,
|
|
321
|
+
InProcessApprovalRequiredError,
|
|
322
|
+
streamAgentTurnInProcess,
|
|
323
|
+
handleMcpStdioLine,
|
|
324
|
+
serveMcpStdio
|
|
325
|
+
};
|
|
326
|
+
//# sourceMappingURL=chunk-SPFMJAFW.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/server/agent/workflow-tool.ts","../src/server/agent/acp-tool.ts","../src/server/agent/vendor-agent-tool.ts","../src/server/agent/code-mode.ts","../src/server/agent/channel-webhook.ts","../src/server/agent/stream-agent-turn-in-process.ts","../src/server/agent/mcp-stdio.ts"],"sourcesContent":["/**\n * M26 (ADR-0041) — `createWorkflowTool`: wrap an SDK `Workflow` as a `CustomTool`.\n *\n * THIN adapter. `packages/workflows/` stays G13-forbidden — the workflow ENGINE is the SDK's\n * (`Workflow.create(...).run(input)`). This exposes an already-built `Workflow` to an agent as one\n * callable tool: it validates the tool input, delegates to `workflow.run(input)`, and shapes the\n * result for the model. It calls no LLM, dispatches no tool, and runs no orchestration of its own —\n * the SDK owns all of that (sdk-runtime.md / G2).\n */\nimport { z } from 'zod'\n\nimport { defineAgentTool, type CustomTool } from '../define/define-agent-tool.js'\n\n/**\n * Structural stand-in for the SDK `Workflow` (the adapter never imports the SDK type — keeps the\n * SDK an optional peer). Any object with a `run(input)` resolving to `{ status, output }` matches.\n */\nexport interface WorkflowLike {\n run(input: unknown): Promise<{ status: string; output: unknown; runId?: string }>\n}\n\n/** Config for {@link createWorkflowTool}. `inputSchema` defaults to an open object. */\nexport interface WorkflowToolConfig {\n /** Tool name surfaced to the LLM. */\n name: string\n /** Tool description surfaced to the LLM. */\n description: string\n /** Zod schema for the workflow input (defaults to `z.object({}).passthrough()`). */\n inputSchema?: z.ZodType\n}\n\n/** Statuses `Workflow.run` reports as a non-success terminal state. */\nconst FAILURE_STATUSES = new Set(['failed', 'error', 'cancelled', 'canceled'])\n\n/**\n * Wrap an SDK `Workflow` as a {@link CustomTool}. Fails fast if `workflow` does not expose a\n * `run()` method (the SDK Workflow contract), so a mis-wired call is caught at definition time, not\n * at the first invocation (error-handling.md).\n */\nexport function createWorkflowTool(workflow: WorkflowLike, config: WorkflowToolConfig): CustomTool {\n const runFn = (workflow as { run?: unknown } | null | undefined)?.run\n if (typeof runFn !== 'function') {\n throw new Error(\n 'createWorkflowTool: the SDK does not expose a Workflow (expected an object with a run() method). ' +\n 'Pass a `Workflow.create(...).…build()` instance from @theokit/sdk.',\n )\n }\n // Open object by default so arbitrary workflow inputs pass through un-stripped.\n const inputSchema = config.inputSchema ?? z.looseObject({})\n\n return defineAgentTool({\n name: config.name,\n description: config.description,\n inputSchema: inputSchema as z.ZodObject<z.ZodRawShape>,\n handler: async (input: unknown): Promise<string> => {\n const run = await workflow.run(input)\n if (FAILURE_STATUSES.has(run.status)) {\n throw new Error(\n `createWorkflowTool(${JSON.stringify(config.name)}): workflow run ${\n run.runId ? `'${run.runId}' ` : ''\n }failed with status '${run.status}'.`,\n )\n }\n return typeof run.output === 'string' ? run.output : JSON.stringify(run.output)\n },\n })\n}\n","/**\n * M17 (theokit-ai-first) — createACPTool: wrap a coding agent (Claude Code, Amp, Codex) as a tool.\n *\n * Spawns the agent as a subprocess (Node `child_process` — an adapter concern per G8), drives it\n * with the transport-agnostic {@link AcpClient} over newline-delimited JSON-RPC, and returns a\n * `CustomTool`. `onPermissionRequest` is REQUIRED — security by default (no default-allow for file/\n * shell operations). The transport is injectable for tests.\n */\nimport { spawn, type ChildProcessByStdio } from 'node:child_process'\nimport type { Readable, Writable } from 'node:stream'\n\nimport { AcpClient, type AcpTransport } from '@theokit/agents'\nimport { encodeAcpMessage } from '@theokit/agents'\nimport type { CustomTool } from '@theokit/sdk'\n\n/** Stdio transport backed by a spawned subprocess (the default for {@link createACPTool}). */\nexport class NodeAcpTransport implements AcpTransport {\n // stdin=pipe, stdout=pipe, stderr=inherit → the third stream is null.\n private readonly proc: ChildProcessByStdio<Writable, Readable, null>\n\n constructor(command: string, args: string[] = [], cwd?: string) {\n this.proc = spawn(command, args, { cwd, stdio: ['pipe', 'pipe', 'inherit'] })\n }\n\n send(line: string): void {\n this.proc.stdin.write(line)\n }\n\n subscribe(onData: (chunk: string) => void): void {\n this.proc.stdout.on('data', (buf: Buffer) => {\n onData(buf.toString('utf8'))\n })\n }\n\n close(): void {\n this.proc.kill()\n }\n}\n\nexport interface AcpToolConfig {\n /** Executable for the coding agent (e.g. `claude`, `amp`, `codex`). */\n command: string\n /** Command-line arguments. */\n args?: string[]\n /** Working directory for the spawned agent. */\n cwd?: string\n /** Tool name the model calls. */\n name: string\n /** Tool description surfaced to the model. */\n description: string\n /**\n * REQUIRED — decide file/shell permission requests from the coding agent. Security by default:\n * there is NO default-allow. Return `{ granted: boolean }` (may be async).\n */\n onPermissionRequest: (params: unknown) => { granted: boolean } | Promise<{ granted: boolean }>\n /** Injected transport factory (defaults to spawning via {@link NodeAcpTransport}) — for tests. */\n transportFactory?: (config: AcpToolConfig) => AcpTransport\n}\n\nfunction defaultTransport(config: AcpToolConfig): AcpTransport {\n return new NodeAcpTransport(config.command, config.args, config.cwd)\n}\n\n/** Wrap a coding agent as a `CustomTool`. Fails fast if `onPermissionRequest` is missing. */\nexport function createACPTool(config: AcpToolConfig): CustomTool {\n if (typeof config.onPermissionRequest !== 'function') {\n throw new Error('[theokit] createACPTool requires onPermissionRequest (security by default — no default-allow)')\n }\n const makeTransport = config.transportFactory ?? defaultTransport\n return {\n name: config.name,\n description: config.description,\n inputSchema: {\n type: 'object',\n properties: { message: { type: 'string', description: 'The task/prompt for the coding agent.' } },\n required: ['message'],\n },\n handler: async (input: Record<string, unknown>): Promise<string> => {\n const message = typeof input.message === 'string' ? input.message : ''\n const client = new AcpClient(makeTransport(config))\n client.onRequest('session/request_permission', (params) => config.onPermissionRequest(params))\n const result = (await client.request('session/prompt', { message })) as { text?: string }\n return result.text ?? ''\n },\n }\n}\n\n// `encodeAcpMessage` is re-exported for callers building custom transports/handshakes.\nexport { encodeAcpMessage }\n","/**\n * M28 (ADR-0041) — `createVendorAgentTool`: expose a third-party agent SDK (Claude Agent SDK,\n * OpenAI, Cursor) behind a uniform `CustomTool`, mirroring the M17 ACP pattern.\n *\n * The vendor RUNTIME stays theirs — TheoKit only wires. The vendor client is INJECTED (the real\n * vendor SDK client in prod, a fake in tests), so no vendor dependency enters core; vendor-specific\n * client packages belong under `@theokit/agent-*`, never here. This calls no LLM of its own and runs\n * no loop — it delegates each prompt to `client.query(...)` (sdk-runtime.md / G2). Resume is threaded\n * via the vendor's own session id.\n */\nimport type { CustomTool } from '../define/define-agent-tool.js'\n\n/**\n * Structural contract a vendor agent client must satisfy (the adapter never imports a vendor type).\n * `query` runs one turn; `resumeSessionId` continues a prior vendor session; the returned\n * `sessionId` identifies the session to resume next.\n */\nexport interface VendorAgentClient {\n query(\n prompt: string,\n opts?: { resumeSessionId?: string },\n ): Promise<{ text: string; sessionId?: string }>\n}\n\n/** Config for {@link createVendorAgentTool}. */\nexport interface VendorAgentToolConfig {\n /** Vendor label (e.g. `claude`, `openai`, `cursor`). Drives the default tool name. */\n vendor: string\n /** The injected vendor client (real SDK client in prod, a fake in tests). */\n client: VendorAgentClient\n /** Tool name the model calls (defaults to `<vendor>_agent`). */\n name?: string\n /** Tool description surfaced to the model (defaults to a one-line delegate hint). */\n description?: string\n /**\n * Side-channel callback invoked with the vendor session id after each turn — lets the app capture\n * it for a later resume WITHOUT leaking session bookkeeping into the model's view of the result.\n */\n onSession?: (sessionId: string) => void\n}\n\n/**\n * Wrap a vendor agent SDK as a {@link CustomTool}. Fails fast if `vendor` is empty or the client\n * does not expose `query()` (error-handling.md) — a mis-wired call is caught at definition time.\n */\nexport function createVendorAgentTool(config: VendorAgentToolConfig): CustomTool {\n if (!config.vendor || config.vendor.length === 0) {\n throw new Error('createVendorAgentTool: `vendor` is required (e.g. \"claude\", \"openai\").')\n }\n const queryFn = (config.client as { query?: unknown } | null | undefined)?.query\n if (typeof queryFn !== 'function') {\n throw new Error(\n `createVendorAgentTool(${JSON.stringify(config.vendor)}): the vendor client does not expose a query() method. ` +\n 'Pass the vendor SDK client (or a @theokit/agent-* wrapper).',\n )\n }\n\n const name = config.name ?? `${config.vendor}_agent`\n const description =\n config.description ?? `Delegate a task to the ${config.vendor} agent and return its answer.`\n\n return {\n name,\n description,\n inputSchema: {\n type: 'object',\n properties: {\n prompt: { type: 'string', description: 'The task/prompt for the vendor agent.' },\n resumeSessionId: {\n type: 'string',\n description: 'Optional vendor session id to resume a prior conversation.',\n },\n },\n required: ['prompt'],\n },\n handler: async (input: Record<string, unknown>): Promise<string> => {\n const prompt = typeof input.prompt === 'string' ? input.prompt : ''\n const resumeSessionId =\n typeof input.resumeSessionId === 'string' ? input.resumeSessionId : undefined\n const result = await config.client.query(\n prompt,\n resumeSessionId !== undefined ? { resumeSessionId } : undefined,\n )\n if (result.sessionId !== undefined && config.onSession) config.onSession(result.sessionId)\n return result.text\n },\n }\n}\n","/**\n * M29 (ADR-0041) — `createCodeMode`: expose a set of tools to agent-authored code run inside an\n * ISOLATION boundary, so the agent composes tools programmatically instead of one call at a time.\n *\n * Security posture (the whole point of this feature):\n * - The isolation boundary (`sandbox`) is **injected**, never hand-rolled here (Top-risk 1). The app\n * supplies a vetted sandbox — isolated-vm, QuickJS-WASM, or a locked-down worker. TheoKit core\n * ships no VM and adds no sandbox dependency (same posture as the injected deploy adapter / the\n * M17 transport). `node:vm` is NOT a security boundary and MUST NOT be used as the sandbox.\n * - TheoKit owns the **restricted API** (only the declared tools are reachable from the code — no\n * `fs`, `process`, `require`, or network unless a declared, permission-gated tool provides it) and\n * the **mandatory permission gate**: every tool call from the code passes `onPermissionRequest`\n * first, and there is NO default-allow (mirrors M17 `onPermissionRequest`).\n *\n * Threat model (summary): a malicious model could author code that (a) calls a dangerous tool, or\n * (b) tries to reach a host capability. (a) is stopped by the permission gate (deny → the API call\n * throws). (b) is stopped by the injected sandbox (the restricted API is the ONLY surface the code\n * sees). If the app injects a weak sandbox, (b) is on the app — hence the vetted-sandbox requirement.\n */\nimport type { CustomTool } from '../define/define-agent-tool.js'\n\n/** The restricted API handed to sandboxed code: declared tool names → permission-gated callables. */\nexport type CodeModeApi = Record<string, (args: unknown) => Promise<unknown>>\n\n/** The injected isolation boundary. The app supplies a vetted implementation. */\nexport interface Sandbox {\n /** Run `code` with access to ONLY `api` (the restricted tool surface). Resolve the code's result. */\n run(code: string, api: CodeModeApi): Promise<unknown>\n}\n\n/** A permission decision for one tool call from sandboxed code. */\nexport interface CodeModePermission {\n granted: boolean\n /** Optional reason surfaced to the model on denial. */\n reason?: string\n}\n\nexport interface CodeModeConfig {\n /** The tools reachable from the code (the restricted API). */\n tools: CustomTool[]\n /** The injected isolation boundary (vetted sandbox — NEVER node:vm). */\n sandbox: Sandbox\n /**\n * REQUIRED — decide each tool call the code attempts. Security by default: NO default-allow.\n * Return `{ granted }` (may be async). Mirrors M17 `onPermissionRequest`.\n */\n onPermissionRequest: (req: {\n tool: string\n args: unknown\n }) => CodeModePermission | Promise<CodeModePermission>\n /** Tool name the model calls (default `run_code`). */\n name?: string\n /** Tool description surfaced to the model. */\n description?: string\n}\n\n/** Thrown when the permission gate denies a tool call from sandboxed code. */\nexport class CodeModePermissionDeniedError extends Error {\n constructor(tool: string, reason?: string) {\n const suffix = reason ? `: ${reason}` : ''\n super(`code-mode: tool '${tool}' denied by permission gate${suffix}`)\n this.name = 'CodeModePermissionDeniedError'\n }\n}\n\n/** M40 (ADR-0049) — render a tool's JSON-Schema input as a readable arg shape, e.g. `{ limit: number, region?: string }`. */\nfunction describeToolInput(inputSchema: Record<string, unknown>): string {\n const props = (inputSchema.properties as Record<string, { type?: unknown }> | undefined) ?? {}\n const required = new Set((inputSchema.required as string[] | undefined) ?? [])\n const entries = Object.entries(props).map(([key, spec]) => {\n // Complex Zod types (union/intersection/enum) may emit no top-level `type` → 'unknown' (safe).\n const type = typeof spec.type === 'string' ? spec.type : 'unknown'\n return `${key}${required.has(key) ? '' : '?'}: ${type}`\n })\n return entries.length > 0 ? `{ ${entries.join(', ')} }` : '{}'\n}\n\n/**\n * M40 (ADR-0049) — generate the model-facing instructions from the SAME `tools` allow-list\n * `createCodeMode` captures (DRY — cannot drift from the api surface it describes). Teaches the model\n * that its code runs in a sandbox, the available `api.<name>(input)` calls (ONLY this allow-list —\n * least-privilege scoping), and the return contract. Add it to the agent's system prompt.\n */\nfunction generateCodeModeInstructions(tools: CustomTool[], toolName: string): string {\n const calls = tools\n .map((t) => `- \\`await api.${t.name}(${describeToolInput(t.inputSchema)})\\` — ${t.description}`)\n .join('\\n')\n return [\n `The \\`${toolName}\\` tool runs your code in a sandbox. Your code may call ONLY these functions (each bridges to a real, validated tool on the host):`,\n calls,\n 'Write an async function body that composes these calls and return exactly ONE structured result. Prefer `Promise.all` for independent calls; do arithmetic and aggregation in code, not in prose.',\n ].join('\\n\\n')\n}\n\n/**\n * Build a code-mode tool + its generated model instructions (M40 / ADR-0049). Fails fast if\n * `onPermissionRequest` or `sandbox` is missing (security by default). `tool` takes `{ code }`,\n * assembles the permission-gated restricted API from `tools`, runs the code in the injected sandbox,\n * and returns the code's result. `instructions` (add it to the agent's system prompt) teaches the\n * model the sandboxed-code contract + the available `api.<name>(input)` calls.\n */\nexport function createCodeMode(config: CodeModeConfig): { tool: CustomTool; instructions: string } {\n if (typeof config.onPermissionRequest !== 'function') {\n throw new Error(\n 'createCodeMode requires onPermissionRequest (security by default — no default-allow for any tool).',\n )\n }\n const sandboxRun = (config.sandbox as { run?: unknown } | null | undefined)?.run\n if (typeof sandboxRun !== 'function') {\n throw new Error(\n 'createCodeMode requires an injected `sandbox` with a run() method (a vetted isolation boundary — never node:vm).',\n )\n }\n // M40 — an empty allow-list is a config mistake: the restricted API (and the generated\n // instructions) would be empty, and the code could call nothing. Fail fast.\n if (config.tools.length === 0) {\n throw new Error(\n 'createCodeMode requires a non-empty tools[] — the restricted API would be empty.',\n )\n }\n\n // Assemble the restricted API: each declared tool becomes a permission-gated callable.\n const api: CodeModeApi = {}\n for (const tool of config.tools) {\n api[tool.name] = async (args: unknown): Promise<unknown> => {\n const decision = await config.onPermissionRequest({ tool: tool.name, args })\n if (!decision.granted) throw new CodeModePermissionDeniedError(tool.name, decision.reason)\n return tool.handler(args as Record<string, unknown>)\n }\n }\n\n const name = config.name ?? 'run_code'\n const tool: CustomTool = {\n name,\n description:\n config.description ??\n 'Run code that composes the available tools. Only the declared tools are callable.',\n inputSchema: {\n type: 'object',\n properties: { code: { type: 'string', description: 'The code to run in the sandbox.' } },\n required: ['code'],\n },\n handler: async (input: Record<string, unknown>): Promise<string> => {\n const code = typeof input.code === 'string' ? input.code : ''\n const result = await config.sandbox.run(code, api)\n return typeof result === 'string' ? result : JSON.stringify(result)\n },\n }\n return { tool, instructions: generateCodeModeInstructions(config.tools, name) }\n}\n","/**\n * M27 (ADR-0041) — channel webhook routes: `POST /api/agents/<name>/channels/<platform>/webhook`.\n *\n * Auto-generates a per-platform inbound webhook endpoint that VALIDATES the platform signature\n * (reusing the existing webhook `VerifyFn` providers — Slack/Telegram/Discord — never a hand-rolled\n * scheme) and hands the parsed payload to an injected `onMessage` seam. The seam is where an app\n * wires the SDK gateway package (`@theokit/gateway-*`) that translates the payload into an agent\n * turn — TheoKit provides the route + signature gate, NOT the gateway's parsing (G2 / it does not\n * reimplement the gateway).\n */\nimport type { VerifyFn } from '../webhook/webhook-types.js'\n\nconst CHANNEL_PATH = /^\\/api\\/agents\\/([^/]+)\\/channels\\/([^/]+)\\/webhook$/\n\n/** Parsed `{ agent, platform }` from a channel webhook path, or `null` when it doesn't match. */\nexport function parseChannelPath(urlPath: string): { agent: string; platform: string } | null {\n const match = CHANNEL_PATH.exec(urlPath)\n if (!match) return null\n return { agent: decodeURIComponent(match[1]), platform: decodeURIComponent(match[2]) }\n}\n\n/** True when `urlPath` targets a channel webhook (dev/prod routing branches on this). */\nexport function isChannelPath(urlPath: string): boolean {\n return CHANNEL_PATH.test(urlPath)\n}\n\n/** The inbound message handed to the app after signature validation passes. */\nexport interface ChannelMessage {\n agent: string\n platform: string\n /** The parsed JSON payload from the platform (the gateway translates this to an agent turn). */\n payload: unknown\n}\n\nexport interface ChannelWebhookConfig {\n /** Per-platform signature validators (e.g. `{ slack: slack({...}), telegram: telegram({...}) }`). */\n validators: Record<string, VerifyFn>\n /** Handoff seam — wire the SDK gateway / agent here. Invoked only after signature validation. */\n onMessage: (message: ChannelMessage) => void | Promise<void>\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 * Handle one channel webhook request. Returns:\n * 404 UNKNOWN_PLATFORM — no validator configured for `<platform>`\n * 400 BAD_REQUEST — path is not a channel webhook, or the body is not JSON\n * 401 INVALID_SIGNATURE — the platform signature check failed (negative case)\n * 200 { ok: true } — validated + handed to `onMessage`\n */\nexport async function handleChannelWebhook(\n request: Request,\n urlPath: string,\n config: ChannelWebhookConfig,\n): Promise<Response> {\n const parsed = parseChannelPath(urlPath)\n if (parsed === null) {\n return jsonError(\n 400,\n 'BAD_REQUEST',\n 'Path must be /api/agents/<name>/channels/<platform>/webhook.',\n )\n }\n if (!Object.hasOwn(config.validators, parsed.platform)) {\n return jsonError(\n 404,\n 'UNKNOWN_PLATFORM',\n `No validator configured for platform '${parsed.platform}'.`,\n )\n }\n const verify = config.validators[parsed.platform]\n\n // Validate the signature against a CLONE so the body stays readable for the payload parse.\n const verifyResult = await verify(request.clone())\n if (!verifyResult.ok) {\n return jsonError(\n 401,\n 'INVALID_SIGNATURE',\n `Signature validation failed: ${verifyResult.reason}`,\n )\n }\n\n let payload: unknown\n try {\n payload = await request.json()\n } catch {\n return jsonError(400, 'BAD_REQUEST', 'Request body must be JSON.')\n }\n\n await config.onMessage({ agent: parsed.agent, platform: parsed.platform, payload })\n return new Response(JSON.stringify({ ok: true }), {\n status: 200,\n headers: { 'content-type': 'application/json' },\n })\n}\n","/**\n * M35 (multi-surface) — the in-process agent-turn seam (Model A).\n *\n * The FRAMEWORK-owned sibling of the HTTP `mountAgent` and the stdout `runAgentInTerminal`: it runs a\n * compiled agent with the SAME `compileAgentModule` + SAME `streamAgentUIMessages` (G2 — reuses the\n * SDK runtime, reimplements nothing), but returns the raw `UIMessageChunk` generator so ANY consumer\n * drives it directly — the Ink TUI (M35), a Tauri window (M36), or a test — in a SINGLE process with\n * NO HTTP loopback, NO port, and NO CSRF (there is no network boundary to defend).\n *\n * The ONLY difference from the HTTP mount is HITL resolution: the mount pauses the run and resolves\n * the approval via a SECOND HTTP request to `/approve/:id` (the approval registry). In-process there\n * is no second request — the caller resolves the approval INLINE via `awaitApproval` (e.g. the Ink\n * TUI's y/n prompt). The gated-tool map is `compiled.hitl` verbatim, so the pause semantics are\n * byte-identical to the HTTP path; only the resolver differs. Parity with the mount is by\n * construction: both compile the module, resolve function-form skills, and call `streamAgentUIMessages`\n * with the same `{ message, sessionId, hitl }`.\n *\n * Consumers WILL still receive `tool-approval-request` chunks from the returned generator — they are\n * INFORMATIONAL (render them or ignore them). The authoritative human gate is `awaitApproval`, which\n * the SDK awaits BEFORE the gated tool runs; the chunk is not the gate.\n */\nimport {\n compileAgentModule,\n resolveEnabledSkills,\n streamAgentUIMessages,\n type HitlDecision,\n type HumanInTheLoopOptions,\n} from '@theokit/agents'\nimport type { UIMessageChunk } from 'ai'\n\n/** An inline approval request handed to the caller's `awaitApproval` (the Ink/Tauri prompt). */\nexport interface InProcessApprovalRequest {\n approvalId: string\n toolName: string\n opts: HumanInTheLoopOptions\n}\n\n/** Resolve one gated-tool approval inline (approve/deny, or a structured {@link HitlDecision}). */\nexport type InProcessAwaitApproval = (\n req: InProcessApprovalRequest,\n) => Promise<boolean | HitlDecision>\n\nexport interface StreamAgentTurnInProcessInput {\n message: string\n /** Resume key; a fresh id per run when omitted. */\n sessionId?: string\n /**\n * Inline HITL resolver — required IFF the agent has `@HumanInTheLoop`-gated tools. Omitting it for a\n * gated agent is a fail-fast error, never a silent bypass (Rule 8, the #99 lesson).\n */\n awaitApproval?: InProcessAwaitApproval\n /** Labels a fail-fast `AgentDefinitionError` (the file path). */\n source?: string\n /** Abort signal forwarded to the SDK stream (client disconnect / window close). */\n signal?: AbortSignal\n}\n\n/** Injectable stream fn (defaults to the real SDK bridge) — lets tests drive a deterministic stream. */\nexport interface StreamAgentTurnDeps {\n stream: typeof streamAgentUIMessages\n}\n\n/**\n * Thrown when a gated agent is run in-process without an `awaitApproval` resolver. Refusing loudly is\n * the correct posture: silently running a `@HumanInTheLoop`-gated tool with no human gate is exactly\n * the #99 class of bug. Typed so callers can catch it distinctly.\n */\nexport class InProcessApprovalRequiredError extends Error {\n constructor(toolNames: readonly string[]) {\n super(\n `Agent has HITL-gated tool(s) [${toolNames.join(', ')}] but no \\`awaitApproval\\` resolver was ` +\n `supplied to streamAgentTurnInProcess. In-process runs must resolve approvals inline — pass ` +\n `awaitApproval, or remove the gate. Refused (fail-closed).`,\n )\n this.name = 'InProcessApprovalRequiredError'\n }\n}\n\n/**\n * Run a compiled agent in-process and return its `UIMessageChunk` stream. `apiKey` is resolved by the\n * caller (same contract as the HTTP mount). Validation + compile happen SYNCHRONOUSLY (so a gated\n * agent without a resolver throws at call time, not lazily on first iteration); the returned value is\n * the SDK's `streamAgentUIMessages` generator.\n */\nexport function streamAgentTurnInProcess(\n mod: unknown,\n apiKey: string,\n input: StreamAgentTurnInProcessInput,\n deps: StreamAgentTurnDeps = { stream: streamAgentUIMessages },\n): AsyncGenerator<UIMessageChunk> {\n const compiled = compileAgentModule(mod, input.source)\n const gated = compiled.hitl\n\n // Fail-fast BEFORE building the stream: a gated agent with no inline resolver would silently bypass\n // the human gate (the #99 class of bug). Refuse loudly (Rule 8, fail-closed).\n if (gated && gated.size > 0 && !input.awaitApproval) {\n throw new InProcessApprovalRequiredError([...gated.keys()])\n }\n const resolve = input.awaitApproval\n\n // Mirror mount-agent's HITL wiring cast-free (structural inference); absent gate ⇒ the non-HITL\n // stream path (M2), unchanged. The SDK calls (approvalId, opts, toolName); route to the caller.\n const hitl =\n gated && gated.size > 0 && resolve\n ? {\n gated,\n awaitApproval: (approvalId: string, opts: HumanInTheLoopOptions, toolName: string) =>\n resolve({ approvalId, toolName, opts }),\n }\n : undefined\n\n const sessionId = input.sessionId ?? crypto.randomUUID()\n\n // Resolve function-form skills (`defineAgent({ skills: (ctx) => [...] })`) BEFORE streaming — exact\n // parity with mount-agent. Done INSIDE the returned generator so the synchronous fail-fast above is\n // preserved (the caller still gets a plain `AsyncGenerator`, no `await` at the call site). A static\n // skill list leaves `skillsResolver` undefined and this is a no-op.\n return (async function* () {\n if (compiled.skillsResolver) {\n const enabled = await resolveEnabledSkills(compiled.skillsResolver, compiled.runContext ?? {})\n if (enabled !== undefined) compiled.skills = { enabled, autoInject: true }\n }\n yield* deps.stream(compiled, apiKey, {\n message: input.message,\n sessionId,\n hitl,\n signal: input.signal,\n })\n })()\n}\n","/**\n * MCP stdio transport (M16 follow-up) — expose a TheoKit agent as an MCP server over stdin/stdout,\n * the sibling of the M16 HTTP route (`POST /api/agents/<name>/mcp`). A desktop MCP client (e.g.\n * Claude Desktop) spawns `theokit mcp <agent>` and speaks newline-delimited JSON-RPC over the pipe.\n *\n * This is a TRANSPORT over the framework's OWN {@link handleMcpJsonRpc} — it reuses the exact handler\n * the HTTP route uses; it calls no LLM, spawns no MCP client, and reimplements no runtime (G2 /\n * sdk-runtime.md). Distinct from the SDK's MCP CLIENT stdio (which spawns external MCP servers via\n * `mcpServers` command/args) — that stays SDK-side. Here TheoKit is the SERVER. The framework-side\n * placement of this server-exposure transport is recorded in ADR-0042 (refining ADR-0040's M16 note).\n */\nimport type { AppResource } from './mcp-app-resources.js'\nimport { handleMcpJsonRpc } from './mcp-handler.js'\n\n/**\n * Handle one newline-delimited JSON-RPC line. Returns the response line to write to stdout, or\n * `null` for a blank line (nothing to emit). A malformed JSON line yields a `-32700` (Parse error)\n * envelope — never throws, so the stdio loop never dies on bad input.\n */\nexport async function handleMcpStdioLine(\n line: string,\n mod: unknown,\n name: string,\n appResources: readonly AppResource[] = [],\n): Promise<string | null> {\n const trimmed = line.trim()\n if (trimmed.length === 0) return null\n let body: unknown\n try {\n body = JSON.parse(trimmed)\n } catch {\n return JSON.stringify({\n jsonrpc: '2.0',\n id: null,\n error: { code: -32700, message: 'Parse error' },\n })\n }\n const response = await handleMcpJsonRpc(mod, name, body, appResources)\n const payload: unknown = await response.json()\n return JSON.stringify(payload)\n}\n\n/** A minimal readable line source (an async iterable of lines) + a writable sink. */\nexport interface StdioStreams {\n /** Async iterable of newline-delimited input lines (e.g. `readline.createInterface({ input })`). */\n lines: AsyncIterable<string>\n /** Write a response line (the caller appends no newline). */\n write: (line: string) => void\n}\n\n/**\n * Drive the MCP stdio server loop: for each input line, dispatch via {@link handleMcpStdioLine} and\n * write the response line (with a trailing `\\n`). Returns when the input stream ends (EOF).\n */\nexport async function serveMcpStdio(\n mod: unknown,\n name: string,\n appResources: readonly AppResource[],\n streams: StdioStreams,\n): Promise<void> {\n for await (const line of streams.lines) {\n const out = await handleMcpStdioLine(line, mod, name, appResources)\n if (out !== null) streams.write(`${out}\\n`)\n }\n}\n"],"mappings":"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","names":["tool"]}
|