@tangle-network/agent-runtime 0.317.0 → 0.319.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/dist/{activation-CvdE2HIU.js → activation-ClcJ6wO1.js} +3 -3
  2. package/dist/{activation-CvdE2HIU.js.map → activation-ClcJ6wO1.js.map} +1 -1
  3. package/dist/{activation-BFDmjaKP.d.ts → activation-OColGsfd.d.ts} +2 -2
  4. package/dist/agent.d.ts +3 -3
  5. package/dist/agent.js +3 -3
  6. package/dist/{bundle-RgHpAFDY.js → bundle-Cw-QdGmH.js} +2 -2
  7. package/dist/{bundle-RgHpAFDY.js.map → bundle-Cw-QdGmH.js.map} +1 -1
  8. package/dist/candidate-execution/index.d.ts +40 -3
  9. package/dist/candidate-execution/index.js +86 -7
  10. package/dist/candidate-execution/index.js.map +1 -1
  11. package/dist/{coordination-driver-DGhN8GO9.js → coordination-driver-Culvomn2.js} +3 -3
  12. package/dist/{coordination-driver-DGhN8GO9.js.map → coordination-driver-Culvomn2.js.map} +1 -1
  13. package/dist/{delegate-B5pmSbQa.js → delegate-CpMbpiIR.js} +2 -2
  14. package/dist/{delegate-B5pmSbQa.js.map → delegate-CpMbpiIR.js.map} +1 -1
  15. package/dist/durable.d.ts +1 -1
  16. package/dist/durable.js +4 -4
  17. package/dist/{graph-G0j1ysqe.js → graph-DnfSGUM8.js} +5 -5
  18. package/dist/{graph-G0j1ysqe.js.map → graph-DnfSGUM8.js.map} +1 -1
  19. package/dist/graph-registry-BuJbzTzF.js +211 -0
  20. package/dist/graph-registry-BuJbzTzF.js.map +1 -0
  21. package/dist/{improvement-cycle-D7yR6GC2.js → improvement-cycle-xnQwUaC3.js} +7 -7
  22. package/dist/{improvement-cycle-D7yR6GC2.js.map → improvement-cycle-xnQwUaC3.js.map} +1 -1
  23. package/dist/{index-CUh40yUJ.d.ts → index-Da7w2H86.d.ts} +351 -132
  24. package/dist/index.d.ts +5 -5
  25. package/dist/index.js +10 -10
  26. package/dist/intelligence.d.ts +4 -4
  27. package/dist/intelligence.js +7 -7
  28. package/dist/kernel.d.ts +3 -2
  29. package/dist/kernel.js +12 -11
  30. package/dist/{loop-runner-bin-DQKLUt0l.d.ts → loop-runner-bin-CbBoLC9D.d.ts} +2 -2
  31. package/dist/{loop-runner-bin-CFVX4oi0.js → loop-runner-bin-uoeGHNZA.js} +3 -3
  32. package/dist/{loop-runner-bin-CFVX4oi0.js.map → loop-runner-bin-uoeGHNZA.js.map} +1 -1
  33. package/dist/loop-runner-bin.d.ts +1 -1
  34. package/dist/loop-runner-bin.js +1 -1
  35. package/dist/mcp/bin.js +3 -3
  36. package/dist/mcp/index.d.ts +2 -2
  37. package/dist/mcp/index.js +6 -6
  38. package/dist/{model-policy-DhvyZ8-O.js → model-policy-CNe257pB.js} +37 -2
  39. package/dist/model-policy-CNe257pB.js.map +1 -0
  40. package/dist/{openai-tools-u3gWBi3x.d.ts → openai-tools-Cu9E1Bda.d.ts} +2 -2
  41. package/dist/placement.d.ts +1 -1
  42. package/dist/{prepare-CD9kR0gS.js → prepare-DjCLEN-q.js} +3 -3
  43. package/dist/prepare-DjCLEN-q.js.map +1 -0
  44. package/dist/{profile-CHXYmpGd.js → profile-DmVM4Gxb.js} +9 -2
  45. package/dist/profile-DmVM4Gxb.js.map +1 -0
  46. package/dist/{profile-materialization-Cca615eu.d.ts → profile-materialization-C5-t3HJ7.d.ts} +14 -3
  47. package/dist/profiles.d.ts +1 -1
  48. package/dist/profiles.js +1 -1
  49. package/dist/{protected-model-port-C9QtEf3y.d.ts → protected-model-port-CQg2gstJ.d.ts} +3 -3
  50. package/dist/{protected-model-port-B0pHd65J.js → protected-model-port-D1fcnYdj.js} +4 -4
  51. package/dist/{protected-model-port-B0pHd65J.js.map → protected-model-port-D1fcnYdj.js.map} +1 -1
  52. package/dist/{provision-supervisor-BQLLrUtn.js → provision-supervisor-DiZTjzdp.js} +5 -5
  53. package/dist/{provision-supervisor-BQLLrUtn.js.map → provision-supervisor-DiZTjzdp.js.map} +1 -1
  54. package/dist/{runtime-B9BOFyEp.js → runtime-6qzuLTJ7.js} +514 -277
  55. package/dist/runtime-6qzuLTJ7.js.map +1 -0
  56. package/dist/{search-executor-B5ZVFsDV.js → search-executor-DlaLafvy.js} +4 -4
  57. package/dist/{search-executor-B5ZVFsDV.js.map → search-executor-DlaLafvy.js.map} +1 -1
  58. package/dist/{server-TwpH9aik.js → server-sLrmWqKw.js} +4 -4
  59. package/dist/{server-TwpH9aik.js.map → server-sLrmWqKw.js.map} +1 -1
  60. package/dist/{supervise-CU6AuxWY.js → supervise-D4CnC-cl.js} +5 -5
  61. package/dist/{supervise-CU6AuxWY.js.map → supervise-D4CnC-cl.js.map} +1 -1
  62. package/dist/{supervisor-CbFyf48U.js → supervisor-Cqhu3xH-.js} +709 -80
  63. package/dist/supervisor-Cqhu3xH-.js.map +1 -0
  64. package/dist/testing.d.ts +1 -1
  65. package/dist/testing.js +14 -14
  66. package/dist/tool-loop.d.ts +5 -1
  67. package/dist/tool-loop.js +4 -3
  68. package/dist/tool-loop.js.map +1 -1
  69. package/dist/tui/index.d.ts +1 -1
  70. package/dist/tui/index.js +1 -1
  71. package/dist/workspace-store/index.d.ts +171 -3
  72. package/dist/workspace-store/index.js +529 -8
  73. package/dist/workspace-store/index.js.map +1 -1
  74. package/dist/{workspace-streams-DkijayqX.js → workspace-streams-DFAhW7hF.js} +300 -42
  75. package/dist/workspace-streams-DFAhW7hF.js.map +1 -0
  76. package/dist/{workspace-streams-CpSmt3zx.d.ts → workspace-streams-U6_UfOXu.d.ts} +10 -2
  77. package/package.json +15 -11
  78. package/skills/agent-graphs/references/authoring.md +7 -0
  79. package/skills/codemode/SKILL.md +6 -4
  80. package/skills/codemode/references/runtime-execution.md +63 -11
  81. package/skills/graph-designer/SKILL.md +57 -0
  82. package/skills/profile-authoring/SKILL.md +17 -15
  83. package/dist/model-policy-DhvyZ8-O.js.map +0 -1
  84. package/dist/prepare-CD9kR0gS.js.map +0 -1
  85. package/dist/profile-CHXYmpGd.js.map +0 -1
  86. package/dist/runtime-B9BOFyEp.js.map +0 -1
  87. package/dist/supervisor-CbFyf48U.js.map +0 -1
  88. package/dist/workspace-streams-DkijayqX.js.map +0 -1
@@ -1,16 +1,17 @@
1
- import { $i as executableAgentSpecSnapshot, Bn as InMemoryResultBlobStore, Cn as readPromptOptions, Hn as insideCursorNamespace, It as createWorktreeCliExecutor, Jn as readCommittedJsonLines, Mr as contentAddress, Nt as createExecutor, Ot as verdictFromJudgeScore, Pt as createExecutorRegistry, Qi as executableAgentProfileSnapshot, Qn as isTraceAnalysisStore, Sn as assertBoxlessPromptOptions, Tn as runBrainLoop, Vn as InMemorySpawnJournal, Yn as writeAllBytes, _n as runAgentRounds, ea as detachedFrozen, fa as notifySandboxEventObserver, hn as defaultSelectWinner, ht as mapExecutorResult, j as settledToIteration, mt as gateOnDeliverable, pt as rollingDispatch, qn as prepareJsonlAppend, r as createSupervisor, ta as detachedSnapshot, v as withDriverExecutor, vn as createSandboxLineage, vt as CheckUnavailableError, wn as routerBrain } from "./supervisor-CbFyf48U.js";
1
+ import { a as graphRunsFor, d as recordGraphRun, i as graphRegistry, l as lookupGraph, p as taskShapeDigest, r as graphFromStored, t as digestOfGraph } from "./graph-registry-BuJbzTzF.js";
2
+ import { Bn as InMemoryResultBlobStore, Cn as readPromptOptions, Hn as insideCursorNamespace, It as createWorktreeCliExecutor, Jn as readCommittedJsonLines, Mr as contentAddress, Nt as createExecutor, Ot as verdictFromJudgeScore, Pt as createExecutorRegistry, Qn as isTraceAnalysisStore, Sn as assertBoxlessPromptOptions, Tn as runBrainLoop, Vn as InMemorySpawnJournal, Yn as writeAllBytes, _n as runAgentRounds, aa as detachedSnapshot, ga as notifySandboxEventObserver, hn as defaultSelectWinner, ht as mapExecutorResult, ia as detachedFrozen, j as settledToIteration, mt as gateOnDeliverable, na as executableAgentProfileSnapshot, pt as rollingDispatch, qn as prepareJsonlAppend, r as createSupervisor, ra as executableAgentSpecSnapshot, v as withDriverExecutor, vn as createSandboxLineage, vt as CheckUnavailableError, wn as routerBrain } from "./supervisor-Cqhu3xH-.js";
2
3
  import { i as ConfigError, m as ValidationError, n as AnalystError, s as PlannerError } from "./errors-DodWX-cb.js";
