@tangle-network/agent-runtime 0.117.0 → 0.120.0

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 (80) hide show
  1. package/README.md +2 -0
  2. package/dist/{activation-BZ2FyX0O.js → activation-DdIpwQ0k.js} +3 -3
  3. package/dist/{activation-BZ2FyX0O.js.map → activation-DdIpwQ0k.js.map} +1 -1
  4. package/dist/agent.d.ts +1 -1
  5. package/dist/agent.js +3 -3
  6. package/dist/candidate-execution/index.d.ts +2 -2
  7. package/dist/candidate-execution/index.js +4 -4
  8. package/dist/{candidate-execution-PX61zXd2.js → candidate-execution-DDkSRPjY.js} +4 -4
  9. package/dist/{candidate-execution-PX61zXd2.js.map → candidate-execution-DDkSRPjY.js.map} +1 -1
  10. package/dist/{environment-provider-CTZjdIUg.d.ts → environment-provider-Bh4nX2qt.d.ts} +9 -1
  11. package/dist/{environment-provider-CzcEyRL7.js → environment-provider-Cr75N-rU.js} +2 -2
  12. package/dist/{environment-provider-CzcEyRL7.js.map → environment-provider-Cr75N-rU.js.map} +1 -1
  13. package/dist/environment-provider.d.ts +1 -1
  14. package/dist/environment-provider.js +1 -1
  15. package/dist/{improvement-cycle-Bcqz8rZI.js → improvement-cycle-zjR-MXwK.js} +7 -7
  16. package/dist/improvement-cycle-zjR-MXwK.js.map +1 -0
  17. package/dist/{index-X2YwyWkc.d.ts → index-B3bHsVLw.d.ts} +2 -2
  18. package/dist/{index-BHSYaWrC.d.ts → index-BLsKcxNd.d.ts} +3 -3
  19. package/dist/{index-DPWJxSRa.d.ts → index-CGADWaa_.d.ts} +20 -165
  20. package/dist/{index-BTUG4Xln.d.ts → index-I35151Fr.d.ts} +9 -9
  21. package/dist/index.d.ts +8 -8
  22. package/dist/index.js +12 -12
  23. package/dist/index.js.map +1 -1
  24. package/dist/intelligence.d.ts +4 -4
  25. package/dist/intelligence.js +6 -6
  26. package/dist/kernel.d.ts +4 -4
  27. package/dist/kernel.js +8 -7
  28. package/dist/{knowledge-CDlrayRc.js → knowledge-I1e9zQGj.js} +5 -5
  29. package/dist/{knowledge-CDlrayRc.js.map → knowledge-I1e9zQGj.js.map} +1 -1
  30. package/dist/knowledge.d.ts +1 -1
  31. package/dist/knowledge.js +1 -1
  32. package/dist/{local-harness-t6cDWDQ2.d.ts → local-harness-BIajef4A.d.ts} +29 -7
  33. package/dist/{loop-runner-bin-oyL7it5O.d.ts → loop-runner-bin-BeG9vTdE.d.ts} +3 -3
  34. package/dist/{loop-runner-bin-BxhhDtmG.js → loop-runner-bin-L996YVPS.js} +3 -3
  35. package/dist/{loop-runner-bin-BxhhDtmG.js.map → loop-runner-bin-L996YVPS.js.map} +1 -1
  36. package/dist/loop-runner-bin.d.ts +1 -1
  37. package/dist/loop-runner-bin.js +1 -1
  38. package/dist/mcp/bin.js +2 -2
  39. package/dist/mcp/bin.js.map +1 -1
  40. package/dist/mcp/index.d.ts +5 -5
  41. package/dist/mcp/index.js +8 -13
  42. package/dist/mcp/index.js.map +1 -1
  43. package/dist/{openai-tools-CO9X36zy.js → openai-tools-CvFEzw6f.js} +2 -2
  44. package/dist/{openai-tools-CO9X36zy.js.map → openai-tools-CvFEzw6f.js.map} +1 -1
  45. package/dist/{prepare-BbI6qGOW.js → prepare-DpV6np9e.js} +44 -34
  46. package/dist/{prepare-BbI6qGOW.js.map → prepare-DpV6np9e.js.map} +1 -1
  47. package/dist/primeintellect/index.d.ts +1 -1
  48. package/dist/{protected-model-port-B0yXMpLL.d.ts → protected-model-port-CoJOiINX.d.ts} +2 -2
  49. package/dist/{protected-model-port-COnyZfML.js → protected-model-port-vQLBRoAQ.js} +2 -2
  50. package/dist/{protected-model-port-COnyZfML.js.map → protected-model-port-vQLBRoAQ.js.map} +1 -1
  51. package/dist/{redact-BEtQtvd6.d.ts → redact-PQzmE1Jn.d.ts} +4 -4
  52. package/dist/run-layout-C2FGmZ3v.js +158 -0
  53. package/dist/run-layout-C2FGmZ3v.js.map +1 -0
  54. package/dist/{runtime-Ce39zfqn.js → runtime-D4e3GP5K.js} +7 -142
  55. package/dist/runtime-D4e3GP5K.js.map +1 -0
  56. package/dist/{spawn-journal-B_mu_oH2.js → spawn-journal-IeXpidO2.js} +2 -2
  57. package/dist/{spawn-journal-B_mu_oH2.js.map → spawn-journal-IeXpidO2.js.map} +1 -1
  58. package/dist/{structural-rollout-DLwW69R4.js → structural-rollout-DQHO3b2Y.js} +3 -3
  59. package/dist/{structural-rollout-DLwW69R4.js.map → structural-rollout-DQHO3b2Y.js.map} +1 -1
  60. package/dist/{supervise-U7_K5yuV.js → supervise-Cq50UT5-.js} +289 -1084
  61. package/dist/supervise-Cq50UT5-.js.map +1 -0
  62. package/dist/{supervisor-CAXiCKnX.js → supervisor-CpT9yAxL.js} +113 -35
  63. package/dist/supervisor-CpT9yAxL.js.map +1 -0
  64. package/dist/testing.js +10 -10
  65. package/dist/top-app-5unxqovu.js +1088 -0
  66. package/dist/top-app-5unxqovu.js.map +1 -0
  67. package/dist/tui/bin.d.ts +1 -0
  68. package/dist/tui/bin.js +19 -0
  69. package/dist/tui/bin.js.map +1 -0
  70. package/dist/tui/index.d.ts +209 -0
  71. package/dist/tui/index.js +2 -0
  72. package/dist/{util-Cc9g9Y-o.js → util-MVgdwuIS.js} +2 -2
  73. package/dist/{util-Cc9g9Y-o.js.map → util-MVgdwuIS.js.map} +1 -1
  74. package/dist/{workspace-archive-CqPIijFA.js → workspace-archive-B4SkNJjw.js} +2 -2
  75. package/dist/{workspace-archive-CqPIijFA.js.map → workspace-archive-B4SkNJjw.js.map} +1 -1
  76. package/package.json +11 -5
  77. package/dist/improvement-cycle-Bcqz8rZI.js.map +0 -1
  78. package/dist/runtime-Ce39zfqn.js.map +0 -1
  79. package/dist/supervise-U7_K5yuV.js.map +0 -1
  80. package/dist/supervisor-CAXiCKnX.js.map +0 -1
@@ -1,23 +1,43 @@
1
1
  import { c as RuntimeRunStateError, i as ConfigError, o as NotFoundError, t as AgentEvalError$1, u as ValidationError } from "./errors-DEAvWQPy.js";
2
- import { S as detachedSnapshot, b as workerTraceAnalysisStore, d as writeAllBytes, i as InMemorySpawnJournal, l as parseCommittedJsonLines, n as FileSpawnJournal, r as InMemoryResultBlobStore, t as FileResultBlobStore, u as prepareJsonlAppend, x as contentAddress } from "./spawn-journal-B_mu_oH2.js";
3
- import { a as mapWithConcurrency, c as sleep, d as throwIfAborted, f as withTimeout, l as stringifySafe, n as addTokenUsage, o as randomSuffix, p as zeroTokenUsage, r as deleteBoxSafe, s as randomUuid, t as abortError, u as throwAbort } from "./util-Cc9g9Y-o.js";
4
- import { C as buildLoopSpanNodes, Ct as controlProfileMaterialization, Dt as promptControlProfileMaterialization, E as createOtelExporter, Et as profileMaterializationAxes, Ot as promptModelProfileMaterialization, Pt as worktreeCliProfileMaterialization, Q as assertValidBudget, St as assertProfileMaterialization, Tt as fullProfileMaterialization, W as createActivityLog, X as DEFAULT_SUCCESSFUL_SHUTDOWN_MS, Z as teardownExecutor, _t as worktreeProfileExecutionPlan, a as pickBestDelivered, at as runtimeOwnedExecutorExecutionBinding, bt as removeWorktree, c as driverChild, ct as routerBrain, d as deriveNodeExecutionIdentity, et as spendFromUsageEvents, f as recordScopeOwnerMaterialization, g as workerTraceEnv, gt as runWorktreeHarness, ht as runWorktreeChecks, it as newExecutionAttemptId, j as toOtelAttributes, k as generateSpanId, l as withDriverExecutor, n as createSupervisor, nt as attestRuntimeOwnedScopeOwner, o as runFinalizer, ot as runtimeOwnedExecutorMaterialization, p as scopeOwnerExecutorNodeContext, pt as runBrainLoop, q as freeSlots, r as bestDelivered, rt as inheritRuntimeOwnedExecutorAttestation, s as runTree, st as runtimeOwnedScopeOwnerRuntime, tt as attestRuntimeOwnedExecutor, ut as routerChatWithUsage, vt as captureWorktreeDiff, wt as defineProfileMaterializationContract, yt as createWorktree } from "./supervisor-CAXiCKnX.js";
2
+ import { S as detachedSnapshot, b as workerTraceAnalysisStore, d as writeAllBytes, i as InMemorySpawnJournal, l as parseCommittedJsonLines, n as FileSpawnJournal, r as InMemoryResultBlobStore, t as FileResultBlobStore, u as prepareJsonlAppend, x as contentAddress } from "./spawn-journal-IeXpidO2.js";
3
+ import { a as randomSuffix, c as stringifySafe, d as withTimeout, f as zeroTokenUsage, i as mapWithConcurrency, l as throwAbort, n as deleteBoxSafe, o as randomUuid, s as sleep, t as addTokenUsage, u as throwIfAborted } from "./util-MVgdwuIS.js";
4
+ import { $ as WORKER_TOKEN_FLOOR, At as promptControlProfileMaterialization, C as buildLoopSpanNodes, Ct as removeWorktree, Dt as defineProfileMaterializationContract, E as createOtelExporter, Et as controlProfileMaterialization, G as describeToolArgs, J as freeSlots, Lt as worktreeCliProfileMaterialization, Ot as fullProfileMaterialization, Q as teardownExecutor, St as createWorktree, Tt as assertProfileMaterialization, W as createActivityLog, Z as DEFAULT_SUCCESSFUL_SHUTDOWN_MS, a as pickBestDelivered, at as attestRuntimeOwnedScopeOwner, bt as worktreeProfileExecutionPlan, c as driverChild, ct as runtimeOwnedExecutorExecutionBinding, d as deriveNodeExecutionIdentity, dt as routerBrain, et as workerTokenFloor, f as recordScopeOwnerMaterialization, g as workerTraceEnv, gt as runBrainLoop, it as attestRuntimeOwnedExecutor, j as toOtelAttributes, jt as promptModelProfileMaterialization, k as generateSpanId, kt as profileMaterializationAxes, l as withDriverExecutor, lt as runtimeOwnedExecutorMaterialization, n as createSupervisor, o as runFinalizer, ot as inheritRuntimeOwnedExecutorAttestation, p as scopeOwnerExecutorNodeContext, pt as routerChatWithUsage, r as bestDelivered, rt as spendFromUsageEvents, s as runTree, st as newExecutionAttemptId, tt as assertValidBudget, ut as runtimeOwnedScopeOwnerRuntime, vt as runWorktreeChecks, xt as captureWorktreeDiff, yt as runWorktreeHarness } from "./supervisor-CpT9yAxL.js";
5
5
  import { i as notifyRuntimeHookEvent, t as composeRuntimeHooks } from "./runtime-hooks-C7iJOWm3.js";
6
6
  import { a as notifySandboxEventObserver, n as extractLlmCallEvent } from "./sandbox-events-Yhd1GYWl.js";
7
- import { i as resolveAgentEnvironmentProvider, n as providerAsExecutor, r as providerAsSandboxClient, s as buildBackendOptions } from "./environment-provider-CzcEyRL7.js";
7
+ import { i as resolveAgentEnvironmentProvider, n as providerAsExecutor, r as providerAsSandboxClient, s as buildBackendOptions } from "./environment-provider-Cr75N-rU.js";
8
8
  import { t as createStdioToolServer } from "./tool-server-RcWgLIsL.js";
9
9
  import { n as UI_LENSES } from "./substrate-B0TYNrXn.js";
10
10
  import { argHash, computeFindingId, errorStreakDetector, estimateCost, isModelPriced, makeFinding, observeAll, repeatedActionDetector } from "@tangle-network/agent-eval";
11
11
  import { agentProfileSchema, canonicalCandidateDigest, mergeAgentProfiles, validateAgentProfileSecurity } from "@tangle-network/agent-interface";
12
12
  import { randomUUID } from "node:crypto";
13
13
  import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
