@tangle-network/agent-runtime 0.185.0 → 0.185.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.
@@ -1 +1 @@
1
- {"version":3,"file":"bin.js","names":[],"sources":["../../src/mcp/delegate-supervisor-provisioning.ts","../../src/mcp/bin.ts"],"sourcesContent":["/**\n *\n * Resolve the `delegate` supervisor substrate (router brain + worker backend) from env, so the\n * `agent-runtime-mcp` bin can serve the ONE generic `delegate` verb by env, over the SAME stdio\n * invocation a consumer already mounts.\n *\n * `delegate` is wired into `createMcpServer` via `McpServerOptions.delegateSupervisor`, which needs a\n * router (the supervisor brain's transport) and a backend (WHERE the authored workers run). Inside a\n * sandbox child the natural backend is `sandbox`: authored workers run as sub-sandboxes through the\n * SAME `SandboxClient` the bin already loads from `TANGLE_API_KEY`. Dedicated MCP variables name\n * the supervisor's router connection and exact model; inherited worker/model aliases are rejected.\n *\n * @experimental\n */\n\nimport type { AgentProfile } from '@tangle-network/agent-interface'\nimport { ConfigError } from '../errors'\nimport type { SandboxClient } from '../runtime'\nimport type { RouterTransportConfig } from '../runtime/router-client'\nimport { supervisorInstructions } from '../runtime/supervise/authoring'\nimport type { ExecutorConfig } from '../runtime/supervise/runtime'\nimport type { DelegateHandlerOptions } from './tools/delegate'\n\nfunction trimmed(value: string | undefined): string | undefined {\n const v = value?.trim()\n return v ? v : undefined\n}\n\nfunction requiredEnv(env: NodeJS.ProcessEnv, name: string): string {\n const value = trimmed(env[name])\n if (value) return value\n throw new ConfigError(`agent-runtime-mcp: ${name} is required when MCP_ENABLE_DELEGATE=1`)\n}\n\n/** True when the operator opted the generic `delegate` verb in (`MCP_ENABLE_DELEGATE=1`). Default off:\n * the wiring is additive, so consumers that do not enable it are unaffected. */\nexport function delegateEnabled(env: NodeJS.ProcessEnv = process.env): boolean {\n return env.MCP_ENABLE_DELEGATE === '1'\n}\n\n/**\n * Resolve the supervisor brain from dedicated MCP configuration. The model is part of the exact\n * profile; the URL and key are transport-only. No generic worker/model/key variable may silently\n * select this paid execution path.\n */\nfunction resolveRouterSupervisor(env: NodeJS.ProcessEnv): {\n router: RouterTransportConfig\n profile: AgentProfile\n} {\n const routerKey = requiredEnv(env, 'MCP_SUPERVISOR_ROUTER_KEY')\n const base = requiredEnv(env, 'MCP_SUPERVISOR_ROUTER_BASE_URL')\n const routerBaseUrl = /\\/v\\d+\\/?$/.test(base)\n ? base.replace(/\\/$/, '')\n : `${base.replace(/\\/$/, '')}/v1`\n const model = requiredEnv(env, 'MCP_SUPERVISOR_MODEL')\n return {\n router: { routerBaseUrl, routerKey },\n profile: {\n name: 'delegate-supervisor',\n harness: 'cli-base',\n model: { provider: 'tangle-router', default: model },\n prompt: { systemPrompt: supervisorInstructions() },\n },\n }\n}\n\n/**\n * Build the `delegateSupervisor` substrate for `createMcpServer` from env + the bin's loaded\n * `SandboxClient`. Returns `undefined` when `delegate` is not opted in, so the caller mounts it only\n * when asked. The worker backend is `sandbox`; every authored worker's exact profile selects its\n * own harness/provider/model.\n */\nexport function resolveDelegateSupervisor(\n sandboxClient: SandboxClient,\n env: NodeJS.ProcessEnv = process.env,\n): DelegateHandlerOptions | undefined {\n if (!delegateEnabled(env)) return undefined\n const supervisor = resolveRouterSupervisor(env)\n const backend: ExecutorConfig = {\n backend: 'sandbox',\n sandboxClient,\n }\n return {\n router: supervisor.router,\n supervisorProfile: supervisor.profile,\n backend,\n }\n}\n","#!/usr/bin/env node\n\n/**\n *\n * `agent-runtime-mcp` — stdio MCP server entry point.\n *\n * Serves the ONE generic `delegate` verb (opt-in via `MCP_ENABLE_DELEGATE=1`): one intent → a\n * supervisor that authors + drives its own worker over `supervise()`, returning the delivered output\n * with its cost. The supervisor brain runs on the router; authored workers run as sub-sandboxes\n * through the same `SandboxClient` the bin loads from `TANGLE_API_KEY`. The queue-bound tools\n * (`delegate_feedback`, `delegation_status`, `delegation_history`) are always served.\n *\n * Environment variables:\n * TANGLE_API_KEY required — passed to `new Sandbox({ apiKey })`\n * SANDBOX_BASE_URL optional — sandbox-SDK base URL override\n * MCP_ENABLE_DELEGATE set to `1` to serve the generic `delegate` verb. Its authoring\n * supervisor runs the brain on the router and spawns authored\n * workers as sub-sandboxes via the same client; needs TANGLE_API_KEY.\n * MCP_SUPERVISOR_MODEL required exact supervisor brain model id\n * MCP_SUPERVISOR_ROUTER_KEY required router key for the supervisor brain\n * MCP_SUPERVISOR_ROUTER_BASE_URL required router base, normalized to `/v1`\n * AGENT_RUNTIME_DELEGATION_STATE_FILE\n * optional — absolute path of a JSON state\n * file. When set, delegation records persist\n * across MCP restarts (FileDelegationStore):\n * status/history survive and idempotency keys\n * dedupe across processes.\n * AGENT_RUNTIME_DELEGATION_STATE_RECOVER\n * set to `1` to archive a corrupt state file\n * (`<file>.corrupt-<ts>`) and start empty\n * instead of refusing to boot.\n * AGENT_RUNTIME_DELEGATION_RETAIN_TERMINAL\n * optional — positive integer cap on retained\n * terminal records. Unset = keep forever.\n *\n * @experimental\n */\n\nimport type { SandboxClient } from '../runtime'\nimport { delegateEnabled, resolveDelegateSupervisor } from './delegate-supervisor-provisioning'\nimport { FileDelegationStore } from './delegation-store'\nimport { createMcpServer } from './server'\nimport { DelegationTaskQueue } from './task-queue'\nimport type { DelegateHandlerOptions } from './tools/delegate'\nimport { readTraceContextFromEnv, type TraceContext } from './trace-propagation'\n\nconst DEFAULT_SANDBOX_BASE_URL = 'https://sandbox.tangle.tools'\n\nasync function main(): Promise<void> {\n const wantDelegate = delegateEnabled(process.env)\n\n // The generic `delegate` verb needs the sandbox client: its authored workers run as sub-sandboxes\n // (the `sandbox` backend). When `delegate` is not opted in, the server runs the queue-only subset\n // (feedback + status + history) with no sandbox.\n let sandboxClient: SandboxClient | undefined\n if (wantDelegate) {\n const apiKey = process.env.TANGLE_API_KEY\n if (!apiKey && !process.env.AGENT_RUNTIME_MCP_ALLOW_NO_KEY) {\n process.stderr.write(\n 'agent-runtime-mcp: TANGLE_API_KEY is required to serve `delegate`. Set AGENT_RUNTIME_MCP_ALLOW_NO_KEY=1 to run without it for diagnostics, or unset MCP_ENABLE_DELEGATE to run the queue-only subset.\\n',\n )\n process.exit(2)\n }\n sandboxClient = await loadSandboxClient(apiKey)\n }\n\n // The supervisor's loop topology spans export to the OTLP / Tangle Intelligence sink when\n // OTEL_EXPORTER_OTLP_ENDPOINT is set (+ TRACE_ID / PARENT_SPAN_ID for correlation). The same\n // context is stamped onto every delegation record so journal consumers join records into the\n // caller's trace.\n const traceContext = readTraceContextFromEnv()\n if (process.env.OTEL_EXPORTER_OTLP_ENDPOINT) {\n process.stderr.write(\n `agent-runtime-mcp: exporting loop topology → ${process.env.OTEL_EXPORTER_OTLP_ENDPOINT}\\n`,\n )\n }\n\n // The ONE generic `delegate` verb — opt-in via MCP_ENABLE_DELEGATE=1. Its authoring supervisor\n // runs the brain on the router and spawns authored workers as sub-sandboxes through the SAME\n // client, so it needs the loaded `sandboxClient`. Gated on the client resolving (no key → no\n // delegate, fail-closed).\n let delegateSupervisor: DelegateHandlerOptions | undefined\n if (wantDelegate && sandboxClient) {\n try {\n delegateSupervisor = resolveDelegateSupervisor(sandboxClient)\n } catch (error) {\n // Refuse to serve a verb that cannot work. Advertising `delegate` on an\n // unresolvable substrate spends an agent's turn on a tool whose first\n // inference is guaranteed to reject.\n process.stderr.write(`${error instanceof Error ? error.message : String(error)}\\n`)\n process.exit(2)\n }\n }\n if (delegateSupervisor) {\n process.stderr.write(\n `agent-runtime-mcp: delegate enabled — generic authoring supervisor on ${delegateSupervisor.supervisorProfile.model?.default}\\n`,\n )\n }\n\n const durableQueue = await buildDurableQueueFromEnv(traceContext)\n const server = createMcpServer({\n ...(delegateSupervisor ? { delegateSupervisor } : {}),\n traceContext,\n ...(durableQueue ? { queue: durableQueue } : {}),\n })\n\n const shutdown = () => {\n server.stop()\n // Drain journal writes so the state file reflects the final record\n // states before the process exits. A persist failure already routed\n // through onPersistError; swallow the duplicate rejection here.\n if (durableQueue) {\n void durableQueue\n .flush()\n .catch(() => {})\n .finally(() => process.exit(0))\n return\n }\n process.exit(0)\n }\n process.on('SIGINT', shutdown)\n process.on('SIGTERM', shutdown)\n\n await server.serve()\n}\n\nasync function buildDurableQueueFromEnv(\n traceContext: TraceContext,\n): Promise<DelegationTaskQueue | undefined> {\n const stateFile = process.env.AGENT_RUNTIME_DELEGATION_STATE_FILE?.trim()\n if (!stateFile) return undefined\n const store = new FileDelegationStore({\n filePath: stateFile,\n recoverCorrupt: process.env.AGENT_RUNTIME_DELEGATION_STATE_RECOVER === '1',\n })\n const maxTerminalRecords = parseRetention(process.env.AGENT_RUNTIME_DELEGATION_RETAIN_TERMINAL)\n const queue = await DelegationTaskQueue.restore({\n store,\n traceContext,\n ...(maxTerminalRecords !== undefined ? { maxTerminalRecords } : {}),\n onPersistError: (error) => {\n // Durable mode that can no longer write is a broken contract: crash\n // loud instead of degrading to memory-only behind the caller's back.\n process.stderr.write(`agent-runtime-mcp: ${error.message}\\n`)\n process.exit(1)\n },\n })\n process.stderr.write(`agent-runtime-mcp: durable delegation state → ${stateFile}\\n`)\n return queue\n}\n\nfunction parseRetention(raw: string | undefined): number | undefined {\n if (raw === undefined || raw.trim() === '') return undefined\n const n = Number(raw)\n if (!Number.isInteger(n) || n < 1) {\n process.stderr.write(\n `agent-runtime-mcp: AGENT_RUNTIME_DELEGATION_RETAIN_TERMINAL must be a positive integer, got \"${raw}\"\\n`,\n )\n process.exit(2)\n }\n return n\n}\n\nasync function loadSandboxClient(apiKey: string | undefined): Promise<SandboxClient> {\n // Diagnostic mode: AGENT_RUNTIME_MCP_ALLOW_NO_KEY=1 enables tools/list + the\n // queue-bound tools (status / history / feedback) without sandbox creds.\n // `delegate` requires a real client; the stub fails loud at create() so the\n // agent observes the cause instead of silent success.\n if (!apiKey) {\n return {\n async create() {\n throw new Error(\n 'agent-runtime-mcp: TANGLE_API_KEY is unset; `delegate` is disabled in diagnostic mode. Set TANGLE_API_KEY or unset MCP_ENABLE_DELEGATE to remove the unsupported tool from the tool list.',\n )\n },\n } satisfies SandboxClient\n }\n // Dynamic import keeps the bin importable in environments that haven't\n // installed `@tangle-network/sandbox` yet (the runtime package lists it\n // as a peer dep, not a hard dep).\n const mod = await import('@tangle-network/sandbox').catch((err) => {\n process.stderr.write(\n `agent-runtime-mcp: failed to load @tangle-network/sandbox (${err.message}); install the peer dependency\\n`,\n )\n process.exit(2)\n })\n const SandboxCtor = (mod as { Sandbox?: new (config: unknown) => SandboxClient }).Sandbox\n if (!SandboxCtor) {\n process.stderr.write(\n 'agent-runtime-mcp: @tangle-network/sandbox does not export Sandbox; cannot construct client\\n',\n )\n process.exit(2)\n }\n // @tangle-network/sandbox ≥0.6 makes baseUrl required; default it so the MCP server\n // starts without forcing every caller to set SANDBOX_BASE_URL. Treat empty/whitespace as\n // unset (|| not ??) so `SANDBOX_BASE_URL=` still resolves to the default.\n const baseUrl = process.env.SANDBOX_BASE_URL?.trim() || DEFAULT_SANDBOX_BASE_URL\n return new SandboxCtor({ apiKey, baseUrl })\n}\n\nmain().catch((err) => {\n process.stderr.write(`agent-runtime-mcp: ${err instanceof Error ? err.stack : String(err)}\\n`)\n process.exit(1)\n})\n"],"mappings":"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"}
1
+ {"version":3,"file":"bin.js","names":[],"sources":["../../src/mcp/delegate-supervisor-provisioning.ts","../../src/mcp/bin.ts"],"sourcesContent":["/**\n *\n * Resolve the `delegate` supervisor substrate (router brain + worker backend) from env, so the\n * `agent-runtime-mcp` bin can serve the ONE generic `delegate` verb by env, over the SAME stdio\n * invocation a consumer already mounts.\n *\n * `delegate` is wired into `createMcpServer` via `McpServerOptions.delegateSupervisor`, which needs a\n * router (the supervisor brain's transport) and a backend (WHERE the authored workers run). Inside a\n * sandbox child the natural backend is `sandbox`: authored workers run as sub-sandboxes through the\n * SAME `SandboxClient` the bin already loads from `TANGLE_API_KEY`. Dedicated MCP variables name\n * the supervisor's router connection and exact model; inherited worker/model aliases are rejected.\n *\n * @experimental\n */\n\nimport type { AgentProfile } from '@tangle-network/agent-interface'\nimport { ConfigError } from '../errors'\nimport type { SandboxClient } from '../runtime'\nimport type { RouterTransportConfig } from '../runtime/router-client'\nimport { supervisorInstructions } from '../runtime/supervise/authoring'\nimport type { ExecutorConfig } from '../runtime/supervise/runtime'\nimport type { DelegateHandlerOptions } from './tools/delegate'\n\nfunction trimmed(value: string | undefined): string | undefined {\n const v = value?.trim()\n return v ? v : undefined\n}\n\nfunction requiredEnv(env: NodeJS.ProcessEnv, name: string): string {\n const value = trimmed(env[name])\n if (value) return value\n throw new ConfigError(`agent-runtime-mcp: ${name} is required when MCP_ENABLE_DELEGATE=1`)\n}\n\n/** True when the operator opted the generic `delegate` verb in (`MCP_ENABLE_DELEGATE=1`). Default off:\n * the wiring is additive, so consumers that do not enable it are unaffected. */\nexport function delegateEnabled(env: NodeJS.ProcessEnv = process.env): boolean {\n return env.MCP_ENABLE_DELEGATE === '1'\n}\n\n/**\n * Resolve the supervisor brain from dedicated MCP configuration. The model is part of the exact\n * profile; the URL and key are transport-only. No generic worker/model/key variable may silently\n * select this paid execution path.\n */\nfunction resolveRouterSupervisor(env: NodeJS.ProcessEnv): {\n router: RouterTransportConfig\n profile: AgentProfile\n} {\n const routerKey = requiredEnv(env, 'MCP_SUPERVISOR_ROUTER_KEY')\n const base = requiredEnv(env, 'MCP_SUPERVISOR_ROUTER_BASE_URL')\n const routerBaseUrl = /\\/v\\d+\\/?$/.test(base)\n ? base.replace(/\\/$/, '')\n : `${base.replace(/\\/$/, '')}/v1`\n const model = requiredEnv(env, 'MCP_SUPERVISOR_MODEL')\n return {\n router: { routerBaseUrl, routerKey },\n profile: {\n name: 'delegate-supervisor',\n harness: 'cli-base',\n model: { provider: 'tangle-router', default: model },\n prompt: { systemPrompt: supervisorInstructions() },\n },\n }\n}\n\n/**\n * Build the `delegateSupervisor` substrate for `createMcpServer` from env + the bin's loaded\n * `SandboxClient`. Returns `undefined` when `delegate` is not opted in, so the caller mounts it only\n * when asked. The worker backend is `sandbox`; every authored worker's exact profile selects its\n * own harness/provider/model.\n */\nexport function resolveDelegateSupervisor(\n sandboxClient: SandboxClient,\n env: NodeJS.ProcessEnv = process.env,\n): DelegateHandlerOptions | undefined {\n if (!delegateEnabled(env)) return undefined\n const supervisor = resolveRouterSupervisor(env)\n const backend: ExecutorConfig = {\n backend: 'sandbox',\n sandboxClient,\n }\n return {\n router: supervisor.router,\n supervisorProfile: supervisor.profile,\n backend,\n }\n}\n","#!/usr/bin/env node\n\n/**\n *\n * `agent-runtime-mcp` — stdio MCP server entry point.\n *\n * Serves the ONE generic `delegate` verb (opt-in via `MCP_ENABLE_DELEGATE=1`): one intent → a\n * supervisor that authors + drives its own worker over `supervise()`, returning the delivered output\n * with its cost. The supervisor brain runs on the router; authored workers run as sub-sandboxes\n * through the same `SandboxClient` the bin loads from `TANGLE_API_KEY`. The queue-bound tools\n * (`delegate_feedback`, `delegation_status`, `delegation_history`) are always served.\n *\n * Environment variables:\n * TANGLE_API_KEY required — passed to `new Sandbox({ apiKey })`\n * SANDBOX_BASE_URL optional — sandbox-SDK base URL override\n * MCP_ENABLE_DELEGATE set to `1` to serve the generic `delegate` verb. Its authoring\n * supervisor runs the brain on the router and spawns authored\n * workers as sub-sandboxes via the same client; needs TANGLE_API_KEY.\n * MCP_SUPERVISOR_MODEL required exact supervisor brain model id\n * MCP_SUPERVISOR_ROUTER_KEY required router key for the supervisor brain\n * MCP_SUPERVISOR_ROUTER_BASE_URL required router base, normalized to `/v1`\n * AGENT_RUNTIME_DELEGATION_STATE_FILE\n * optional — absolute path of a JSON state\n * file. When set, delegation records persist\n * across MCP restarts (FileDelegationStore):\n * status/history survive and idempotency keys\n * dedupe across processes.\n * AGENT_RUNTIME_DELEGATION_STATE_RECOVER\n * set to `1` to archive a corrupt state file\n * (`<file>.corrupt-<ts>`) and start empty\n * instead of refusing to boot.\n * AGENT_RUNTIME_DELEGATION_RETAIN_TERMINAL\n * optional — positive integer cap on retained\n * terminal records. Unset = keep forever.\n *\n * @experimental\n */\n\nimport type { SandboxClient } from '../runtime'\nimport { delegateEnabled, resolveDelegateSupervisor } from './delegate-supervisor-provisioning'\nimport { FileDelegationStore } from './delegation-store'\nimport { createMcpServer } from './server'\nimport { DelegationTaskQueue } from './task-queue'\nimport type { DelegateHandlerOptions } from './tools/delegate'\nimport { readTraceContextFromEnv, type TraceContext } from './trace-propagation'\n\nconst DEFAULT_SANDBOX_BASE_URL = 'https://sandbox.tangle.tools'\n\nasync function main(): Promise<void> {\n const wantDelegate = delegateEnabled(process.env)\n\n // The generic `delegate` verb needs the sandbox client: its authored workers run as sub-sandboxes\n // (the `sandbox` backend). When `delegate` is not opted in, the server runs the queue-only subset\n // (feedback + status + history) with no sandbox.\n let sandboxClient: SandboxClient | undefined\n if (wantDelegate) {\n const apiKey = process.env.TANGLE_API_KEY\n if (!apiKey && !process.env.AGENT_RUNTIME_MCP_ALLOW_NO_KEY) {\n process.stderr.write(\n 'agent-runtime-mcp: TANGLE_API_KEY is required to serve `delegate`. Set AGENT_RUNTIME_MCP_ALLOW_NO_KEY=1 to run without it for diagnostics, or unset MCP_ENABLE_DELEGATE to run the queue-only subset.\\n',\n )\n process.exit(2)\n }\n sandboxClient = await loadSandboxClient(apiKey)\n }\n\n // The supervisor's loop topology spans export to the OTLP / Tangle Intelligence sink when\n // OTEL_EXPORTER_OTLP_ENDPOINT is set (+ TRACE_ID / PARENT_SPAN_ID for correlation). The same\n // context is stamped onto every delegation record so journal consumers join records into the\n // caller's trace.\n const traceContext = readTraceContextFromEnv()\n if (process.env.OTEL_EXPORTER_OTLP_ENDPOINT) {\n process.stderr.write(\n `agent-runtime-mcp: exporting loop topology → ${process.env.OTEL_EXPORTER_OTLP_ENDPOINT}\\n`,\n )\n }\n\n // The ONE generic `delegate` verb — opt-in via MCP_ENABLE_DELEGATE=1. Its authoring supervisor\n // runs the brain on the router and spawns authored workers as sub-sandboxes through the SAME\n // client, so it needs the loaded `sandboxClient`. Gated on the client resolving (no key → no\n // delegate, fail-closed).\n let delegateSupervisor: DelegateHandlerOptions | undefined\n if (wantDelegate && sandboxClient) {\n try {\n delegateSupervisor = resolveDelegateSupervisor(sandboxClient)\n } catch (error) {\n // Refuse to serve a verb that cannot work. Advertising `delegate` on an\n // unresolvable substrate spends an agent's turn on a tool whose first\n // inference is guaranteed to reject.\n process.stderr.write(`${error instanceof Error ? error.message : String(error)}\\n`)\n process.exit(2)\n }\n }\n if (delegateSupervisor) {\n process.stderr.write(\n `agent-runtime-mcp: delegate enabled — generic authoring supervisor on ${delegateSupervisor.supervisorProfile.model?.default}\\n`,\n )\n }\n\n const durableQueue = await buildDurableQueueFromEnv(traceContext)\n const server = createMcpServer({\n ...(delegateSupervisor ? { delegateSupervisor } : {}),\n traceContext,\n ...(durableQueue ? { queue: durableQueue } : {}),\n })\n\n const shutdown = () => {\n server.stop()\n // Drain journal writes so the state file reflects the final record\n // states before the process exits. A persist failure already routed\n // through onPersistError; swallow the duplicate rejection here.\n if (durableQueue) {\n void durableQueue\n .flush()\n .catch(() => {})\n .finally(() => process.exit(0))\n return\n }\n process.exit(0)\n }\n process.on('SIGINT', shutdown)\n process.on('SIGTERM', shutdown)\n\n await server.serve()\n}\n\nasync function buildDurableQueueFromEnv(\n traceContext: TraceContext,\n): Promise<DelegationTaskQueue | undefined> {\n const stateFile = process.env.AGENT_RUNTIME_DELEGATION_STATE_FILE?.trim()\n if (!stateFile) return undefined\n const store = new FileDelegationStore({\n filePath: stateFile,\n recoverCorrupt: process.env.AGENT_RUNTIME_DELEGATION_STATE_RECOVER === '1',\n })\n const maxTerminalRecords = parseRetention(process.env.AGENT_RUNTIME_DELEGATION_RETAIN_TERMINAL)\n const queue = await DelegationTaskQueue.restore({\n store,\n traceContext,\n ...(maxTerminalRecords !== undefined ? { maxTerminalRecords } : {}),\n onPersistError: (error) => {\n // Durable mode that can no longer write is a broken contract: crash\n // loud instead of degrading to memory-only behind the caller's back.\n process.stderr.write(`agent-runtime-mcp: ${error.message}\\n`)\n process.exit(1)\n },\n })\n process.stderr.write(`agent-runtime-mcp: durable delegation state → ${stateFile}\\n`)\n return queue\n}\n\nfunction parseRetention(raw: string | undefined): number | undefined {\n if (raw === undefined || raw.trim() === '') return undefined\n const n = Number(raw)\n if (!Number.isInteger(n) || n < 1) {\n process.stderr.write(\n `agent-runtime-mcp: AGENT_RUNTIME_DELEGATION_RETAIN_TERMINAL must be a positive integer, got \"${raw}\"\\n`,\n )\n process.exit(2)\n }\n return n\n}\n\nasync function loadSandboxClient(apiKey: string | undefined): Promise<SandboxClient> {\n // Diagnostic mode: AGENT_RUNTIME_MCP_ALLOW_NO_KEY=1 enables tools/list + the\n // queue-bound tools (status / history / feedback) without sandbox creds.\n // `delegate` requires a real client; the stub fails loud at create() so the\n // agent observes the cause instead of silent success.\n if (!apiKey) {\n return {\n async create() {\n throw new Error(\n 'agent-runtime-mcp: TANGLE_API_KEY is unset; `delegate` is disabled in diagnostic mode. Set TANGLE_API_KEY or unset MCP_ENABLE_DELEGATE to remove the unsupported tool from the tool list.',\n )\n },\n } satisfies SandboxClient\n }\n // Diagnostic mode does not need a client, so defer SDK initialization until\n // delegation is requested. Runtime declares the SDK as a required peer.\n const mod = await import('@tangle-network/sandbox').catch((err) => {\n process.stderr.write(\n `agent-runtime-mcp: failed to load @tangle-network/sandbox (${err.message}); install the peer dependency\\n`,\n )\n process.exit(2)\n })\n const SandboxCtor = (mod as { Sandbox?: new (config: unknown) => SandboxClient }).Sandbox\n if (!SandboxCtor) {\n process.stderr.write(\n 'agent-runtime-mcp: @tangle-network/sandbox does not export Sandbox; cannot construct client\\n',\n )\n process.exit(2)\n }\n // @tangle-network/sandbox ≥0.6 makes baseUrl required; default it so the MCP server\n // starts without forcing every caller to set SANDBOX_BASE_URL. Treat empty/whitespace as\n // unset (|| not ??) so `SANDBOX_BASE_URL=` still resolves to the default.\n const baseUrl = process.env.SANDBOX_BASE_URL?.trim() || DEFAULT_SANDBOX_BASE_URL\n return new SandboxCtor({ apiKey, baseUrl })\n}\n\nmain().catch((err) => {\n process.stderr.write(`agent-runtime-mcp: ${err instanceof Error ? err.stack : String(err)}\\n`)\n process.exit(1)\n})\n"],"mappings":"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"}
package/dist/testing.js CHANGED
@@ -7,7 +7,7 @@ import { SANDBOX_SIZE_PRESET_NAMES } from "@tangle-network/agent-interface";
7
7
  //#region src/testing/fixtures/agent-improvement-proposal.json