3
- import { B as runAgentic, G as observe, K as renderReport, U as ObservationError, V as sample, X as strategyAuthorMethod, a as reconcileInterruptedSearchCalls, c as searchExecutor, q as profileChatClient, r as dedicatedLane, x as runtimeShipDecision, z as refine } from "./search-executor-B5ZVFsDV.js";
4
- import { $ as randomSuffix, B as addSpend, H as chargedTokens, I as immutableCandidateValue, J as hasCompleteCacheBreakdown, K as errorMessage, M as canonicalCandidateDigest$1, R as sha256Bytes$1, U as cloneSpend, X as isAsyncIterable, Y as isAbortError, bt as armDeadlineTimer, ct as zeroSpend, k as markPutStreamVerified, nt as sleep, q as fnv1a32Hex, rt as stringifySafe, v as WorkspaceScanLimitError, x as captureMaterializedWorkspace } from "./workspace-streams-DkijayqX.js";
5
- import { c as profileModelExecutionSettings, i as concreteModelId, l as profileProviderModel, o as enforceTokenLimits, p as canonicalObservedModelParts, t as assertExecutableAgentProfile } from "./model-policy-DhvyZ8-O.js";
4
+ import { B as runAgentic, G as observe, K as renderReport, U as ObservationError, V as sample, X as strategyAuthorMethod, a as reconcileInterruptedSearchCalls, c as searchExecutor, q as profileChatClient, r as dedicatedLane, x as runtimeShipDecision, z as refine } from "./search-executor-DlaLafvy.js";
5
+ import { A as markPutStreamVerified, J as fnv1a32Hex, L as immutableCandidateValue, N as canonicalCandidateDigest$1, S as captureMaterializedWorkspace, U as chargedTokens, V as addSpend, W as cloneSpend, X as isAbortError, Y as hasCompleteCacheBreakdown, Z as isAsyncIterable, et as randomSuffix, it as stringifySafe, lt as zeroSpend, q as errorMessage, rt as sleep, xt as armDeadlineTimer, y as WorkspaceScanLimitError, z as sha256Bytes$1 } from "./workspace-streams-DFAhW7hF.js";
6
+ import { c as profileModelExecutionSettings, i as concreteModelId, l as profileProviderModel, o as enforceTokenLimits, p as canonicalObservedModelParts, t as assertExecutableAgentProfile } from "./model-policy-CNe257pB.js";
6
7
  import { i as notifyRuntimeHookEvent } from "./runtime-hooks-tXpAarhW.js";
7
8
  import { i as redactProtectedValue, r as redactProtectedReason } from "./protected-redaction-DylCC_ot.js";
8
- import { C as createInMemoryRunContext, E as foldCoordinationRecords } from "./coordination-driver-DGhN8GO9.js";
9
- import { r as supervise } from "./supervise-CU6AuxWY.js";
9
+ import { C as createInMemoryRunContext, E as foldCoordinationRecords } from "./coordination-driver-Culvomn2.js";
10
+ import { r as supervise } from "./supervise-D4CnC-cl.js";
10
11
  import "./run-layout-DSCWdbX2.js";
11
- import "./provision-supervisor-BQLLrUtn.js";
12
- import "./delegate-B5pmSbQa.js";
13
- import "./graph-G0j1ysqe.js";
12
+ import "./provision-supervisor-DiZTjzdp.js";
13
+ import "./delegate-CpMbpiIR.js";
14
+ import "./graph-DnfSGUM8.js";
14
15
  import { agentProfileSchema, canonicalAgentProfileDigest, canonicalCandidateDigest, sha256Bytes, validateAgentProfileSecurity } from "@tangle-network/agent-interface";
15
16
  import { CODING_HARNESSES, DEFAULT_TRACE_ANALYST_KINDS, InMemoryTraceStore, OUTPUT_VALUE, benjaminiHochberg, buildTrajectory, computeFindingId as computeFindingId$1, confidenceInterval, createTraceAnalyst, expandProfileAxes, makeFinding as makeFinding$1, pairedBootstrap, paretoFrontier, wilcoxonSignedRank, wilson } from "@tangle-network/agent-eval";
16
17
  import { createAgentRunOutcomeTracker } from "@tangle-network/sandbox/runtime";
@@ -29,7 +30,6 @@ import { defineEvaluationClaim, summarizeEvaluationUnits } from "@tangle-network
29
30
  import { createInterface } from "node:readline";
30
31
  import { pathToFileURL } from "node:url";
31
32
  import { gzipSync } from "node:zlib";
32
- import { createContext, runInContext } from "node:vm";
33
33
  import { stuckLoopView, toolWasteView } from "@tangle-network/agent-eval/pipelines";
34
34
  //#region src/runtime/tangle-sandbox-exact-process-provider.ts
35
35
  const exactProcessProviderMarker = "agent-runtime.exact-process-provider";
@@ -393,6 +393,165 @@ async function afterAbortCheck(signal, operation) {
393
393
  return result;
394
394
  }
395
395
  //#endregion
