@tangle-network/agent-app 0.39.0 → 0.39.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/{auth-BlS9GWfL.d.ts → auth-DcK5ERaL.d.ts} +7 -4
- package/dist/{chunk-IOREXWLM.js → chunk-2FDTJIU4.js} +8 -35
- package/dist/{chunk-IOREXWLM.js.map → chunk-2FDTJIU4.js.map} +1 -1
- package/dist/{chunk-CDYYR3Z7.js → chunk-AF5ZST7M.js} +60 -26
- package/dist/chunk-AF5ZST7M.js.map +1 -0
- package/dist/{chunk-JT6HHO5P.js → chunk-BUUJ7TEQ.js} +8 -33
- package/dist/{chunk-JT6HHO5P.js.map → chunk-BUUJ7TEQ.js.map} +1 -1
- package/dist/{chunk-VT5DI6GL.js → chunk-FDJ6JQCI.js} +3 -3
- package/dist/{chunk-JZZ6AWF4.js → chunk-PPSZNVKT.js} +9 -7
- package/dist/chunk-PPSZNVKT.js.map +1 -0
- package/dist/{chunk-A76ZHWNF.js → chunk-RH74YJIK.js} +31 -1
- package/dist/chunk-RH74YJIK.js.map +1 -0
- package/dist/{chunk-4IQMTTNE.js → chunk-TQ5M7BFV.js} +2 -2
- package/dist/design-canvas/index.d.ts +8 -21
- package/dist/design-canvas/index.js +2 -2
- package/dist/eval/index.d.ts +1 -1
- package/dist/index.d.ts +5 -5
- package/dist/index.js +10 -9
- package/dist/{mcp-BShTlESm.d.ts → mcp-4I_RHhNa.d.ts} +38 -3
- package/dist/preset-cloudflare/index.d.ts +1 -1
- package/dist/runtime/index.d.ts +3 -3
- package/dist/runtime/index.js +2 -2
- package/dist/sandbox/index.d.ts +2 -2
- package/dist/sandbox/index.js +4 -4
- package/dist/sequences/index.d.ts +10 -27
- package/dist/sequences/index.js +5 -4
- package/dist/tools/index.d.ts +5 -5
- package/dist/tools/index.js +5 -3
- package/dist/{types-2rOJo8Hc.d.ts → types-wHs0rmtu.d.ts} +12 -1
- package/package.json +1 -1
- package/dist/chunk-A76ZHWNF.js.map +0 -1
- package/dist/chunk-CDYYR3Z7.js.map +0 -1
- package/dist/chunk-JZZ6AWF4.js.map +0 -1
- /package/dist/{chunk-VT5DI6GL.js.map → chunk-FDJ6JQCI.js.map} +0 -0
- /package/dist/{chunk-4IQMTTNE.js.map → chunk-TQ5M7BFV.js.map} +0 -0
package/dist/sequences/index.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
DEFAULT_SEQUENCES_MCP_DESCRIPTION,
|
|
3
3
|
MAX_CAPTION_BATCH,
|
|
4
|
-
SEQUENCES_MCP_PROTOCOL_VERSIONS,
|
|
5
4
|
SEQUENCE_EXPORT_FORMATS,
|
|
6
5
|
SEQUENCE_MCP_TOOLS,
|
|
7
6
|
SEQUENCE_MEDIA_KINDS,
|
|
@@ -41,7 +40,7 @@ import {
|
|
|
41
40
|
validateSetClipText,
|
|
42
41
|
validateSplitClip,
|
|
43
42
|
validateTrimClip
|
|
44
|
-
} from "../chunk-
|
|
43
|
+
} from "../chunk-BUUJ7TEQ.js";
|
|
45
44
|
import {
|
|
46
45
|
MIN_SEQUENCE_CLIP_FRAMES,
|
|
47
46
|
assertClipFitsSequence,
|
|
@@ -56,12 +55,14 @@ import {
|
|
|
56
55
|
trackIntervals
|
|
57
56
|
} from "../chunk-ZYBWGSAZ.js";
|
|
58
57
|
import "../chunk-HFC4BTWJ.js";
|
|
59
|
-
import
|
|
58
|
+
import {
|
|
59
|
+
MCP_PROTOCOL_VERSIONS
|
|
60
|
+
} from "../chunk-RH74YJIK.js";
|
|
60
61
|
export {
|
|
61
62
|
DEFAULT_SEQUENCES_MCP_DESCRIPTION,
|
|
62
63
|
MAX_CAPTION_BATCH,
|
|
63
64
|
MIN_SEQUENCE_CLIP_FRAMES,
|
|
64
|
-
SEQUENCES_MCP_PROTOCOL_VERSIONS,
|
|
65
|
+
MCP_PROTOCOL_VERSIONS as SEQUENCES_MCP_PROTOCOL_VERSIONS,
|
|
65
66
|
SEQUENCE_EXPORT_FORMATS,
|
|
66
67
|
SEQUENCE_MCP_TOOLS,
|
|
67
68
|
SEQUENCE_MEDIA_KINDS,
|
package/dist/tools/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { a as AppToolName, c as ToolHeaderNames } from '../auth-
|
|
2
|
-
export { A as APP_TOOL_NAMES, b as AuthenticateOptions, D as DEFAULT_HEADER_NAMES, O as OpenAIFunctionTool, T as ToolAuthResult, d as authenticateToolRequest, e as buildAppToolOpenAITools, i as isAppToolName, r as readToolArgs } from '../auth-
|
|
3
|
-
import { f as AppToolTaxonomy, c as AppToolHandlers, b as AppToolContext, e as AppToolProducedEvent, d as AppToolOutcome } from '../types-
|
|
4
|
-
export { A as AddCitationArgs, a as AddCitationResult, R as RenderUiArgs, g as RenderUiResult, S as ScheduleFollowupArgs, h as ScheduleFollowupResult, i as SubmitProposalArgs, j as SubmitProposalResult } from '../types-
|
|
5
|
-
export { A as AppToolMcpServer, B as BuildHttpMcpServerOptions, a as BuildMcpServerOptions, D as DEFAULT_APP_TOOL_PATHS, b as buildAppToolMcpServer, c as buildHttpMcpServer } from '../mcp-
|
|
1
|
+
import { a as AppToolName, c as ToolHeaderNames } from '../auth-DcK5ERaL.js';
|
|
2
|
+
export { A as APP_TOOL_NAMES, b as AuthenticateOptions, D as DEFAULT_HEADER_NAMES, O as OpenAIFunctionTool, T as ToolAuthResult, d as authenticateToolRequest, e as buildAppToolOpenAITools, i as isAppToolName, r as readToolArgs } from '../auth-DcK5ERaL.js';
|
|
3
|
+
import { f as AppToolTaxonomy, c as AppToolHandlers, b as AppToolContext, e as AppToolProducedEvent, d as AppToolOutcome } from '../types-wHs0rmtu.js';
|
|
4
|
+
export { A as AddCitationArgs, a as AddCitationResult, B as BuildAppToolsOptions, R as RenderUiArgs, g as RenderUiResult, S as ScheduleFollowupArgs, h as ScheduleFollowupResult, i as SubmitProposalArgs, j as SubmitProposalResult } from '../types-wHs0rmtu.js';
|
|
5
|
+
export { A as AppToolMcpServer, B as BuildHttpMcpServerOptions, a as BuildMcpServerOptions, D as DEFAULT_APP_TOOL_PATHS, S as ScopedMcpServerEntryOptions, b as buildAppToolMcpServer, c as buildHttpMcpServer, d as buildScopedMcpServerEntry } from '../mcp-4I_RHhNa.js';
|
|
6
6
|
export { C as CreateMcpToolHandlerOptions, M as MCP_PROTOCOL_VERSIONS, a as McpProtocolVersion, b as McpServerInfo, c as McpToolDefinition, d as createMcpToolHandler } from '../mcp-rpc-DLw_r9PQ.js';
|
|
7
7
|
|
|
8
8
|
/** A correctable bad-input error a tool handler throws; the HTTP layer maps it
|
package/dist/tools/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
restrictTaxonomy,
|
|
7
7
|
verifyCapabilityToken,
|
|
8
8
|
verifyExpiringCapabilityToken
|
|
9
|
-
} from "../chunk-
|
|
9
|
+
} from "../chunk-FDJ6JQCI.js";
|
|
10
10
|
import {
|
|
11
11
|
DEFAULT_APP_TOOL_PATHS,
|
|
12
12
|
DEFAULT_HEADER_NAMES,
|
|
@@ -14,9 +14,10 @@ import {
|
|
|
14
14
|
authenticateToolRequest,
|
|
15
15
|
buildAppToolMcpServer,
|
|
16
16
|
buildHttpMcpServer,
|
|
17
|
+
buildScopedMcpServerEntry,
|
|
17
18
|
createMcpToolHandler,
|
|
18
19
|
readToolArgs
|
|
19
|
-
} from "../chunk-
|
|
20
|
+
} from "../chunk-RH74YJIK.js";
|
|
20
21
|
import {
|
|
21
22
|
APP_TOOL_NAMES,
|
|
22
23
|
ToolInputError,
|
|
@@ -25,7 +26,7 @@ import {
|
|
|
25
26
|
dispatchAppTool,
|
|
26
27
|
isAppToolName,
|
|
27
28
|
outcomeStatus
|
|
28
|
-
} from "../chunk-
|
|
29
|
+
} from "../chunk-PPSZNVKT.js";
|
|
29
30
|
export {
|
|
30
31
|
APP_TOOL_NAMES,
|
|
31
32
|
DEFAULT_APP_TOOL_PATHS,
|
|
@@ -36,6 +37,7 @@ export {
|
|
|
36
37
|
buildAppToolMcpServer,
|
|
37
38
|
buildAppToolOpenAITools,
|
|
38
39
|
buildHttpMcpServer,
|
|
40
|
+
buildScopedMcpServerEntry,
|
|
39
41
|
createAppToolRuntimeExecutor,
|
|
40
42
|
createCapabilityToken,
|
|
41
43
|
createExpiringCapabilityToken,
|
|
@@ -37,6 +37,17 @@ interface AppToolTaxonomy {
|
|
|
37
37
|
* approval executor. */
|
|
38
38
|
regulatedTypes: readonly string[];
|
|
39
39
|
}
|
|
40
|
+
/** Optional overrides for {@link buildAppToolOpenAITools}. The tool NAMES and
|
|
41
|
+
* JSON-Schema parameter shapes are mechanism (stable identifiers the model and
|
|
42
|
+
* both transports rely on); the model-facing PROSE and the follow-up priority
|
|
43
|
+
* vocabulary are domain — a product with no vault/contacts/OpenUI can retune
|
|
44
|
+
* them here instead of forking the shell. Omit for the defaults. */
|
|
45
|
+
interface BuildAppToolsOptions {
|
|
46
|
+
/** Override the model-facing description of any of the four tools. */
|
|
47
|
+
descriptions?: Partial<Record<'submit_proposal' | 'schedule_followup' | 'render_ui' | 'add_citation', string>>;
|
|
48
|
+
/** The `schedule_followup.priority` enum (defaults to `['low','medium','high']`). */
|
|
49
|
+
priorityValues?: readonly string[];
|
|
50
|
+
}
|
|
40
51
|
interface SubmitProposalArgs {
|
|
41
52
|
type: string;
|
|
42
53
|
title: string;
|
|
@@ -131,4 +142,4 @@ type AppToolOutcome = {
|
|
|
131
142
|
status?: number;
|
|
132
143
|
};
|
|
133
144
|
|
|
134
|
-
export type { AddCitationArgs as A, RenderUiArgs as R, ScheduleFollowupArgs as S, AddCitationResult as a, AppToolContext as b, AppToolHandlers as c, AppToolOutcome as d, AppToolProducedEvent as e, AppToolTaxonomy as f, RenderUiResult as g, ScheduleFollowupResult as h, SubmitProposalArgs as i, SubmitProposalResult as j };
|
|
145
|
+
export type { AddCitationArgs as A, BuildAppToolsOptions as B, RenderUiArgs as R, ScheduleFollowupArgs as S, AddCitationResult as a, AppToolContext as b, AppToolHandlers as c, AppToolOutcome as d, AppToolProducedEvent as e, AppToolTaxonomy as f, RenderUiResult as g, ScheduleFollowupResult as h, SubmitProposalArgs as i, SubmitProposalResult as j };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tangle-network/agent-app",
|
|
3
|
-
"version": "0.39.
|
|
3
|
+
"version": "0.39.1",
|
|
4
4
|
"packageManager": "pnpm@10.33.4",
|
|
5
5
|
"description": "Application-shell framework for Tangle agent products: a bounded tool loop, the structured agent→app tool side channel, integration-hub client, per-workspace billing, and crypto — composed over the Tangle agent substrate through typed seams.",
|
|
6
6
|
"keywords": [
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/tools/auth.ts","../src/tools/mcp.ts","../src/tools/mcp-rpc.ts"],"sourcesContent":["import type { AppToolContext } from './types'\n\n/**\n * Header names carrying the server-set per-turn context + the capability token.\n * Defaults are product-neutral (`X-Agent-App-*`); a product that already ships\n * a header convention (e.g. `X-Acme-User-Id`) passes its own.\n */\nexport interface ToolHeaderNames {\n userId: string\n workspaceId: string\n threadId: string\n}\n\nexport const DEFAULT_HEADER_NAMES: ToolHeaderNames = {\n userId: 'X-Agent-App-User-Id',\n workspaceId: 'X-Agent-App-Workspace-Id',\n threadId: 'X-Agent-App-Thread-Id',\n}\n\nexport interface AuthenticateOptions {\n /** Verify the bearer capability token belongs to `userId`. The product's\n * HMAC/JWT impl — the seam that keeps token crypto out of this package. */\n verifyToken: (userId: string, bearer: string) => Promise<boolean>\n headerNames?: ToolHeaderNames\n}\n\nexport type ToolAuthResult =\n | { ok: true; ctx: AppToolContext }\n | { ok: false; response: Response }\n\n/**\n * Recover + verify the trusted context for a tool request. The user comes from\n * a server-set header and the bearer token MUST verify against THAT user; the\n * workspace comes from a header too — never from tool args — so the model can\n * neither forge identity nor target another workspace. Fail-closed: any missing\n * credential or a token minted for another user yields a 401/400 Response.\n */\nexport async function authenticateToolRequest(request: Request, opts: AuthenticateOptions): Promise<ToolAuthResult> {\n const h = opts.headerNames ?? DEFAULT_HEADER_NAMES\n const userId = request.headers.get(h.userId)?.trim()\n const workspaceId = request.headers.get(h.workspaceId)?.trim()\n const threadId = request.headers.get(h.threadId)?.trim() || null\n const bearer = request.headers.get('authorization')?.match(/^Bearer\\s+(.+)$/i)?.[1]\n\n if (!userId || !bearer) {\n return { ok: false, response: Response.json({ error: 'Missing capability credentials' }, { status: 401 }) }\n }\n if (!(await opts.verifyToken(userId, bearer))) {\n return { ok: false, response: Response.json({ error: 'Invalid capability token' }, { status: 401 }) }\n }\n if (!workspaceId) {\n return { ok: false, response: Response.json({ error: 'Missing workspace context' }, { status: 400 }) }\n }\n return { ok: true, ctx: { userId, workspaceId, threadId } }\n}\n\n/** Read a tool's argument object from the request body, tolerant of MCP host\n * aliases (`args` / `arguments`) or a bare body. Returns null on non-JSON. */\nexport async function readToolArgs<T>(request: Request): Promise<T | null> {\n let body: { args?: T; arguments?: T }\n try {\n body = (await request.json()) as typeof body\n } catch {\n return null\n }\n return (body.args ?? body.arguments ?? (body as T)) as T\n}\n","import type { AppToolContext } from './types'\nimport type { AppToolName } from './openai'\nimport type { ToolHeaderNames } from './auth'\nimport { DEFAULT_HEADER_NAMES } from './auth'\n\n/** Default route path each app tool is served at. A product mounts its routes\n * at these paths (or supplies its own via {@link BuildMcpServerOptions.paths}). */\nexport const DEFAULT_APP_TOOL_PATHS: Record<AppToolName, string> = {\n submit_proposal: '/api/tools/propose',\n schedule_followup: '/api/tools/followup',\n render_ui: '/api/tools/render-ui',\n add_citation: '/api/tools/citation',\n}\n\n/** The portable MCP server entry the sandbox SDK accepts (transport + url +\n * headers). Matches `AgentProfileMcpServer` structurally without importing the\n * sandbox SDK — products spread it into their profile's `mcp` map. */\nexport interface AppToolMcpServer {\n transport: 'http'\n url: string\n headers: Record<string, string>\n enabled: true\n metadata: { description: string }\n}\n\nexport interface BuildHttpMcpServerOptions {\n /** Route path on the app the sandbox POSTs to (e.g. `/api/tools/propose`). */\n path: string\n /** App base URL the sandbox reaches back to (no trailing slash required). */\n baseUrl: string\n /** Per-user capability token, baked into the Authorization header. */\n token: string\n ctx: AppToolContext\n /** Tool description the model sees. */\n description: string\n headerNames?: ToolHeaderNames\n}\n\n/**\n * Build ONE HTTP MCP server entry — the generic agent→app bridge. The\n * capability token + the user/workspace/thread ids ride in server-set headers\n * (never tool args), so the model can't forge identity or target another\n * workspace. Workspace/thread headers are omitted when their `ctx` value is\n * empty/null (e.g. an integration-invoke bridge that's user-scoped only). Used\n * directly for non-app-tool bridges (integration_invoke) and via\n * {@link buildAppToolMcpServer} for the four app tools.\n */\nexport function buildHttpMcpServer(opts: BuildHttpMcpServerOptions): AppToolMcpServer {\n const base = opts.baseUrl.replace(/\\/+$/, '')\n const h = opts.headerNames ?? DEFAULT_HEADER_NAMES\n return {\n transport: 'http',\n url: `${base}${opts.path}`,\n headers: {\n Authorization: `Bearer ${opts.token}`,\n [h.userId]: opts.ctx.userId,\n ...(opts.ctx.workspaceId ? { [h.workspaceId]: opts.ctx.workspaceId } : {}),\n ...(opts.ctx.threadId ? { [h.threadId]: opts.ctx.threadId } : {}),\n 'Content-Type': 'application/json',\n },\n enabled: true,\n metadata: { description: opts.description },\n }\n}\n\nexport interface BuildMcpServerOptions {\n tool: AppToolName\n baseUrl: string\n token: string\n ctx: AppToolContext\n description: string\n headerNames?: ToolHeaderNames\n paths?: Partial<Record<AppToolName, string>>\n}\n\n/** Build one of the four app-tool MCP servers — a thin wrapper over\n * {@link buildHttpMcpServer} that maps the tool name to its route path. */\nexport function buildAppToolMcpServer(opts: BuildMcpServerOptions): AppToolMcpServer {\n return buildHttpMcpServer({\n path: opts.paths?.[opts.tool] ?? DEFAULT_APP_TOOL_PATHS[opts.tool],\n baseUrl: opts.baseUrl,\n token: opts.token,\n ctx: opts.ctx,\n description: opts.description,\n headerNames: opts.headerNames,\n })\n}\n","/**\n * Generic streamable-HTTP JSON-RPC 2.0 envelope for a tools-only MCP server.\n * Stateless, Workers-compatible: no session table, no SSE — every request gets\n * a single `application/json` response, which the streamable-HTTP transport\n * explicitly permits for tools-only servers.\n *\n * Protocol surface:\n * initialize → echo client's protocolVersion if supported, else latest\n * ping → empty result {}\n * notifications/* (no `id`) → 202 with no body\n * tools/list → tool manifest\n * tools/call → run + surface execution failures as isError text results\n * anything else → -32601\n *\n * Execution failures (argument shape, validation, store throws) become `isError`\n * tool results carrying the thrown message verbatim — the model reads WHY and\n * retries. Protocol misuse becomes a JSON-RPC error object.\n */\n\nexport const MCP_PROTOCOL_VERSIONS = ['2025-06-18', '2025-03-26', '2024-11-05'] as const\nexport type McpProtocolVersion = (typeof MCP_PROTOCOL_VERSIONS)[number]\n\nconst LATEST_PROTOCOL_VERSION: McpProtocolVersion = MCP_PROTOCOL_VERSIONS[0]\n\nexport interface McpServerInfo {\n name: string\n version: string\n}\n\n/** One tool entry in the registry the handler owns. */\nexport interface McpToolDefinition<TEnv = Record<string, never>> {\n name: string\n description: string\n /** JSON Schema for the `params.arguments` object. */\n inputSchema: Record<string, unknown>\n /** Receive validated (Record) args + the env the handler threaded; throw to\n * surface an isError result — never throw for protocol/framing issues. */\n run(args: Record<string, unknown>, env: TEnv): Promise<unknown>\n}\n\nexport interface CreateMcpToolHandlerOptions<TEnv = Record<string, never>> {\n serverInfo: McpServerInfo\n /** Full tool list; order IS the tools/list order. */\n tools: McpToolDefinition<TEnv>[]\n /** Per-request environment threaded into every `run` call. If your tools are\n * stateless (or carry state through closure) pass an empty builder:\n * `() => ({} as TEnv)`. */\n buildEnv(request: Request): TEnv | Promise<TEnv>\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\ntype JsonRpcId = string | number | null\n\ninterface ToolCallContent {\n content: Array<{ type: 'text'; text: string }>\n isError?: true\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\nfunction rpcResult(id: JsonRpcId, result: unknown): Response {\n return Response.json({ jsonrpc: '2.0', id, result })\n}\n\nfunction rpcError(id: JsonRpcId, code: number, message: string, status = 200): Response {\n return Response.json({ jsonrpc: '2.0', id, error: { code, message } }, { status })\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\n/**\n * Build a request handler for a tools-only MCP server. The returned function\n * accepts a standard `Request` and resolves to a `Response` — mount it on any\n * Cloudflare Worker route or Remix `loader`.\n *\n * The handler calls `buildEnv` exactly ONCE per `tools/call` request (after\n * the tool is found, before `run`) — non-`tools/call` paths skip it entirely\n * so metadata requests do not pay env-build cost.\n */\nexport function createMcpToolHandler<TEnv = Record<string, never>>(\n opts: CreateMcpToolHandlerOptions<TEnv>,\n): (request: Request) => Promise<Response> {\n const toolMap = new Map<string, McpToolDefinition<TEnv>>()\n for (const tool of opts.tools) {\n if (toolMap.has(tool.name)) throw new Error(`duplicate MCP tool name: ${tool.name}`)\n toolMap.set(tool.name, tool)\n }\n\n return async (request: Request): Promise<Response> => {\n if (request.method !== 'POST') {\n return new Response('MCP server accepts JSON-RPC 2.0 over POST only', {\n status: 405,\n headers: { Allow: 'POST' },\n })\n }\n\n let body: unknown\n try {\n body = await request.json()\n } catch {\n return rpcError(null, -32700, 'Parse error: request body is not valid JSON', 400)\n }\n\n if (Array.isArray(body)) {\n return rpcError(null, -32600, 'Invalid request: JSON-RPC batching is not supported', 400)\n }\n if (!isRecord(body) || body.jsonrpc !== '2.0' || typeof body.method !== 'string') {\n return rpcError(\n null,\n -32600,\n 'Invalid request: expected a JSON-RPC 2.0 object with jsonrpc \"2.0\" and a string method',\n 400,\n )\n }\n\n const method = body.method\n const params = isRecord(body.params) ? body.params : {}\n\n // Notifications have no `id` — acknowledge without a JSON-RPC body.\n if (!('id' in body) || body.id === undefined) {\n return new Response(null, { status: 202 })\n }\n const id = body.id as JsonRpcId\n\n switch (method) {\n case 'initialize': {\n const requested = typeof params.protocolVersion === 'string' ? params.protocolVersion : undefined\n const protocolVersion =\n requested !== undefined && (MCP_PROTOCOL_VERSIONS as readonly string[]).includes(requested)\n ? requested\n : LATEST_PROTOCOL_VERSION\n return rpcResult(id, {\n protocolVersion,\n capabilities: { tools: { listChanged: false } },\n serverInfo: opts.serverInfo,\n })\n }\n\n case 'ping':\n return rpcResult(id, {})\n\n case 'tools/list':\n return rpcResult(id, {\n tools: opts.tools.map((tool) => ({\n name: tool.name,\n description: tool.description,\n inputSchema: tool.inputSchema,\n })),\n })\n\n case 'tools/call': {\n const name = params.name\n if (typeof name !== 'string' || name.length === 0) {\n return rpcError(id, -32602, 'tools/call requires params.name (string)')\n }\n const tool = toolMap.get(name)\n if (!tool) {\n return rpcError(\n id,\n -32602,\n `Unknown tool: ${name}. Available tools: ${opts.tools.map((t) => t.name).join(', ')}`,\n )\n }\n if (params.arguments !== undefined && !isRecord(params.arguments)) {\n return rpcError(id, -32602, 'tools/call params.arguments must be an object when provided')\n }\n const args = isRecord(params.arguments) ? params.arguments : {}\n let env: TEnv\n try {\n env = await opts.buildEnv(request)\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n const payload: ToolCallContent = {\n content: [{ type: 'text', text: `${name} failed to build env: ${message}` }],\n isError: true,\n }\n return rpcResult(id, payload)\n }\n try {\n const result = await tool.run(args, env)\n const payload: ToolCallContent = { content: [{ type: 'text', text: JSON.stringify(result) }] }\n return rpcResult(id, payload)\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n const payload: ToolCallContent = {\n content: [{ type: 'text', text: `${name} failed: ${message}` }],\n isError: true,\n }\n return rpcResult(id, payload)\n }\n }\n\n default:\n return rpcError(id, -32601, `Method not found: ${method}`)\n }\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/sandbox/index.ts","../src/sandbox/outcome.ts","../src/sandbox/terminal-proxy-token.ts","../src/sandbox/workspace-terminal.ts"],"sourcesContent":["import {\n Sandbox,\n type AgentProfile,\n type AgentProfileFileMount,\n type AgentProfileMcpServer,\n type ExecResult,\n type SandboxConnection,\n type SandboxInstance,\n type ScopedTokenScope,\n type StorageConfig,\n type PromptResult,\n} from '@tangle-network/sandbox'\nimport {\n buildAppToolMcpServer,\n type AppToolName,\n type AppToolContext,\n type ToolHeaderNames,\n} from '../tools/index'\nimport { assertHarnessModelCompatible, type Harness } from '../harness/index'\nimport {\n resolveTangleExecutionEnvironment,\n trimOrNull,\n type TangleExecutionEnvironment,\n} from '../runtime/model'\nimport { ok, fail, type Outcome } from './outcome'\n\nexport type { Outcome } from './outcome'\n\nexport interface SandboxClientCredentials {\n apiKey: string\n baseUrl: string\n}\n\n/**\n * Sandbox credential policy reuses the canonical execution-environment union\n * (development/test/staging/production) so env classification stays in one place\n * (see resolveTangleExecutionEnvironment in runtime/model).\n */\nexport type SandboxCredentialEnvironment = TangleExecutionEnvironment\n\nexport interface ResolveSandboxClientCredentialsOptions {\n /**\n * Environment object to read from. Defaults to process.env when available.\n */\n env?: Record<string, string | undefined>\n /**\n * Explicit environment classification. Defaults to APP_ENV/NODE_ENV derived\n * behavior: local/development/test use direct env credentials; staging/prod\n * require the provision callback unless allowDirectEnvCredentials opts in.\n */\n environment?: SandboxCredentialEnvironment\n /**\n * Env names that may carry a sandbox-compatible bearer. The first non-empty\n * value wins when direct env credentials are allowed.\n */\n directKeyNames?: readonly string[]\n /**\n * Env names that may carry the sandbox gateway URL. The first non-empty value\n * wins, then defaultBaseUrl.\n */\n baseUrlNames?: readonly string[]\n /**\n * Base URL used when none of baseUrlNames are present.\n */\n defaultBaseUrl?: string\n /**\n * Whether direct env credentials are allowed for this environment. Defaults\n * to true in development/test and false in staging/production.\n */\n allowDirectEnvCredentials?: boolean | ((environment: SandboxCredentialEnvironment) => boolean)\n /**\n * Product-owned provision path, usually minting a per-user sandbox key from a\n * linked platform account. Called before direct env credentials in\n * staging/production and after direct env credentials in development/test.\n */\n provision?: (\n context: {\n environment: SandboxCredentialEnvironment\n env: Record<string, string | undefined>\n },\n ) => SandboxClientCredentials | null | undefined | Promise<SandboxClientCredentials | null | undefined>\n}\n\nconst DEFAULT_SANDBOX_DIRECT_KEY_NAMES = [\n 'TCLOUD_SANDBOX_API_KEY',\n 'SANDBOX_API_KEY',\n 'TANGLE_API_KEY',\n] as const\nconst DEFAULT_SANDBOX_BASE_URL_NAMES = ['SANDBOX_GATEWAY_URL', 'SANDBOX_API_URL'] as const\n\nfunction normalizeBaseUrl(value: string): string {\n return value.trim().replace(/\\/v1\\/?$/, '').replace(/\\/+$/, '')\n}\n\nfunction processEnv(): Record<string, string | undefined> {\n return typeof process === 'undefined' ? {} : process.env\n}\n\nfunction directEnvCredentialsAllowed(\n environment: SandboxCredentialEnvironment,\n allow: ResolveSandboxClientCredentialsOptions['allowDirectEnvCredentials'],\n): boolean {\n if (typeof allow === 'function') return allow(environment)\n if (typeof allow === 'boolean') return allow\n return environment === 'development' || environment === 'test'\n}\n\nfunction resolveSandboxBaseUrl(\n env: Record<string, string | undefined>,\n names: readonly string[],\n defaultBaseUrl: string | undefined,\n): string {\n for (const name of names) {\n const value = trimOrNull(env[name])\n if (value) return normalizeBaseUrl(value)\n }\n const value = trimOrNull(defaultBaseUrl)\n if (value) return normalizeBaseUrl(value)\n throw new Error(\n `Sandbox base URL is required (set one of ${names.join(', ')} or pass defaultBaseUrl).`,\n )\n}\n\nfunction resolveDirectSandboxCredentials(\n env: Record<string, string | undefined>,\n keyNames: readonly string[],\n baseUrlNames: readonly string[],\n defaultBaseUrl: string | undefined,\n): SandboxClientCredentials | null {\n for (const name of keyNames) {\n const apiKey = trimOrNull(env[name])\n if (!apiKey) continue\n return {\n apiKey,\n baseUrl: resolveSandboxBaseUrl(env, baseUrlNames, defaultBaseUrl),\n }\n }\n return null\n}\n\nexport async function resolveSandboxClientCredentials(\n options: ResolveSandboxClientCredentialsOptions = {},\n): Promise<SandboxClientCredentials> {\n const env = options.env ?? processEnv()\n const environment = options.environment ?? resolveTangleExecutionEnvironment(env)\n const keyNames = options.directKeyNames ?? DEFAULT_SANDBOX_DIRECT_KEY_NAMES\n const baseUrlNames = options.baseUrlNames ?? DEFAULT_SANDBOX_BASE_URL_NAMES\n const directAllowed = directEnvCredentialsAllowed(environment, options.allowDirectEnvCredentials)\n const direct = () =>\n directAllowed\n ? resolveDirectSandboxCredentials(env, keyNames, baseUrlNames, options.defaultBaseUrl)\n : null\n\n if (environment === 'development' || environment === 'test') {\n const credentials = direct()\n if (credentials) return credentials\n }\n\n const provisioned = await options.provision?.({ environment, env })\n if (provisioned) {\n return {\n apiKey: provisioned.apiKey,\n baseUrl: normalizeBaseUrl(provisioned.baseUrl),\n }\n }\n\n const credentials = direct()\n if (credentials) return credentials\n\n const directHint = directAllowed\n ? ` or set one of ${keyNames.join(', ')}`\n : ''\n throw new Error(\n `Sandbox credentials are required for ${environment} (provide a provision callback${directHint}).`,\n )\n}\n\nexport interface SandboxResourceConfig {\n image: string\n cpuCores: number\n memoryMB: number\n diskGB: number\n maxLifetimeSeconds: number\n idleTimeoutSeconds: number\n}\n\nexport interface ProviderResolutionConfig {\n routerBaseUrl?: string\n apiKey?: string\n providerName?: string\n modelName?: string\n defaultModel?: string\n openaiApiKey?: string\n}\n\nexport interface SandboxBuildContext {\n workspaceId: string\n connectedIntegrationIds: string[]\n userId?: string\n}\n\n// SDK-typed snapshot storage config (re-exported for product seam closures).\nexport type { StorageConfig }\n\n// Scope handed to per-identity seams. workspaceId is always present; userId is\n// present when the lifecycle op carries one. Workspace-keyed products ignore userId.\nexport interface SandboxScope {\n workspaceId: string\n userId?: string\n}\n\n// Snapshot RESTORE-on-create. Returned alongside storage; undefined => fresh box.\nexport interface SandboxRestoreSpec {\n fromSnapshot: string\n fromSandboxId: string\n}\n\n// Reuse health gate + sidecar liveness. The exec+timeout-race is generic; the\n// sidecarProcessPattern is harness-specific (which process is the live sidecar),\n// so it is a closure. Absent => no liveness probe (reuse on metadata.harness match).\nexport interface LivenessProbeConfig {\n sidecarProcessPattern: (harness: Harness) => string\n execTimeoutMs?: number\n psTimeoutMs?: number\n}\n\nexport interface ProfileComposeOptions {\n systemPrompt?: string\n extraFiles?: AgentProfileFileMount[]\n extraMcp?: Record<string, AgentProfileMcpServer>\n name?: string\n}\n\nexport interface SandboxRuntimeConfig {\n // Widened to accept an optional scope and be async so a per-user key can be\n // minted. The sync, no-arg form still satisfies the type (back-compat).\n credentials: (\n scope?: SandboxScope,\n ) => SandboxClientCredentials | null | Promise<SandboxClientCredentials | null>\n name: (workspaceId: string) => string\n metadata: (harness: Harness) => Record<string, unknown>\n connectedIntegrationIds: (workspaceId: string) => Promise<string[]>\n env: (ctx: SandboxBuildContext) => Promise<Record<string, string>>\n files: (ctx: SandboxBuildContext) => Promise<AgentProfileFileMount[]>\n secrets: (workspaceId: string) => Promise<string[]>\n profile: (options: ProfileComposeOptions) => AgentProfile\n permissionRole?: (workspaceRole: string) => SandboxPermissionLevel\n resources?: SandboxResourceConfig\n provider?: ProviderResolutionConfig\n\n // BYOS3/R2 snapshot storage. Returns undefined => key omitted entirely\n // (fail-closed when creds absent). Product owns bucket/endpoint/credentials/prefix.\n storage?: (ctx: SandboxBuildContext) => StorageConfig | undefined\n // Snapshot RESTORE-on-create. undefined => fresh box.\n restore?: (ctx: SandboxBuildContext) => SandboxRestoreSpec | undefined\n // Per-identity box NAME. Defaults to name(scope.workspaceId) when absent.\n boxKey?: (scope: SandboxScope) => string\n // Per-workspace child-key mint: overrides the resolved model apiKey before create.\n // Applied only when a model is resolved and its provider is openai-compat.\n childKeyMint?: (scope: SandboxScope) => Promise<Outcome<string>>\n // One-shot post-running bootstrap, on BOTH create and reuse paths (idempotency\n // is the closure's job — it owns the marker check).\n bootstrap?: (box: SandboxInstance, scope: SandboxScope) => Promise<Outcome<void>>\n // Reuse health gate + sidecar liveness probe.\n livenessProbe?: LivenessProbeConfig\n // Enable browser terminal endpoints on newly-created sandboxes.\n webTerminalEnabled?: boolean\n // default true: try stopped-resume before create.\n resumeStopped?: boolean\n // default false: bake resolveModel() into backend.model at create.\n backendModelAtCreate?: boolean\n // default false: write the profile's `resources.files` INTO the box after it\n // reaches running (via `box.exec`), instead of inlining them in the create\n // payload. The orchestrator caps the provision body at 256 KiB; a large\n // file corpus (skills, tool scripts) blows that cap. Deferring it keeps the\n // provision body small and uncapped in corpus size, and lands real files on\n // disk — which is also the only path that works for harnesses whose backend\n // does not materialize the provider-neutral `resources.files` channel.\n //\n // Only `kind: 'inline'` files are deferred; non-inline refs (e.g. github)\n // stay in the create payload so the orchestrator resolves them. Runs on the\n // create AND resume/reuse paths (idempotent overwrite). Inline files are\n // STRIPPED from `resources.files` before create when this is set.\n deferProfileFiles?: boolean\n}\n\nexport const DEFAULT_SANDBOX_RESOURCES: SandboxResourceConfig = {\n image: 'universal',\n cpuCores: 2,\n memoryMB: 4096,\n diskGB: 10,\n maxLifetimeSeconds: 86400,\n idleTimeoutSeconds: 3600,\n}\n\ninterface ClientCacheEntry {\n client: Sandbox\n fingerprint: string\n}\n\nlet _cached: ClientCacheEntry | null = null\n\nfunction getClientFromCreds(creds: SandboxClientCredentials): Sandbox {\n const fingerprint = `${creds.apiKey} ${creds.baseUrl}`\n if (_cached && _cached.fingerprint === fingerprint) return _cached.client\n\n const client = new Sandbox({ apiKey: creds.apiKey, baseUrl: creds.baseUrl })\n _cached = { client, fingerprint }\n return client\n}\n\n// Sync client for non-scoped callers (secretStoreFromClient etc.). Resolves\n// credentials with no scope; throws if the seam returns a Promise — scoped\n// products must use the async ensureWorkspaceSandbox path.\nexport function getClient(shell: SandboxRuntimeConfig): Sandbox {\n const creds = shell.credentials()\n if (creds && typeof (creds as Promise<unknown>).then === 'function') {\n throw new Error('getClient: scoped (async) credentials require the async sandbox path')\n }\n if (!creds) throw new Error('sandbox credentials are required (apiKey/baseUrl)')\n return getClientFromCreds(creds as SandboxClientCredentials)\n}\n\nexport function resetClientCache(): void {\n _cached = null\n}\n\nexport interface AppToolDescriptor {\n tool: AppToolName\n key: string\n description: string\n}\n\nexport interface BuildAppToolMcpServersOptions {\n tools: AppToolDescriptor[]\n baseUrl: string\n token: string\n ctx: AppToolContext\n headerNames?: ToolHeaderNames\n}\n\nexport function buildAppToolMcpServers(\n options: BuildAppToolMcpServersOptions,\n): Record<string, AgentProfileMcpServer> {\n const entries: Record<string, AgentProfileMcpServer> = {}\n for (const { tool, key, description } of options.tools) {\n entries[key] = buildAppToolMcpServer({\n tool,\n baseUrl: options.baseUrl,\n token: options.token,\n ctx: options.ctx,\n description,\n headerNames: options.headerNames,\n }) as AgentProfileMcpServer\n }\n return entries\n}\n\nexport interface EnsureWorkspaceSandboxOptions {\n workspaceId: string\n userId?: string\n harness: Harness\n // When set, both the running-reuse and stopped-resume short-circuits are\n // skipped and any name-matched box is deleted before create.\n forceNew?: boolean\n}\n\n// Single-quote a string for safe interpolation into a shell command.\nfunction shellSingleQuote(value: string): string {\n return `'${value.replace(/'/g, `'\\\\''`)}'`\n}\n\nexport interface SandboxToolSpec {\n name: string\n content: string\n executable?: boolean\n}\n\nexport interface SandboxToolPathOptions {\n appName: string\n baseDir?: string\n binDir?: string\n}\n\nexport interface BuildSandboxToolFileMountsOptions extends SandboxToolPathOptions {\n tools: readonly SandboxToolSpec[]\n}\n\nconst DEFAULT_SANDBOX_TOOL_BASE_DIR = '/home/agent/tools'\nconst SAFE_TOOL_SEGMENT = /^[A-Za-z0-9._-]+$/\n\nfunction normalizeSandboxToolSegment(value: string, label: string): string {\n const segment = value.trim()\n if (!segment || segment === '.' || segment === '..' || !SAFE_TOOL_SEGMENT.test(segment)) {\n throw new Error(`${label} must contain only letters, numbers, dots, underscores, or hyphens.`)\n }\n return segment\n}\n\nfunction normalizeSandboxToolDir(value: string, label: string): string {\n const dir = value.trim().replace(/\\/+$/, '')\n if (!dir || !dir.startsWith('/') || dir.includes('\\0') || dir.includes('\\n')) {\n throw new Error(`${label} must be an absolute sandbox path.`)\n }\n return dir === '' ? '/' : dir\n}\n\nexport function sandboxToolRootDir(options: SandboxToolPathOptions): string {\n const appName = normalizeSandboxToolSegment(options.appName, 'sandbox tool appName')\n const baseDir = normalizeSandboxToolDir(\n options.baseDir ?? DEFAULT_SANDBOX_TOOL_BASE_DIR,\n 'sandbox tool baseDir',\n )\n return `${baseDir}/${appName}`\n}\n\nexport function sandboxToolBinDir(options: SandboxToolPathOptions): string {\n normalizeSandboxToolSegment(options.appName, 'sandbox tool appName')\n if (options.binDir) return normalizeSandboxToolDir(options.binDir, 'sandbox tool binDir')\n return `${sandboxToolRootDir(options)}/bin`\n}\n\nexport function sandboxToolPath(options: SandboxToolPathOptions & { toolName: string }): string {\n const toolName = normalizeSandboxToolSegment(options.toolName, 'sandbox tool name')\n return `${sandboxToolBinDir(options)}/${toolName}`\n}\n\nexport function buildSandboxToolFileMounts(\n options: BuildSandboxToolFileMountsOptions,\n): AgentProfileFileMount[] {\n return options.tools.map((tool) => {\n const name = normalizeSandboxToolSegment(tool.name, 'sandbox tool name')\n return {\n path: sandboxToolPath({ ...options, toolName: name }),\n resource: { kind: 'inline' as const, name, content: tool.content },\n executable: tool.executable ?? true,\n }\n })\n}\n\nexport function buildSandboxToolPathSetupScript(options: SandboxToolPathOptions): string {\n const binDir = sandboxToolBinDir(options)\n const exportLine = `export PATH=${binDir}:$PATH`\n return [\n 'set -eu',\n `mkdir -p ${shellSingleQuote(binDir)}`,\n `PATH=${shellSingleQuote(binDir)}:$PATH`,\n 'export PATH',\n 'for profile in \"${HOME:-/home/agent}/.profile\" \"${HOME:-/home/agent}/.bashrc\" \"${HOME:-/home/agent}/.zshrc\"; do',\n ' mkdir -p \"$(dirname \"$profile\")\"',\n ' touch \"$profile\"',\n ` grep -Fqx ${shellSingleQuote(exportLine)} \"$profile\" || printf '\\\\n%s\\\\n' ${shellSingleQuote(exportLine)} >> \"$profile\"`,\n 'done',\n ].join('\\n')\n}\n\nexport async function runSandboxToolPathSetup(\n box: SandboxInstance,\n options: SandboxToolPathOptions,\n): Promise<Outcome<void>> {\n try {\n const res = await box.exec(buildSandboxToolPathSetupScript(options))\n if (res.exitCode !== 0) {\n return fail(\n new Error(\n `runSandboxToolPathSetup: failed to configure PATH for ${sandboxToolBinDir(options)} ` +\n `(exit ${res.exitCode}): ${res.stderr.slice(0, 500)}`,\n ),\n )\n }\n return ok(undefined)\n } catch (err) {\n return fail(new Error('runSandboxToolPathSetup: exec failed', { cause: err }))\n }\n}\n\n// Build a shell-safe path token that preserves tilde-home semantics. A path\n// beginning `~/` (or a bare `~`) must resolve to the box user's real `$HOME`,\n// but single-quoting the whole path suppresses shell `~` expansion and lands\n// the file in a literal directory named `~`. Expand the leading `~` to an\n// UNQUOTED `\"$HOME\"` (so the shell expands it) and single-quote only the\n// remainder. Absolute and relative paths are single-quoted unchanged.\nfunction shellPath(path: string): string {\n if (path === '~') return '\"$HOME\"'\n if (path.startsWith('~/')) {\n const rest = path.slice(2)\n return rest ? `\"$HOME\"/${shellSingleQuote(rest)}` : '\"$HOME\"'\n }\n return shellSingleQuote(path)\n}\n\n// Split a profile's `resources.files` into the inline mounts that can be\n// written into a running box and the rest (non-inline refs that the\n// orchestrator must resolve, so they stay in the create payload). Returns the\n// inline set to defer and a profile copy with those inline files removed.\nexport function splitDeferredProfileFiles(\n profile: AgentProfile,\n): { leanProfile: AgentProfile; deferredFiles: AgentProfileFileMount[] } {\n const files = profile.resources?.files ?? []\n const deferredFiles: AgentProfileFileMount[] = []\n const keptFiles: AgentProfileFileMount[] = []\n for (const mount of files) {\n if (mount.resource.kind === 'inline') deferredFiles.push(mount)\n else keptFiles.push(mount)\n }\n if (deferredFiles.length === 0) return { leanProfile: profile, deferredFiles }\n const leanProfile: AgentProfile = {\n ...profile,\n resources: { ...(profile.resources ?? {}), files: keptFiles },\n }\n return { leanProfile, deferredFiles }\n}\n\n// The runtime exec proxy (`box.exec` → /terminals/commands) hangs (30s\n// timeout) on any request whose body crosses ~4096 bytes, and one oversized\n// exec wedges the channel so every later exec on the box hangs too. We slice\n// each file's base64 into appends whose full command string stays well under\n// that cap. 3000 chars of base64 leaves ~1000 bytes of headroom for the\n// surrounding `printf '%s' '<slice>' >> <path>.b64` command plus the proxy's\n// JSON request envelope — comfortably below 4096.\nconst PROFILE_WRITE_B64_CHUNK_CHARS = 3000\n\n// gtm's corpus is ~135 small execs; on a cold box the runtime exec proxy\n// hard-throttles (HTTP 429 after ~21 execs in ~34s) and, under sustained load,\n// SILENTLY HANGS instead of returning 429 — one parked exec wedges the channel\n// and `writeProfileFilesToBox` never returns, so `ensureWorkspaceSandbox`\n// parks the worker (~140s, no exception). Both failure shapes are transient:\n// retry the SAME exec with exponential backoff before failing loud. A non-429,\n// non-timeout error fails loud immediately; a non-zero exit is a real command\n// failure, never retried.\nconst PROFILE_WRITE_MAX_RETRIES = 4\nconst PROFILE_WRITE_RETRY_BASE_MS = 250\nconst PROFILE_WRITE_RETRY_MAX_MS = 2000\n\n// Client-side hard ceiling per exec. The proxy's own 30s timeout is unreliable\n// once the channel wedges (it can hang past it), so we race each exec against an\n// independent timer we control: a wedged exec is abandoned here and retried as a\n// transient transport error, never silently parked. We also pass timeoutMs to\n// the SDK as defense-in-depth, but the race is the guarantee.\nconst PROFILE_WRITE_EXEC_TIMEOUT_MS = 30_000\n\n// Pacing between execs to stay under the proxy throttle that triggers the hang.\n// The drill saw throttling at ~0.6 exec/s bursts; ~150ms between ~135 execs\n// adds well under a minute total and keeps the burst rate below the trip point.\n// On by default for the deferred path; overridable for tests/tuning.\nconst PROFILE_WRITE_PACE_MS = 150\n\nconst sleep = (ms: number): Promise<void> => new Promise((resolve) => setTimeout(resolve, ms))\n\n// Sentinel cause for a client-side per-exec timeout (a hung/wedged proxy exec).\n// Carried as the `.cause` of the fail-loud Outcome so callers see the wedged\n// command instead of an opaque infinite hang.\nclass ProfileWriteExecTimeoutError extends Error {\n constructor(timeoutMs: number) {\n super(`exec exceeded ${timeoutMs}ms (proxy hang/wedge)`)\n this.name = 'ProfileWriteExecTimeoutError'\n }\n}\n\n// Run a box.exec, abandoning it if it does not settle within timeoutMs. The\n// returned promise rejects with ProfileWriteExecTimeoutError on timeout so the\n// retry loop treats a hang exactly like a transient transport error. We also\n// forward timeoutMs to the SDK so the underlying request is cancelled when the\n// SDK honors it; the race covers the case where it does not.\nfunction execWithTimeout(\n box: SandboxInstance,\n cmd: string,\n timeoutMs: number,\n): Promise<ExecResult> {\n return new Promise<ExecResult>((resolve, reject) => {\n let settled = false\n const timer = setTimeout(() => {\n if (settled) return\n settled = true\n reject(new ProfileWriteExecTimeoutError(timeoutMs))\n }, timeoutMs)\n box.exec(cmd, { timeoutMs }).then(\n (res) => {\n if (settled) return\n settled = true\n clearTimeout(timer)\n resolve(res)\n },\n (err) => {\n if (settled) return\n settled = true\n clearTimeout(timer)\n reject(err)\n },\n )\n })\n}\n\n// Classify an exec failure as transient-retryable. Two shapes are transient and\n// share the backoff path: (1) a rate-limit (HTTP 429) the proxy surfaces as a\n// SandboxError-shaped object (status/code/retryAfterMs) or a thrown error whose\n// message carries the 429; (2) a client-side per-exec timeout, i.e. a wedged/\n// hung proxy exec abandoned by execWithTimeout. Everything else fails loud.\nfunction transientExecError(err: unknown): { retryable: boolean; retryAfterMs?: number } {\n if (err instanceof ProfileWriteExecTimeoutError) return { retryable: true }\n if (err && typeof err === 'object') {\n const e = err as { status?: unknown; code?: unknown; message?: unknown; retryAfterMs?: unknown }\n const retryAfterMs = typeof e.retryAfterMs === 'number' ? e.retryAfterMs : undefined\n if (e.status === 429) return { retryable: true, retryAfterMs }\n if (typeof e.code === 'string' && /rate.?limit|too.?many.?requests|429/i.test(e.code)) {\n return { retryable: true, retryAfterMs }\n }\n if (typeof e.message === 'string' && /\\b429\\b|rate.?limit|too many requests/i.test(e.message)) {\n return { retryable: true, retryAfterMs }\n }\n }\n return { retryable: false }\n}\n\n// Materialize inline profile files into a running box via `box.exec`. Uses a\n// base64 pipe so arbitrary content (scripts, unicode, special chars) lands\n// byte-exact, and writes to ANY absolute path (e.g. /usr/local/bin) or a\n// `~`-relative path — the exec runs as the sidecar, which is not bound by the\n// safe-prefix allow-list the /files/write API enforces. Sets the executable\n// bit when the mount declares it OR the target is a bin directory.\n//\n// Each file is written in several small execs (mkdir, one append per base64\n// chunk, then a decode+cleanup) so no single exec request body trips the\n// ~4 KiB proxy cap. Writes are sequential; a single bad mount stops the batch\n// and the first failure is returned (fail-loud), the rest are not attempted.\n//\n// Each exec is bounded by a client-side hard timeout and retried with backoff\n// on a 429 OR a hang/timeout, so one wedged proxy exec can never park the\n// caller (and thus never park provisioning). A small pace between execs keeps\n// the burst rate below the proxy throttle that triggers the hang.\nexport interface WriteProfileFilesOptions {\n // Hard ceiling per exec (ms). A hung/wedged exec is abandoned and retried.\n execTimeoutMs?: number\n // Delay between execs (ms) to stay under the proxy throttle. 0 disables it.\n paceMs?: number\n // Max retries for a transient (429 or timeout/hang) exec before failing loud.\n maxRetries?: number\n}\n\nexport async function writeProfileFilesToBox(\n box: SandboxInstance,\n files: AgentProfileFileMount[],\n options: WriteProfileFilesOptions = {},\n): Promise<Outcome<void>> {\n const execTimeoutMs = options.execTimeoutMs ?? PROFILE_WRITE_EXEC_TIMEOUT_MS\n const paceMs = options.paceMs ?? PROFILE_WRITE_PACE_MS\n const maxRetries = options.maxRetries ?? PROFILE_WRITE_MAX_RETRIES\n // Pace BETWEEN execs, not before the first or after the last. Shared across\n // every mount in this call so the whole ~135-exec corpus stays paced.\n let execStarted = false\n\n for (const mount of files) {\n if (mount.resource.kind !== 'inline') continue\n const content = mount.resource.content ?? ''\n const b64 = Buffer.from(content, 'utf8').toString('base64')\n const path = mount.path\n const dir = path.replace(/\\/[^/]*$/, '')\n const isBin = /(^|\\/)(s?bin)\\//.test(path)\n const executable = mount.executable ?? isBin\n const q = shellPath(path)\n const qb64 = shellPath(`${path}.b64`)\n\n // Run one exec step, surfacing a non-zero exit or transport error as a\n // fail-loud Outcome with the underlying error as cause. A transient failure\n // — a 429 (rate limit) OR a client-side timeout (a hung/wedged proxy exec) —\n // is retried with exponential backoff up to maxRetries. Any other error\n // fails loud immediately. A non-zero exit is a real command failure, not\n // retried. After maxRetries the timeout is surfaced as the Outcome cause so\n // a persistently-wedged exec fails fast and visibly instead of hanging.\n const step = async (cmd: string): Promise<Outcome<void>> => {\n for (let attempt = 0; ; attempt++) {\n if (execStarted && paceMs > 0) await sleep(paceMs)\n execStarted = true\n try {\n const res = await execWithTimeout(box, cmd, execTimeoutMs)\n if (res.exitCode !== 0) {\n return fail(\n new Error(\n `writeProfileFilesToBox: failed to write ${path} (exit ${res.exitCode}): ${res.stderr.slice(0, 500)}`,\n ),\n )\n }\n return ok(undefined)\n } catch (err) {\n const { retryable, retryAfterMs } = transientExecError(err)\n if (retryable && attempt < maxRetries) {\n const backoff = Math.min(\n PROFILE_WRITE_RETRY_BASE_MS * 2 ** attempt,\n PROFILE_WRITE_RETRY_MAX_MS,\n )\n await sleep(retryAfterMs ?? backoff)\n continue\n }\n return fail(new Error(`writeProfileFilesToBox: exec failed for ${path}`, { cause: err }))\n }\n }\n }\n\n // Start the staging file empty so re-runs (redeploy with new skills)\n // overwrite rather than append. mkdir -p creates the target directory;\n // shellPath keeps `~/...` resolving to the box user's real $HOME.\n const mkdir = dir && dir !== path ? `mkdir -p ${shellPath(dir)} && ` : ''\n let res = await step(`${mkdir}: > ${qb64}`)\n if (!res.succeeded) return res\n\n // Append the base64 in capped slices; the base64 alphabet has no single\n // quotes, so single-quoting each slice is safe. printf '%s' adds no newline.\n for (let i = 0; i < b64.length; i += PROFILE_WRITE_B64_CHUNK_CHARS) {\n const slice = b64.slice(i, i + PROFILE_WRITE_B64_CHUNK_CHARS)\n res = await step(`printf '%s' '${slice}' >> ${qb64}`)\n if (!res.succeeded) return res\n }\n\n // Decode the staged base64 to the real path, drop the staging file, and set\n // the executable bit when required. base64 -d reconstructs the exact bytes.\n const chmod = executable ? ` && chmod +x ${q}` : ''\n res = await step(`base64 -d ${qb64} > ${q} && rm -f ${qb64}${chmod}`)\n if (!res.succeeded) return res\n }\n return ok(undefined)\n}\n\n// Resolve the shell's deferred (inline) profile files and write them into a\n// box that already exists (reuse/resume paths). No-op unless the shell opts\n// into deferProfileFiles. Idempotent overwrite — a redeploy with new skills\n// refreshes the corpus on the next ensure call.\nasync function materializeDeferredFilesForExistingBox(\n shell: SandboxRuntimeConfig,\n box: SandboxInstance,\n workspaceId: string,\n userId: string | undefined,\n): Promise<Outcome<void>> {\n if (!shell.deferProfileFiles) return ok(undefined)\n const connectedIntegrationIds = await shell.connectedIntegrationIds(workspaceId)\n const buildCtx: SandboxBuildContext = {\n workspaceId,\n connectedIntegrationIds,\n ...(userId ? { userId } : {}),\n }\n const files = await shell.files(buildCtx)\n const fullProfile = shell.profile({ extraFiles: files })\n const { deferredFiles } = splitDeferredProfileFiles(fullProfile)\n if (deferredFiles.length === 0) return ok(undefined)\n return writeProfileFilesToBox(box, deferredFiles)\n}\n\nasync function listRunning(\n client: Sandbox,\n name: string,\n): Promise<Outcome<SandboxInstance | null>> {\n try {\n const running = await client.list({ status: 'running' })\n return ok(running.find((s) => s.name === name) ?? null)\n } catch (err) {\n return fail(err)\n }\n}\n\nasync function deleteBox(box: SandboxInstance): Promise<Outcome<void>> {\n try {\n await box.delete()\n return ok(undefined)\n } catch (err) {\n return fail(err)\n }\n}\n\n// The SDK narrows `backend.type` to its own BackendType union and\n// `initialUsers[].role` to PermissionLevel — neither symbol is exported. The\n// create payload is assembled with the product's Harness/role strings, which\n// are a superset surface; the localized cast at the boundary is the only place\n// this widening is allowed, and the runtime contract (the sidecar boots the\n// named harness) is what enforces correctness.\ntype CreatePayload = Parameters<Sandbox['create']>[0]\n\n// Generic exec+sidecar liveness probe. Absent probe => always alive (the prior\n// reuse-on-metadata-match behavior). With a probe: the container must answer an\n// `echo alive` exec within execTimeoutMs, and the sidecar process must be found\n// by pgrep within psTimeoutMs (an inconclusive pgrep is treated as reusable).\nasync function isBoxAlive(\n box: SandboxInstance,\n harness: Harness,\n probe: LivenessProbeConfig | undefined,\n): Promise<boolean> {\n if (!probe) return true\n const execTimeout = probe.execTimeoutMs ?? 5000\n const psTimeout = probe.psTimeoutMs ?? 3000\n const race = <T>(p: Promise<T>, ms: number, label: string): Promise<T> =>\n Promise.race([\n p,\n new Promise<T>((_, reject) => setTimeout(() => reject(new Error(label)), ms)),\n ])\n try {\n const alive = await race(box.exec('echo alive'), execTimeout, 'alive check timeout')\n if (!alive.stdout.includes('alive')) return false\n const pattern = probe.sidecarProcessPattern(harness)\n try {\n const ps = await race(\n box.exec(`pgrep -f ${shellSingleQuote(pattern)} || echo no-sidecar`),\n psTimeout,\n 'ps check timeout',\n )\n if (ps.stdout.includes('no-sidecar')) return false\n } catch {\n // sidecar probe inconclusive — container is alive, treat as reusable\n }\n return true\n } catch {\n return false\n }\n}\n\nconst RUNTIME_CONNECTION_WAIT_MS = 30_000\nconst RUNTIME_CONNECTION_POLL_MS = 1_000\n\n// `sidecarUrl` is a product/forward-compat field the orchestrator may set on\n// the connection ahead of the SDK declaring it — the v0.6 `SandboxConnection`\n// exposes only `runtimeUrl`. Read it through a typed augmentation rather than an\n// ad-hoc cast (a plain `SandboxConnection` satisfies the optional field), and\n// fall back to the SDK runtime URL. The product-facing `WorkspaceSandboxInstanceLike`\n// carries the same field for consumers driving their own box types.\ntype RuntimeConnectionFields = SandboxConnection & { sidecarUrl?: string }\n\nfunction sandboxRuntimeUrl(box: SandboxInstance): string | undefined {\n const connection: RuntimeConnectionFields | undefined = box.connection\n return connection?.sidecarUrl ?? connection?.runtimeUrl\n}\n\nfunction sandboxEdgeFailed(box: SandboxInstance): boolean {\n // `edgeStatus`/`edgeError` are declared on the SDK's `SandboxConnection`, so no cast.\n const connection = box.connection\n return connection?.edgeStatus === 'failed' || Boolean(connection?.edgeError)\n}\n\nasync function refreshRuntimeConnection(\n client: Sandbox,\n box: SandboxInstance,\n): Promise<SandboxInstance> {\n let current = box\n if (sandboxRuntimeUrl(current)) return current\n\n const deadline = Date.now() + RUNTIME_CONNECTION_WAIT_MS\n while (Date.now() < deadline) {\n // Tolerate transient refresh/get failures (5xx, network blips) while the\n // orchestrator is still attaching the connection: swallow and retry. A box\n // that never surfaces a runtime URL is returned as-is so the caller's\n // readiness gate (isReusableBox) drops and recreates it — rather than this\n // poll throwing a hard provisioning failure on a recoverable hiccup.\n try {\n await current.refresh()\n if (sandboxRuntimeUrl(current)) return current\n\n const latest = await client.get(current.id)\n if (latest) current = latest\n if (sandboxRuntimeUrl(current)) return current\n } catch {\n // transient — fall through to the poll delay and retry\n }\n\n await new Promise((resolve) => setTimeout(resolve, RUNTIME_CONNECTION_POLL_MS))\n }\n\n return current\n}\n\n// Decide whether an existing (reused or resumed) box is safe to hand back.\n// `refreshRuntimeConnection` has already polled for the runtime URL, so a box\n// that still has none never became connectable and must be recreated rather\n// than silently reused — downstream exec/terminal traffic would fail against\n// it. A failed edge is likewise unusable. Only when the connection is present\n// do we spend an exec round-trip on the liveness probe.\nasync function isReusableBox(\n box: SandboxInstance,\n harness: Harness,\n probe: LivenessProbeConfig | undefined,\n): Promise<boolean> {\n if (sandboxEdgeFailed(box)) return false\n if (!sandboxRuntimeUrl(box)) return false\n return isBoxAlive(box, harness, probe)\n}\n\n// Resume a name-matched stopped box and wait for it to reach running. Returns\n// ok(null) when no stopped box matches the name.\nasync function resumeStoppedBox(\n client: Sandbox,\n name: string,\n timeoutMs: number,\n): Promise<Outcome<SandboxInstance | null>> {\n try {\n const stopped = await client.list({ status: 'stopped' })\n const match = stopped.find((s) => s.name === name) ?? null\n if (!match) return ok(null)\n await match.resume()\n await match.waitFor('running', { timeoutMs })\n return ok(match)\n } catch (err) {\n return fail(err)\n }\n}\n\nexport async function ensureWorkspaceSandbox(\n shell: SandboxRuntimeConfig,\n options: EnsureWorkspaceSandboxOptions,\n): Promise<SandboxInstance> {\n const { workspaceId, userId, harness, forceNew } = options\n const scope: SandboxScope = { workspaceId, ...(userId ? { userId } : {}) }\n const creds = await shell.credentials(scope)\n if (!creds) throw new Error('sandbox credentials are required (apiKey/baseUrl)')\n const client = getClientFromCreds(creds)\n const name = shell.boxKey ? shell.boxKey(scope) : shell.name(workspaceId)\n const resources = shell.resources ?? DEFAULT_SANDBOX_RESOURCES\n const resumeTimeout = 120_000\n\n // Stage 1 — running-box reuse (skipped on forceNew).\n const existing = await listRunning(client, name)\n if (existing.succeeded && existing.value) {\n const found = existing.value\n if (forceNew) {\n const dropped = await deleteBox(found)\n if (!dropped.succeeded) {\n throw new Error(`forceNew: sandbox ${name} could not be deleted`, { cause: dropped.error })\n }\n } else if (found.metadata?.harness === harness) {\n const ready = await refreshRuntimeConnection(client, found)\n if (await isReusableBox(ready, harness, shell.livenessProbe)) {\n const written = await materializeDeferredFilesForExistingBox(shell, ready, workspaceId, userId)\n if (!written.succeeded) {\n throw new Error(`deferred file write failed on reused box ${name}`, { cause: written.error })\n }\n if (shell.bootstrap) {\n const boot = await shell.bootstrap(ready, scope)\n if (!boot.succeeded) {\n throw new Error(`bootstrap failed on reused box ${name}`, { cause: boot.error })\n }\n }\n return ready\n }\n const dropped = await deleteBox(ready)\n if (!dropped.succeeded) {\n throw new Error(\n `sandbox ${name} ` +\n `(was ${String(found.metadata?.harness ?? 'unknown')}, want ${harness}, or unresponsive) ` +\n `could not be deleted`,\n { cause: dropped.error },\n )\n }\n } else {\n const dropped = await deleteBox(found)\n if (!dropped.succeeded) {\n throw new Error(\n `sandbox ${name} ` +\n `(was ${String(found.metadata?.harness ?? 'unknown')}, want ${harness}, or unresponsive) ` +\n `could not be deleted`,\n { cause: dropped.error },\n )\n }\n }\n }\n\n // Stage 2 — stopped-box resume (skipped on forceNew or resumeStopped===false).\n if (!forceNew && shell.resumeStopped !== false) {\n const resumed = await resumeStoppedBox(client, name, resumeTimeout)\n if (resumed.succeeded && resumed.value) {\n const box = await refreshRuntimeConnection(client, resumed.value)\n if (await isReusableBox(box, harness, shell.livenessProbe)) {\n const written = await materializeDeferredFilesForExistingBox(shell, box, workspaceId, userId)\n if (!written.succeeded) {\n throw new Error(`deferred file write failed on resumed box ${name}`, { cause: written.error })\n }\n if (shell.bootstrap) {\n const boot = await shell.bootstrap(box, scope)\n if (!boot.succeeded) {\n throw new Error(`bootstrap failed on resumed box ${name}`, { cause: boot.error })\n }\n }\n return box\n }\n const dropped = await deleteBox(box)\n if (!dropped.succeeded) {\n throw new Error(\n `resumed sandbox ${name} ` +\n `(was ${String(box.metadata?.harness ?? 'unknown')}, want ${harness}, or unresponsive) ` +\n `could not be deleted`,\n { cause: dropped.error },\n )\n }\n }\n }\n\n // Stage 3 — create fresh.\n const connectedIntegrationIds = await shell.connectedIntegrationIds(workspaceId)\n const buildCtx: SandboxBuildContext = {\n workspaceId,\n connectedIntegrationIds,\n ...(userId ? { userId } : {}),\n }\n const [secrets, env, files] = await Promise.all([\n shell.secrets(workspaceId),\n shell.env(buildCtx),\n shell.files(buildCtx),\n ])\n const fullProfile = shell.profile({ extraFiles: files })\n // When deferring, strip inline files from the create payload and write them\n // into the box after it reaches running. Keeps the provision body under the\n // orchestrator's 256 KiB cap and lands real files on disk.\n const { leanProfile, deferredFiles } = shell.deferProfileFiles\n ? splitDeferredProfileFiles(fullProfile)\n : { leanProfile: fullProfile, deferredFiles: [] as AgentProfileFileMount[] }\n const profile = leanProfile\n\n const role = userId && shell.permissionRole ? shell.permissionRole('developer') : undefined\n\n // Bake the model at create when opted in. childKeyMint overrides the apiKey\n // per-workspace; a typed mint failure falls through to the parent key (logged).\n let model = shell.backendModelAtCreate ? resolveModel(shell.provider) : undefined\n if (model && shell.childKeyMint && model.provider === 'openai-compat') {\n const minted = await shell.childKeyMint(scope)\n if (minted.succeeded) model = { ...model, apiKey: minted.value }\n else {\n console.error(\n `[sandbox] childKeyMint failed for ${workspaceId}; using parent key:`,\n minted.error.message,\n )\n }\n }\n\n const storage = shell.storage?.(buildCtx)\n const restore = shell.restore?.(buildCtx)\n\n const payload = {\n name,\n image: resources.image,\n metadata: shell.metadata(harness),\n ...(userId ? { permissions: { initialUsers: [{ userId, role }] } } : {}),\n env,\n secrets,\n backend: { type: harness, profile, ...(model ? { model } : {}) },\n ...(storage ? { storage } : {}),\n ...(restore ? restore : {}),\n ...(shell.webTerminalEnabled ? { webTerminalEnabled: true } : {}),\n maxLifetimeSeconds: resources.maxLifetimeSeconds,\n idleTimeoutSeconds: resources.idleTimeoutSeconds,\n resources: {\n cpuCores: resources.cpuCores,\n memoryMB: resources.memoryMB,\n diskGB: resources.diskGB,\n },\n } as CreatePayload\n\n let box = await client.create(payload)\n\n await box.waitFor('running', { timeoutMs: 120_000 })\n box = await refreshRuntimeConnection(client, box)\n\n if (deferredFiles.length > 0) {\n const written = await writeProfileFilesToBox(box, deferredFiles)\n if (!written.succeeded) {\n throw new Error(`deferred file write failed on new box ${name}`, { cause: written.error })\n }\n }\n\n if (shell.bootstrap) {\n const boot = await shell.bootstrap(box, scope)\n if (!boot.succeeded) {\n throw new Error(`bootstrap failed on new box ${name}`, { cause: boot.error })\n }\n }\n return box\n}\n\nexport interface ResolvedModel {\n model: string\n provider: string\n apiKey: string\n baseUrl?: string\n}\n\nexport function resolveModel(\n config: ProviderResolutionConfig | undefined,\n override?: { model?: string; modelApiKey?: string },\n): ResolvedModel | undefined {\n const c = config ?? {}\n const explicitBaseUrl = c.routerBaseUrl\n const explicitApiKey = override?.modelApiKey ?? c.apiKey\n const provider =\n c.providerName ?? (explicitApiKey ? 'openai-compat' : c.openaiApiKey ? 'openai' : undefined)\n const modelName =\n override?.model ??\n c.modelName ??\n (provider === 'openai' || provider === 'openai-compat' ? c.defaultModel : undefined)\n const apiKey = explicitApiKey ?? (provider === 'openai' ? c.openaiApiKey : undefined)\n if (!provider || !modelName || !apiKey) return undefined\n return {\n model: modelName,\n provider,\n apiKey,\n ...(explicitBaseUrl ? { baseUrl: explicitBaseUrl } : {}),\n }\n}\n\nexport function flattenHistory(\n message: string,\n history?: Array<{ role: 'user' | 'assistant'; content: string }>,\n): string {\n if (!history?.length) return message\n const transcript = history\n .map((entry) => `${entry.role === 'assistant' ? 'Assistant' : 'User'}: ${entry.content}`)\n .join('\\n\\n')\n return `${transcript}\\n\\nUser: ${message}`\n}\n\nexport function mergeExtraMcp(\n appToolMcp: Record<string, AgentProfileMcpServer>,\n baseProfileMcp: Record<string, AgentProfileMcpServer>,\n extra: Record<string, AgentProfileMcpServer> | undefined,\n): Record<string, AgentProfileMcpServer> {\n for (const key of Object.keys(extra ?? {})) {\n if (key in appToolMcp || key in baseProfileMcp) {\n throw new Error(`extraMcp key '${key}' collides with an existing profile MCP server`)\n }\n }\n return { ...appToolMcp, ...(extra ?? {}) }\n}\n\nexport function attachReasoningEffort(\n profile: AgentProfile,\n harness: Harness,\n effort: 'auto' | 'low' | 'medium' | 'high' | undefined,\n): AgentProfile {\n if (!effort || effort === 'auto') return profile\n return {\n ...profile,\n extensions: {\n ...(profile.extensions ?? {}),\n [harness]: {\n ...(profile.extensions?.[harness] ?? {}),\n reasoningEffort: effort,\n },\n },\n }\n}\n\nexport interface StreamSandboxPromptOptions {\n sessionId?: string\n executionId?: string\n lastEventId?: string\n systemPrompt?: string\n model?: string\n modelApiKey?: string\n history?: Array<{ role: 'user' | 'assistant'; content: string }>\n harness?: Harness\n effort?: 'auto' | 'low' | 'medium' | 'high'\n appToolMcp?: Record<string, AgentProfileMcpServer>\n baseProfileMcp?: Record<string, AgentProfileMcpServer>\n extraMcp?: Record<string, AgentProfileMcpServer>\n signal?: AbortSignal\n timeoutMs?: number\n // When true, an interactive question event throws instead of yielding —\n // detached (cron/mission-step) runs have no consumer to answer it.\n disallowQuestions?: boolean\n}\n\ntype StreamPromptOptions = Parameters<SandboxInstance['streamPrompt']>[1]\n\nexport async function* streamSandboxPrompt(\n shell: SandboxRuntimeConfig,\n box: SandboxInstance,\n message: string,\n options?: StreamSandboxPromptOptions,\n): AsyncGenerator<unknown> {\n const harness = options?.harness ?? 'opencode'\n const model = resolveModel(shell.provider, {\n model: options?.model,\n modelApiKey: options?.modelApiKey,\n })\n\n // Server-side enforcement of the harness↔model policy: a vendor-locked harness\n // (claude-code/codex/kimi-code) must not be sent a foreign-provider model, even\n // if the UI snap was bypassed. Provider-less ids pass (session's own config).\n if (model?.model) assertHarnessModelCompatible(harness, model.model)\n\n const prompt = flattenHistory(message, options?.history)\n\n const appToolMcp = options?.appToolMcp ?? {}\n const extraMcp = mergeExtraMcp(appToolMcp, options?.baseProfileMcp ?? {}, options?.extraMcp)\n\n const profile = shell.profile({ systemPrompt: options?.systemPrompt, extraMcp })\n const profileWithEffort = attachReasoningEffort(profile, harness, options?.effort)\n\n const stream = box.streamPrompt(prompt, {\n sessionId: options?.sessionId,\n executionId: options?.executionId,\n lastEventId: options?.lastEventId,\n ...(options?.signal ? { signal: options.signal } : {}),\n ...(options?.timeoutMs !== undefined ? { timeoutMs: options.timeoutMs } : {}),\n backend: {\n type: harness,\n profile: profileWithEffort,\n ...(model ? { model } : {}),\n },\n } as StreamPromptOptions)\n\n let severedFinishReason: string | null = null\n for await (const event of stream) {\n const step = classifySeveredStream(event)\n if (step) severedFinishReason = step.kind === 'step-finish' && step.severed ? step.reason : null\n if (severedFinishReason && isTerminalPromptEvent(event)) {\n throw new Error(`sandbox model stream severed mid-turn (reason=\"${severedFinishReason}\")`)\n }\n if (options?.disallowQuestions) {\n const q = detectInteractiveQuestion(event)\n if (q) {\n throw new Error(`sandbox agent asked an interactive question during an autonomous run: ${q}`)\n }\n }\n yield event\n }\n // Reconnect-exhausted path: the stream ended on a severed step without a\n // terminal event. A truncated turn must fail loud, not return silently.\n if (severedFinishReason) {\n throw new Error(`sandbox model stream severed mid-turn (reason=\"${severedFinishReason}\")`)\n }\n}\n\nexport async function runSandboxPrompt(\n shell: SandboxRuntimeConfig,\n box: SandboxInstance,\n message: string,\n options?: StreamSandboxPromptOptions,\n): Promise<string> {\n let fullText = ''\n let firstTextSeen = false\n\n for await (const rawEvent of streamSandboxPrompt(shell, box, message, options)) {\n const event = rawEvent as { type?: string; data?: Record<string, unknown> }\n if (!event.type) continue\n\n if (event.type === 'message.part.updated') {\n const part = event.data?.part as Record<string, unknown> | undefined\n const delta = typeof event.data?.delta === 'string' ? event.data.delta : null\n if (String(part?.type ?? '') === 'text') {\n if (!firstTextSeen) {\n firstTextSeen = true\n continue\n }\n if (delta) fullText += delta\n else if (typeof part?.text === 'string') fullText = part.text\n }\n } else if (event.type === 'result') {\n const finalText = typeof event.data?.finalText === 'string' ? event.data.finalText : null\n if (finalText) fullText = finalText\n }\n }\n\n return fullText\n}\n\n// Mirrors the SDK's PermissionLevel union (not re-exported by\n// @tangle-network/sandbox). The product's role-mapping seam must produce one of\n// these; binding the seam's return type to the union makes a wrong mapping a\n// compile error rather than a runtime 400 from the orchestrator.\nexport type SandboxPermissionLevel = 'owner' | 'admin' | 'developer' | 'viewer'\n\nexport interface MemberSyncSeam {\n roleToSandboxRole: (workspaceRole: string) => SandboxPermissionLevel\n}\n\nexport async function syncSandboxMemberAdd(\n box: SandboxInstance,\n seam: MemberSyncSeam,\n userId: string,\n role: string,\n): Promise<Outcome<void>> {\n try {\n await box.permissions.add({ userId, role: seam.roleToSandboxRole(role) })\n return ok(undefined)\n } catch (err) {\n return fail(err)\n }\n}\n\nexport async function syncSandboxMemberRemove(\n box: SandboxInstance,\n userId: string,\n): Promise<Outcome<void>> {\n try {\n await box.permissions.remove(userId, { preserveHomeDir: true })\n return ok(undefined)\n } catch (err) {\n return fail(err)\n }\n}\n\nexport async function syncSandboxMemberRole(\n box: SandboxInstance,\n seam: MemberSyncSeam,\n userId: string,\n role: string,\n): Promise<Outcome<void>> {\n try {\n await box.permissions.update(userId, { role: seam.roleToSandboxRole(role) })\n return ok(undefined)\n } catch (err) {\n return fail(err)\n }\n}\n\nexport interface SecretStore {\n create: (name: string, value: string) => Promise<void>\n update: (name: string, value: string) => Promise<void>\n get: (name: string) => Promise<string>\n delete: (name: string) => Promise<void>\n}\n\nexport function secretStoreFromClient(shell: SandboxRuntimeConfig): SecretStore {\n const client = getClient(shell)\n return {\n create: async (name, value) => {\n await client.secrets.create(name, value)\n },\n update: async (name, value) => {\n await client.secrets.update(name, value)\n },\n get: (name) => client.secrets.get(name),\n delete: async (name) => {\n await client.secrets.delete(name)\n },\n }\n}\n\nexport async function storeSecret(\n store: SecretStore,\n name: string,\n value: string,\n): Promise<Outcome<void>> {\n try {\n await store.create(name, value)\n return ok(undefined)\n } catch {\n try {\n await store.update(name, value)\n return ok(undefined)\n } catch (err) {\n return fail(new Error(`Failed to store sandbox secret ${name}`, { cause: err }))\n }\n }\n}\n\nexport async function readSecret(store: SecretStore, name: string): Promise<Outcome<string>> {\n try {\n return ok(await store.get(name))\n } catch (err) {\n return fail(err)\n }\n}\n\nexport async function deleteSecret(store: SecretStore, name: string): Promise<Outcome<void>> {\n try {\n await store.delete(name)\n return ok(undefined)\n } catch (err) {\n return fail(err)\n }\n}\n\nexport interface ScopedTokenResult {\n token: string\n expiresAt: Date\n scope: ScopedTokenScope\n}\n\n/**\n * Mint a scoped token for an already-provisioned box (e.g. to hand a terminal\n * proxy a narrowed credential). Uses the SDK's native `box.mintScopedToken`,\n * which normalizes `expiresAt` to a Date — no hand-rolled wire call.\n */\nexport async function mintSandboxScopedToken(\n box: SandboxInstance,\n options: { scope: ScopedTokenScope; sessionId?: string; ttlMinutes?: number },\n): Promise<Outcome<ScopedTokenResult>> {\n try {\n const token = await box.mintScopedToken({\n scope: options.scope,\n ...(options.sessionId ? { sessionId: options.sessionId } : {}),\n ...(options.ttlMinutes ? { ttlMinutes: options.ttlMinutes } : {}),\n })\n return ok({ token: token.token, expiresAt: token.expiresAt, scope: token.scope })\n } catch (err) {\n return fail(err)\n }\n}\n\n// Detached single-turn advance. The SDK SandboxInstance has no `driveTurn`; the\n// non-streaming sibling of streamPrompt is box.prompt(message, opts) -> PromptResult.\n// Returns a typed Outcome so a failed turn is inspected, not swallowed.\nexport async function driveSandboxTurn(\n shell: SandboxRuntimeConfig,\n box: SandboxInstance,\n message: string,\n options: StreamSandboxPromptOptions & { sessionId: string },\n): Promise<Outcome<PromptResult>> {\n const harness = options.harness ?? 'opencode'\n const model = resolveModel(shell.provider, {\n model: options.model,\n modelApiKey: options.modelApiKey,\n })\n if (model?.model) assertHarnessModelCompatible(harness, model.model)\n const prompt = flattenHistory(message, options.history)\n const appToolMcp = options.appToolMcp ?? {}\n const extraMcp = mergeExtraMcp(appToolMcp, options.baseProfileMcp ?? {}, options.extraMcp)\n const profile = attachReasoningEffort(\n shell.profile({ systemPrompt: options.systemPrompt, extraMcp }),\n harness,\n options.effort,\n )\n try {\n const result = await box.prompt(prompt, {\n sessionId: options.sessionId,\n ...(options.executionId ? { executionId: options.executionId } : {}),\n ...(options.timeoutMs !== undefined ? { timeoutMs: options.timeoutMs } : {}),\n ...(options.signal ? { signal: options.signal } : {}),\n backend: { type: harness, profile, ...(model ? { model } : {}) },\n } as Parameters<SandboxInstance['prompt']>[1])\n if (!result.success) return fail(new Error(result.error ?? 'sandbox turn failed'))\n return ok(result)\n } catch (err) {\n return fail(err)\n }\n}\n\n// Severed-stream classifier. Generic to any router-backed harness: a final step\n// that finished with error/other/unknown is a truncated turn, not a completed one.\nconst SEVERED_FINISH_REASONS = new Set(['error', 'other', 'unknown'])\n\nexport type SandboxStepTransition =\n | { kind: 'step-start' }\n | { kind: 'step-finish'; reason: string; severed: boolean }\n\nfunction asPlainRecord(v: unknown): Record<string, unknown> | null {\n return v && typeof v === 'object' && !Array.isArray(v) ? (v as Record<string, unknown>) : null\n}\n\nexport function classifySeveredStream(event: unknown): SandboxStepTransition | null {\n const root = asPlainRecord(event)\n if (!root || root.type !== 'message.part.updated') return null\n const body = asPlainRecord(root.properties) ?? asPlainRecord(root.data) ?? root\n const part = asPlainRecord(body.part)\n if (!part) return null\n if (part.type === 'step-start') return { kind: 'step-start' }\n if (part.type !== 'step-finish') return null\n const reason = typeof part.reason === 'string' && part.reason ? part.reason : 'unknown'\n return { kind: 'step-finish', reason, severed: SEVERED_FINISH_REASONS.has(reason) }\n}\n\nexport function isTerminalPromptEvent(event: unknown): boolean {\n const t = asPlainRecord(event)?.type\n return t === 'result' || t === 'done'\n}\n\n// Interactive-question detector. Returns the question text or null. Used by\n// streamSandboxPrompt when disallowQuestions is set.\nexport function detectInteractiveQuestion(event: unknown): string | null {\n const root = asPlainRecord(event)\n if (!root) return null\n const type = typeof root.type === 'string' ? root.type : undefined\n const data = asPlainRecord(root.data)\n const props = asPlainRecord(root.properties)\n const body = props ?? data ?? root\n if (type === 'question.asked' || type === 'question') return firstQuestionText(body)\n const part = asPlainRecord(data?.part) ?? asPlainRecord(body.part)\n const tool =\n (typeof part?.tool === 'string' && part.tool) ||\n (typeof part?.name === 'string' && part.name) ||\n (typeof body.tool === 'string' && body.tool) ||\n undefined\n const isQ =\n type === 'message.part.updated' &&\n (tool === 'question' || asPlainRecord(part)?.type === 'question')\n if (!isQ) return null\n const state = asPlainRecord(asPlainRecord(part)?.state)\n return firstQuestionText(asPlainRecord(state?.input) ?? state ?? part ?? body)\n}\n\nfunction firstQuestionText(value: Record<string, unknown> | null): string {\n const arr = Array.isArray(value?.questions)\n ? value!.questions\n : Array.isArray(asPlainRecord(value?.input)?.questions)\n ? (asPlainRecord(value!.input)!.questions as unknown[])\n : []\n const first = asPlainRecord(arr[0])\n const q =\n (typeof first?.question === 'string' && first.question) ||\n (typeof first?.prompt === 'string' && first.prompt) ||\n undefined\n return q ?? 'interactive question'\n}\n// Workspace sandbox terminal handlers: WebSocket upgrade proxy, connection\n// + runtime-proxy handlers, and scoped terminal-token mint/verify.\nexport * from './terminal-proxy-token'\nexport * from './workspace-terminal'\n","// Shared Outcome triple for the sandbox modules. Lives in a dependency-free\n// leaf so both index.ts and terminal-proxy-token.ts import it instead of\n// re-declaring the type (index.ts re-exports * from terminal-proxy-token, so\n// the token module cannot import from index without a cycle).\nexport type Outcome<T> =\n | { succeeded: true; value: T }\n | { succeeded: false; error: Error }\n\nexport const ok = <T>(value: T): Outcome<T> => ({ succeeded: true, value })\nexport const fail = (error: unknown): Outcome<never> => ({\n succeeded: false,\n error: error instanceof Error ? error : new Error(String(error)),\n})\n","import {\n base64UrlDecodeText,\n base64UrlEncodeText,\n constantTimeEqual,\n hmacSha256Base64Url,\n} from '../crypto/web-token'\nimport { ok, fail, type Outcome } from './outcome'\n\n// Terminal-proxy HMAC token. Identity tuple is generic; the secret comes from a\n// closure (fail-loud if absent).\nexport interface TerminalProxyIdentity {\n userId: string\n workspaceId: string\n sandboxId: string\n}\n\nconst TERMINAL_PROXY_TOKEN_TTL_MS = 15 * 60 * 1000\n\nexport async function mintTerminalProxyToken(\n secret: string,\n identity: TerminalProxyIdentity,\n ttlMs = TERMINAL_PROXY_TOKEN_TTL_MS,\n now: () => number = Date.now,\n): Promise<Outcome<{ token: string; expiresAt: Date }>> {\n if (!secret) return fail(new Error('mintTerminalProxyToken: secret is required'))\n if (!identity.userId || !identity.workspaceId || !identity.sandboxId) {\n return fail(new Error('mintTerminalProxyToken: userId/workspaceId/sandboxId are required'))\n }\n const expiresAt = new Date(now() + ttlMs)\n const payload = { ...identity, exp: Math.floor(expiresAt.getTime() / 1000) }\n const encoded = base64UrlEncodeText(JSON.stringify(payload))\n const sig = await hmacSha256Base64Url(encoded, secret)\n return ok({ token: `${encoded}.${sig}`, expiresAt })\n}\n\nexport async function verifyTerminalProxyToken(\n secret: string,\n token: string,\n expected: TerminalProxyIdentity,\n now: () => number = Date.now,\n): Promise<boolean> {\n if (!secret) return false\n const [encoded, sig, extra] = token.split('.')\n if (!encoded || !sig || extra !== undefined) return false\n const expectedSig = await hmacSha256Base64Url(encoded, secret)\n if (!constantTimeEqual(sig, expectedSig)) return false\n let payload: TerminalProxyIdentity & { exp: number }\n try {\n payload = JSON.parse(base64UrlDecodeText(encoded))\n } catch {\n return false\n }\n return (\n payload.userId === expected.userId &&\n payload.workspaceId === expected.workspaceId &&\n payload.sandboxId === expected.sandboxId &&\n Number.isFinite(payload.exp) &&\n payload.exp > Math.floor(now() / 1000)\n )\n}\n","import { base64UrlDecodeText } from '../crypto/web-token'\nimport {\n mintTerminalProxyToken,\n verifyTerminalProxyToken,\n type TerminalProxyIdentity,\n} from './terminal-proxy-token'\n\nexport interface WorkspaceSandboxInstanceLike {\n id: string\n name?: string\n status?: string\n connection?: {\n runtimeUrl?: string\n sidecarUrl?: string\n authToken?: string\n sidecarToken?: string\n authTokenExpiresAt?: string\n } | null\n}\n\n// Generic name-keyed sandbox lifecycle manager. A structural, substrate-free\n// helper for products that drive their own SDK/box types (distinct from the\n// concrete `ensureWorkspaceSandbox` in ./index, which is bound to the\n// @tangle-network/sandbox client). Kept as a public export — external\n// consumers compose it; removing it would be a breaking change.\nexport interface WorkspaceSandboxEnsureContext {\n workspaceId: string\n userId: string\n}\n\nexport interface WorkspaceSandboxManagerOptions<TClient, TBox extends WorkspaceSandboxInstanceLike, TEnsureOptions = void> {\n getClient: (ctx: WorkspaceSandboxEnsureContext) => Promise<TClient> | TClient\n nameForWorkspace: (workspaceId: string, ctx: WorkspaceSandboxEnsureContext) => string\n listSandboxes: (client: TClient, ctx: WorkspaceSandboxEnsureContext) => Promise<TBox[]>\n createSandbox: (args: {\n client: TClient\n ctx: WorkspaceSandboxEnsureContext\n name: string\n options: TEnsureOptions\n listError?: unknown\n }) => Promise<TBox>\n waitForRunning?: (box: TBox, ctx: WorkspaceSandboxEnsureContext) => Promise<void>\n prepareExisting?: (box: TBox, ctx: WorkspaceSandboxEnsureContext, options: TEnsureOptions) => Promise<TBox | void>\n prepareCreated?: (box: TBox, ctx: WorkspaceSandboxEnsureContext, options: TEnsureOptions) => Promise<TBox | void>\n onListError?: (error: unknown, ctx: WorkspaceSandboxEnsureContext) => void\n}\n\nexport interface WorkspaceSandboxManager<TBox extends WorkspaceSandboxInstanceLike, TEnsureOptions = void> {\n ensureWorkspaceSandbox: (\n workspaceId: string,\n userId: string,\n options?: TEnsureOptions,\n ) => Promise<TBox>\n}\n\nexport function createWorkspaceSandboxManager<TClient, TBox extends WorkspaceSandboxInstanceLike, TEnsureOptions = void>(\n opts: WorkspaceSandboxManagerOptions<TClient, TBox, TEnsureOptions>,\n): WorkspaceSandboxManager<TBox, TEnsureOptions> {\n return {\n async ensureWorkspaceSandbox(workspaceId, userId, options) {\n if (!workspaceId) throw new Error('workspaceId is required')\n if (!userId) throw new Error('userId is required')\n const ctx = { workspaceId, userId }\n const client = await opts.getClient(ctx)\n const name = opts.nameForWorkspace(workspaceId, ctx)\n let listError: unknown\n let existing: TBox[] = []\n\n try {\n existing = await opts.listSandboxes(client, ctx)\n } catch (err) {\n listError = err\n opts.onListError?.(err, ctx)\n }\n\n const found = existing.find((box) => box.name === name)\n if (found) {\n return (await opts.prepareExisting?.(found, ctx, options as TEnsureOptions)) ?? found\n }\n\n const created = await opts.createSandbox({\n client,\n ctx,\n name,\n options: options as TEnsureOptions,\n listError,\n })\n await opts.waitForRunning?.(created, ctx)\n return (await opts.prepareCreated?.(created, ctx, options as TEnsureOptions)) ?? created\n },\n }\n}\n\nexport interface SandboxTerminalTokenOptions {\n secret?: string\n expiresInMs?: number\n now?: () => number\n}\n\nexport type SandboxTerminalTokenSubject = TerminalProxyIdentity\n\nexport interface SandboxTerminalTokenResult {\n token: string\n expiresAt: Date\n}\n\nconst DEFAULT_TERMINAL_TOKEN_TTL_MS = 15 * 60 * 1000\nconst BEARER_SUBPROTOCOL_PREFIX = 'bearer.'\n// Legacy `createSandboxTerminalToken` (pre proxy-token extraction) prefixed\n// minted tokens with `sbxt_` and signed the unprefixed payload. New tokens\n// carry no prefix. Strip it on verify so tokens minted by a prior deploy still\n// validate within their (default 15-min) TTL window — avoids a wave of 403s on\n// in-flight browser terminal sessions right after rollout.\nconst LEGACY_TERMINAL_TOKEN_PREFIX = 'sbxt_'\n\nexport async function createSandboxTerminalToken(\n subject: SandboxTerminalTokenSubject,\n opts: SandboxTerminalTokenOptions,\n): Promise<SandboxTerminalTokenResult> {\n validateTerminalSubject(subject)\n const secret = opts.secret?.trim()\n if (!secret) throw new Error('terminal token secret is required')\n const now = opts.now ?? Date.now\n const expiresInMs = opts.expiresInMs ?? DEFAULT_TERMINAL_TOKEN_TTL_MS\n if (!Number.isFinite(expiresInMs) || expiresInMs <= 0) throw new Error('expiresInMs must be a positive number')\n const minted = await mintTerminalProxyToken(secret, subject, expiresInMs, now)\n if (!minted.succeeded) throw minted.error\n return minted.value\n}\n\nexport async function verifySandboxTerminalToken(\n token: string,\n expected: SandboxTerminalTokenSubject,\n opts: SandboxTerminalTokenOptions,\n): Promise<boolean> {\n validateTerminalSubject(expected)\n const secret = opts.secret?.trim()\n const now = opts.now ?? Date.now\n const normalized = token.startsWith(LEGACY_TERMINAL_TOKEN_PREFIX)\n ? token.slice(LEGACY_TERMINAL_TOKEN_PREFIX.length)\n : token\n return verifyTerminalProxyToken(secret ?? '', normalized, expected, now)\n}\n\nexport interface AuthenticatedSandboxUser {\n id: string\n}\n\nexport interface WorkspaceSandboxConnectionHandlerOptions<TBox extends WorkspaceSandboxInstanceLike> {\n requireUser: (request: Request) => Promise<AuthenticatedSandboxUser>\n requireWorkspaceAccess: (args: { request: Request; userId: string; workspaceId: string }) => Promise<void>\n ensureWorkspaceSandbox: (workspaceId: string, userId: string) => Promise<TBox>\n tokenSecret: string | (() => string | undefined)\n tokenExpiresInMs?: number\n proxyRuntimeUrl?: (args: { request: Request; workspaceId: string; sandboxId: string; box: TBox }) => string\n exposeDirectSidecar?: boolean\n}\n\nexport interface WorkspaceSandboxConnectionArgs {\n request: Request\n params: {\n workspaceId?: string\n }\n}\n\nexport function createWorkspaceSandboxConnectionHandler<TBox extends WorkspaceSandboxInstanceLike>(\n opts: WorkspaceSandboxConnectionHandlerOptions<TBox>,\n) {\n return async function handleWorkspaceSandboxConnection({ request, params }: WorkspaceSandboxConnectionArgs): Promise<Response> {\n const user = await opts.requireUser(request)\n const workspaceId = params.workspaceId\n if (!workspaceId) return Response.json({ error: 'workspaceId is required' }, { status: 400 })\n await opts.requireWorkspaceAccess({ request, userId: user.id, workspaceId })\n\n let box: TBox\n try {\n box = await opts.ensureWorkspaceSandbox(workspaceId, user.id)\n } catch (err) {\n return Response.json(\n { error: err instanceof Error ? err.message : 'Failed to provision workspace sandbox' },\n { status: 500 },\n )\n }\n\n const directSidecarUrl = box.connection?.sidecarUrl ?? box.connection?.runtimeUrl\n const directSidecarToken = box.connection?.authToken ?? box.connection?.sidecarToken\n const directSidecarExpiresAt = box.connection?.authTokenExpiresAt\n if (opts.exposeDirectSidecar && directSidecarUrl && directSidecarToken && directSidecarExpiresAt) {\n return Response.json({\n runtimeUrl: directSidecarUrl,\n sidecarUrl: directSidecarUrl,\n token: directSidecarToken,\n expiresAt: directSidecarExpiresAt,\n status: box.status,\n sandboxId: box.id,\n })\n }\n\n if (!directSidecarUrl) {\n return Response.json(\n {\n error: 'Workspace sandbox runtime not ready. The sandbox is still initializing -- retry in a few seconds.',\n status: box.status,\n },\n { status: 503 },\n )\n }\n\n const secret = typeof opts.tokenSecret === 'function' ? opts.tokenSecret() : opts.tokenSecret\n let scoped: SandboxTerminalTokenResult\n try {\n scoped = await createSandboxTerminalToken(\n { userId: user.id, workspaceId, sandboxId: box.id },\n { secret, expiresInMs: opts.tokenExpiresInMs },\n )\n } catch (err) {\n return Response.json(\n { error: err instanceof Error ? err.message : 'Failed to mint sandbox token' },\n { status: 503 },\n )\n }\n\n const runtimeUrl = opts.proxyRuntimeUrl\n ? opts.proxyRuntimeUrl({ request, workspaceId, sandboxId: box.id, box })\n : `/api/workspaces/${encodeURIComponent(workspaceId)}/sandbox/runtime/${encodeURIComponent(box.id)}`\n\n return Response.json({\n runtimeUrl,\n sidecarUrl: runtimeUrl,\n token: scoped.token,\n expiresAt: scoped.expiresAt.toISOString(),\n status: box.status,\n sandboxId: box.id,\n })\n }\n}\n\nexport interface SandboxApiCredentials {\n baseUrl: string\n apiKey: string\n}\n\nexport interface SandboxRuntimeConnection {\n runtimeUrl: string\n /** Server-side sidecar bearer. Must authorize terminal routes; never expose it to browser code. */\n authToken?: string\n}\n\nexport interface WorkspaceSandboxRuntimeProxyHandlerOptions {\n requireUser: (request: Request) => Promise<AuthenticatedSandboxUser>\n requireWorkspaceAccess: (args: { request: Request; userId: string; workspaceId: string; sandboxId: string }) => Promise<void>\n getSandboxApiCredentials: (args: { request: Request; userId: string; workspaceId: string; sandboxId: string }) => Promise<SandboxApiCredentials>\n getSandboxRuntimeConnection?: (args: { request: Request; userId: string; workspaceId: string; sandboxId: string }) => Promise<SandboxRuntimeConnection | null | undefined>\n tokenSecret: string | (() => string | undefined)\n fetch?: typeof fetch\n forwardHeaders?: string[]\n}\n\nexport interface WorkspaceSandboxRuntimeProxyArgs {\n request: Request\n params: {\n workspaceId?: string\n sandboxId?: string\n '*'?: string\n }\n}\n\nexport function createWorkspaceSandboxRuntimeProxyHandler(opts: WorkspaceSandboxRuntimeProxyHandlerOptions) {\n return async function handleWorkspaceSandboxRuntimeProxy({ request, params }: WorkspaceSandboxRuntimeProxyArgs): Promise<Response> {\n const user = await opts.requireUser(request)\n const workspaceId = params.workspaceId\n const sandboxId = params.sandboxId\n const runtimePath = params['*']\n if (!workspaceId || !sandboxId || !runtimePath) {\n return Response.json({ error: 'workspaceId, sandboxId, and runtime path are required' }, { status: 400 })\n }\n const encodedRuntimePath = encodeSandboxRuntimePath(runtimePath)\n if (!encodedRuntimePath) return Response.json({ error: 'Invalid sandbox runtime path' }, { status: 400 })\n\n await opts.requireWorkspaceAccess({ request, userId: user.id, workspaceId, sandboxId })\n\n const token = terminalTokenFromRequest(request.headers)\n const secret = typeof opts.tokenSecret === 'function' ? opts.tokenSecret() : opts.tokenSecret\n if (!token || !(await verifySandboxTerminalToken(token, { userId: user.id, workspaceId, sandboxId }, { secret }))) {\n return Response.json({ error: 'Invalid terminal token' }, { status: 403 })\n }\n\n const requestUrl = new URL(request.url)\n const runtimeConnection = await opts.getSandboxRuntimeConnection?.({ request, userId: user.id, workspaceId, sandboxId })\n const directRuntimeConnection = runtimeConnection?.runtimeUrl && runtimeConnection.authToken ? runtimeConnection : null\n const credentials = directRuntimeConnection ? null : await opts.getSandboxApiCredentials({ request, userId: user.id, workspaceId, sandboxId })\n const upstreamUrl = directRuntimeConnection\n ? new URL(encodedRuntimePath, `${directRuntimeConnection.runtimeUrl.replace(/\\/+$/, '')}/`)\n : new URL(\n `/v1/sandboxes/${encodeURIComponent(sandboxId)}/runtime/${encodedRuntimePath}`,\n credentials!.baseUrl,\n )\n upstreamUrl.search = requestUrl.search\n\n const headers = buildSandboxRuntimeProxyHeaders(\n request.headers,\n directRuntimeConnection?.authToken ?? credentials!.apiKey,\n opts.forwardHeaders,\n )\n const init: RequestInit & { duplex?: 'half' } = {\n method: request.method,\n headers,\n redirect: 'manual',\n }\n if (request.method !== 'GET' && request.method !== 'HEAD' && request.body) {\n init.body = request.body\n init.duplex = 'half'\n }\n\n const fetchImpl = opts.fetch ?? fetch\n const response = await fetchImpl(upstreamUrl, init)\n const responseHeaders = new Headers(response.headers)\n responseHeaders.delete('set-cookie')\n return new Response(response.body, {\n status: response.status,\n statusText: response.statusText,\n headers: responseHeaders,\n })\n }\n}\n\n// ---------------------------------------------------------------------------\n// Terminal WebSocket upgrade\n//\n// The interactive terminal is WebSocket-only on the current sidecar (the REST\n// `POST /terminals` create route was removed in the websocket-first migration).\n// `createWorkspaceSandboxRuntimeProxyHandler` runs inside a React Router\n// loader/action, which can only return a normal Response — never a 101 — so it\n// cannot perform the upgrade. The upgrade must be intercepted at the Worker\n// fetch entry (server.ts) BEFORE React Router, mirroring the session-stream WS\n// interceptor. This handler does exactly that: it auth-gates the upgrade (the\n// scoped terminal token rides in the `bearer.` subprotocol because browsers\n// can't set Authorization on a WS handshake) and forwards it to the sandbox API\n// runtime proxy with the server-to-server credential. Returning the upstream\n// 101 passes the live socket straight through to the browser — the same idiom\n// the sandbox API uses to reach the orchestrator.\n//\n// NOTE: this only runs under a WebSocket-capable runtime (Cloudflare Workers /\n// `wrangler`). `react-router dev` (Vite) never invokes the Worker fetch entry,\n// so the terminal WS is exercised under `wrangler dev` / production.\n// ---------------------------------------------------------------------------\n\nconst SANDBOX_TERMINAL_WS_PATHNAME =\n /^\\/api\\/workspaces\\/([^/]+)\\/sandbox\\/runtime\\/([^/]+)\\/(terminals\\/[^/]+\\/ws)$/\n\nexport interface SandboxTerminalWsMatch {\n workspaceId: string\n sandboxId: string\n subPath: string\n}\n\n/**\n * Parse a same-origin terminal-WS pathname into its parts, or `null` when the\n * path is not a sandbox terminal WebSocket. Matches the default `runtimeUrl`\n * convention emitted by {@link createWorkspaceSandboxConnectionHandler}\n * (`/api/workspaces/:workspaceId/sandbox/runtime/:sandboxId`) with a canonical\n * `terminals/:id/ws` sub-path. `subPath` is left URL-encoded for re-use in the\n * upstream URL; the ids are decoded for auth checks.\n */\nexport function matchSandboxTerminalWsPath(pathname: string): SandboxTerminalWsMatch | null {\n const m = SANDBOX_TERMINAL_WS_PATHNAME.exec(pathname)\n if (!m) return null\n const [, workspaceId, sandboxId, subPath] = m\n if (!workspaceId || !sandboxId || !subPath) return null\n const decodedWorkspaceId = safeDecodeURIComponent(workspaceId)\n const decodedSandboxId = safeDecodeURIComponent(sandboxId)\n if (!decodedWorkspaceId || !decodedSandboxId) return null\n return { workspaceId: decodedWorkspaceId, sandboxId: decodedSandboxId, subPath }\n}\n\n/** True when `request` is a WebSocket upgrade for a sandbox terminal path. */\nexport function isSandboxTerminalWsUpgrade(request: Request): boolean {\n if (request.headers.get('Upgrade')?.toLowerCase() !== 'websocket') return false\n try {\n return matchSandboxTerminalWsPath(new URL(request.url).pathname) !== null\n } catch {\n return false\n }\n}\n\nexport interface WorkspaceSandboxTerminalUpgradeHandlerOptions {\n requireUser: (request: Request) => Promise<AuthenticatedSandboxUser>\n requireWorkspaceAccess: (args: { request: Request; userId: string; workspaceId: string; sandboxId: string }) => Promise<void>\n getSandboxApiCredentials: (args: { request: Request; userId: string; workspaceId: string; sandboxId: string }) => Promise<SandboxApiCredentials>\n getSandboxRuntimeConnection?: (args: { request: Request; userId: string; workspaceId: string; sandboxId: string }) => Promise<SandboxRuntimeConnection | null | undefined>\n tokenSecret: string | (() => string | undefined)\n fetch?: typeof fetch\n}\n\n/**\n * Build a Worker-entry handler that proxies a sandbox terminal WebSocket\n * upgrade to the sandbox API runtime proxy. Returns `null` when the request is\n * not a terminal WS upgrade, so the caller can fall through to its normal\n * request handler:\n *\n * ```ts\n * const handled = await handleSandboxTerminalUpgrade(request)\n * if (handled) return handled\n * ```\n */\nexport function createWorkspaceSandboxTerminalUpgradeHandler(opts: WorkspaceSandboxTerminalUpgradeHandlerOptions) {\n return async function handleWorkspaceSandboxTerminalUpgrade(request: Request): Promise<Response | null> {\n if (request.headers.get('Upgrade')?.toLowerCase() !== 'websocket') return null\n let url: URL\n try {\n url = new URL(request.url)\n } catch {\n return null\n }\n const match = matchSandboxTerminalWsPath(url.pathname)\n if (!match) return null\n const { workspaceId, sandboxId, subPath } = match\n\n let user: AuthenticatedSandboxUser\n try {\n user = await opts.requireUser(request)\n } catch {\n return new Response('Unauthorized', { status: 401 })\n }\n try {\n await opts.requireWorkspaceAccess({ request, userId: user.id, workspaceId, sandboxId })\n } catch {\n return new Response('Forbidden', { status: 403 })\n }\n\n const token = terminalTokenFromRequest(request.headers)\n const secret = typeof opts.tokenSecret === 'function' ? opts.tokenSecret() : opts.tokenSecret\n if (!token || !(await verifySandboxTerminalToken(token, { userId: user.id, workspaceId, sandboxId }, { secret }))) {\n return new Response('Invalid terminal token', { status: 403 })\n }\n\n const runtimeConnection = await opts.getSandboxRuntimeConnection?.({ request, userId: user.id, workspaceId, sandboxId })\n const directRuntimeConnection = runtimeConnection?.runtimeUrl && runtimeConnection.authToken ? runtimeConnection : null\n const credentials = directRuntimeConnection ? null : await opts.getSandboxApiCredentials({ request, userId: user.id, workspaceId, sandboxId })\n const upstreamUrl = directRuntimeConnection\n ? new URL(subPath, `${directRuntimeConnection.runtimeUrl.replace(/\\/+$/, '')}/`)\n : new URL(`/v1/sandboxes/${encodeURIComponent(sandboxId)}/runtime/${subPath}`, credentials!.baseUrl)\n upstreamUrl.search = url.search\n\n // Forward the upgrade verbatim — keep the Upgrade/Connection + Sec-WebSocket-*\n // headers the handshake needs, but strip the browser-only bearer subprotocol\n // and send the server-to-server sandbox credential only as Authorization.\n // Returning the upstream 101 passes the live socket straight through to the browser.\n const upstreamHeaders = new Headers(request.headers)\n const upstreamBearer = directRuntimeConnection?.authToken ?? credentials!.apiKey\n upstreamHeaders.set('Authorization', `Bearer ${upstreamBearer}`)\n upstreamHeaders.delete('host')\n stripBearerSubprotocol(upstreamHeaders)\n const fetchImpl = opts.fetch ?? fetch\n return fetchImpl(upstreamUrl.toString(), { method: request.method, headers: upstreamHeaders })\n }\n}\n\nconst DEFAULT_RUNTIME_PROXY_HEADERS = ['accept', 'content-type', 'last-event-id', 'x-session-id']\n\nexport function buildSandboxRuntimeProxyHeaders(source: Headers, sandboxApiKey: string, forwardHeaders = DEFAULT_RUNTIME_PROXY_HEADERS): Headers {\n const headers = new Headers()\n headers.set('Authorization', `Bearer ${sandboxApiKey}`)\n for (const name of forwardHeaders) {\n const value = source.get(name)\n if (value) headers.set(name, value)\n }\n return headers\n}\n\nexport function encodeSandboxRuntimePath(runtimePath: string): string | null {\n const segments = runtimePath.split('/')\n if (segments.some((segment) => !segment || segment === '.' || segment === '..')) return null\n return segments.map((segment) => encodeURIComponent(segment)).join('/')\n}\n\nexport function bearerToken(value: string | null): string | null {\n if (!value) return null\n const trimmed = value.trim()\n if (!trimmed) return null\n if (trimmed.toLowerCase() === 'bearer') return null\n if (trimmed.toLowerCase().startsWith('bearer ')) {\n const token = trimmed.slice('bearer '.length).trim()\n return token || null\n }\n return trimmed\n}\n\nexport function bearerSubprotocolToken(value: string | null): string | null {\n if (!value) return null\n for (const part of value.split(',')) {\n const protocol = part.trim()\n if (!protocol.toLowerCase().startsWith(BEARER_SUBPROTOCOL_PREFIX)) continue\n const encoded = protocol.slice(BEARER_SUBPROTOCOL_PREFIX.length)\n if (!encoded) return null\n try {\n const token = base64UrlDecodeText(encoded).trim()\n return token || null\n } catch {\n return null\n }\n }\n return null\n}\n\nexport function terminalTokenFromRequest(headers: Headers): string | null {\n return bearerToken(headers.get('Authorization')) ?? bearerSubprotocolToken(headers.get('Sec-WebSocket-Protocol'))\n}\n\nfunction safeDecodeURIComponent(value: string): string | null {\n try {\n return decodeURIComponent(value)\n } catch {\n return null\n }\n}\n\nfunction stripBearerSubprotocol(headers: Headers): void {\n const value = headers.get('Sec-WebSocket-Protocol')\n if (!value) return\n const protocols = value\n .split(',')\n .map((part) => part.trim())\n .filter((part) => part && !part.toLowerCase().startsWith(BEARER_SUBPROTOCOL_PREFIX))\n if (protocols.length) {\n headers.set('Sec-WebSocket-Protocol', protocols.join(', '))\n } else {\n headers.delete('Sec-WebSocket-Protocol')\n }\n}\n\nfunction validateTerminalSubject(subject: SandboxTerminalTokenSubject): void {\n if (!subject.userId) throw new Error('userId is required')\n if (!subject.workspaceId) throw new Error('workspaceId is required')\n if (!subject.sandboxId) throw new Error('sandboxId is required')\n}\n"],"mappings":"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","names":["value","credentials","res","box"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/tools/errors.ts","../src/tools/openai.ts","../src/tools/dispatch.ts","../src/tools/runtime.ts"],"sourcesContent":["/** A correctable bad-input error a tool handler throws; the HTTP layer maps it\n * to a 4xx with the code, the runtime layer to a failed tool_result. So the\n * agent learns the call failed and can correct, instead of a silent success. */\nexport class ToolInputError extends Error {\n constructor(\n public code: string,\n message: string,\n public status = 400,\n ) {\n super(message)\n this.name = 'ToolInputError'\n }\n}\n","import type { AppToolTaxonomy } from './types'\n\n/** The four canonical app-tool names. Stable identifiers the model calls in\n * both the sandbox (MCP server name) and runtime (function-tool name) paths. */\nexport const APP_TOOL_NAMES = ['submit_proposal', 'schedule_followup', 'render_ui', 'add_citation'] as const\nexport type AppToolName = (typeof APP_TOOL_NAMES)[number]\n\nconst NAME_SET = new Set<string>(APP_TOOL_NAMES)\nexport function isAppToolName(name: string): name is AppToolName {\n return NAME_SET.has(name)\n}\n\n/** A minimal OpenAI Chat Completions function-tool shape — structurally\n * compatible with `@tangle-network/agent-runtime`'s `OpenAIChatTool` without\n * importing it (keeps this package runtime-free). */\nexport interface OpenAIFunctionTool {\n type: 'function'\n function: {\n name: string\n description: string\n parameters: Record<string, unknown>\n }\n}\n\n/**\n * Build the four app tools in OpenAI function-tool shape. `submit_proposal`'s\n * `type` enum is the product's {@link AppToolTaxonomy.proposalTypes}; the other\n * three are fixed. Pass the result to the agent-runtime backend's `tools`.\n */\nexport function buildAppToolOpenAITools(taxonomy: AppToolTaxonomy): OpenAIFunctionTool[] {\n return [\n {\n type: 'function',\n function: {\n name: 'submit_proposal',\n description:\n 'Route a regulated or state-changing action to a human for approval (a recommendation, contacting/soliciting a contact, outreach, a record/account change, scheduling). Queues it for a named certified human to approve before it executes.',\n parameters: {\n type: 'object',\n properties: {\n type: { type: 'string', enum: [...taxonomy.proposalTypes] },\n title: { type: 'string', description: 'Short label for the approval queue.' },\n description: { type: 'string', description: 'The full drafted message/recommendation, with sources.' },\n },\n required: ['type', 'title'],\n },\n },\n },\n {\n type: 'function',\n function: {\n name: 'schedule_followup',\n description: 'Register a dated cadence step (a reminder, chase, or check-in) on the follow-up calendar. Executes immediately.',\n parameters: {\n type: 'object',\n properties: {\n title: { type: 'string' },\n dueDate: { type: 'string', description: 'ISO date YYYY-MM-DD.' },\n priority: { type: 'string', enum: ['low', 'medium', 'high'] },\n },\n required: ['title', 'dueDate'],\n },\n },\n },\n {\n type: 'function',\n function: {\n name: 'render_ui',\n description: 'Show a generated view live in the workspace. Validates the OpenUI JSON and persists the artifact. Executes immediately.',\n parameters: {\n type: 'object',\n properties: {\n title: { type: 'string' },\n schema: { type: 'object', description: 'The OpenUI JSON object.' },\n },\n required: ['title', 'schema'],\n },\n },\n },\n {\n type: 'function',\n function: {\n name: 'add_citation',\n description: 'Anchor a grounding reference: the exact quote from a file backing a figure or claim. Verifies the quote appears in the file. Executes immediately.',\n parameters: {\n type: 'object',\n properties: {\n path: { type: 'string', description: 'The vault file path.' },\n quote: { type: 'string', description: 'The exact text from it.' },\n },\n required: ['path', 'quote'],\n },\n },\n },\n ]\n}\n","import { ToolInputError } from './errors'\nimport { isAppToolName } from './openai'\nimport type {\n AppToolContext,\n AppToolHandlers,\n AppToolOutcome,\n AppToolProducedEvent,\n AppToolTaxonomy,\n} from './types'\n\nexport interface DispatchOptions {\n handlers: AppToolHandlers\n taxonomy: AppToolTaxonomy\n /** Per-call approval policy. When provided it OVERRIDES the static\n * `taxonomy.regulatedTypes` membership check, so products can gate by\n * cost threshold, environment, or first-use instead of always/never.\n * Fail-closed: a predicate that throws counts as \"approval required\". */\n needsApproval?: (type: string, args: { title: string; description: string | null }, ctx: AppToolContext) => boolean | Promise<boolean>\n /** Called at the real side-effect site for proposals (proposal_created) and\n * generated views (artifact) so a consumer's completion oracle credits\n * persisted state. Omit when produced state isn't tracked. */\n onProduced?: (event: AppToolProducedEvent) => void\n}\n\n/**\n * The ONE place an app-tool call is validated, dispatched to the product's\n * handler, and turned into an {@link AppToolOutcome} + produced events. Shared\n * by the HTTP route layer and the agent-runtime executor so both paths apply\n * identical validation and identical side effects. A {@link ToolInputError}\n * (bad input the agent can correct) and any other throw both become\n * `{ ok: false }` — a tool call never silently \"succeeds\" without its effect.\n */\nexport async function dispatchAppTool(\n toolName: string,\n rawArgs: Record<string, unknown>,\n ctx: AppToolContext,\n opts: DispatchOptions,\n): Promise<AppToolOutcome> {\n try {\n if (!isAppToolName(toolName)) {\n return { ok: false, code: 'unknown_tool', message: `${toolName} is not an app tool.` }\n }\n\n if (toolName === 'submit_proposal') {\n const type = String(rawArgs.type ?? '').trim()\n const title = String(rawArgs.title ?? '').trim()\n if (!type || !opts.taxonomy.proposalTypes.includes(type)) {\n return { ok: false, code: 'invalid_type', message: `type must be one of: ${opts.taxonomy.proposalTypes.join(', ')}.` }\n }\n if (!title) return { ok: false, code: 'missing_title', message: 'title is required.' }\n const description = rawArgs.description == null ? null : String(rawArgs.description)\n // Approval policy runs BEFORE the handler so the decision can gate the\n // side effect itself, not merely re-label it afterwards.\n let regulated = opts.taxonomy.regulatedTypes.includes(type)\n if (opts.needsApproval) {\n try {\n regulated = await opts.needsApproval(type, { title, description }, ctx)\n } catch {\n regulated = true // fail-closed: a broken policy means approval required\n }\n }\n const r = await opts.handlers.submitProposal({ type, title, description, regulated }, ctx)\n // Pass the handler's result through: products with immediate-execute\n // proposal types return status 'executed' plus their own fields\n // (e.g. datasetId) — the model must see what actually happened, not a\n // hard-coded \"queued for approval\".\n const { proposalId, deduped, status, ...extra } = r\n const effectiveStatus = status ?? 'queued_for_approval'\n opts.onProduced?.({\n type: 'proposal_created',\n proposalId,\n title,\n status: effectiveStatus === 'executed' ? 'executed' : 'pending',\n content: description ?? undefined,\n })\n return { ok: true, result: { ...extra, status: effectiveStatus, proposalId, deduped, regulated } }\n }\n\n if (toolName === 'schedule_followup') {\n const r = await opts.handlers.scheduleFollowup(\n { title: String(rawArgs.title ?? ''), dueDate: String(rawArgs.dueDate ?? ''), priority: rawArgs.priority as string | undefined },\n ctx,\n )\n return { ok: true, result: { followupId: r.id, dueDate: r.dueDate, deduped: r.deduped } }\n }\n\n if (toolName === 'render_ui') {\n const r = await opts.handlers.renderUi({ title: String(rawArgs.title ?? ''), schema: rawArgs.schema }, ctx)\n opts.onProduced?.({ type: 'artifact', path: r.path, content: r.content })\n return { ok: true, result: { path: r.path } }\n }\n\n // add_citation\n const r = await opts.handlers.addCitation(\n { path: String(rawArgs.path ?? ''), quote: String(rawArgs.quote ?? ''), label: rawArgs.label as string | undefined },\n ctx,\n )\n return { ok: true, result: { citationId: r.citationId, path: r.path } }\n } catch (err) {\n if (err instanceof ToolInputError) return { ok: false, code: err.code, message: err.message, status: err.status }\n return { ok: false, code: 'app_tool_error', message: err instanceof Error ? err.message : String(err), status: 500 }\n }\n}\n\n/** HTTP status for a failed outcome — the handler's `ToolInputError.status`\n * when present, else 400 for a validation reject. */\nexport function outcomeStatus(outcome: Extract<AppToolOutcome, { ok: false }>): number {\n return outcome.status ?? 400\n}\n","import { dispatchAppTool, type DispatchOptions } from './dispatch'\nimport type { AppToolContext, AppToolOutcome } from './types'\n\n/** Executes an app-tool call the model emits on the agent-runtime chat path.\n * Plug into `runChatThroughRuntime({ appToolExecutor })` (or any loop that\n * dispatches function tool_calls). */\nexport type AppToolRuntimeExecutor = (call: {\n toolName: string\n args: Record<string, unknown>\n}) => Promise<AppToolOutcome>\n\nexport interface RuntimeExecutorOptions extends DispatchOptions {\n /** The trusted per-turn context — supplied directly (not from headers), since\n * the runtime path has no HTTP request. */\n ctx: AppToolContext\n}\n\n/**\n * Build the runtime executor for one turn. The agent-runtime backend must also\n * advertise the tools (`buildAppToolOpenAITools(taxonomy)` on the backend's\n * `tools`) for the model to call them; this executor fulfils each call against\n * the product's handlers and emits produced events via `opts.onProduced`.\n */\nexport function createAppToolRuntimeExecutor(opts: RuntimeExecutorOptions): AppToolRuntimeExecutor {\n return ({ toolName, args }) => dispatchAppTool(toolName, args, opts.ctx, opts)\n}\n"],"mappings":"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","names":["r"]}
|
|
File without changes
|
|
File without changes
|