8
8
  var agent_improvement_proposal_default = {
9
9
  changedSurfaces: ["prompt"],
10
- digest: "sha256:4f43d6b8aa9d87eca2eb26ac835596290d03cd14376e60a180c70099a74cb540",
10
+ digest: "sha256:2615497471797b698f9cc4ca6ce01419bfd3ad028d757030554817b5541746f3",
11
11
  evaluation: {
12
12
  "decision": {
13
13
  "contributingChecks": [
@@ -4578,7 +4578,7 @@ var agent_improvement_proposal_default = {
4578
4578
  ],
4579
4579
  "metadata": {
4580
4580
  "fixture": "agent-improvement-proposal",
4581
- "runtimeVersion": "0.185.0"
4581
+ "runtimeVersion": "0.185.1"
4582
4582
  },
4583
4583
  "objectives": [
4584
4584
  {
@@ -4689,8 +4689,8 @@ var agent_improvement_proposal_default = {
4689
4689
  "baselineContentHash": "sha256:5c21ee53e513fc604cb09754e21c392b24a424da0ef37dbf8f1ee4a8a0b08f09",
4690
4690
  "candidateContentHash": "sha256:60fcbb1c728194bd51d7d19cb732d1c3f1881dce7e0a6266b41c8b98cfd65693",
4691
4691
  "kind": "agent-eval-loop",
4692
- "recordDigest": "sha256:2ca2d58ef2376f0e3dadbe6e3eb0f4fc3dffff9c61b16278b6ecbd4c346df1c1",
4693
- "runId": "agent-runtime-0.185.0-proposal-fixture",
4692
+ "recordDigest": "sha256:4fb71ee51ce267b68c4d046f0a78e7c638505de5caa63da2bc379505eab0933f",
4693
+ "runId": "agent-runtime-0.185.1-proposal-fixture",
4694
4694
  "schema": "agent-candidate-experiment"
4695
4695
  }
4696
4696
  },
@@ -4713,13 +4713,13 @@ var agent_improvement_proposal_default = {
4713
4713
  }],
4714
4714
  kind: "agent-improvement-proposal",
4715
4715
  proposedAt: "2026-07-10T01:00:00.000Z",
4716
- runId: "agent-runtime-0.185.0-proposal-fixture"
4716
+ runId: "agent-runtime-0.185.1-proposal-fixture"
4717
4717
  };
4718
4718
  //#endregion
4719
4719
  //#region src/testing/fixtures/agent-profile-improvement-proposal.json
4720
4720
  var agent_profile_improvement_proposal_default = {
4721
4721
  changedSurfaces: ["prompt", "skills"],
4722
- digest: "sha256:d109d8a979e6476cd1ded4c5c9af05fbda08d37e92ceadcb20736a53d16a7883",
4722
+ digest: "sha256:8526b4e42205eb0f636a95ecefcb7883116852839e35415a461c235a36572c9a",
4723
4723
  evaluation: {
4724
4724
  "decision": {
4725
4725
  "contributingChecks": [
@@ -6353,7 +6353,7 @@ var agent_profile_improvement_proposal_default = {
6353
6353
  ],
6354
6354
  "metadata": {
6355
6355
  "fixture": "agent-profile-improvement-proposal",
6356
- "runtimeVersion": "0.185.0"
6356
+ "runtimeVersion": "0.185.1"
6357
6357
  },
6358
6358
  "objectives": [
6359
6359
  {
@@ -6464,7 +6464,7 @@ var agent_profile_improvement_proposal_default = {
6464
6464
  "baselineContentHash": "sha256:21c495a37c418c10bde64fbaa188beddeed31f1f051ea60a6a6582a9ee0db704",
6465
6465
  "candidateContentHash": "sha256:103f77bc8481601eef1ad5fe6ba84a40dffabc3a44f421f8c8559121edab84e9",
6466
6466
  "kind": "agent-eval-loop",
6467
- "recordDigest": "sha256:5d38bd01aa0145788b16be44f8107c8bea8e109a7859bc34d8c27f6b2fa38401",
6467
+ "recordDigest": "sha256:a5be555f3b08df6b112d710e25a4e3a3e6f4b0a4f3dd3db3e57c1be36563787b",
6468
6468
  "runId": "profile-improvement-1",
6469
6469
  "schema": "agent-profile-improvement-experiment"
6470
6470
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/agent-runtime",
3
- "version": "0.185.0",
3
+ "version": "0.185.1",
4
4
  "description": "Shared task-lifecycle skeleton for agents: a recursive loop kernel for chat turns, one-shot tasks, and multi-attempt loops, with trace capture and eval-gated self-improvement. Domain behavior lives in adapters; scoring and ship-gates in @tangle-network/agent-eval.",
5
5
  "homepage": "https://github.com/tangle-network/agent-runtime#readme",
6
6
  "repository": {
@@ -150,11 +150,6 @@
150
150
  "@tangle-network/agent-interface": "^2.0.0",
151
151
  "@tangle-network/sandbox": ">=0.36.1 <0.37.0"
152
152
  },
153
- "peerDependenciesMeta": {
154
- "@tangle-network/sandbox": {
155
- "optional": true
156
- }
157
- },
158
153
  "dependencies": {
159
154
  "@tangle-network/agent-core": ">=0.9.6 <0.10.0",
160
155
  "@tangle-network/agent-knowledge": "^11.0.0",