396
+ //#region src/runtime/http-mcp-client.ts
397
+ /**
398
+ * `connectHttpMcp` — the remote (streamable HTTP) MCP client, sibling of
399
+ * `connectStdioMcp`. Same connection contract (`tools`, `callTool`, `close`),
400
+ * same error semantics (a JSON-RPC error / `isError` result is the agent's
401
+ * `ERROR: …` outcome; a transport fault or timeout throws), no child process —
402
+ * which is the point: this is the MCP path for environments that cannot spawn
403
+ * stdio servers (Workers, browser-context agents, locked-down sandboxes) and
404
+ * for servers that only exist as endpoints (a remote Hindsight, a hosted MCP).
405
+ *
406
+ * Transport: MCP "streamable HTTP" (protocol 2025-03-26 / 2025-06-18). Every
407
+ * request is a POST with `Accept: application/json, text/event-stream`; the
408
+ * server may answer either shape, so both are parsed. A server-assigned
409
+ * `Mcp-Session-Id` response header is captured and replayed on every later
410
+ * request (and the DELETE on close) — stateful servers refuse without it,
411
+ * stateless ones simply never send it.
412
+ *
413
+ * Auth headers can be protected values: they are sent to the server but never
414
+ * allowed back out — error messages and tool results are redacted against the
415
+ * exact protected values, mirroring `connectStdioMcp`'s protectedEnv handling.
416
+ */
417
+ const HTTP_MCP_PROTOCOL_VERSION = "2025-06-18";
418
+ /** A remote MCP endpoint refused the connection before the handshake completed. */
419
+ var McpConnectFault = class extends Error {
420
+ status;
421
+ constructor(message, status) {
422
+ super(message);
423
+ this.status = status;
424
+ this.name = "McpConnectFault";
425
+ }
426
+ };
427
+ const SSE_DATA_LINE = /^data:\s?(.*)$/s;
428
+ function parseSseBody(body, wantId) {
429
+ for (const chunk of body.split(/\r?\n\r?\n/)) for (const line of chunk.split(/\r?\n/)) {
430
+ const match = SSE_DATA_LINE.exec(line);
431
+ if (!match) continue;
432
+ try {
433
+ const parsed = JSON.parse(match[1] ?? "");
434
+ if (parsed.id === wantId) return parsed;
435
+ } catch {}
436
+ }
437
+ }
438
+ function redactValuesFrom(text, values) {
439
+ let out = text;
440
+ for (const value of values) if (value.length > 0) out = out.split(value).join("[redacted]");
441
+ return out;
442
+ }
443
+ /** Connect to a remote MCP endpoint: initialize, list tools, return the live connection. */
444
+ async function connectHttpMcp(spec) {
445
+ let endpoint;
446
+ try {
447
+ endpoint = new URL(spec.url);
448
+ } catch {
449
+ throw new ValidationError(`connectHttpMcp: invalid MCP endpoint URL: ${spec.url}`);
450
+ }
451
+ if (endpoint.protocol !== "https:" && endpoint.hostname !== "localhost" && endpoint.hostname !== "127.0.0.1") throw new ValidationError(`connectHttpMcp: refusing non-HTTPS MCP endpoint ${endpoint.origin} (localhost excepted) — a bearer or api key on plain HTTP would cross the network in the clear`);
452
+ const fetchImpl = spec.fetchImpl ?? fetch;
453
+ const timeoutMs = spec.timeoutMs ?? 3e4;
454
+ const protectedValues = Object.values(spec.protectedHeaders ?? {});
455
+ const redact = (text) => redactValuesFrom(text, protectedValues);
456
+ let sessionId;
457
+ let nextId = 1;
458
+ let closed = false;
459
+ const post = async (path, body, method = "POST") => fetchImpl(`${endpoint.origin}${endpoint.pathname}${endpoint.search}${path}`, {
460
+ method,
461
+ headers: {
462
+ accept: "application/json, text/event-stream",
463
+ ...body !== void 0 ? { "content-type": "application/json" } : {},
464
+ ...sessionId !== void 0 ? { "mcp-session-id": sessionId } : {},
465
+ ...body !== void 0 ? { "x-request-id": String(nextId) } : {},
466
+ ...spec.headers ?? {},
467
+ ...spec.protectedHeaders ?? {}
468
+ },
469
+ ...body !== void 0 ? { body: JSON.stringify(body) } : {}
470
+ });
471
+ const request = async (method, params) => {
472
+ if (closed) throw new Error("HTTP MCP connection closed");
473
+ const id = nextId++;
474
+ const controller = new AbortController();
475
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
476
+ try {
477
+ const res = await post("", {
478
+ jsonrpc: "2.0",
479
+ id,
480
+ method,
481
+ params
482
+ }, "POST");
483
+ const sessionHeader = res.headers.get("mcp-session-id");
484
+ if (sessionHeader) sessionId = sessionHeader;
485
+ if (!res.ok) {
486
+ const text = redact((await res.text()).slice(0, 500));
487
+ throw new McpConnectFault(`HTTP MCP ${method} failed (${res.status} ${res.statusText}): ${text}`, res.status);
488
+ }
489
+ if ((res.headers.get("content-type") ?? "").includes("text/event-stream")) {
490
+ const parsed = parseSseBody(await res.text(), id);
491
+ if (!parsed) throw new Error(`HTTP MCP ${method}: SSE response carried no JSON-RPC message for request ${id}`);
492
+ return parsed;
493
+ }
494
+ return JSON.parse(await res.text());
495
+ } catch (error) {
496
+ if (error instanceof McpConnectFault) throw error;
497
+ const reason = error instanceof Error ? error.message : String(error);
498
+ throw new Error(redact(`HTTP MCP ${method} transport failed: ${reason}`));
499
+ } finally {
500
+ clearTimeout(timer);
501
+ }
502
+ };
503
+ `${endpoint.origin}${endpoint.pathname}${timeoutMs}`;
504
+ try {
505
+ const init = await request("initialize", {
506
+ protocolVersion: HTTP_MCP_PROTOCOL_VERSION,
507
+ capabilities: {},
508
+ clientInfo: {
509
+ name: "agent-runtime-http-mcp",
510
+ version: "0"
511
+ }
512
+ });
513
+ if (init.error) throw new McpConnectFault(redact(`initialize errored: ${JSON.stringify(init.error)}`), void 0);
514
+ await post("", {
515
+ jsonrpc: "2.0",
516
+ method: "notifications/initialized"
517
+ }, "POST").catch(() => void 0);
518
+ const list = await request("tools/list", void 0);
519
+ if (list.error) throw new Error(redact(`tools/list errored: ${JSON.stringify(list.error)}`));
520
+ const rawTools = list.result?.tools;
521
+ if (!Array.isArray(rawTools)) throw new Error("tools/list result has no tools array");
522
+ const tools = JSON.parse(redact(JSON.stringify(rawTools)));
523
+ const callTool = async (name, args) => {
524
+ const res = await request("tools/call", {
525
+ name,
526
+ arguments: args
527
+ });
528
+ if (res.error) return `ERROR: ${redact(JSON.stringify(res.error)).slice(0, 300)}`;
529
+ const result = res.result;
530
+ const chunks = result?.content?.map((c) => c.text ?? "");
531
+ const joined = chunks?.join("\n") ?? JSON.stringify(result ?? null);
532
+ const collapsed = chunks?.join("");
533
+ let text = joined;
534
+ if (collapsed !== void 0) {
535
+ const collapsedRedacted = redact(collapsed);
536
+ if (collapsedRedacted !== collapsed) text = collapsedRedacted;
537
+ }
538
+ return redact(result?.isError ? `ERROR: ${text}` : text);
539
+ };
540
+ const close = async () => {
541
+ if (closed) return;
542
+ closed = true;
543
+ await post("", void 0, "DELETE").catch(() => void 0);
544
+ };
545
+ return {
546
+ tools,
547
+ callTool,
548
+ close
549
+ };
550
+ } catch (error) {
551
+ throw error instanceof Error ? error : new Error(String(error));
552
+ }
553
+ }
554
+ //#endregion
396
555
  //#region src/runtime/key-provider.ts
397
556
  /** The env-backed provider: reads the (dotenvx-loaded) process env. Empty /
398
557
  * whitespace-only values count as absent — fail loud, not with a blank key. */
@@ -404,12 +563,23 @@ function envKeyProvider(env = process.env) {
404
563
  }
405
564
  /** The `AgentProfileMcpServer.metadata` key the declarative secret-env map
406
565
  * rides under: `{ ENV_VAR_NAME: 'PROVIDER_KEY_NAME' }`. Names only — values
407
- * are resolved at materialize time and never stored. */
566
+ * are resolved at materialize time and never stored.
567
+ *
568
+ * DEPRECATED (2026-10-12): this is the interface <0.40 channel. Author
569
+ * `profile.mcp[key].env.ENV_VAR_NAME = { kind: 'secret-ref', key }` instead —
570
+ * the canonical form since 0.40. The legacy map keeps resolving (profiles
571
+ * are data in the wild and must not break at boot) but every use now warns
572
+ * once per server; removal lands at the next interface major. */
408
573
  const mcpSecretEnvMetadataKey = "secretEnv";
574
+ const warnedSecretEnv = /* @__PURE__ */ new Set();
409
575
  /** Read (and validate) a server entry's declared secret-env map, if any.
410
576
  * Malformed metadata throws — a half-declared secret must never half-boot. */