14
- import { existsSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
15
- import path, { dirname, isAbsolute, join, resolve, sep } from "node:path";
14
+ import path, { dirname, resolve } from "node:path";
16
15
  import { spawn } from "node:child_process";
17
- import { homedir, tmpdir } from "node:os";
18
16
  import { createServer, request } from "node:http";
19
17
  import { request as request$1 } from "node:https";
20
18
  import { Readable, Writable } from "node:stream";
19
+ //#region src/runtime/harness-role.ts
20
+ /**
21
+ * Harness ids that select a router/no-agent mode rather than a coding-agent runtime.
22
+ *
23
+ * `nanoclaw` is deliberately ABSENT: the shared capability table clamps its reasoning ceiling the
24
+ * same way it clamps `cli-base`, but nanoclaw does run an agent (a socket-bridge runner to the
25
+ * NanoClaw daemon) and every caller here treats it as a full harness. Add a row only with the
26
+ * behavior change stated, never to make the two clamps look symmetric.
27
+ */
28
+ const NO_AGENT_HARNESSES = /* @__PURE__ */ new Set(["cli-base"]);
29
+ /** Whether this profile harness selects a real coding-agent runtime (vs. the router/no-agent mode
30
+ * or no preference at all). Accepts the looser `string` some local profile shapes declare, and
31
+ * preserves the caller's own harness type when it is narrower. */
32
+ function harnessRunsAgent(harness) {
33
+ return harness != null && !NO_AGENT_HARNESSES.has(harness);
34
+ }
35
+ /** The coding-agent harness this profile selects, or `undefined` when it selects none — the shape
36
+ * callers want when a no-agent mode must fall through to the router arm. */
37
+ function agentHarness(harness) {
38
+ return harnessRunsAgent(harness) ? harness : void 0;
39
+ }
40
+ //#endregion
21
41
  //#region src/runtime/supervise/completion-gate.ts
22
42
  /**
23
43
  *
@@ -1949,402 +1969,16 @@ function createInbox() {
1949
1969
  };
1950
1970
  }
1951
1971
  //#endregion
1952
- //#region src/runtime/supervise/pi-mcp.ts
1953
- /**
1954
- *
1955
- * MCP for the `pi` harness — the mount `piExecutor` performs before it spawns pi.
1956
- *
1957
- * pi ships NO MCP support of its own. MCP arrives through the `pi-mcp-adapter` extension, and the
1958
- * adapter takes its config from a path the CALLER names: it registers a `--mcp-config <path>` flag
1959
- * (`pi-mcp-adapter` `index.ts:252`, read back at `init.ts:101` and off raw argv at `utils.ts:55`).
1960
- * So "give pi an MCP server" means: write the canonical `{ mcpServers: … }` object to a path this
1961
- * run owns, pass that path with `--mcp-config`, make sure the adapter is loaded, and delete the
1962
- * file afterwards. This module does exactly that and nothing else.
1963
- *
1964
- * Three rules carry the whole design:
1965
- *
1966
- * 1. **Fail loud, never fail safe.** If the profile declares MCP servers and `pi-mcp-adapter` is not
1967
- * installed, `preparePiMcp` throws BEFORE any file is written and before pi is spawned. A run
1968
- * whose tools never existed must fail, not score zero for the wrong reason.
1969
- * 2. **`--no-extensions` is a trap.** When `extensions.pi.load` is an array, pi runs
1970
- * `--no-extensions`, which suppresses ALL auto-discovery — cwd `.pi/extensions`, global
1971
- * `~/.pi/agent/extensions`, AND the `settings.json` `packages` list that would otherwise have
1972
- * auto-loaded the adapter. So `load: ['pi-memory']` alongside `mcp` kills MCP dead even though
1973
- * the adapter IS installed. When MCP is declared, this module adds `pi-mcp-adapter` to the
1974
- * resolved load list and REPORTS that it did (`PiMcpReceipt.adapterInjected`). The caller's
1975
- * profile object is never mutated.
1976
- * 3. **One config file per worker EXECUTION, never one per workspace.** `PiSeam.cwd` comes from the
1977
- * `ExecutorConfig` and is therefore shared by every worker the factory builds, so a config
1978
- * discovered by cwd is a file two concurrent siblings fight over. `mountPiMcpConfig` instead
1979
- * creates a fresh `mkdtemp` directory — OS-guaranteed unique, so no counter, lock, merge or
1980
- * restore is needed — and hands its path to `--mcp-config`. Concurrent workers built from ONE
1981
- * seam get ONE config each and never collide.
1982
- *
1983
- * Why the flag is safe to emit here even though pi only lists it under `Extension CLI Flags`: it is
1984
- * emitted ONLY when the profile declares at least one usable MCP server, and that case has already
1985
- * passed the `piMcpAdapterAvailable()` gate above, so the extension that registers the flag is
1986
- * guaranteed loadable (auto-discovered, or force-loaded by the `--extension` this module appends).
1987
- * A machine without the adapter never reaches the flag — it fails at the gate with an install
1988
- * command instead.
1989
- *
1990
- * Where the run's servers sit in pi's config layering, stated plainly because it is a real
1991
- * behavioral edge: `--mcp-config` REPLACES pi's user-global override file (`config.ts:310`,
1992
- * `getPiGlobalConfigPath(overridePath)`), so the operator's own `~/.pi/agent/mcp.json` servers no
1993
- * longer leak into a worker — good. It does NOT suppress project discovery: the adapter still
1994
- * layers `<cwd>/.mcp.json` and `<cwd>/.pi/mcp.json` on top (`config.ts:352-373`), and those are
1995
- * later sources, so a workspace that ships a server under the SAME NAME overrides this run's entry.
1996
- * The consequence is worth naming: an A/B whose arms differ only in `profile.mcp` is confounded
1997
- * when the shared workspace carries its own MCP config. Deleting a file the run did not create
1998
- * would be worse — a run cannot quietly edit the repo it was pointed at.
1999
- *
2000
- * Upstream note: `@tangle-network/agent-profile-materialize` is the shared owner of profile → harness
2001
- * mapping and already declares `pi` a first-class `HarnessId`, but as of 0.9.4 its pi arm answers
2002
- * `unsupported('mcp', 'pi MCP requires a loaded MCP extension; the base Pi runtime has no MCP
2003
- * control')` and its pi extensions arm answers `unsupported('extensions', …)`. That refusal is now
2004
- * factually stale — the shape is exactly its `kimi-code` arm's (write a config file, push a config
2005
- * flag, because the harness has no cwd discovery worth trusting). Teaching the materializer these
2006
- * two rows is the durable home for this mapping; until it lands, this module is the only thing in
2007
- * the `backend: 'pi'` path that can honor `profile.mcp` at all.
2008
- *
2009
- * Verified against pi 0.83.0 and pi-mcp-adapter 2.15.0.
2010
- *
2011
- * @experimental
2012
- */
2013
- /** The pi extension that gives pi MCP at all. Everything here is gated on it being loadable. */
2014
- const PI_MCP_ADAPTER = "pi-mcp-adapter";
2015
- /** Overrides adapter detection for installs whose path does not carry the adapter's name. */
2016
- const PI_MCP_ADAPTER_ENV = "AGENT_RUNTIME_PI_MCP_ADAPTER";
2017
- /** The adapter-registered flag that names this run's config file (`pi-mcp-adapter` `index.ts:252`). */
2018
- const PI_MCP_CONFIG_FLAG = "--mcp-config";
2019
- /** pi's agent home. Holds `settings.json` and `npm/node_modules/<extension>`. */
2020
- function piAgentDir() {
2021
- return process.env.PI_CODING_AGENT_DIR ?? join(homedir(), ".pi", "agent");
2022
- }
2023
- /** `<agentDir>/npm`, whose `node_modules/<pkg>` holds installed pi extensions. */
2024
- function piNpmRoot() {
2025
- return join(piAgentDir(), "npm");
2026
- }
2027
- /**
2028
- * True when pi can actually consume MCP config — i.e. `pi-mcp-adapter` is installed. Ported from
2029
- * cli-bridge `pi.ts:189`: an env override first (for vendored installs whose path does not carry the
2030
- * adapter's name), then the npm install directory, then a scan of `settings.json` `packages` in
2031
- * which a local-path entry is resolved by reading the target's `package.json` `name`. An
2032
- * unreadable or absent `settings.json` reports "not detected"; it never throws and never assumes.
2033
- */
2034
- function piMcpAdapterAvailable() {
2035
- const override = process.env[PI_MCP_ADAPTER_ENV];
2036
- if (override === "1" || override === "true") return true;
2037
- if (override === "0" || override === "false") return false;
2038
- const agentDir = piAgentDir();
2039
- if (existsSync(join(agentDir, "npm", "node_modules", "pi-mcp-adapter"))) return true;
2040
- try {
2041
- const settings = JSON.parse(readFileSync(join(agentDir, "settings.json"), "utf-8"));
2042
- if (!Array.isArray(settings.packages)) return false;
2043
- return settings.packages.some((entry) => packageEntryIsAdapter(entry, agentDir));
2044
- } catch {
2045
- return false;
2046
- }
2047
- }
2048
- /** One `settings.json` `packages[]` entry: a name containing the adapter, or a local path to it. */
2049
- function packageEntryIsAdapter(entry, agentDir) {
2050
- if (typeof entry !== "string") return false;
2051
- if (entry.includes("pi-mcp-adapter")) return true;
2052
- const spec = entry.replace(/^(file|path):(\/\/)?/, "");
2053
- const windowsAbsolute = /^[A-Za-z]:[\\/]/.test(spec);
2054
- if (!isAbsolute(spec) && !windowsAbsolute && !spec.startsWith(".")) return false;
2055
- return readPackageName(isAbsolute(spec) || windowsAbsolute ? spec : join(agentDir, spec)) === PI_MCP_ADAPTER;
2056
- }
2057
- function readPackageName(packageDir) {
2058
- try {
2059
- const manifest = JSON.parse(readFileSync(join(packageDir, "package.json"), "utf-8"));
2060
- return typeof manifest.name === "string" ? manifest.name : void 0;
2061
- } catch {
2062
- return;
2063
- }
2064
- }
2065
- /**
2066
- * Read one `AgentProfileConfigValue` as public bytes. A `secret-ref` is REFUSED rather than
2067
- * stringified: this executor has no secret provider, so the only alternatives are writing a
2068
- * placeholder into the config (an auth failure disguised as a tool bug) or writing the literal
2069
- * reference object (nonsense to the adapter). Plain strings, numbers and booleans are accepted
2070
- * because profiles authored as JSON commonly carry them where the type says `{kind:'public'}`.
2071
- */
2072
- function publicConfigString(value, where) {
2073
- if (typeof value === "string") return value;
2074
- if (typeof value === "number" || typeof value === "boolean") return String(value);
2075
- if (value && typeof value === "object" && value.kind === "public") return value.value;
2076
- if (value && typeof value === "object" && value.kind === "secret-ref") throw new ConfigError(`piExecutor: ${where} is a secret-ref ("${value.key}") and piExecutor has no secret provider; resolve it before handing the profile to the executor, or declare a public value`);
2077
- throw new ValidationError(`piExecutor: ${where} is not a public configuration value`);
2078
- }
2079
- /**
2080
- * Build the canonical `{ mcpServers }` body the adapter reads, from `AgentProfile.mcp`.
2081
- *
2082
- * Every server is written with `directTools: true` (`pi-mcp-adapter` types.ts:351), which
2083
- * registers its tools as NATIVE pi tools instead of hiding them behind the generic `mcp` tool.
2084
- * Without it an agent must discover its own verbs before it can use them — connect to the server,
2085
- * then describe each tool — and a measured run spent 58 turns and 639,632 input tokens doing that
2086
- * before it could delegate once. A profile that declares an MCP server is asking for its tools,
2087
- * not for a directory it has to browse.
2088
- *
2089
- * This is cli-bridge's `buildCanonicalMcpServers`
2090
- * (`/home/drew/code/cli-bridge/src/backends/profile-support.ts:253`) reproduced field for field and
2091
- * in the same key order — same drop of `enabled:false`, same stdio-versus-remote decision
2092
- * (`isStdioMcpSpec`, `profile-support.ts:177`), same `{command, args, env}` and `{type, url,
2093
- * headers}` bodies, same silent drop of an entry with nothing to connect to. For any server both
2094
- * input types can express, the emitted JSON is byte-identical.
2095
- *
2096
- * The two input types are not the same type, so three differences remain and each is a field one
2097
- * side cannot express:
2098
- *
2099
- * 1. **`cwd` (emitted here, absent there).** `AgentProfileLocalMcpServer` has `cwd`; cli-bridge's
2100
- * `McpServerSpec` (`src/backends/types.ts:53`) has no such field, so it can never emit one. The
2101
- * adapter honors it (`pi-mcp-adapter` `types.ts:325`, `ServerEntry.cwd`), so dropping it would
2102
- * silently discard a declared setting. It is emitted where cli-bridge emits `timeout`.
2103
- * 2. **`timeout` (emitted there, absent here).** `AgentProfileMcpServer` has no timeout field, so
2104
- * there is nothing to emit. (The adapter's own knob is `requestTimeoutMs`, not `timeout`.)
2105
- * 3. **A `url` with no declared transport.** cli-bridge DROPS it — its remote branch requires an
2106
- * explicit `spec.type` — even though its own `McpServerSpec` docstring says "http is implied if
2107
- * `url` is set" (`types.ts:53-56`). `transport` is optional on `AgentProfileRemoteMcpServer`, so
2108
- * dropping such a server here would be exactly the silent-drop bug this module exists to fix.
2109
- * We honor the documented convention and emit `type: 'http'`.
2110
- *
2111
- * `sse` is ACCEPTED and forwarded verbatim, as cli-bridge forwards it. The adapter connects a URL
2112
- * server by probing StreamableHTTP and falling back to `SSEClientTransport` when the probe fails
2113
- * (`pi-mcp-adapter` `server-manager.ts:765`), so an SSE-only endpoint does connect. Its
2114
- * `ServerEntry` (`types.ts:319`) carries no transport discriminator at all, which means the `type`
2115
- * key is inert to the adapter either way — but it is what cli-bridge writes, and a config file that
2116
- * round-trips the caller's declared transport is the one that stays readable by both.
2117
- */
2118
- function buildPiMcpServers(mcp) {
2119
- const out = {};
2120
- if (!mcp || typeof mcp !== "object") return out;
2121
- for (const [name, raw] of Object.entries(mcp)) {
2122
- if (!name || !raw || typeof raw !== "object") continue;
2123
- if (raw.enabled === false) continue;
2124
- const transport = raw.transport;
2125
- const command = typeof raw.command === "string" && raw.command.length > 0 ? raw.command : null;
2126
- const url = typeof raw.url === "string" && raw.url.length > 0 ? raw.url : null;
2127
- if ((transport === "http" || transport === "sse" ? false : command !== null) && command !== null) {
2128
- const args = Array.isArray(raw.args) ? raw.args.map((a, i) => publicConfigString(a, `mcp["${name}"].args[${i}]`)) : [];
2129
- const env = readConfigRecord(raw.env, `mcp["${name}"].env`);
2130
- out[name] = {
2131
- command,
2132
- ...args.length > 0 ? { args } : {},
2133
- ...Object.keys(env).length > 0 ? { env } : {},
2134
- ...typeof raw.cwd === "string" && raw.cwd.length > 0 ? { cwd: raw.cwd } : {},
2135
- directTools: true
2136
- };
2137
- continue;
2138
- }
2139
- if (url !== null && transport !== "stdio") {
2140
- const headers = readConfigRecord(raw.headers, `mcp["${name}"].headers`);
2141
- out[name] = {
2142
- type: transport === "sse" ? "sse" : "http",
2143
- url,
2144
- ...Object.keys(headers).length > 0 ? { headers } : {},
2145
- directTools: true
2146
- };
2147
- }
2148
- }
2149
- return out;
2150
- }
2151
- function readConfigRecord(record, where) {
2152
- if (!record || typeof record !== "object") return {};
2153
- const out = {};
2154
- for (const [key, value] of Object.entries(record)) {
2155
- if (value === void 0) continue;
2156
- out[key] = publicConfigString(value, `${where}["${key}"]`);
2157
- }
2158
- return out;
2159
- }
2160
- /**
2161
- * Resolve one `extensions.pi.load` entry to an absolute extension entry file. Ported from cli-bridge
2162
- * `resolvePiExtensionEntry` (`pi.ts:123`): an absolute path is used verbatim; a bare package name
2163
- * resolves to `<npmRoot>/node_modules/<pkg>/<package.json main>`, `main` defaulting to `index.ts`.
2164
- * The resolution is required because pi's per-file loader does NOT resolve bare specifiers against
2165
- * `~/.pi/agent/npm/node_modules`. Fail-closed: an unreadable manifest or a missing entry file
2166
- * rejects the run rather than letting an ablation arm run without the extension it declared.
2167
- */
2168
- function resolvePiExtensionEntry(spec, npmRoot) {
2169
- let entry;
2170
- if (isAbsolute(spec)) entry = spec;
2171
- else {
2172
- const packageDir = join(npmRoot, "node_modules", spec);
2173
- let main = "index.ts";
2174
- try {
2175
- const manifest = JSON.parse(readFileSync(join(packageDir, "package.json"), "utf-8"));
2176
- if (typeof manifest.main === "string" && manifest.main) main = manifest.main;
2177
- } catch (err) {
2178
- throw new ConfigError(`piExecutor: cannot load pi extension "${spec}": ${join(packageDir, "package.json")} is not readable (install it with \`pi install npm:${spec}\`): ${err instanceof Error ? err.message : String(err)}`);
2179
- }
2180
- entry = join(packageDir, main);
2181
- }
2182
- if (!existsSync(entry)) throw new ConfigError(`piExecutor: cannot load pi extension "${spec}": resolved entry ${entry} does not exist`);
2183
- return entry;
2184
- }
2185
- /** Does this `load` entry name the MCP adapter, before resolution? */
2186
- function entryNamesAdapter(spec) {
2187
- if (!isAbsolute(spec)) return spec === PI_MCP_ADAPTER;
2188
- if (spec.split(/[\\/]/).includes("pi-mcp-adapter")) return true;
2189
- const parent = spec.slice(0, spec.lastIndexOf(sep) + 1);
2190
- return parent.length > 0 && readPackageName(parent) === "pi-mcp-adapter";
2191
- }
2192
- /**
2193
- * Lower `AgentProfile.extensions.pi.load` into pi argv, honoring the `--no-extensions` trap.
2194
- *
2195
- * `load` absent (not an array) → no flags at all, so pi keeps its default discovery and the adapter
2196
- * auto-loads from `settings.json` `packages`. `load` present → `--no-extensions` plus one
2197
- * `--extension <absolute entry>` per listed extension, which is what makes an ablation arm
2198
- * reproducible and leak-free.
2199
- *
2200
- * When `mcpRequested` is true and an explicit `load` array does not already name the adapter, the
2201
- * adapter is APPENDED — otherwise `--no-extensions` would suppress the very extension that turns
2202
- * the `--mcp-config` file into tools. The caller's profile is not touched; the addition is reported
2203
- * through `adapterInjected` so it reaches the executor receipt and the live progress channel.
2204
- *
2205
- * A non-string or blank entry is a schema error here, not a silent skip: an ablation arm that
2206
- * quietly drops a typo'd extension is the same class of structural false-null this module exists to
2207
- * eliminate.
2208
- */
2209
- function derivePiExtensionArgs(profile, mcpRequested) {
2210
- const load = profile?.extensions?.pi?.load;
2211
- if (!Array.isArray(load)) return {
2212
- args: [],
2213
- entries: [],
2214
- adapterInjected: false
2215
- };
2216
- const declared = [];
2217
- for (const [index, entry] of load.entries()) {
2218
- if (typeof entry !== "string" || !entry.trim()) throw new ValidationError(`piExecutor: extensions.pi.load[${index}] must be a non-empty string (got ${entry === null ? "null" : typeof entry})`);
2219
- declared.push(entry.trim());
2220
- }
2221
- const adapterInjected = mcpRequested && !declared.some(entryNamesAdapter);
2222
- const resolvedSpecs = adapterInjected ? [...declared, PI_MCP_ADAPTER] : declared;
2223
- const npmRoot = piNpmRoot();
2224
- const entries = resolvedSpecs.map((spec) => resolvePiExtensionEntry(spec, npmRoot));
2225
- const args = ["--no-extensions"];
2226
- for (const entry of entries) args.push("--extension", entry);
2227
- return {
2228
- args,
2229
- entries,
2230
- adapterInjected
2231
- };
2232
- }
2233
- /** Directory-name-safe form of a run id; the `mkdtemp` suffix supplies the uniqueness. */
2234
- function runIdSlug(runId) {
2235
- const slug = runId.replace(/[^A-Za-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "");
2236
- return slug.length > 0 ? slug.slice(0, 60) : "worker";
2237
- }
2238
- /**
2239
- * Write this worker execution's MCP config to a directory only it owns, and hand back the removal.
2240
- *
2241
- * Every property of the old cwd-discovered mount — a cross-process lock, a merge under whatever the
2242
- * workspace already had, a verbatim byte restore, a symlink guard on `.pi` — existed because two
2243
- * workers sharing a `PiSeam` shared one file path. `mkdtemp` deletes the shared path, and with it
2244
- * the entire class of problem: the directory did not exist a moment ago, nothing else can name it,
2245
- * `cleanup()` removes it whole, and two siblings started in the same millisecond get two configs.
2246
- *
2247
- * Returns `null` when there are no usable servers, so the no-MCP path writes nothing at all.
2248
- */
2249
- function mountPiMcpConfig(mcpServers, options) {
2250
- const serverNames = Object.keys(mcpServers);
2251
- if (serverNames.length === 0) return null;
2252
- const slug = runIdSlug(options.runId);
2253
- const base = tmpdir();
2254
- const prefix = `agent-runtime-pi-mcp-${slug}-`;
2255
- let dir;
2256
- try {
2257
- dir = mkdtempSync(join(base, prefix));
2258
- } catch (err) {
2259
- throw new ConfigError(`piExecutor: cannot create a private MCP config directory under ${base}: ${err instanceof Error ? err.message : String(err)}`);
2260
- }
2261
- const configPath = join(dir, "mcp.json");
2262
- try {
2263
- writeFileSync(configPath, JSON.stringify({ mcpServers }, null, 2), {
2264
- flag: "wx",
2265
- mode: 384
2266
- });
2267
- } catch (err) {
2268
- rmSync(dir, {
2269
- recursive: true,
2270
- force: true
2271
- });
2272
- throw new ConfigError(`piExecutor: failed to write the MCP config at ${configPath}: ${err instanceof Error ? err.message : String(err)}`);
2273
- }
2274
- let cleaned = false;
2275
- return {
2276
- configPath,
2277
- serverNames,
2278
- cleanup: () => {
2279
- if (cleaned) return;
2280
- cleaned = true;
2281
- rmSync(dir, {
2282
- recursive: true,
2283
- force: true
2284
- });
2285
- }
2286
- };
2287
- }
2288
- /**
2289
- * The one call `piExecutor` makes before spawning pi: decide what pi must be told, fail loud if it
2290
- * cannot work, then mount.
2291
- *
2292
- * Order matters and is the point:
2293
- *
2294
- * 1. Canonicalize `profile.mcp` first, so the adapter gate keys off servers that would ACTUALLY be
2295
- * written — a block of `enabled:false` entries must not trip it.
2296
- * 2. If any server survives and `pi-mcp-adapter` is not installed, throw. No file has been written
2297
- * and pi has not been spawned.
2298
- * 3. Lower `extensions.pi.load` (which may itself throw on an unresolvable extension) — still before
2299
- * any file exists.
2300
- * 4. Only then create this execution's private config directory and emit `--mcp-config`.
2301
- *
2302
- * A profile with no usable MCP server is a pure no-op: no adapter check, no directory, no flags, no
2303
- * throw. `extensions.pi.load` on its own still produces flags, because reproducible ablation arms
2304
- * need them whether or not MCP is involved.
2305
- *
2306
- * The caller MUST call `mount.cleanup()` on every exit path — settle, throw, abort, timeout, and
2307
- * spawn failure — so the directory does not outlive the worker that owns it.
2308
- */
2309
- function preparePiMcp(profile, options) {
2310
- const mcpServers = buildPiMcpServers(profile?.mcp);
2311
- const requested = Object.keys(mcpServers);
2312
- if (requested.length > 0 && !piMcpAdapterAvailable()) throw new ConfigError(`piExecutor: cannot mount MCP servers for pi (${requested.join(", ")}): the ${PI_MCP_ADAPTER} extension is not installed. pi ships no MCP support of its own, so these servers would never connect and the worker would run tool-less. Looked in ${join(piNpmRoot(), "node_modules", PI_MCP_ADAPTER)} and in the \`packages\` list of ${join(piAgentDir(), "settings.json")}. Install it with \`pi install npm:${PI_MCP_ADAPTER}\`, or set ${PI_MCP_ADAPTER_ENV}=1 if it is vendored somewhere this check cannot see.`);
2313
- const extensions = derivePiExtensionArgs(profile, requested.length > 0);
2314
- if (requested.length === 0) return {
2315
- args: extensions.args,
2316
- mount: null,
2317
- receipt: void 0
2318
- };
2319
- const mount = mountPiMcpConfig(mcpServers, options);
2320
- if (!mount) return {
2321
- args: extensions.args,
2322
- mount: null,
2323
- receipt: void 0
2324
- };
2325
- return {
2326
- args: [
2327
- ...extensions.args,
2328
- PI_MCP_CONFIG_FLAG,
2329
- mount.configPath
2330
- ],
2331
- mount,
2332
- receipt: {
2333
- servers: mount.serverNames,
2334
- configPath: mount.configPath,
2335
- extensions: extensions.entries,
2336
- adapterInjected: extensions.adapterInjected
2337
- }
2338
- };
2339
- }
2340
- //#endregion
2341
1972
  //#region src/runtime/supervise/trace-source.ts
2342
1973
  /** Project a normalized tool step into the canonical agent-eval `ToolSpan`. When the step carries
2343
1974
  * real wall-clock (`startedAt`/`endedAt`) the span gets a true duration; otherwise it collapses to
2344
- * the single instant `at` (order + counts only — the historical behavior). */
1975
+ * the single instant `at` (order + counts only — the historical behavior). A step whose source
1976
+ * never saw the call finish (`statusCaptured: false`) gets NO `status` — `ToolSpan.status` is
1977
+ * optional precisely so an unobserved outcome can stay unstated rather than defaulting to 'ok'. */
2345
1978
  function toToolSpan(input, runId, seq, at) {
2346
1979
  const startedAt = typeof input.startedAt === "number" ? input.startedAt : at;
2347
1980
  const endedAt = typeof input.endedAt === "number" ? input.endedAt : startedAt;
1981
+ const status = input.statusCaptured === false ? void 0 : input.status ?? "ok";
2348
1982
  return {
2349
1983
  spanId: `${runId}-t${seq}`,
2350
1984
  runId,
@@ -2353,7 +1987,7 @@ function toToolSpan(input, runId, seq, at) {
2353
1987
  toolName: input.toolName,
2354
1988
  args: input.args,
2355
1989
  ...input.argsCaptured === false ? { argsCaptured: false } : {},
2356
- status: input.status ?? "ok",
1990
+ ...status === void 0 ? {} : { status },
2357
1991
  ...input.error !== void 0 ? { error: input.error } : {},
2358
1992
  startedAt,
2359
1993
  endedAt,
@@ -2542,653 +2176,6 @@ function sandboxSessionTraceSource(box, sessionId, opts = {}) {
2542
2176
  });
2543
2177
  }
2544
2178
  //#endregion
2545
- //#region src/runtime/supervise/pi-executor.ts
2546
- /**
2547
- *
2548
- * `piExecutor` — pi wrapped behind `Executor`, NOT forked.
2549
- *
2550
- * pi already implements everything a steerable worker needs, and implements it well: a queued
2551
- * steering channel delivered between turns, follow-ups, abort, compaction, session persistence
2552
- * with fork/branch, and an out-of-process line-delimited JSON protocol over stdin/stdout
2553
- * (`pi --mode rpc`). Reimplementing any of that here would mean owning a turn loop someone else
2554
- * already maintains. So this module is a thin protocol adapter, and every capability maps onto a
2555
- * verb pi already has:
2556
- *
2557
- * `execute` → `prompt`, draining pi's event stream until `agent_settled`
2558
- * `deliver` → `prompt` with `streamingBehavior` — pi owns the queue, we do not
2559
- * `teardown` → `abort`, then close stdin and reap the process
2560
- * `progress` → pi's `tool_execution_start`/`_end` + `turn_end` events, plus `get_state`'s
2561
- * `pendingMessageCount` mirrored locally so the read stays synchronous
2562
- * `traceSource` → the same tool events decoded into the shared `ToolSpan` currency
2563
- * `resultArtifact` → the terminal successful assistant turn
2564
- *
2565
- * It is registered through the DOCUMENTED extension point (`ExecutorRegistry.register('pi', …)`),
2566
- * so nothing in the resolver switches on it and a consumer can replace it wholesale.
2567
- *
2568
- * agent-runtime does NOT take a dependency on pi. The wire shapes (`RpcCommand`, `AgentEvent`)
2569
- * are read structurally off JSON lines, so a pi that adds commands stays compatible and a pi that
2570
- * is not installed simply fails loud at spawn instead of at import.
2571
- *
2572
- * Usage accounting: pi repeats the same assistant receipt in `message_end` and `turn_end`.
2573
- * Only `turn_end` is counted, once per model call. Pi reports cache traffic separately from fresh
2574
- * input, so all three input classes are folded into Runtime's two-field token total. Subscription
2575
- * usage whose dollar price is absent or zero stays explicitly unknown through the live usage
2576
- * stream and terminal artifact.
2577
- *
2578
- * ## What of the `AgentProfile` this executor honors, and what it does not
2579
- *
2580
- * This adapter is NOT a profile materializer. It reads a deliberately small part of
2581
- * `spec.profile`, and a caller who needs the rest must lower it before handing the profile over.
2582
- * Stated exhaustively so a silent drop is never a surprise:
2583
- *
2584
- * | Field | Status |
2585
- * | --- | --- |
2586
- * | `profile.name` | honored — trace `runId` label only, no behavioral effect |
2587
- * | `profile.prompt.systemPrompt` | honored — prepended to the task text (pi RPC takes no separate system-prompt channel) |
2588
- * | `profile.mcp` | honored — written to this execution's own file and passed as `--mcp-config` for `pi-mcp-adapter`; see `pi-mcp.ts` |
2589
- * | `profile.extensions.pi.load` | honored — lowered to `--no-extensions` + `--extension <abs>` |
2590
- * | `profile.prompt.instructions` | honored — appended to the system prompt, one per line |
2591
- * | `profile.model.default` | honored — overrides the seam's `model`; the seam is the fallback for profiles that select none |
2592
- * | `profile.model.reasoningEffort` | DROPPED — no `--thinking` flag is emitted, so pi's configured `defaultThinkingLevel` applies |
2593
- * | `profile.tools` | DROPPED — no `--no-tools` / allow-deny mapping; pi runs its full builtin tool set |
2594
- * | `profile.permissions` | DROPPED |
2595
- * | `profile.resources` (context / skills / commands / subagents / instructions) | DROPPED — nothing is written into the run cwd |
2596
- * | `profile.hooks` | DROPPED |
2597
- * | `profile.subagents`, `profile.connections`, `profile.modes`, `profile.confidential` | DROPPED |
2598
- * | `profile.resources.failOnError` | not consulted — the MCP path above is unconditionally fail-closed, which is the stricter reading |
2599
- * | every other `profile.extensions.<ns>` | DROPPED — only the `pi` namespace is read |
2600
- *
2601
- * Closing those gaps belongs in `@tangle-network/agent-profile-materialize`, whose plan this
2602
- * executor would then apply, rather than in a second mapping grown here.
2603
- *
2604
- * @experimental
2605
- */
2606
- /** The runtime name `piExecutor` registers under. */
2607
- const PI_RUNTIME = "pi";
2608
- /** Seam key the registry threads a `PiSeam` through (`ExecutorContext.seams['pi']`). */
2609
- const piSeamKey = "pi";
2610
- /** Build the `Executor` for one pi worker. Registered as runtime `'pi'`. */
2611
- const piExecutor = (spec, ctx) => {
2612
- const seam = {
2613
- ...readPiSeam(ctx),
2614
- ...spec.profile.model?.default ? { model: spec.profile.model.default } : {}
2615
- };
2616
- const traceEnv = workerTraceEnv(ctx);
2617
- const inbox = createInbox();
2618
- const activity = createActivityLog(seam.activityWindow ?? 12);
2619
- const runId = `pi-${spec.profile.name ?? "worker"}-${Date.now()}`;
2620
- const trace = createPushTraceSource({ runId });
2621
- const executionId = ctx.node?.nodeId ?? `pi-run-${randomUUID()}`;
2622
- const attemptId = ctx.node?.attemptId ?? newExecutionAttemptId(executionId);
2623
- const derived = [];
2624
- const controller = new AbortController();
2625
- const cascade = () => controller.abort();
2626
- if (ctx.signal.aborted) controller.abort();
2627
- else ctx.signal.addEventListener("abort", cascade, { once: true });
2628
- const state = {
2629
- turns: 0,
2630
- lastText: "",
2631
- note: "starting",
2632
- proc: void 0,
2633
- artifact: void 0
2634
- };
2635
- const executor = {
2636
- runtime: "pi",
2637
- deliver: (m) => inbox.deliver(m),
2638
- progress: () => ({
2639
- turns: state.turns,
2640
- pendingMessages: inbox.pending(),
2641
- recentActivity: activity.read(),
2642
- ...derived.length > 0 ? { derived: [...derived] } : {},
2643
- note: state.note
2644
- }),
2645
- traceSource: () => trace.source,
2646
- execute(task, signal) {
2647
- return streamPiSession({
2648
- task,
2649
- signal,
2650
- controller,
2651
- seam,
2652
- traceEnv,
2653
- spec,
2654
- runId,
2655
- inbox,
2656
- activity,
2657
- derived,
2658
- record: (step) => {
2659
- trace.record(step);
2660
- },
2661
- state
2662
- });
2663
- },
2664
- async teardown(grace) {
2665
- controller.abort();
2666
- const proc = state.proc;
2667
- if (!proc || proc.exitCode !== null || proc.killed) return { destroyed: true };
2668
- sendCommand(proc, { type: "abort" });
2669
- return killPi(proc, grace);
2670
- },
2671
- resultArtifact() {
2672
- if (!state.artifact) throw new ValidationError("piExecutor: resultArtifact() read before stream drained");
2673
- return state.artifact;
2674
- }
2675
- };
2676
- if (ctx.node === void 0) return executor;
2677
- return attestRuntimeOwnedExecutor(executor, {
2678
- effectiveProfile: spec.profile,
2679
- backend: "pi",
2680
- model: seam.model ? {
2681
- status: "known",
2682
- id: seam.model
2683
- } : {
2684
- status: "unknown",
2685
- reason: "pi selected its configured default model"
2686
- },
2687
- execution: {
2688
- kind: "run",
2689
- id: executionId
2690
- },
2691
- materializer: "pi-rpc-agent-profile",
2692
- plan: {
2693
- kind: "pi-rpc-session",
2694
- bin: seam.bin ?? "pi",
2695
- args: seam.args ?? [],
2696
- cwd: seam.cwd ?? null,
2697
- model: seam.model ?? null,
2698
- turnTimeoutMs: seam.turnTimeoutMs ?? null
2699
- }
2700
- }, {
2701
- attemptId,
2702
- binding: {
2703
- executionId,
2704
- bin: seam.bin ?? "pi",
2705
- cwd: seam.cwd ?? null,
2706
- model: seam.model ?? null
2707
- },
2708
- descriptor: {
2709
- kind: "pi-rpc-run",
2710
- transport: "process",
2711
- backend: "pi"
2712
- }
2713
- });
2714
- };
2715
- /**
2716
- * One pi RPC session, run to `agent_settled`. Every steer delivered while the turn is in flight is
2717
- * forwarded to pi IMMEDIATELY (pi queues and delivers it at its own turn boundary — the queue we
2718
- * deliberately do not reimplement), and any steer still unread when pi settles re-prompts it.
2719
- * `agent_end` is deliberately not terminal: Pi may auto-retry or compact after emitting it.
2720
- */
2721
- async function* streamPiSession(args) {
2722
- const { seam, inbox, activity, state } = args;
2723
- const started = Date.now();
2724
- const tokens = {
2725
- input: 0,
2726
- output: 0
2727
- };
2728
- let usd = 0;
2729
- let usdKnown = true;
2730
- let tokensKnown = true;
2731
- throwIfAborted(args.signal);
2732
- throwIfAborted(args.controller.signal);
2733
- const piMcp = preparePiMcp(args.spec.profile, {
2734
- ...seam.cwd !== void 0 ? { cwd: seam.cwd } : {},
2735
- runId: args.runId
2736
- });
2737
- if (piMcp.receipt) {
2738
- const injected = piMcp.receipt.adapterInjected ? ` (+${PI_MCP_ADAPTER} added)` : "";
2739
- activity.push({
2740
- at: Date.now(),
2741
- kind: "note",
2742
- label: "mcp",
2743
- detail: `${piMcp.receipt.servers.join(", ")}${injected}`
2744
- });
2745
- args.derived.push(`mcp: mounted ${piMcp.receipt.servers.join(", ")} via --mcp-config ${piMcp.receipt.configPath}`);
2746
- if (piMcp.receipt.adapterInjected) args.derived.push(`extensions: added ${PI_MCP_ADAPTER} to extensions.pi.load — the profile's own list would have run under --no-extensions and suppressed it, mounting zero servers`);
2747
- }
2748
- let proc;
2749
- try {
2750
- proc = spawnPi(seam, piMcp.args, args.traceEnv);
2751
- } catch (spawnFailure) {
2752
- piMcp.mount?.cleanup();
2753
- throw spawnFailure;
2754
- }
2755
- state.proc = proc;
2756
- state.note = "connected";
2757
- const events = [];
2758
- const pendingTools = /* @__PURE__ */ new Map();
2759
- const awaitingPromptResponses = /* @__PURE__ */ new Set();
2760
- let settled = false;
2761
- let acceptedPromptSinceSettlement = false;
2762
- let promptSequence = 0;
2763
- let lastAssistant;
2764
- let failure;
2765
- let abortDeadline;
2766
- let exited = false;
2767
- let exitCode;
2768
- let wake;
2769
- const notify = () => {
2770
- const w = wake;
2771
- wake = void 0;
2772
- w?.();
2773
- };
2774
- const stdoutLines = readJsonLines(proc, (value) => {
2775
- const ev = value;
2776
- events.push(ev);
2777
- notify();
2778
- });
2779
- proc.once("exit", (code) => {
2780
- exited = true;
2781
- exitCode = code;
2782
- notify();
2783
- });
2784
- proc.once("error", (e) => {
2785
- failure = new ValidationError(`piExecutor: pi failed to start: ${e.message}`);
2786
- notify();
2787
- });
2788
- const abortAll = () => {
2789
- if (abortDeadline !== void 0) return;
2790
- sendCommand(proc, { type: "abort" });
2791
- abortDeadline = Date.now() + PI_ABORT_RECEIPT_MS;
2792
- state.note = "aborting";
2793
- notify();
2794
- };
2795
- args.signal.addEventListener("abort", abortAll, { once: true });
2796
- args.controller.signal.addEventListener("abort", abortAll, { once: true });
2797
- const system = [args.spec.profile.prompt?.systemPrompt, ...args.spec.profile.prompt?.instructions ?? []].filter((line) => typeof line === "string" && line.trim().length > 0).join("\n");
2798
- const opening = system ? `${system}\n\n${taskText(args.task)}` : taskText(args.task);
2799
- const deadline = seam.turnTimeoutMs ? Date.now() + seam.turnTimeoutMs : void 0;
2800
- const sendPrompt = (message, streamingBehavior) => {
2801
- const id = `agent-runtime-prompt-${++promptSequence}`;
2802
- awaitingPromptResponses.add(id);
2803
- sendCommand(proc, {
2804
- id,
2805
- type: "prompt",
2806
- message,
2807
- ...streamingBehavior ? { streamingBehavior } : {}
2808
- });
2809
- };
2810
- try {
2811
- if (args.signal.aborted || args.controller.signal.aborted) throw abortError();
2812
- sendPrompt(opening);
2813
- state.note = "turn 0";
2814
- for (;;) {
2815
- if (!settled && abortDeadline === void 0) forwardPending(inbox, activity, sendPrompt);
2816
- while (events.length > 0) {
2817
- const ev = events.shift();
2818
- if (ev.type === "response" && ev.command === "prompt" && typeof ev.id === "string" && awaitingPromptResponses.delete(ev.id)) if (ev.success === true) acceptedPromptSinceSettlement = true;
2819
- else failure = new ValidationError(`piExecutor: Pi rejected prompt: ${ev.error ?? "unknown RPC error"}`);
2820
- if (ev.type === "agent_start") settled = false;
2821
- if (ev.type === "agent_settled") {
2822
- settled = true;
2823
- acceptedPromptSinceSettlement = false;
2824
- }
2825
- const projected = projectPiEvent(ev, args, tokens, pendingTools);
2826
- if (projected.assistant) lastAssistant = projected.assistant;
2827
- if (projected.tokensUnknown) tokensKnown = false;
2828
- for (const usage of projected.events) {
2829
- if (usage.kind === "cost") {
2830
- usd += usage.usd;
2831
- if (usage.usdKnown === false) usdKnown = false;
2832
- }
2833
- yield usage;
2834
- }
2835
- }
2836
- if (failure) throw failure;
2837
- if (abortDeadline !== void 0 && (settled || exited)) throw abortError();
2838
- if (abortDeadline === void 0 && exited && (!settled || awaitingPromptResponses.size > 0 || acceptedPromptSinceSettlement)) throw new ValidationError(`piExecutor: pi exited before agent_settled (code ${exitCode ?? "unknown"})`);
2839
- if (abortDeadline === void 0 && deadline !== void 0 && Date.now() > deadline) throw new ValidationError("piExecutor: turn exceeded turnTimeoutMs");
2840
- if (abortDeadline !== void 0 && Date.now() > abortDeadline) {
2841
- const error = abortError();
2842
- error.message = `piExecutor: abort did not settle within ${PI_ABORT_RECEIPT_MS}ms`;
2843
- throw error;
2844
- }
2845
- if (settled && awaitingPromptResponses.size === 0 && !acceptedPromptSinceSettlement) {
2846
- const pending = inbox.drain();
2847
- if (pending.length === 0) break;
2848
- settled = false;
2849
- lastAssistant = void 0;
2850
- sendPrompt(inbox.fold(pending));
2851
- state.note = `turn ${state.turns}`;
2852
- continue;
2853
- }
2854
- await new Promise((resolve) => {
2855
- wake = resolve;
2856
- const t = setTimeout(() => {
2857
- if (wake === resolve) {
2858
- wake = void 0;
2859
- resolve();
2860
- }
2861
- }, 50);
2862
- if (typeof t.unref === "function") t.unref();
2863
- });
2864
- }
2865
- } finally {
2866
- stdoutLines();
2867
- state.note = "settled";
2868
- args.signal.removeEventListener("abort", abortAll);
2869
- args.controller.signal.removeEventListener("abort", abortAll);
2870
- await killPi(proc, 2e3).catch(() => ({ destroyed: false }));
2871
- piMcp.mount?.cleanup();
2872
- }
2873
- if (args.signal.aborted || args.controller.signal.aborted) throw abortError();
2874
- if (!lastAssistant) throw new ValidationError("piExecutor: agent_settled without an assistant turn");
2875
- if (lastAssistant.stopReason === "aborted") throw abortError();
2876
- if (lastAssistant.stopReason === "error") throw new ValidationError(`piExecutor: Pi assistant failed: ${lastAssistant.errorMessage ?? "unknown provider error"}`);
2877
- state.lastText = lastAssistant.text;
2878
- const spent = {
2879
- iterations: state.turns,
2880
- tokens,
2881
- ...tokensKnown ? {} : { tokensKnown: false },
2882
- usd,
2883
- ...usdKnown ? {} : { usdKnown: false },
2884
- ms: Date.now() - started
2885
- };
2886
- const out = {
2887
- content: state.lastText,
2888
- turns: state.turns,
2889
- ...piMcp.receipt ? { mcp: piMcp.receipt } : {}
2890
- };
2891
- state.artifact = {
2892
- outRef: `pi:${hash(state.lastText)}`,
2893
- out,
2894
- spent
2895
- };
2896
- }
2897
- /** Route messages through pi's state-safe prompt command; pi owns the queue and idle transition. */
2898
- function forwardPending(inbox, activity, sendPrompt) {
2899
- const pending = inbox.drain();
2900
- for (const m of pending) {
2901
- sendPrompt(renderOne(m), m.interrupt ? "steer" : "followUp");
2902
- activity.push({
2903
- at: Date.now(),
2904
- kind: "note",
2905
- label: m.interrupt ? "steer" : "follow-up",
2906
- detail: m.text.length > 80 ? `${m.text.slice(0, 77)}...` : m.text
2907
- });
2908
- }
2909
- }
2910
- function renderOne(m) {
2911
- if (m.kind === "answer") return `Answer to your question${m.questionId ? ` (${m.questionId})` : ""}: ${m.text}`;
2912
- return `New instruction from your supervisor: ${m.text}`;
2913
- }
2914
- /** Project one pi event onto usage events, updating turn count / activity / trace as a side
2915
- * effect. pi's tool events are the live trace; its `turn_end` bumps the turn counter. */
2916
- function projectPiEvent(ev, args, tokens, pendingTools) {
2917
- const out = [];
2918
- const at = Date.now();
2919
- if (ev.type === "tool_execution_start" && typeof ev.toolName === "string") {
2920
- args.activity.push({
2921
- at,
2922
- kind: "tool",
2923
- label: ev.toolName,
2924
- detail: describeArgs$1(ev.args)
2925
- });
2926
- if (typeof ev.toolCallId === "string") pendingTools.set(ev.toolCallId, {
2927
- args: ev.args ?? {},
2928
- startedAt: at
2929
- });
2930
- return { events: out };
2931
- }
2932
- if (ev.type === "tool_execution_end" && typeof ev.toolName === "string") {
2933
- const status = ev.isError === true ? "error" : "ok";
2934
- const callId = typeof ev.toolCallId === "string" ? ev.toolCallId : void 0;
2935
- const started = callId ? pendingTools.get(callId) : void 0;
2936
- if (callId) pendingTools.delete(callId);
2937
- const error = status === "error" ? describeToolError(ev.result) : void 0;
2938
- args.activity.push({
2939
- at,
2940
- kind: "tool",
2941
- label: ev.toolName,
2942
- status
2943
- });
2944
- args.record({
2945
- toolName: ev.toolName,
2946
- args: started?.args ?? {},
2947
- ...started ? {} : { argsCaptured: false },
2948
- status,
2949
- ...ev.result !== void 0 ? { result: ev.result } : {},
2950
- ...error !== void 0 ? { error } : {},
2951
- ...callId ? { callId } : {},
2952
- ...started ? { startedAt: started.startedAt } : {},
2953
- endedAt: at
2954
- });
2955
- return { events: out };
2956
- }
2957
- if (ev.type === "message_end") return { events: out };
2958
- if (ev.type === "turn_end") {
2959
- const usage = readUsage(ev.message);
2960
- if (usage && (usage.input || usage.output)) {
2961
- tokens.input += usage.input;
2962
- tokens.output += usage.output;
2963
- out.push({
2964
- kind: "tokens",
2965
- input: usage.input,
2966
- output: usage.output
2967
- });
2968
- }
2969
- out.push(usage?.usd !== void 0 ? {
2970
- kind: "cost",
2971
- usd: usage.usd
2972
- } : {
2973
- kind: "cost",
2974
- usd: 0,
2975
- usdKnown: false
2976
- });
2977
- args.state.turns += 1;
2978
- args.activity.push({
2979
- at,
2980
- kind: "turn",
2981
- label: `turn ${args.state.turns}`
2982
- });
2983
- out.push({ kind: "iteration" });
2984
- const assistant = readAssistantOutcome(ev.message);
2985
- const tokensUnknown = !usage || usage.tokensKnown === false;
2986
- return {
2987
- events: out,
2988
- ...assistant ? { assistant } : {},
2989
- ...tokensUnknown ? { tokensUnknown: true } : {}
2990
- };
2991
- }
2992
- return { events: out };
2993
- }
2994
- /** Pi's fresh input excludes cache reads and writes. Runtime's input channel includes all model
2995
- * input, so combine them once at the assistant receipt. A missing or zero price is unknown because
2996
- * subscription-backed providers report zero even when compute was not free. */
2997
- function readUsage(message) {
2998
- if (!message || typeof message !== "object") return void 0;
2999
- const usage = message.usage;
3000
- if (!usage || typeof usage !== "object") return void 0;
3001
- const u = usage;
3002
- const promptTokens = num(u.prompt_tokens);
3003
- const freshInput = num(u.input) ?? num(u.inputTokens);
3004
- const outputRaw = num(u.output) ?? num(u.outputTokens) ?? num(u.completion_tokens);
3005
- const input = promptTokens ?? (freshInput ?? 0) + (num(u.cacheRead) ?? num(u.cache_read_input_tokens) ?? num(u.cacheReadInputTokens) ?? 0) + (num(u.cacheWrite) ?? num(u.cache_creation_input_tokens) ?? num(u.cacheCreationInputTokens) ?? 0);
3006
- const output = outputRaw ?? 0;
3007
- const costRaw = u.cost;
3008
- const reportedUsd = num(costRaw) ?? (costRaw && typeof costRaw === "object" ? num(costRaw.total) ?? num(costRaw.totalCost) : void 0);
3009
- return {
3010
- input,
3011
- output,
3012
- ...reportedUsd !== void 0 && reportedUsd > 0 ? { usd: reportedUsd } : {},
3013
- tokensKnown: promptTokens !== void 0 || freshInput !== void 0 || outputRaw !== void 0
3014
- };
3015
- }
3016
- function readAssistantOutcome(message) {
3017
- if (!message || typeof message !== "object") return void 0;
3018
- const value = message;
3019
- if (value.role !== "assistant") return void 0;
3020
- return {
3021
- text: readText(message) ?? "",
3022
- ...typeof value.stopReason === "string" ? { stopReason: value.stopReason } : {},
3023
- ...typeof value.errorMessage === "string" ? { errorMessage: value.errorMessage } : {}
3024
- };
3025
- }
3026
- function readText(message) {
3027
- if (!message || typeof message !== "object") return void 0;
3028
- const content = message.content;
3029
- if (typeof content === "string" && content.length > 0) return content;
3030
- if (!Array.isArray(content)) return void 0;
3031
- const text = content.map((block) => block && typeof block === "object" && typeof block.text === "string" ? block.text ?? "" : "").join("");
3032
- return text.length > 0 ? text : void 0;
3033
- }
3034
- function num(v) {
3035
- return typeof v === "number" && Number.isFinite(v) && v >= 0 ? v : void 0;
3036
- }
3037
- function describeArgs$1(argsValue) {
3038
- if (!argsValue || typeof argsValue !== "object") return void 0;
3039
- const a = argsValue;
3040
- for (const key of [
3041
- "filePath",
3042
- "file_path",
3043
- "path",
3044
- "file",
3045
- "command",
3046
- "cmd",
3047
- "pattern"
3048
- ]) {
3049
- const v = a[key];
3050
- if (typeof v === "string" && v.length > 0) return v.length > 120 ? `${v.slice(0, 117)}...` : v;
3051
- }
3052
- }
3053
- function describeToolError(result) {
3054
- if (typeof result === "string" && result.length > 0) return result;
3055
- if (result && typeof result === "object") {
3056
- const value = result;
3057
- const direct = value.error ?? value.message;
3058
- if (typeof direct === "string" && direct.length > 0) return direct;
3059
- if (Array.isArray(value.content)) {
3060
- const text = value.content.map((block) => block && typeof block === "object" && typeof block.text === "string" ? block.text : "").filter(Boolean).join("\n");
3061
- if (text.length > 0) return text;
3062
- }
3063
- }
3064
- }
3065
- function taskText(task) {
3066
- if (typeof task === "string") return task;
3067
- try {
3068
- return JSON.stringify(task) ?? String(task);
3069
- } catch {
3070
- return String(task);
3071
- }
3072
- }
3073
- /**
3074
- * Launch `pi --mode rpc`, plus provider/model flags derived from the seam's `provider/model` and
3075
- * the profile-derived extension flags (`--no-extensions` / `--extension`). Seam args go LAST so an
3076
- * operator's explicit flag wins over a derived one under pi's last-flag-wins parsing; RPC mode has
3077
- * no positional prompt, so nothing here has to precede an argument.
3078
- *
3079
- * `traceEnv` is the inherited `TRACE_ID` / `PARENT_SPAN_ID` pair (empty when the run records no
3080
- * spans). It sits ABOVE the supervisor's ambient `process.env` — a supervisor that was itself
3081
- * launched as someone's worker holds ids describing ITS place in an outer trace, which are the
3082
- * wrong parent for this child — and BELOW `seam.env`, so an operator who sets either id explicitly
3083
- * still wins. See `worker-trace.ts` for the full precedence rule.
3084
- */
3085
- function spawnPi(seam, profileArgs = [], traceEnv = {}) {
3086
- const bin = seam.bin ?? "pi";
3087
- const argv = ["--mode", "rpc"];
3088
- if (seam.model) {
3089
- const slash = seam.model.indexOf("/");
3090
- if (slash > 0) argv.push("--provider", seam.model.slice(0, slash), "--model", seam.model.slice(slash + 1));
3091
- else argv.push("--model", seam.model);
3092
- }
3093
- argv.push(...profileArgs);
3094
- if (seam.args) argv.push(...seam.args);
3095
- return spawn(bin, argv, {
3096
- ...seam.cwd ? { cwd: seam.cwd } : {},
3097
- env: {
3098
- ...process.env,
3099
- ...traceEnv,
3100
- ...seam.env ?? {}
3101
- },
3102
- stdio: [
3103
- "pipe",
3104
- "pipe",
3105
- "pipe"
3106
- ]
3107
- });
3108
- }
3109
- /** Write one `RpcCommand` as a JSON line on pi's stdin. Best-effort: a dead process is not an
3110
- * error here (the stream loop already observes the exit and fails loud there). */
3111
- function sendCommand(proc, command) {
3112
- try {
3113
- proc.stdin?.write(`${JSON.stringify(command)}\n`);
3114
- } catch {}
3115
- }
3116
- /** Read newline-delimited JSON off pi's stdout; returns an unsubscribe. Malformed lines are
3117
- * ignored (pi writes only JSONL on stdout; anything else is noise from a wrapper script). */
3118
- function readJsonLines(proc, onValue) {
3119
- let buffer = "";
3120
- const onData = (chunk) => {
3121
- buffer += typeof chunk === "string" ? chunk : chunk.toString("utf8");
3122
- for (;;) {
3123
- const nl = buffer.indexOf("\n");
3124
- if (nl < 0) break;
3125
- const line = buffer.slice(0, nl).trim();
3126
- buffer = buffer.slice(nl + 1);
3127
- if (!line) continue;
3128
- try {
3129
- onValue(JSON.parse(line));
3130
- } catch {}
3131
- }
3132
- };
3133
- proc.stdout?.on("data", onData);
3134
- return () => {
3135
- proc.stdout?.off("data", onData);
3136
- };
3137
- }
3138
- /** The grace window pi gets to act on the `abort` it was just sent, before a signal is used.
3139
- * Without it the SIGTERM races the command down the pipe and pi never sees the abort at all —
3140
- * which defeats the reason to wrap pi rather than kill it (a clean abort finalizes its session). */
3141
- const PI_ABORT_GRACE_MS = 500;
3142
- /** Maximum wait for Pi's aborted receipt and `agent_settled` before forced teardown. */
3143
- const PI_ABORT_RECEIPT_MS = 2e3;
3144
- async function killPi(proc, grace) {
3145
- if (proc.exitCode !== null || proc.killed) return { destroyed: true };
3146
- if (grace === "brutalKill") {
3147
- endStdin(proc);
3148
- proc.kill("SIGKILL");
3149
- return { destroyed: true };
3150
- }
3151
- const total = grace === "infinity" ? 1e4 : Math.max(0, grace);
3152
- if (await waitForExit(proc, Math.min(PI_ABORT_GRACE_MS, total))) return { destroyed: true };
3153
- endStdin(proc);
3154
- proc.kill("SIGTERM");
3155
- if (await waitForExit(proc, Math.max(0, total - PI_ABORT_GRACE_MS))) return { destroyed: true };
3156
- proc.kill("SIGKILL");
3157
- return { destroyed: true };
3158
- }
3159
- function endStdin(proc) {
3160
- try {
3161
- proc.stdin?.end();
3162
- } catch {}
3163
- }
3164
- function waitForExit(proc, ms) {
3165
- if (proc.exitCode !== null) return Promise.resolve(true);
3166
- return new Promise((resolve) => {
3167
- const timer = setTimeout(() => resolve(false), ms);
3168
- if (typeof timer.unref === "function") timer.unref();
3169
- proc.once("exit", () => {
3170
- clearTimeout(timer);
3171
- resolve(true);
3172
- });
3173
- });
3174
- }
3175
- function readPiSeam(ctx) {
3176
- const seam = ctx.seams["pi"];
3177
- if (seam === void 0) return {};
3178
- if (!seam || typeof seam !== "object") throw new ValidationError(`piExecutor: seams['pi'] must be a PiSeam object`);
3179
- return seam;
3180
- }
3181
- /** FNV-1a over the terminal text — the same non-cryptographic content address the other leaf
3182
- * executors mint for their `outRef` dedup hint (the journal re-derives the canonical one). */
3183
- function hash(value) {
3184
- let h = 2166136261;
3185
- for (let i = 0; i < value.length; i += 1) {
3186
- h ^= value.charCodeAt(i);
3187
- h = Math.imul(h, 16777619);
3188
- }
3189
- return (h >>> 0).toString(16).padStart(8, "0");
3190
- }
3191
- //#endregion
3192
2179
  //#region src/runtime/supervise/sandbox-session.ts
3193
2180
  /** Ceiling on continuation turns. Turn 0 is the task; every later turn is a folded steer, so
3194
2181
  * this bounds how many times a supervisor may redirect ONE worker before it must respawn. */
@@ -3226,7 +2213,7 @@ function createSteerableSandboxSession(args) {
3226
2213
  kind: "tool",
3227
2214
  label: step.toolName,
3228
2215
  ...step.status ? { status: step.status } : {},
3229
- ...describeArgs(step.args) ? { detail: describeArgs(step.args) } : {}
2216
+ ...describeToolArgs(step.args) ? { detail: describeToolArgs(step.args) } : {}
3230
2217
  };
3231
2218
  activity.push(note);
3232
2219
  }
@@ -3392,25 +2379,6 @@ function readFinalText(event) {
3392
2379
  const alt = data.text;
3393
2380
  return typeof alt === "string" && alt.length > 0 ? alt : void 0;
3394
2381
  }
3395
- /** A short, already-truncated descriptor of a tool call's target — the file/command a driver
3396
- * needs to tell "editing the right module" from "re-reading the same file for the fifth time",
3397
- * without pulling the worker's transcript across the wire. */
3398
- function describeArgs(argsValue) {
3399
- if (!argsValue || typeof argsValue !== "object") return void 0;
3400
- const a = argsValue;
3401
- for (const key of [
3402
- "filePath",
3403
- "file_path",
3404
- "path",
3405
- "file",
3406
- "command",
3407
- "cmd",
3408
- "pattern"
3409
- ]) {
3410
- const v = a[key];
3411
- if (typeof v === "string" && v.length > 0) return v.length > 120 ? `${v.slice(0, 117)}...` : v;
3412
- }
3413
- }
3414
2382
  /** Prompt options the composed loop context already carries (agent profile / cwd), forwarded so
3415
2383
  * a steerable worker runs with the same box configuration as the single-shot path. */
3416
2384
  function readPromptOptions(loopCtx) {
@@ -4374,11 +3342,25 @@ function killWithGrace(proc, grace) {
4374
3342
  */
4375
3343
  /** Resolve the bridge wire model for this spawn. Per-create matrix settings win, then the
4376
3344
  * canonical profile's harness/model preferences, then the bridge's configured fallback. */
3345
+ /**
3346
+ * A profile's selected model with its provider attached, the way a harness addresses one.
3347
+ *
3348
+ * Returns undefined when the profile selects no model, and the id unchanged when it is already
3349
+ * qualified or when the profile names no provider — there, the harness's own provider resolution
3350
+ * IS the caller's declared intent rather than a gap to fill.
3351
+ */
3352
+ function qualifyProviderModel(model) {
3353
+ const id = model?.default;
3354
+ if (!id) return void 0;
3355
+ const provider = model?.provider;
3356
+ if (!provider || id.includes("/")) return id;
3357
+ return `${provider}/${id}`;
3358
+ }
4377
3359
  function bridgeCellModel(seamModel, ctx, profile) {
4378
3360
  const backend = ctx.seams.createOptions?.backend;
4379
- const profileHarness = profile.harness === "cli-base" ? void 0 : profile.harness;
3361
+ const profileHarness = agentHarness(profile.harness);
4380
3362
  const harness = backend?.type ?? profileHarness;
4381
- const model = backend?.model?.model ?? profile.model?.default;
3363
+ const model = backend?.model?.model ?? qualifyProviderModel(profile.model);
4382
3364
  if (!harness && !model) return seamModel;
4383
3365
  if (!harness) return model;
4384
3366
  if (model) return model.startsWith(`${harness}/`) ? model : `${harness}/${model}`;
@@ -4404,9 +3386,44 @@ const bridgeExecutor = (spec, ctx) => {
4404
3386
  const inbox = createInbox();
4405
3387
  let artifact;
4406
3388
  const activeRuns = /* @__PURE__ */ new Map();
3389
+ const observation = {
3390
+ turns: 0,
3391
+ note: "starting",
3392
+ activity: createActivityLog(seam.activityWindow ?? 12),
3393
+ derived: [],
3394
+ refreshedAt: 0,
3395
+ refreshing: false,
3396
+ closed: false
3397
+ };
3398
+ if (base.agentProfile !== void 0) observation.derived.push("profile: merged the bridge seam's agentProfile over the spawn profile before materialization");
3399
+ if (seam.model !== base.model) observation.derived.push(`model: resolved the bridge wire model to ${seam.model} (seam default ${base.model ?? "none"})`);
3400
+ const trace = createPushTraceSource({ runId: sessionId });
4407
3401
  return attestRuntimeOwnedExecutor({
4408
3402
  runtime: "cli",
4409
3403
  deliver: (m) => inbox.deliver(m),
3404
+ /**
3405
+ * The LIVE read of this worker, answered entirely from local mirrors so it is synchronous
3406
+ * and cannot block or fail the run. The bridge's own run state is refreshed OUT OF BAND —
3407
+ * a read schedules a fetch whose answer lands for the NEXT read — because the executor
3408
+ * cannot know from its own stream whether a silent run is thinking, detached mid-reconnect,
3409
+ * or already cancelled server-side, and that is exactly the distinction a supervisor's
3410
+ * respawn decision turns on.
3411
+ */
3412
+ progress: () => {
3413
+ try {
3414
+ scheduleBridgeRunStateRefresh(seam, activeRuns, observation);
3415
+ return {
3416
+ turns: observation.turns,
3417
+ pendingMessages: inbox.pending(),
3418
+ recentActivity: observation.activity.read(),
3419
+ ...observation.derived.length > 0 ? { derived: [...observation.derived] } : {},
3420
+ note: bridgeProgressNote(observation, liveBridgeRunId(activeRuns))
3421
+ };
3422
+ } catch {
3423
+ return;
3424
+ }
3425
+ },
3426
+ traceSource: () => trace.source,
4410
3427
  execute(task, signal) {
4411
3428
  return streamBridgeSession({
4412
3429
  task,
@@ -4418,6 +3435,10 @@ const bridgeExecutor = (spec, ctx) => {
4418
3435
  inbox,
4419
3436
  controller,
4420
3437
  activeRuns,
3438
+ observation,
3439
+ record: (step) => {
3440
+ trace.record(step);
3441
+ },
4421
3442
  onArtifact: (a) => {
4422
3443
  artifact = a;
4423
3444
  }
@@ -4471,6 +3492,103 @@ const bridgeExecutor = (spec, ctx) => {
4471
3492
  }
4472
3493
  });
4473
3494
  };
3495
+ /** Minimum spacing between out-of-band run-state reads. A driver polling `observe_agent` in a
3496
+ * tight loop must not turn an observability read into load on the bridge. */
3497
+ const BRIDGE_RUN_STATE_REFRESH_MS = 1e3;
3498
+ /** Ceiling on one run-state read. A bridge that hangs must not pin `refreshing` forever and
3499
+ * silently stop every later refresh. */
3500
+ const BRIDGE_RUN_STATE_TIMEOUT_MS = 2e3;
3501
+ /** One human-readable line: the local turn phase, plus the bridge's own run state when a snapshot
3502
+ * has been read for a run that is still live. The snapshot's AGE is stated because a driver
3503
+ * reading "running" must be able to tell a fresh read from a 40-second-old one; and once this
3504
+ * executor holds terminal proof the line is DROPPED rather than left repeating a last-known
3505
+ * "running" that stopped being true the moment the run ended. */
3506
+ function bridgeProgressNote(observation, liveRunId) {
3507
+ const run = observation.runState;
3508
+ if (!run || run.runId !== liveRunId) return observation.note;
3509
+ const age = Math.max(0, Date.now() - run.at);
3510
+ return `${observation.note} · bridge run ${run.state} (status ${run.status}, seq ${run.lastSeq}, read ${age}ms ago)`;
3511
+ }
3512
+ /** The run this executor still believes live, if any — the one worth reporting on and the only one
3513
+ * worth asking the bridge about. */
3514
+ function liveBridgeRunId(activeRuns) {
3515
+ for (const run of activeRuns.values()) if (!run.terminal) return run.id;
3516
+ }
3517
+ /**
3518
+ * Start a NON-BLOCKING read of the bridge's run state; the answer lands on `observation` for a
3519
+ * later `progress()` call. Nothing here is awaited by the caller and no failure escapes: a bridge
3520
+ * that is down, slow, or has forgotten the run leaves the previous snapshot in place, and
3521
+ * `progress()` still returns the local mirror.
3522
+ */
3523
+ function scheduleBridgeRunStateRefresh(seam, activeRuns, observation) {
3524
+ if (observation.closed) return;
3525
+ if (observation.refreshing) return;
3526
+ const now = Date.now();
3527
+ if (now - observation.refreshedAt < BRIDGE_RUN_STATE_REFRESH_MS) return;
3528
+ const runId = liveBridgeRunId(activeRuns);
3529
+ if (runId === void 0) return;
3530
+ observation.refreshedAt = now;
3531
+ observation.refreshing = true;
3532
+ (async () => {
3533
+ try {
3534
+ const snapshot = await bridgeRunStateGet(seam, runId);
3535
+ if (snapshot) {
3536
+ observation.runState = snapshot;
3537
+ if (snapshot.terminal) observation.closed = true;
3538
+ }
3539
+ } catch {} finally {
3540
+ observation.refreshing = false;
3541
+ }
3542
+ })();
3543
+ }
3544
+ /** `GET /v1/runs/:id` — the bridge's durable-run registry read. Resolves `undefined` for any
3545
+ * non-200 or non-conforming body rather than throwing: this is only ever an observability read. */
3546
+ function bridgeRunStateGet(seam, runId) {
3547
+ const target = new URL(`${seam.bridgeUrl.replace(/\/$/, "")}/v1/runs/${encodeURIComponent(runId)}`);
3548
+ target.searchParams.set("wait_ms", "0");
3549
+ const requestFn = target.protocol === "https:" ? request$1 : request;
3550
+ return new Promise((resolve, reject) => {
3551
+ const req = requestFn(target, {
3552
+ method: "GET",
3553
+ headers: { authorization: `Bearer ${seam.bridgeBearer}` },
3554
+ timeout: BRIDGE_RUN_STATE_TIMEOUT_MS
3555
+ }, (res) => {
3556
+ (async () => {
3557
+ const chunks = [];
3558
+ for await (const chunk of res) chunks.push(Buffer.from(chunk));
3559
+ if (res.statusCode !== 200) {
3560
+ resolve(void 0);
3561
+ return;
3562
+ }
3563
+ resolve(readBridgeRunState(runId, Buffer.concat(chunks).toString("utf8")));
3564
+ })().catch(reject);
3565
+ });
3566
+ req.on("timeout", () => req.destroy(/* @__PURE__ */ new Error("bridgeExecutor: run state read timed out")));
3567
+ req.on("error", reject);
3568
+ req.end();
3569
+ });
3570
+ }
3571
+ /** Decode one run snapshot. A body that does not carry the fields we report is `undefined`, never
3572
+ * a partially-invented snapshot. */
3573
+ function readBridgeRunState(runId, body) {
3574
+ let parsed;
3575
+ try {
3576
+ parsed = JSON.parse(body);
3577
+ } catch {
3578
+ return;
3579
+ }
3580
+ if (parsed.id !== runId) return void 0;
3581
+ if (typeof parsed.status !== "string" || typeof parsed.state !== "string") return void 0;
3582
+ if (typeof parsed.terminal !== "boolean" || typeof parsed.lastSeq !== "number") return void 0;
3583
+ return {
3584
+ runId,
3585
+ status: parsed.status,
3586
+ state: parsed.state,
3587
+ terminal: parsed.terminal,
3588
+ lastSeq: parsed.lastSeq,
3589
+ at: Date.now()
3590
+ };
3591
+ }
4474
3592
  /**
4475
3593
  * One resumable cli-bridge session, run as a streamed turn loop. Turn 0 sends the
4476
3594
  * task; each subsequent turn fires ONLY when the inbox has a steer/answer to fold —
@@ -4479,7 +3597,7 @@ const bridgeExecutor = (spec, ctx) => {
4479
3597
  * full streamed harness session call rather than a single chat round.
4480
3598
  */
4481
3599
  async function* streamBridgeSession(args) {
4482
- const { seam, inbox } = args;
3600
+ const { seam, inbox, observation } = args;
4483
3601
  const started = Date.now();
4484
3602
  const external = mergeAbortSignals(args.signal, args.controller.signal);
4485
3603
  const tokens = zeroTokenUsage();
@@ -4495,8 +3613,15 @@ async function* streamBridgeSession(args) {
4495
3613
  if (pending.length) {
4496
3614
  const folded = inbox.fold(pending);
4497
3615
  nextPrompt = t === 0 && nextPrompt ? `${nextPrompt}\n\n${folded}` : folded;
3616
+ for (const m of pending) observation.activity.push({
3617
+ at: Date.now(),
3618
+ kind: "note",
3619
+ label: m.interrupt ? "steer" : "follow-up",
3620
+ detail: m.text.length > 80 ? `${m.text.slice(0, 77)}...` : m.text
3621
+ });
4498
3622
  }
4499
3623
  if (nextPrompt === void 0) break;
3624
+ observation.note = `turn ${t}`;
4500
3625
  const messages = [];
4501
3626
  messages.push({
4502
3627
  role: "user",
@@ -4546,7 +3671,16 @@ async function* streamBridgeSession(args) {
4546
3671
  run: activeRun
4547
3672
  })) {
4548
3673
  if (chunk.content) turnText += chunk.content;
4549
- if (chunk.toolCall) toolCalls.push(chunk.toolCall);
3674
+ for (const step of chunk.toolCalls ?? []) {
3675
+ toolCalls.push(step.toolName);
3676
+ observation.activity.push({
3677
+ at: Date.now(),
3678
+ kind: "tool",
3679
+ label: step.toolName,
3680
+ ...describeToolArgs(step.args) ? { detail: describeToolArgs(step.args) } : {}
3681
+ });
3682
+ args.record(step);
3683
+ }
4550
3684
  if (chunk.usage) {
4551
3685
  turnTokensKnown = true;
4552
3686
  tokens.input += chunk.usage.input;
@@ -4581,13 +3715,23 @@ async function* streamBridgeSession(args) {
4581
3715
  }
4582
3716
  if (interruptSig.aborted && !args.signal.aborted && !args.controller.signal.aborted) interrupted = true;
4583
3717
  turns += 1;
3718
+ observation.turns = turns;
3719
+ observation.activity.push({
3720
+ at: Date.now(),
3721
+ kind: "turn",
3722
+ label: `turn ${turns}`
3723
+ });
4584
3724
  if (!turnTokensKnown) tokensKnown = false;
4585
3725
  if (!turnUsdKnown) usdKnown = false;
4586
3726
  yield { kind: "iteration" };
4587
3727
  if (!interrupted && turnText) lastText = turnText;
4588
- if (interrupted) continue;
3728
+ if (interrupted) {
3729
+ observation.note = `turn ${turns} interrupted, resuming`;
3730
+ continue;
3731
+ }
4589
3732
  if (inbox.pending() === 0) break;
4590
3733
  }
3734
+ observation.note = "settled";
4591
3735
  const spent = {
4592
3736
  iterations: turns,
4593
3737
  tokens,
@@ -4833,6 +3977,50 @@ function errorMessage$1(error) {
4833
3977
  return error instanceof Error ? error.message : String(error);
4834
3978
  }
4835
3979
  /**
3980
+ * Decode the OpenAI-shaped `tool_calls` of one delta into the shared `ToolStepInput` currency,
3981
+ * through the SAME `decodeOpenAiPart` adapter the sandbox/parts trace source uses — the wire shape
3982
+ * cli-bridge emits (`{index, id, type:'function', function:{name, arguments}}`) is exactly the one
3983
+ * that decoder owns, so there is no second mapping to drift.
3984
+ *
3985
+ * FIDELITY, stated once: this wire carries the model's DECISION to call a tool. cli-bridge's
3986
+ * backends surface the call the moment the harness announces it and NEVER report the call
3987
+ * finishing, so no outcome, result, or duration exists to read. Each step is therefore marked
3988
+ * `statusCaptured: false` and carries no `startedAt`/`endedAt` — its span is an instant with no
3989
+ * status. Synthesising an end time would inject a fabricated 0ms latency, and defaulting to 'ok'
3990
+ * would count an unobserved call as a success; both would silently corrupt any downstream latency
3991
+ * or error-rate analysis. An honest lower-fidelity span beats a fabricated one. A harness whose
3992
+ * native protocol reports tool completion could carry true durations; this wire does not.
3993
+ *
3994
+ * cli-bridge emits each call complete in ONE delta (`{id, name, arguments}` together), so no
3995
+ * cross-delta argument-fragment assembly is needed; a frame that carries argument bytes without a
3996
+ * name decodes to nothing rather than to a nameless call.
3997
+ */
3998
+ function decodeBridgeToolCalls(raw) {
3999
+ if (!Array.isArray(raw)) return [];
4000
+ const steps = [];
4001
+ for (const call of raw) {
4002
+ if (!call || typeof call !== "object") continue;
4003
+ const record = call;
4004
+ const fn = record.function;
4005
+ const step = decodeOpenAiPart(typeof fn?.name === "string" && fn.name.length > 0 ? {
4006
+ ...record,
4007
+ type: "function"
4008
+ } : record);
4009
+ if (!step) continue;
4010
+ const rawArgs = fn?.arguments ?? record.arguments;
4011
+ const argsCaptured = typeof rawArgs === "string" ? rawArgs.trim().length > 0 : rawArgs !== void 0;
4012
+ steps.push({
4013
+ ...step,
4014
+ ...argsCaptured ? {} : {
4015
+ args: {},
4016
+ argsCaptured: false
4017
+ },
4018
+ statusCaptured: false
4019
+ });
4020
+ }
4021
+ return steps;
4022
+ }
4023
+ /**
4836
4024
  * Parse cli-bridge's OpenAI-compatible SSE stream into normalized chunks. Each
4837
4025
  * `data:` line is an OpenAI chat-completion chunk (`choices[].delta`). Every
4838
4026
  * run-owned frame, including an id-only comment, is returned so the caller can
@@ -4919,8 +4107,8 @@ function parseSseFrame(frame) {
4919
4107
  const choice = parsed.choices?.[0];
4920
4108
  const content = choice?.delta?.content ?? choice?.message?.content;
4921
4109
  if (typeof content === "string" && content.length > 0) out.content = content;
4922
- const toolName = choice?.delta?.tool_calls?.[0]?.function?.name;
4923
- if (typeof toolName === "string" && toolName.length > 0) out.toolCall = toolName;
4110
+ const toolCalls = decodeBridgeToolCalls(choice?.delta?.tool_calls);
4111
+ if (toolCalls.length > 0) out.toolCalls = toolCalls;
4924
4112
  const u = parsed.usage;
4925
4113
  if (u && (typeof u.prompt_tokens === "number" || typeof u.completion_tokens === "number")) out.usage = {
4926
4114
  input: u.prompt_tokens ?? 0,
@@ -5221,8 +4409,7 @@ function snapshotExecutorConfig(config) {
5221
4409
  }
5222
4410
  case "router":
5223
4411
  case "bridge":
5224
- case "cli":
5225
- case "pi": return detachedSnapshot(config, `createExecutor ${config.backend} config`);
4412
+ case "cli": return detachedSnapshot(config, `createExecutor ${config.backend} config`);
5226
4413
  }
5227
4414
  }
5228
4415
  /** A backend config reused for multiple workers/managers cannot pin execution identity or carry a
@@ -5254,7 +4441,6 @@ function bindReusableExecutorExecutionId(captured, executionId) {
5254
4441
  case "router-tools":
5255
4442
  case "cli":
5256
4443
  case "provider":
5257
- case "pi":
5258
4444
  case "sandbox": return captured;
5259
4445
  }
5260
4446
  }
@@ -5284,7 +4470,6 @@ function createExecutor(config) {
5284
4470
  case "bridge": return bridgeExecutor(spec, seamed);
5285
4471
  case "cli": return cliExecutor(spec, seamed);
5286
4472
  case "cli-worktree": return cliWorktreeExecutor(spec, seamed);
5287
- case "pi": return piExecutor(spec, seamed);
5288
4473
  case "provider": {
5289
4474
  const providerSeam = readSeam(seamed, providerSeamKey, "provider");
5290
4475
  const provider = resolveAgentEnvironmentProvider(providerSeam.provider, providerSeam.registry);
@@ -5354,7 +4539,6 @@ function createExecutorRegistry() {
5354
4539
  factories.set("inline", routerInlineExecutor);
5355
4540
  factories.set("sandbox", sandboxExecutor);
5356
4541
  factories.set("cli", cliExecutor);
5357
- factories.set("pi", piExecutor);
5358
4542
  return {
5359
4543
  register(runtime, factory) {
5360
4544
  if (factories.has(runtime)) throw new ValidationError(`executor registry: runtime "${runtime}" already registered`);
@@ -5661,6 +4845,19 @@ function createEventBus(now = Date.now) {
5661
4845
  *
5662
4846
  * @experimental
5663
4847
  */
4848
+ const measuredFloors = Object.entries(WORKER_TOKEN_FLOOR).flatMap(([harness, floor]) => floor === null ? [] : [[harness, floor]]);
4849
+ const measuredFloorSentence = measuredFloors.length === 0 ? "" : " A harness child spends a measured minimum before any work — a maxTokens under that minimum is refused (`error: \"below-runtime-floor\"`). Measured floors (input tokens): " + measuredFloors.map(([harness, floor]) => `${harness}=${floor}`).join(", ") + ". Unmeasured harnesses have no floor and are admitted.";
4850
+ /**
4851
+ * The actionable fact behind a `below-runtime-floor` refusal: the floor for the harness the
4852
+ * root declared, and that the only fix is to raise maxTokens to at least that floor. Falls back
4853
+ * to the whole measured census if the declared harness resolves to no floor (a guard fired on a
4854
+ * spec-level harness this profile did not name).
4855
+ */
4856
+ function belowFloorHint(harness) {
4857
+ const floor = workerTokenFloor(harness ?? null);
4858
+ if (floor !== null) return `The ${harness} harness spends a measured minimum of ${floor} input tokens before any work, so this budget's maxTokens can never be satisfied. Raise maxTokens to at least ${floor}; retrying with a smaller budget will fail identically.`;
4859
+ return "This budget's maxTokens is below the measured minimum a harness child spends before any work, so it can never be satisfied. Raise maxTokens to at least the floor for the child harness — measured floors (input tokens): " + measuredFloors.map(([h, f]) => `${h}=${f}`).join(", ") + " — retrying with a smaller budget will fail identically.";
4860
+ }
5664
4861
  /** Default ceiling for a single `await_event` block (ms). Chosen well under any reasonable remote
5665
4862
  * MCP client request timeout so the call returns a `pending` liveness snapshot instead of erroring;
5666
4863
  * the supervisor re-polls until the worker settles. */
@@ -6366,7 +5563,7 @@ function createCoordinationTools(opts) {
6366
5563
  },
6367
5564
  budget: {
6368
5565
  type: "object",
6369
- description: "Optional per-spawn budget that merges over the per-worker default (per field). Only set the ceilings this sub-task needs raised; the conserved pool still fences.",
5566
+ description: "Optional per-spawn budget that merges over the per-worker default (per field). Only set the ceilings this sub-task needs raised; the conserved pool still fences." + measuredFloorSentence,
6370
5567
  properties: {
6371
5568
  maxIterations: {
6372
5569
  type: "number",
@@ -6458,6 +5655,7 @@ function createCoordinationTools(opts) {
6458
5655
  } : {
6459
5656
  error: res.reason,
6460
5657
  ...res.reason === "usd-unbudgeted" ? { hint: "This run's root budget declares no maxUsd, so a child budget naming maxUsd can never be admitted — at any amount. Retrying with a smaller maxUsd will fail identically. Spawn with a budget that omits maxUsd, or ask the caller to give the run a root maxUsd." } : {},
5658
+ ...res.reason === "below-runtime-floor" ? { hint: belowFloorHint(profile.harness) } : {},
6461
5659
  live: liveWorkerCount(),
6462
5660
  freeSlots: freeWorkerSlots()
6463
5661
  });
@@ -9537,7 +8735,7 @@ function supervisorAgent(profile, deps) {
9537
8735
  const nodeContextSeed = deps.nodeContext === void 0 ? void 0 : detachedSnapshot(deps.nodeContext, "supervisorAgent node context");
9538
8736
  if ((resolveTools || observeNodeEvent) && !nodeContextSeed) throw new ValidationError("supervisorAgent: nodeContext is required with resolveSupervisorTools or observeNodeEvent");
9539
8737
  const name = stableProfile.name ?? "supervisor";
9540
- const harness = stableProfile.harness === void 0 || stableProfile.harness === null || stableProfile.harness === "cli-base" ? null : stableProfile.harness;
8738
+ const harness = agentHarness(stableProfile.harness) ?? null;
9541
8739
  const profilePrompt = resolveSupervisorSystemPrompt(stableProfile);
9542
8740
  const coordination = deps.coordination ? { ...deps.coordination } : void 0;
9543
8741
  assertCoordinationBinding(coordination);
@@ -9774,6 +8972,14 @@ function externalExecutionId(kind, identity) {
9774
8972
  identity
9775
8973
  }).slice(7)}`;
9776
8974
  }
8975
+ /**
8976
+ * NOT a harness-name test — `ExecutorConfig.backend` is a discriminated-union TAG naming HOW a
8977
+ * profile is materialized (bridge / sandbox / cli-worktree / router / cli / provider), which is a
8978
+ * different axis from WHICH CLI runs. An exhaustive switch on a closed union tag is the correct
8979
+ * shape and must stay: it is what makes a new executor kind a compile error here rather than a
8980
+ * silently weaker materialization contract. Every other `backend.backend === …` in this file and
8981
+ * in `runtime.ts` is the same tag; none of them are harness names.
8982
+ */
9777
8983
  function backendProfileMaterialization(backend) {
9778
8984
  switch (backend.backend) {
9779
8985
  case "bridge":
@@ -9781,8 +8987,7 @@ function backendProfileMaterialization(backend) {
9781
8987
  case "provider": return fullProfileMaterialization;
9782
8988
  case "cli-worktree": return backend.bridge ? fullProfileMaterialization : worktreeCliProfileMaterialization;
9783
8989
  case "router":
9784
- case "router-tools":
9785
- case "pi": return promptModelProfileMaterialization;
8990
+ case "router-tools": return promptModelProfileMaterialization;
9786
8991
  case "cli": return controlProfileMaterialization;
9787
8992
  }
9788
8993
  }
@@ -9836,7 +9041,7 @@ const DEFAULT_AUTHORED_PROFILE_SECURITY_POLICY = Object.freeze({
9836
9041
  allowConnections: false
9837
9042
  });
9838
9043
  function isExternalSupervisor(profile) {
9839
- return profile.harness !== void 0 && profile.harness !== "cli-base";
9044
+ return harnessRunsAgent(profile.harness);
9840
9045
  }
9841
9046
  function automaticDriverBackendSupported(backend) {
9842
9047
  return backend.backend === "bridge";
@@ -10515,6 +9720,6 @@ function supervise(profile, task, opts) {
10515
9720
  return start();
10516
9721
  }
10517
9722
  //#endregion
10518
- export { allOf as $, profileRichnessFinding as $t, DELEGATE_DESCRIPTION as A, PI_MCP_ADAPTER as At, DELEGATION_TRACE_MAX_SPANS as B, runLoop as Bt, createDelegateUiAuditHandler as C, createSteerableSandboxSession as Ct, DELEGATE_FEEDBACK_TOOL_NAME as D, createPushTraceSource as Dt, DELEGATE_FEEDBACK_INPUT_SCHEMA as E, piSeamKey as Et, defaultDelegateBudget as F, preparePiMcp as Ft, DelegationPersistenceError as G, createSupervisorSpanRecorder as Gt, capDelegationTrace as H, probeSandboxCapabilities as Ht, delegate as I, createInbox as It, InMemoryDelegationStore as J, asAuthoredProfile as Jt, DelegationStateCorruptError as K, assertModelAllowed as Kt, DelegationTaskQueue as L, createSandboxForSpec as Lt, DELEGATE_TOOL_NAME as M, PI_MCP_CONFIG_FLAG as Mt, createDelegateHandler as N, buildPiMcpServers as Nt, createDelegateFeedbackHandler as O, decodeToolPart as Ot, validateDelegateArgs as P, piMcpAdapterAvailable as Pt, finalizeBestDelivered as Q, defaultProfileRichnessThresholds as Qt, hashIdempotencyInput as R, defaultSelectWinner as Rt, DELEGATE_UI_AUDIT_TOOL_NAME as S, DEFAULT_SANDBOX_STEERING_MAX_TURNS as St, DELEGATE_FEEDBACK_DESCRIPTION as T, piExecutor as Tt, composeLoopTraceEmitters as U, acquireSandbox as Ut, buildDelegationTraceSpans as V, createSandboxLineage as Vt, createDelegationTraceCollector as W, FileCoordinationLog as Wt, eventToSnapshot as X, authoredWorker as Xt, InMemoryFeedbackStore as Y, assessAuthoredProfile as Yt, driverAgent as Z, canonicalizeAuthoredProfile as Zt, DELEGATION_HISTORY_TOOL_NAME as _, createInMemoryRunContext as _t, resolveSupervisorProfile as a, sampleFromSettled as at, DELEGATE_UI_AUDIT_DESCRIPTION as b, createExecutorRegistry as bt, createInProcessTransport as c, bestSoFar as ct, DELEGATION_STATUS_INPUT_SCHEMA as d, DEFAULT_AWAIT_EVENT_TIMEOUT_MS as dt, supervisorInstructions as en, allWorkersStalled as et, DELEGATION_STATUS_TOOL_NAME as f, createCoordinationTools as ft, DELEGATION_HISTORY_INPUT_SCHEMA as g, createFileRunContext as gt, DELEGATION_HISTORY_DESCRIPTION as h, watchTrace as ht, assertCoordinationBinding as i, plateau as it, DELEGATE_INPUT_SCHEMA as j, PI_MCP_ADAPTER_ENV as jt, validateDelegateFeedbackArgs as k, sandboxSessionTraceSource as kt, createMcpServer as l, plateauLength as lt, validateDelegationStatusArgs as m, defaultToolDetectors as mt, supervise as n, createProgressTracker as nt, supervisorAgent as o, anytimeReport as ot, createDelegationStatusHandler as p, createEventBus as pt, FileDelegationStore as q, assertProfileModelsAllowed as qt, workerFromBackend as r, noProgressFor as rt, serveCoordinationMcp as s, areaUnderCurve as st, DEFAULT_AUTHORED_PROFILE_SECURITY_POLICY as t, gateOnDeliverable as tn, anyOf as tt, DELEGATION_STATUS_DESCRIPTION as u, renderAnytimeTable as ut, createDelegationHistoryHandler as v, cliWorktreeExecutor as vt, validateDelegateUiAuditArgs as w, PI_RUNTIME as wt, DELEGATE_UI_AUDIT_INPUT_SCHEMA as x, createWorktreeCliExecutor as xt, validateDelegationHistoryArgs as y, createExecutor as yt, DELEGATION_TRACE_MAX_BYTES as z, runAgentRounds as zt };
9723
+ export { allOf as $, DELEGATE_DESCRIPTION as A, runAgentRounds as At, DELEGATION_TRACE_MAX_SPANS as B, assessAuthoredProfile as Bt, createDelegateUiAuditHandler as C, createSteerableSandboxSession as Ct, DELEGATE_FEEDBACK_TOOL_NAME as D, createInbox as Dt, DELEGATE_FEEDBACK_INPUT_SCHEMA as E, sandboxSessionTraceSource as Et, defaultDelegateBudget as F, FileCoordinationLog as Ft, DelegationPersistenceError as G, supervisorInstructions as Gt, capDelegationTrace as H, canonicalizeAuthoredProfile as Ht, delegate as I, createSupervisorSpanRecorder as It, InMemoryDelegationStore as J, DelegationStateCorruptError as K, gateOnDeliverable as Kt, DelegationTaskQueue as L, assertModelAllowed as Lt, DELEGATE_TOOL_NAME as M, createSandboxLineage as Mt, createDelegateHandler as N, probeSandboxCapabilities as Nt, createDelegateFeedbackHandler as O, createSandboxForSpec as Ot, validateDelegateArgs as P, acquireSandbox as Pt, finalizeBestDelivered as Q, hashIdempotencyInput as R, assertProfileModelsAllowed as Rt, DELEGATE_UI_AUDIT_TOOL_NAME as S, DEFAULT_SANDBOX_STEERING_MAX_TURNS as St, DELEGATE_FEEDBACK_DESCRIPTION as T, decodeToolPart as Tt, composeLoopTraceEmitters as U, defaultProfileRichnessThresholds as Ut, buildDelegationTraceSpans as V, authoredWorker as Vt, createDelegationTraceCollector as W, profileRichnessFinding as Wt, eventToSnapshot as X, InMemoryFeedbackStore as Y, driverAgent as Z, DELEGATION_HISTORY_TOOL_NAME as _, createInMemoryRunContext as _t, resolveSupervisorProfile as a, sampleFromSettled as at, DELEGATE_UI_AUDIT_DESCRIPTION as b, createExecutorRegistry as bt, createInProcessTransport as c, bestSoFar as ct, DELEGATION_STATUS_INPUT_SCHEMA as d, DEFAULT_AWAIT_EVENT_TIMEOUT_MS as dt, allWorkersStalled as et, DELEGATION_STATUS_TOOL_NAME as f, createCoordinationTools as ft, DELEGATION_HISTORY_INPUT_SCHEMA as g, createFileRunContext as gt, DELEGATION_HISTORY_DESCRIPTION as h, watchTrace as ht, assertCoordinationBinding as i, plateau as it, DELEGATE_INPUT_SCHEMA as j, runLoop as jt, validateDelegateFeedbackArgs as k, defaultSelectWinner as kt, createMcpServer as l, plateauLength as lt, validateDelegationStatusArgs as m, defaultToolDetectors as mt, supervise as n, createProgressTracker as nt, supervisorAgent as o, anytimeReport as ot, createDelegationStatusHandler as p, createEventBus as pt, FileDelegationStore as q, workerFromBackend as r, noProgressFor as rt, serveCoordinationMcp as s, areaUnderCurve as st, DEFAULT_AUTHORED_PROFILE_SECURITY_POLICY as t, anyOf as tt, DELEGATION_STATUS_DESCRIPTION as u, renderAnytimeTable as ut, createDelegationHistoryHandler as v, cliWorktreeExecutor as vt, validateDelegateUiAuditArgs as w, createPushTraceSource as wt, DELEGATE_UI_AUDIT_INPUT_SCHEMA as x, createWorktreeCliExecutor as xt, validateDelegationHistoryArgs as y, createExecutor as yt, DELEGATION_TRACE_MAX_BYTES as z, asAuthoredProfile as zt };
10519
9724
 
10520
- //# sourceMappingURL=supervise-U7_K5yuV.js.map
9725
+ //# sourceMappingURL=supervise-Cq50UT5-.js.map