411
- function secretEnvOfMcpServer(server) {
577
+ function secretEnvOfMcpServer(server, label = "profile.mcp server") {
412
578
  const raw = server.metadata?.[mcpSecretEnvMetadataKey];
579
+ if (raw !== void 0 && !warnedSecretEnv.has(label)) {
580
+ warnedSecretEnv.add(label);
581
+ console.warn(`${label}: metadata.${mcpSecretEnvMetadataKey} is deprecated — author env secret-refs (profile.mcp[key].env.ENV = { kind: 'secret-ref', key }) instead; support is removed at the next interface major`);
582
+ }
413
583
  if (raw === void 0 || raw === null) return void 0;
414
584
  if (typeof raw !== "object" || Array.isArray(raw)) throw new ValidationError(`secretEnvOfMcpServer: metadata.${mcpSecretEnvMetadataKey} must be an object mapping env var name -> key name`);
415
585
  const entries = Object.entries(raw);
@@ -793,6 +963,34 @@ async function connectStdioMcp(spec) {
793
963
  * child and expose its tools under `<server>__<tool>` names. The default policy
794
964
  * refuses local processes. A profile with no MCP surface returns zero tools.
795
965
  */
966
+ /** Resolve a remote server's `url` + `headers` profile config values into the
967
+ * plain strings + protected headers an HTTP connection needs. Same fail-closed
968
+ * contract as `resolveMcpServerLaunch`: no KeyProvider for a declared secret, or
969
+ * a missing key, aborts the materialization naming the KEY only. */
970
+ async function resolveRemoteMcpHeaders(server, keys, label) {
971
+ if (typeof server.url !== "string" || server.url.trim().length === 0) throw new ValidationError(`${label}: a remote (http/sse) server requires url`);
972
+ const headers = {};
973
+ const protectedHeaders = {};
974
+ for (const [name, value] of Object.entries(server.headers ?? {})) if (value.kind === "public") headers[name] = value.value ?? "";
975
+ else {
976
+ if (!keys) throw new ValidationError(`${label}: header ${name} is a secret-ref but no KeyProvider was supplied — refusing to connect a remote server with unresolvable auth`);
977
+ const secret = await keys.get(value.key ?? "");
978
+ if (secret === void 0) throw new ValidationError(`${label}: the KeyProvider holds no value for '${value.key}' (wanted for header ${name}) — provision the key or drop the header`);
979
+ protectedHeaders[name] = value.format === "bearer" ? `Bearer ${secret}` : secret;
980
+ }
981
+ return {
982
+ url: server.url,
983
+ ...Object.keys(headers).length > 0 ? { headers } : {},
984
+ ...Object.keys(protectedHeaders).length > 0 ? { protectedHeaders } : {}
985
+ };
986
+ }
987
+ /**
988
+ * Spawn every enabled stdio server in `profile.mcp` and expose their tools as
989
+ * `<server>__<tool>` behind one route and close facade.
990
+ *
991
+ * Fails closed: a declared server that cannot boot throws, so a profile is never
992
+ * scored as if it had no MCP surface.
993
+ */
796
994
  async function materializeLocalMcp(profile, opts = {}) {
797
995
  const security = validateAgentProfileSecurity(profile, opts.profileSecurityPolicy);
798
996
  if (!security.ok) throw new ValidationError(`materializeLocalMcp: profile host execution refused: ${security.issues.filter((issue) => issue.level === "error").map((issue) => issue.message).join("; ")}`);
@@ -807,7 +1005,35 @@ async function materializeLocalMcp(profile, opts = {}) {
807
1005
  for (const [key, server] of Object.entries(profile.mcp ?? {})) {
808
1006
  if (server.enabled === false) continue;
809
1007
  const transport = server.transport ?? "stdio";
810
- if (transport !== "stdio") throw new ValidationError(`materializeLocalMcp: profile.mcp['${key}'] has transport '${transport}' — the same-host client only spawns stdio servers; a remote (http/sse) server needs an http MCP client (not yet built here) or the sandbox backend. Failing loud: scoring this profile without its declared server would fake the with/without ablation`);
1008
+ if (transport === "http" || transport === "sse") {
1009
+ const { url, headers, protectedHeaders } = await resolveRemoteMcpHeaders(server, opts.keys, `materializeLocalMcp: profile.mcp['${key}']`);
1010
+ const conn = await connectHttpMcp({
1011
+ url,
1012
+ ...headers !== void 0 ? { headers } : {},
1013
+ ...protectedHeaders !== void 0 ? { protectedHeaders } : {},
1014
+ ...opts.timeoutMs !== void 0 ? { timeoutMs: opts.timeoutMs } : {}
1015
+ });
1016
+ connections.push(conn);
1017
+ const prefix = key.replace(/[^a-zA-Z0-9_-]/g, "_");
1018
+ for (const t of conn.tools) {
1019
+ const name = `${prefix}__${t.name.replace(/[^a-zA-Z0-9_-]/g, "_")}`;
1020
+ if (routes.has(name)) throw new ValidationError(`materializeLocalMcp: namespaced tool '${name}' collides across servers — rename the profile.mcp keys`);
1021
+ routes.set(name, {
1022
+ conn,
1023
+ tool: t.name
1024
+ });
1025
+ tools.push({
1026
+ type: "function",
1027
+ function: {
1028
+ name,
1029
+ description: (t.description ?? "").slice(0, 1e3),
1030
+ parameters: sanitizeMcpToolSchema(t.inputSchema)
1031
+ }
1032
+ });
1033
+ }
1034
+ continue;
1035
+ }
1036
+ if (transport !== "stdio") throw new ValidationError(`materializeLocalMcp: profile.mcp['${key}'] has unsupported transport '${transport}'`);
811
1037
  if (!server.command || server.command.trim().length === 0) throw new ValidationError(`materializeLocalMcp: profile.mcp['${key}'] declares a stdio server with no command`);
812
1038
  const launch = await resolveMcpServerLaunch(server, opts.keys, `materializeLocalMcp: profile.mcp['${key}']`);
813
1039
  const conn = await connectStdioMcp({
@@ -2745,6 +2971,8 @@ function localSandboxClient(opts) {
2745
2971
  const r = await runBrainLoop({
2746
2972
  chat,
2747
2973
  tools: mcp.tools,
2974
+ ...opts.codeMode ? { codeMode: opts.codeMode } : {},
2975
+ ...popts?.signal ? { signal: popts.signal } : {},
2748
2976
  execute: (name, args) => mcp.call(name, args),
2749
2977
  initialMessages: [...system ? [{
2750
2978
  role: "system",
@@ -2760,7 +2988,7 @@ function localSandboxClient(opts) {
2760
2988
  type: "llm_call",
2761
2989
  data: {
2762
2990
  model,
2763
- tools: mcp.tools.map((t) => t.function.name),
2991
+ tools: r.modelToolNames ?? mcp.tools.map((t) => t.function.name),
2764
2992
  ...r.tokensKnown === false ? { tokensKnown: false } : {
2765
2993
  tokensIn: r.usage.input,
2766
2994
  tokensOut: r.usage.output
@@ -5953,9 +6181,8 @@ function hashText(value) {
5953
6181
  /**
5954
6182
  * The ONE lint over model-authored source.
5955
6183
  *
5956
- * Two features have a model write code the runtime then runs: `authorStrategy` (an authored
5957
- * optimization strategy, since 0.60) and code mode's `execute` (a program over the coordination
5958
- * verbs). Both need the same refusals; this module is the single copy both import.
6184
+ * Authored optimization strategies use this static contract lint. General Runtime code-mode
6185
+ * scripts execute in the isolated QuickJS/WASM engine instead of relying on source lint.
5959
6186
  *
5960
6187
  * A LINT, NOT A SANDBOX — it reads text and cannot constrain what running code does, and trivial
5961
6188
  * concatenation defeats any token match. Its only job is refusing the obvious escapes so a typo or
@@ -6901,276 +7128,183 @@ function chatWorkerSeam(opts) {
6901
7128
  };
6902
7129
  }
6903
7130
  //#endregion
6904
- //#region src/runtime/supervise/code-mode.ts
7131
+ //#region src/runtime/supervise/designer-tools.ts
6905
7132
  /**
6906
- * Code mode — the pattern Cloudflare ("Code Mode") and Anthropic ("code execution with MCP")
6907
- * define, over this runtime's coordination verbs: generate a typed API from the tools' schemas,
6908
- * expose exactly two tools (`search`, `execute`), and run model-written code whose only intended
6909
- * capability is the API bindings. One model turn where the tool-calling loop pays one round trip
6910
- * per call; intermediates live in the program, not the context window.
7133
+ * `graphDesignerExtraTools` — the designer's toolset on the existing
7134
+ * `RunGraphOptions.extraTools` seam.
6911
7135
  *
6912
- * WHAT THIS RUNTIME OWNS AND THE SOURCES' CENTRAL PROPERTY — read this before choosing a runner.
6913
- * The sources isolate execution in a REAL boundary: Cloudflare runs the code in a V8 isolate with
6914
- * a separate heap; Anthropic runs it out of process. That is not a detail — it is the whole reason
6915
- * "let the model write code" is safe. This runtime does NOT ship an isolate, so `execute`'s
6916
- * execution boundary is a SEAM you must fill: `codeModeSupervisorTools(runner)` REQUIRES a
6917
- * `CodeModeRunner`, and there is no default. For untrusted model output, supply a jailed runner
6918
- * (a sub-sandbox, a worker isolate). {@link unsafeInProcessRunner} is provided for TRUSTED output
6919
- * only — your own eval harness, an offline test, a model you control — and is named for what it is:
6920
- * `node:vm` shares the host realm, so host code is reachable from inside it and it is not a
6921
- * security boundary. Choosing it for a hostile model is remote code execution, by construction.
7136
+ * A meta-graph designer is an ordinary supervisor ROOT whose ONLY tools are
7137
+ * these three: look up what this task shape has run, run a graph (authored or
7138
+ * replayed — every run recorded, terminal accounting included), and read the
7139
+ * history. The agent authors graphs as DATA (nodes, edges, budget, deliverable
7140
+ * description); execution is ENVIRONMENT-OWNED — the caller closes over how
7141
+ * graphs run (backend/brain/leaf seam) and how the deliverable's check is
7142
+ * re-bound, because oracles are environment, never agent-supplied.
6922
7143
  *
6923
- * WHAT IS GENUINELY THIS RUNTIME'S CONTRIBUTION, and survives audit: every call the program makes
6924
- * crosses the IDENTICAL kernel path an MCP verb crosses. Bindings dispatch through `context.verbs`
6925
- * → the live coordination descriptor's own handler → authorization, the conserved pool, the
6926
- * journal ("there is no second spawn path"). The model authors a workflow as code at runtime and
6927
- * the kernel still meters and records every edge of it — a dynamic workflow system, not a bypass.
6928
- * Verb RESULTS are detached (JSON round-trip) before they re-enter the program, so in-code calls
6929
- * cannot mutate live coordination state the MCP transport would have copied.
6930
- *
6931
- * WHAT STAYS THE MODEL'S: `submit_result`, `stop`, `ask_parent` are never in the API — a program
6932
- * that could settle the run would be a second brain. `search` names them and says why. Code does
6933
- * the mechanics; the model keeps the judgment, the same split the sources draw.
6934
- *
6935
- * THE GRANT CANNOT DRIFT FROM THE DOCS: `search` renders from `context.coordinationTools()`, the
6936
- * same descriptor objects the verbs are served from — never prose written beside them.
7144
+ * This is the `measure_rung` pattern from the graph contract generalized: the
7145
+ * root's profile declares the tools; the graph options supply them; nothing
7146
+ * new exists below this line but the registry calls themselves.
6937
7147
  */
6938
- /** The coordination verbs callable in code, and the `context.verbs` member each maps to. */
6939
- const CODE_CALLABLE_VERBS = {
6940
- spawn_worker: "spawnAgent",
6941
- await_settlement: "awaitSettlement",
6942
- steer_agent: "steerAgent",
6943
- observe_agent: "observeAgent",
6944
- list_questions: "listQuestions",
6945
- answer_question: "answerQuestion",
6946
- run_analyst: "runAnalyst",
6947
- read_journal: "readJournal",
6948
- define_analyst: "defineAnalyst"
7148
+ const taskShapeSchema = {
7149
+ type: "object",
7150
+ description: "Stable descriptor of this class of work (kind, source, constraints) — the lookup key. Not the payload.",
7151
+ additionalProperties: true
6949
7152
  };
6950
- /** A program that spawns may join what it spawned. Not an MCP tool: a manager waits by ending its
6951
- * turn, and only code inside one call needs to wait in place. */
6952
- const AWAIT_SETTLEMENT_FACE = Object.freeze({
6953
- name: "await_settlement",
6954
- description: "Wait until at least one of the named workers has settled; returns { settled: [ids] }. Read each output with observe_agent. Takes nothing from your inbox, so you still hear of each settlement when you are woken.",
6955
- inputSchema: {
6956
- type: "object",
6957
- properties: { workerIds: {
6958
- type: "array",
6959
- items: { type: "string" }
6960
- } },
6961
- required: ["workerIds"]
6962
- }
6963
- });
6964
- /** The manager's own lifecycle verbs — never callable from code; `search` names them and why. */
6965
- const LIFECYCLE_VERBS = [
6966
- "submit_result",
6967
- "stop",
6968
- "ask_parent"
6969
- ];
6970
- /** Render a JSON Schema as TypeScript type text. Bounded and structural: enough for tool input
6971
- * schemas (objects, enums, arrays, unions); anything unrecognized renders `unknown`, never a
6972
- * guess. Module-level (not root) export: consumed by `renderCodeModeApi` and its tests only. */
6973
- function renderJsonSchemaType(schema, depth = 0) {
6974
- if (depth > 6 || typeof schema !== "object" || schema === null) return "unknown";
6975
- const node = schema;
6976
- if (node.const !== void 0) return JSON.stringify(node.const);
6977
- if (Array.isArray(node.enum)) return node.enum.map((value) => JSON.stringify(value)).join(" | ");
6978
- const variants = node.anyOf ?? node.oneOf;
6979
- if (Array.isArray(variants)) return variants.map((variant) => renderJsonSchemaType(variant, depth + 1)).join(" | ");
6980
- const type = Array.isArray(node.type) ? node.type : [node.type];
6981
- if (type.includes("object") || node.type === void 0 && node.properties !== void 0) {
6982
- const properties = node.properties ?? {};
6983
- const required = new Set(Array.isArray(node.required) ? node.required : []);
6984
- const fields = Object.entries(properties).map(([name, property]) => {
6985
- const doc = describeSchema(property);
6986
- const optional = required.has(name) ? "" : "?";
6987
- return `${doc}${JSON.stringify(name).slice(1, -1)}${optional}: ${renderJsonSchemaType(property, depth + 1)}`;
6988
- });
6989
- if (fields.length === 0) return "Record<string, unknown>";
6990
- const indent = " ".repeat(depth + 1);
6991
- return `{\n${fields.map((field) => `${indent}${field}`).join("\n")}\n${" ".repeat(depth)}}`;
6992
- }
6993
- if (type.includes("array")) return `Array<${renderJsonSchemaType(node.items, depth + 1)}>`;
6994
- const primitives = type.map((entry) => entry === "integer" ? "number" : typeof entry === "string" ? entry : void 0).filter((entry) => entry === "string" || entry === "number" || entry === "boolean" || entry === "null");
6995
- return primitives.length > 0 ? primitives.join(" | ") : "unknown";
6996
- }
6997
- function describeSchema(schema) {
6998
- const description = typeof schema === "object" && schema !== null ? schema.description : void 0;
6999
- if (typeof description !== "string" || description.length === 0) return "";
7000
- const first = description.split("\n")[0] ?? "";
7001
- return `/** ${first.length > 140 ? `${first.slice(0, 140)}…` : first} */ `;
7002
- }
7003
- /** Render the callable API from tool faces — `declare function` per tool, doc from its own
7004
- * description. This is the text `search` answers; generated, never hand-written. Module-level
7005
- * export for tests. */
7006
- function renderCodeModeApi(tools, query) {
7007
- const needle = query?.trim().toLowerCase();
7008
- const blocks = tools.filter((tool) => !needle || tool.name.toLowerCase().includes(needle) || (tool.description ?? "").toLowerCase().includes(needle)).map((tool) => {
7009
- const doc = (tool.description ?? "").split("\n")[0] ?? "";
7010
- const args = tool.inputSchema === void 0 ? "unknown" : renderJsonSchemaType(tool.inputSchema);
7011
- return `/** ${doc} */\ndeclare function ${tool.name}(args: ${args}): Promise<unknown>`;
7012
- });
7013
- const header = [
7014
- "// Call these as `api.<name>(args)` from code passed to `execute`.",
7015
- "// Results are the same JSON the tools return. Intermediates stay in your program;",
7016
- "// return only what the next decision needs.",
7017
- `// NOT callable from code: ${LIFECYCLE_VERBS.join(", ")} — those are your own tools,`,
7018
- "// because a program that could settle the run would be a second brain."
7019
- ].join("\n");
7020
- if (blocks.length === 0) return `${header}\n\n// No API member matches ${JSON.stringify(query ?? "")}. Call search with no query for the full API.`;
7021
- return `${header}\n\n${blocks.join("\n\n")}`;
7153
+ const graphDataSchema = {
7154
+ type: "object",
7155
+ description: "A graph as data: { nodes: [{id, profile}], edges: [{kind, from, to, directive, ...}], budget, deliverable: {describe} }. No check function — the environment binds it.",
7156
+ additionalProperties: true
7157
+ };
7158
+ function asRecord(value, context) {
7159
+ if (typeof value !== "object" || value === null || Array.isArray(value)) throw new ValidationError(`${context}: expected an object`);
7160
+ return value;
7022
7161
  }
7023
- /**
7024
- * An in-process runner for TRUSTED model output ONLY. NOT a security boundary.
7025
- *
7026
- * It runs the program in a `node:vm` context whose globals are the bindings (`api`) and a
7027
- * capturing `console`, with code generation disabled and inherited properties stripped. Those are
7028
- * capability discipline, not containment: `node:vm` shares the host realm, and a host function's
7029
- * `.constructor` is the host `Function`, so code that WANTS out can get out
7030
- * (`api.<binding>.constructor('return process')()`). Use this for your own eval harness, offline
7031
- * tests, or a model you trust; for untrusted output supply a jailed `CodeModeRunner` instead.
7032
- */
7033
- function unsafeInProcessRunner() {
7034
- return { async run({ code, bindings, signal }) {
7035
- const logs = [];
7036
- const capture = (level) => (...args) => {
7037
- const line = args.map((value) => typeof value === "string" ? value : safeJson(value)).join(" ");
7038
- if (logs.length < 200) logs.push(level === "log" ? line : `[${level}] ${line}`);
7039
- };
7040
- const target = Object.create(null);
7041
- for (const [name, fn] of Object.entries(bindings)) target[name] = fn;
7042
- const sandbox = Object.create(null);
7043
- sandbox.api = new Proxy(Object.freeze(target), { get(owned, member) {
7044
- if (typeof member !== "string") return void 0;
7045
- if (Object.hasOwn(owned, member)) return owned[member];
7046
- throw new ValidationError(LIFECYCLE_VERBS.includes(member) ? `code mode: api.${member} is not callable from code — it is the manager's own lifecycle verb; return from your program and call it as a tool` : `code mode: api.${member} is not in the granted API — call search to see what is`);
7047
- } });
7048
- sandbox.console = Object.freeze({
7049
- log: capture("log"),
7050
- warn: capture("warn"),
7051
- error: capture("error")
7052
- });
7053
- const context = createContext(sandbox, { codeGeneration: {
7054
- strings: false,
7055
- wasm: false
7056
- } });
7057
- const program = runInContext(`(async () => {\n${code}\n})()`, context);
7058
- const aborted = new Promise((_resolve, reject) => {
7059
- if (signal.aborted) reject(abortReason(signal));
7060
- else signal.addEventListener("abort", () => reject(abortReason(signal)), { once: true });
7162
+ /** The designer's three tools over one registry. Record-every-run is enforced
7163
+ * by construction: running IS recording. */
7164
+ function graphDesignerExtraTools(env) {
7165
+ const registry = graphRegistry(env.registryDir);
7166
+ let runCounter = 0;
7167
+ const runId = env.nextRunId ?? (() => `designer-${++runCounter}`);
7168
+ const runAndRecord = async (taskShape, graph, mode) => {
7169
+ const digest = digestOfGraph(graph);
7170
+ const startedAt = (/* @__PURE__ */ new Date()).toISOString();
7171
+ const result = await env.runGraph(graph);
7172
+ const winner = result.result.kind === "winner";
7173
+ const spent = winner ? result.result.spentTotal : void 0;
7174
+ await recordGraphRun(registry, graph, {
7175
+ graphDigest: digest,
7176
+ taskDigest: taskShapeDigest(taskShape),
7177
+ startedAt,
7178
+ finishedAt: (/* @__PURE__ */ new Date()).toISOString(),
7179
+ outcome: winner ? { kind: "delivered" } : {
7180
+ kind: "no-winner",
7181
+ reason: `mode=${mode} result=${result.result.kind}`
7182
+ },
7183
+ cost: {
7184
+ ...spent?.iterations !== void 0 ? { iterations: spent.iterations } : {},
7185
+ ...spent?.tokens && typeof spent.tokens === "object" && "input" in spent.tokens ? { tokens: (spent.tokens.input ?? 0) + (spent.tokens.output ?? 0) } : {},
7186
+ ...spent?.usd !== void 0 ? { usd: spent.usd } : {}
7187
+ },
7188
+ traversals: result.ledger.length,
7189
+ ...result.exhaustedEdges.length > 0 ? { exhaustedEdges: result.exhaustedEdges } : {}
7061
7190
  });
7062
7191
  return {
7063
- result: await Promise.race([program, aborted]) ?? null,
7064
- logs
7192
+ runId: runId(),
7193
+ outcome: winner ? "delivered" : `no-winner (${result.result.kind})`,
7194
+ graphDigest: digest,
7195
+ traversals: result.ledger.length,
7196
+ ...spent?.iterations !== void 0 ? { iterations: spent.iterations } : {},
7197
+ ...spent?.tokens && typeof spent.tokens === "object" && "input" in spent.tokens ? { tokens: (spent.tokens.input ?? 0) + (spent.tokens.output ?? 0) } : {}
7065
7198
  };
7066
- } };
7067
- }
7068
- function abortReason(signal) {
7069
- const reason = signal.reason;
7070
- return reason instanceof Error ? reason : new ValidationError("code mode: program aborted");
7071
- }
7072
- function safeJson(value) {
7073
- try {
7074
- return JSON.stringify(value) ?? String(value);
7075
- } catch {
7076
- return String(value);
7077
- }
7078
- }
7079
- /** Detach a verb result before it re-enters the program — the deep copy the MCP transport makes,
7080
- * so in-code calls cannot mutate live coordination state. Matches the MCP path exactly (both
7081
- * JSON round-trip); a non-serializable result degrades to a marker rather than leaking a live
7082
- * reference. */
7083
- function detach(value) {
7084
- if (value === void 0) return void 0;
7085
- try {
7086
- return JSON.parse(JSON.stringify(value));
7087
- } catch {
7088
- return { nonSerializable: true };
7089
- }
7090
- }
7091
- /**
7092
- * Put a supervisor in code mode: its product tool surface becomes exactly `search` and `execute`.
7093
- *
7094
- * `runner` is REQUIRED and has no default — this runtime ships no isolate, so the execution
7095
- * boundary is the caller's explicit choice (see the module doc). Use {@link unsafeInProcessRunner}
7096
- * for trusted output; a jailed runner for untrusted models.
7097
- *
7098
- * Pass the result as `SuperviseOptions.resolveSupervisorTools` (which `runGraph` forwards to its
7099
- * root supervisor). The graph engine's `supervisorKind` does not accept it yet, so a graph
7100
- * supervisor node cannot be put in code mode through node config today.
7101
- */
7102
- function codeModeSupervisorTools(runner, options = {}) {
7103
- if (runner === void 0 || typeof runner.run !== "function") throw new ValidationError("codeModeSupervisorTools: a CodeModeRunner is required (no default) — pass unsafeInProcessRunner() for trusted output, or a jailed runner for untrusted models");
7104
- const timeoutMs = options.timeoutMs ?? null;
7105
- if (timeoutMs !== null && (!Number.isSafeInteger(timeoutMs) || timeoutMs <= 0)) throw new ValidationError("codeModeSupervisorTools: timeoutMs must be a positive safe integer or null");
7106
- const faces = (context) => {
7107
- const granted = context.coordinationTools().filter((tool) => CODE_CALLABLE_VERBS[tool.name] !== void 0);
7108
- return granted.some((tool) => tool.name === "spawn_worker") ? [...granted, AWAIT_SETTLEMENT_FACE] : granted;
7109
- };
7110
- return () => [{
7111
- name: "search",
7112
- description: "Search the callable API. Returns TypeScript declarations for the operations your code may call, generated from the live grant. Optional `query` filters by name or description; omit it for the full API.",
7113
- inputSchema: {
7114
- type: "object",
7115
- properties: { query: {
7116
- type: "string",
7117
- description: "Substring filter over names and descriptions."
7118
- } }
7119
- },
7120
- handler: async (raw, context) => {
7121
- const query = typeof raw === "object" && raw !== null ? raw.query : void 0;
7122
- return renderCodeModeApi(faces(context), typeof query === "string" ? query : void 0);
7123
- }
7124
- }, {
7125
- name: "execute",
7126
- description: "Run JavaScript against the API from `search`. Write the BODY of an async function: call `api.<name>(args)` (await each call), use loops and Promise.all for fan-out, keep intermediates in variables, `return` only what the next decision needs. `console.log` lines come back beside the result. No imports, no network, no filesystem — the API is the intended capability.",
7127
- inputSchema: {
7128
- type: "object",
7129
- properties: { code: {
7130
- type: "string",
7131
- description: "The async function body to run."
7132
- } },
7133
- required: ["code"]
7134
- },
7135
- handler: async (raw, context) => {
7136
- const code = typeof raw === "object" && raw !== null ? raw.code : void 0;
7137
- if (typeof code !== "string" || code.trim().length === 0) throw new ValidationError("code mode: execute needs { code: string }");
7138
- assertAuthoredCode(code, { context: `code mode (${context.nodeId})` });
7139
- const execution = new AbortController();
7140
- let timedOut = false;
7141
- const onScopeAbort = () => execution.abort(abortReason(context.signal));
7142
- const clearTimer = timeoutMs === null ? void 0 : armDeadlineTimer(timeoutMs, () => {
7143
- if (!execution.signal.aborted) {
7144
- timedOut = true;
7145
- execution.abort(new ValidationError(`code mode: program timed out after ${timeoutMs}ms`));
7199
+ };
7200
+ const summarize = (row) => ({
7201
+ graphDigest: row.graphDigest.slice(0, 12),
7202
+ outcome: row.outcome.kind,
7203
+ cost: row.cost,
7204
+ finishedAt: row.finishedAt
7205
+ });
7206
+ return {
7207
+ extraTools: [
7208
+ {
7209
+ name: "designer_lookup_graph",
7210
+ description: "Look up what this task shape has run before. Returns replay (a known-good graph — run it verbatim with designer_run_graph, near-zero inference) or author (with the reason), plus the recent history for context.",
7211
+ parameters: {
7212
+ type: "object",
7213
+ properties: { taskShape: taskShapeSchema },
7214
+ required: ["taskShape"],
7215
+ additionalProperties: false
7216
+ }
7217
+ },
7218
+ {
7219
+ name: "designer_run_graph",
7220
+ description: "Run a graph and record it. REPLAY mode: pass the graphDigest from lookup — the stored graph runs verbatim, check re-bound by the environment. AUTHOR mode: pass the graph as data (no check). Every run is recorded with its terminal accounting; the registry is the memory.",
7221
+ parameters: {
7222
+ type: "object",
7223
+ properties: {
7224
+ taskShape: taskShapeSchema,
7225
+ mode: {
7226
+ type: "string",
7227
+ enum: ["replay", "authored"]
7228
+ },
7229
+ graphDigest: {
7230
+ type: "string",
7231
+ description: "Replay mode: the digest lookup returned."
7232
+ },
7233
+ graph: graphDataSchema,
7234
+ mutationNote: {
7235
+ type: "string",
7236
+ description: "Author mode after a failed replay lineage: what the prior record says went wrong and the ONE axis this graph changes."
7237
+ }
7238
+ },
7239
+ required: ["taskShape", "mode"],
7240
+ additionalProperties: false
7241
+ }
7242
+ },
7243
+ {
7244
+ name: "designer_history",
7245
+ description: "Every recorded run for a task shape (or all shapes): graph digest, outcome, cost, finish time. The evidence for improvement decisions.",
7246
+ parameters: {
7247
+ type: "object",
7248
+ properties: { taskShape: taskShapeSchema },
7249
+ additionalProperties: false
7146
7250
  }
7147
- }, true);
7148
- if (context.signal.aborted) execution.abort(abortReason(context.signal));
7149
- else context.signal.addEventListener("abort", onScopeAbort, { once: true });
7150
- const bindings = {};
7151
- for (const face of faces(context)) {
7152
- const member = CODE_CALLABLE_VERBS[face.name];
7153
- if (member === void 0) continue;
7154
- bindings[face.name] = async (args) => {
7155
- if (execution.signal.aborted) {
7156
- if (!timedOut) throw abortReason(execution.signal);
7157
- throw new ValidationError(`code mode: the execute deadline passed; api.${face.name} is refused so no work outlives the call`);
7158
- }
7159
- return detach(await context.verbs[member](args));
7160
- };
7161
- }
7162
- try {
7163
- return await runner.run({
7164
- code,
7165
- bindings,
7166
- signal: execution.signal
7167
- });
7168
- } finally {
7169
- clearTimer?.();
7170
- context.signal.removeEventListener("abort", onScopeAbort);
7171
7251
  }
7252
+ ],
7253
+ executeExtraTool: (name, raw) => {
7254
+ if (name === "designer_lookup_graph") return (async () => {
7255
+ return lookupGraphSyncish(asRecord(asRecord(raw, "designer_lookup_graph").taskShape, "designer_lookup_graph.taskShape"));
7256
+ })();
7257
+ if (name === "designer_history") return (async () => {
7258
+ const args = asRecord(raw ?? {}, "designer_history");
7259
+ const { listGraphRuns } = await import("./graph-registry-BuJbzTzF.js").then((n) => n.o);
7260
+ const all = await listGraphRuns(registry);
7261
+ return { runs: (args.taskShape === void 0 ? all : all.filter((row) => row.taskDigest === taskShapeDigest(asRecord(args.taskShape, "designer_history.taskShape")))).map(summarize) };
7262
+ })();
7263
+ if (name === "designer_run_graph") return (async () => {
7264
+ const args = asRecord(raw, "designer_run_graph");
7265
+ const taskShape = asRecord(args.taskShape, "designer_run_graph.taskShape");
7266
+ const mode = args.mode;
7267
+ if (mode === "replay") {
7268
+ const digest = args.graphDigest;
7269
+ if (typeof digest !== "string" || digest.length === 0) throw new ValidationError("designer_run_graph: replay mode requires graphDigest from lookup");
7270
+ const { loadGraph } = await import("./graph-registry-BuJbzTzF.js").then((n) => n.o);
7271
+ const stored = await loadGraph(registry, digest);
7272
+ if (!stored) throw new ValidationError(`designer_run_graph: no stored graph ${digest.slice(0, 12)} — re-lookup`);
7273
+ const graph = env.bindCheck(graphFromStored(stored, () => {
7274
+ throw new Error("designer: environment did not bind the deliverable check");
7275
+ }));
7276
+ return runAndRecord(taskShape, graph, "replay");
7277
+ }
7278
+ if (mode === "authored") {
7279
+ const graph = asRecord(args.graph, "designer_run_graph.graph");
7280
+ for (const axis of [
7281
+ "nodes",
7282
+ "edges",
7283
+ "budget",
7284
+ "deliverable"
7285
+ ]) if (graph[axis] === void 0) throw new ValidationError(`designer_run_graph.graph: ${axis} is required`);
7286
+ return runAndRecord(taskShape, env.bindCheck(graph), "authored");
7287
+ }
7288
+ throw new ValidationError("designer_run_graph: mode must be 'replay' | 'authored'");
7289
+ })();
7290
+ return null;
7172
7291
  }
7173
- }];
7292
+ };
7293
+ async function lookupGraphSyncish(taskShape) {
7294
+ const selection = await lookupGraph(registry, taskShapeDigest(taskShape));
7295
+ const history = (await graphRunsFor(registry, taskShapeDigest(taskShape))).map(summarize);
7296
+ if (selection.kind === "replay") return {
7297
+ decision: "replay",
7298
+ reason: `a delivered graph exists (${selection.record.cost.iterations ?? "?"} iterations, ${selection.record.cost.tokens ?? "?"} tokens)`,
7299
+ graphDigest: selection.graph.digest,
7300
+ history
7301
+ };
7302
+ return {
7303
+ decision: "author",
7304
+ reason: selection.reason,
7305
+ history
7306
+ };
7307
+ }
7174
7308
  }
7175
7309
  //#endregion
7176
7310
  //#region src/runtime/supervise/patch-checks.ts
@@ -7302,6 +7436,109 @@ function signalPass(value, required) {
7302
7436
  return !required;
7303
7437
  }
7304
7438
  //#endregion
7439
+ //#region src/runtime/supervise/profile-registry.ts
7440
+ /**
7441
+ * `profile-registry` — named AgentProfiles with run receipts: the missing
7442
+ * bottom layer of the designer's memory.
7443
+ *
7444
+ * The graph registry (#1719) answers "which GRAPH won for this task shape?"
7445
+ * but a profile is reused ACROSS graphs and across products — the same
7446
+ * summarizer, the same referee, the same driver — and "did the v2 prompt
7447
+ * actually improve?" was answerable only per-graph. This module gives named
7448
+ * profiles their own receipt loop in the exact same shape as the graph
7449
+ * registry: profiles content-addressed by the interface's canonical digest,
7450
+ * one JSONL record per run, pickBest by the same documented heuristic.
7451
+ *
7452
+ * A profile record answers the question a prompt change has to answer before
7453
+ * it deserves to exist: same name, new digest — what did each version cost,
7454
+ * and did it deliver? Versions never overwrite (immutability), so old-vs-new
7455
+ * is always a controlled diff, and a product mounting the registry can refuse
7456
+ * a profile revision with no runs behind it.
7457
+ */
7458
+ const RECORDS_FILE = "profile-records.jsonl";
7459
+ const PROFILES_DIR = "profiles";
7460
+ function assertRecord(record) {
7461
+ if (typeof record.name !== "string" || record.name.trim().length === 0) throw new ValidationError("profile-registry: record.name is required");
7462
+ if (typeof record.profileDigest !== "string" || record.profileDigest.length === 0) throw new ValidationError("profile-registry: record.profileDigest is required");
7463
+ if (record.outcome.kind !== "delivered" && record.outcome.kind !== "failed") throw new ValidationError("profile-registry: record.outcome.kind must be 'delivered' | 'failed'");
7464
+ }
7465
+ /** Open (or lazily create) a profile registry rooted at a workspace directory. */
7466
+ function profileRegistry(root) {
7467
+ if (!root || root.trim().length === 0) throw new ValidationError("profileRegistry: root directory is required");
7468
+ return { root };
7469
+ }
7470
+ async function ensureLayout(reg) {
7471
+ await mkdir(join(reg.root, PROFILES_DIR), { recursive: true });
7472
+ }
7473
+ async function readRecords(reg) {
7474
+ try {
7475
+ return (await readFile(join(reg.root, RECORDS_FILE), "utf8")).split("\n").filter((line) => line.trim().length > 0).map((line) => JSON.parse(line));
7476
+ } catch {
7477
+ return [];
7478
+ }
7479
+ }
7480
+ /** Record one run and persist the exact profile revision beside it. The
7481
+ * record's digest must match the supplied profile — a record always names
7482
+ * the revision that actually ran. The revision file is write-once: a
7483
+ * different profile under the same digest is refused. */
7484
+ async function recordProfileRun(reg, profile, record) {
7485
+ assertRecord(record);
7486
+ if (typeof profile?.name !== "string" || profile.name !== record.name) throw new ValidationError(`profile-registry: the supplied profile's name must equal record.name ('${record.name}')`);
7487
+ const digest = canonicalAgentProfileDigest(profile);
7488
+ if (digest !== record.profileDigest) throw new ValidationError(`profile-registry: record.profileDigest ${record.profileDigest.slice(0, 12)} does not match the supplied profile (${digest.slice(0, 12)}) — record the revision that actually ran`);
7489
+ await ensureLayout(reg);
7490
+ const file = join(reg.root, PROFILES_DIR, `${digest}.json`);
7491
+ try {
7492
+ if (canonicalAgentProfileDigest(JSON.parse(await readFile(file, "utf8"))) !== digest) throw new ValidationError(`profile-registry: revision ${digest.slice(0, 12)} is immutable but its stored copy differs — refusing to overwrite`);
7493
+ } catch (error) {
7494
+ if (!(error instanceof ValidationError)) await writeFile(file, JSON.stringify(profile), "utf8");
7495
+ else throw error;
7496
+ }
7497
+ const deduped = (await readRecords(reg)).filter((row) => !(row.name === record.name && row.profileDigest === record.profileDigest && row.startedAt === record.startedAt));
7498
+ deduped.push(record);
7499
+ await writeFile(join(reg.root, RECORDS_FILE), deduped.map((row) => JSON.stringify(row)).join("\n") + "\n", "utf8");
7500
+ }
7501
+ /** All records for one name, newest last. */
7502
+ async function profileRunsForName(reg, name) {
7503
+ return (await readRecords(reg)).filter((row) => row.name === name);
7504
+ }
7505
+ /** The best revision of a name by the same documented heuristic as the graph
7506
+ * registry: delivered beats failed, then cheaper (tokens, then iterations),
7507
+ * then most recent. This is the answer to "which prompt revision is winning?"
7508
+ * — and every alternative is a controlled diff away (versions never overwrite). */
7509
+ function pickBestProfile(records) {
7510
+ const delivered = records.filter((row) => row.outcome.kind === "delivered");
7511
+ if (delivered.length === 0) return void 0;
7512
+ const better = (a, b) => {
7513
+ const aTokens = a.cost.tokens ?? Number.POSITIVE_INFINITY;
7514
+ const bTokens = b.cost.tokens ?? Number.POSITIVE_INFINITY;
7515
+ if (aTokens !== bTokens) return aTokens < bTokens ? a : b;
7516
+ const aIter = a.cost.iterations ?? Number.POSITIVE_INFINITY;
7517
+ const bIter = b.cost.iterations ?? Number.POSITIVE_INFINITY;
7518
+ if (aIter !== bIter) return aIter < bIter ? a : b;
7519
+ return a.finishedAt >= b.finishedAt ? a : b;
7520
+ };
7521
+ return delivered.reduce((best, row) => better(best, row) === best ? best : row);
7522
+ }
7523
+ /** Load a stored profile revision by digest (for replaying the winning revision). */
7524
+ async function loadProfile(reg, digest) {
7525
+ try {
7526
+ return JSON.parse(await readFile(join(reg.root, PROFILES_DIR, `${digest}.json`), "utf8"));
7527
+ } catch {
7528
+ return;
7529
+ }
7530
+ }
7531
+ /** The winning revision of a name, ready to mount. */
7532
+ async function bestProfileFor(reg, name) {
7533
+ const best = pickBestProfile(await profileRunsForName(reg, name));
7534
+ if (!best) return void 0;
7535
+ const profile = await loadProfile(reg, best.profileDigest);
7536
+ return profile ? {
7537
+ profile,
7538
+ record: best
7539
+ } : void 0;
7540
+ }
7541
+ //#endregion
7305
7542
  //#region src/runtime/supervise/sql-run-context.ts
7306
7543
  /**
7307
7544
  * A cross-machine run context on the existing autocommit SqlAdapter. Reuse the same database,
@@ -8587,6 +8824,6 @@ function tail(s) {
8587
8824
  return s.slice(-400);
8588
8825
  }
8589
8826
  //#endregion
8590
- export { renderCorpusToInstructions as $, chatWorkerSeam as A, completionAuthorizes as At, createS3PrivateCasStore as B, SqlRunOwnershipError as Bt, analyzeTrace as C, assertDeclaredCheck as Ct, codeModeSupervisorTools as D, declaredCheckJudge as Dt, runCoderChecks as E, declaredCheckDigest as Et, authorStrategy as F, pairwiseSignificance as Ft, trajectoryReport as G, createMcpEnvironment as Gt, runBenchmark as H, McpSpawnFault as Ht, strategyAuthorContract as I, renderLeaderboardHtml as It, builtinShapes as J, mcpSecretEnvMetadataKey as Jt, definePersona as K, sanitizeMcpToolSchema as Kt, strategyAuthorSystemPrompt as L, renderLeaderboardMarkdown as Lt, runStrategyEvolution as M, sentinelCompletion as Mt, strategyTournament as N, stopSentinel as Nt, unsafeInProcessRunner as O, readDeclaredCheck as Ot, assertStrategyContract as P, leaderboard as Pt, InMemoryCorpus as Q, createTangleSandboxExactProcessProvider as Qt, SandboxRunAbortError as R, renderLeaderboardSvg as Rt, withUntrackedArtifacts as S, superviseDispatch as St, patchDelivered as T, declaredCheckDeliverable as Tt, createPrivateCasArtifactPort as U, connectStdioMcp as Ut, printBenchmarkReport as V, openSqlRunStore as Vt, equalKOnCost as W, materializeLocalMcp as Wt, registerShape as X, resolveSecretEnv as Xt, createShapeRegistry as Y, resolveMcpServerLaunch as Yt, FileCorpus as Z, secretEnvOfMcpServer as Zt, VERIFY_TAIL_CHARS as _, resolveSandboxClient as _t, localShell as a, selectValidWinner as at, settledWorkerOut as b, loopCampaignDispatch as bt, createVerifierEnvironment as c, assertTraceDerivedFindings as ct, harvestSurfaceDiffs as d, registryScopeAnalyst as dt, fanout as et, analystsFromRegistry as f, observationFromRegistry as ft, NOTE_MAX_CHARS as g, defineLeaderboard as gt, EVIDENCE_MAX_CHARS as h, harvestCorpus as ht, jjWorkspace as i, pipeline as it, createChatSessionStore as j, deterministicCompletion as jt, chatTransportExecutor as k, runIsolatedCheck as kt, boxSurfaceReader as l, buildSteerContext as lt, worktreeFanout as m, HarvestError as mt, makeFinding$1 as n, loopUntil as nt, runInWorkspace as o, verify as ot, failuresAnalyst as p, inProcessSandboxClient as pt, runPersonified as q, envKeyProvider as qt, gitWorkspace as r, panel as rt, createWaterfallCollector as s, widen as st, computeFindingId$1 as t, flatWidenGate as tt, fsSurfaceReader as u, createScopeAnalyst as ut, closingWorkerNote as v, localSandboxClient as vt, createFencedSqlRunContext as w, checkProgramDigest as wt, copyUntrackedIntoClone as x, loopDispatch as xt, composeWorkerEvidence as y, inlineSandboxClient as yt, openSandboxRun as z, renderPairwiseMarkdown as zt };
8827
+ export { builtinShapes as $, mcpSecretEnvMetadataKey as $t, recordProfileRun as A, declaredCheckDeliverable as At, authorStrategy as B, pairwiseSignificance as Bt, analyzeTrace as C, localSandboxClient as Ct, pickBestProfile as D, superviseDispatch as Dt, loadProfile as E, loopDispatch as Et, chatWorkerSeam as F, completionAuthorizes as Ft, createS3PrivateCasStore as G, SqlRunOwnershipError as Gt, strategyAuthorSystemPrompt as H, renderLeaderboardMarkdown as Ht, createChatSessionStore as I, deterministicCompletion as It, createPrivateCasArtifactPort as J, connectStdioMcp as Jt, printBenchmarkReport as K, openSqlRunStore as Kt, runStrategyEvolution as L, sentinelCompletion as Lt, runCoderChecks as M, declaredCheckJudge as Mt, graphDesignerExtraTools as N, readDeclaredCheck as Nt, profileRegistry as O, assertDeclaredCheck as Ot, chatTransportExecutor as P, runIsolatedCheck as Pt, runPersonified as Q, envKeyProvider as Qt, strategyTournament as R, stopSentinel as Rt, withUntrackedArtifacts as S, resolveSandboxClient as St, bestProfileFor as T, loopCampaignDispatch as Tt, SandboxRunAbortError as U, renderLeaderboardSvg as Ut, strategyAuthorContract as V, renderLeaderboardHtml as Vt, openSandboxRun as W, renderPairwiseMarkdown as Wt, trajectoryReport as X, createMcpEnvironment as Xt, equalKOnCost as Y, materializeLocalMcp as Yt, definePersona as Z, sanitizeMcpToolSchema as Zt, VERIFY_TAIL_CHARS as _, observationFromRegistry as _t, localShell as a, fanout as at, settledWorkerOut as b, harvestCorpus as bt, createVerifierEnvironment as c, panel as ct, harvestSurfaceDiffs as d, verify as dt, resolveMcpServerLaunch as en, createShapeRegistry as et, analystsFromRegistry as f, widen as ft, NOTE_MAX_CHARS as g, registryScopeAnalyst as gt, EVIDENCE_MAX_CHARS as h, createScopeAnalyst as ht, jjWorkspace as i, renderCorpusToInstructions as it, patchDelivered as j, declaredCheckDigest as jt, profileRunsForName as k, checkProgramDigest as kt, boxSurfaceReader as l, pipeline as lt, worktreeFanout as m, buildSteerContext as mt, makeFinding$1 as n, secretEnvOfMcpServer as nn, FileCorpus as nt, runInWorkspace as o, flatWidenGate as ot, failuresAnalyst as p, assertTraceDerivedFindings as pt, runBenchmark as q, McpSpawnFault as qt, gitWorkspace as r, createTangleSandboxExactProcessProvider as rn, InMemoryCorpus as rt, createWaterfallCollector as s, loopUntil as st, computeFindingId$1 as t, resolveSecretEnv as tn, registerShape as tt, fsSurfaceReader as u, selectValidWinner as ut, closingWorkerNote as v, inProcessSandboxClient as vt, createFencedSqlRunContext as w, inlineSandboxClient as wt, copyUntrackedIntoClone as x, defineLeaderboard as xt, composeWorkerEvidence as y, HarvestError as yt, assertStrategyContract as z, leaderboard as zt };
8591
8828
 
8592
- //# sourceMappingURL=runtime-B9BOFyEp.js.map
8829
+ //# sourceMappingURL=runtime-6qzuLTJ7.js.map