@tangle-network/agent-runtime 0.339.0 → 0.340.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 (77) hide show
  1. package/dist/{activation-CVSFxis9.js → activation-D2PVljaw.js} +4 -4
  2. package/dist/{activation-CVSFxis9.js.map → activation-D2PVljaw.js.map} +1 -1
  3. package/dist/{activation-CJIIl_Ey.d.ts → activation-DGDl4wwI.d.ts} +2 -2
  4. package/dist/agent.d.ts +4 -18
  5. package/dist/agent.js +3 -21
  6. package/dist/agent.js.map +1 -1
  7. package/dist/analyst-loop.d.ts +2 -2
  8. package/dist/{bundle-DVOhBTic.js → bundle-DNUq-OPW.js} +2 -2
  9. package/dist/{bundle-DVOhBTic.js.map → bundle-DNUq-OPW.js.map} +1 -1
  10. package/dist/candidate-execution/index.d.ts +2 -40
  11. package/dist/candidate-execution/index.js +9 -255
  12. package/dist/candidate-execution/index.js.map +1 -1
  13. package/dist/{delegate-CCWE_AkU.js → delegate-CtJIS-tp.js} +2 -2
  14. package/dist/{delegate-CCWE_AkU.js.map → delegate-CtJIS-tp.js.map} +1 -1
  15. package/dist/{driver-control-acknowledgers-C53ggdJ6.js → driver-control-acknowledgers-MTrs8HAF.js} +5 -5
  16. package/dist/driver-control-acknowledgers-MTrs8HAF.js.map +1 -0
  17. package/dist/durable.d.ts +1 -1
  18. package/dist/durable.js +5 -5
  19. package/dist/{graph-CI9JXKqO.js → graph-DfWqp_1e.js} +4 -4
  20. package/dist/{graph-CI9JXKqO.js.map → graph-DfWqp_1e.js.map} +1 -1
  21. package/dist/{improve-BOXTLWJ3.js → improve-CKRVQqzt.js} +6 -6
  22. package/dist/{improve-BOXTLWJ3.js.map → improve-CKRVQqzt.js.map} +1 -1
  23. package/dist/{improvement-adapter-HUt_-cWA.d.ts → improvement-adapter-sIthWkcN.d.ts} +2 -2
  24. package/dist/index.d.ts +6 -14
  25. package/dist/index.js +10 -10
  26. package/dist/intelligence.d.ts +4 -4
  27. package/dist/intelligence.js +6 -6
  28. package/dist/kernel.d.ts +3 -3
  29. package/dist/kernel.js +10 -10
  30. package/dist/{loop-runner-CWzG1bH4.d.ts → loop-runner-C4EWEcXf.d.ts} +3 -3
  31. package/dist/{loop-runner-pmPIHFVY.js → loop-runner-DNWbTcVm.js} +2 -2
  32. package/dist/{loop-runner-pmPIHFVY.js.map → loop-runner-DNWbTcVm.js.map} +1 -1
  33. package/dist/loop-runner-bin.d.ts +1 -1
  34. package/dist/loop-runner-bin.js +1 -1
  35. package/dist/mcp/bin.js +3 -3
  36. package/dist/mcp/index.d.ts +1 -1
  37. package/dist/mcp/index.js +5 -5
  38. package/dist/placement.d.ts +2 -26
  39. package/dist/placement.js +1 -35
  40. package/dist/placement.js.map +1 -1
  41. package/dist/{prepare-bXkvPMJA.js → prepare-FzJoS68l.js} +3 -3
  42. package/dist/{prepare-bXkvPMJA.js.map → prepare-FzJoS68l.js.map} +1 -1
  43. package/dist/{profile-Dpei_Sq0.js → profile-DToH0XEs.js} +2 -2
  44. package/dist/{profile-Dpei_Sq0.js.map → profile-DToH0XEs.js.map} +1 -1
  45. package/dist/profile-materialization-bPm0gGLO.d.ts +49 -0
  46. package/dist/profiles.d.ts +1 -1
  47. package/dist/{protected-model-port-BGgpy2r9.d.ts → protected-model-port-DVVh-hEk.d.ts} +2 -6
  48. package/dist/{provision-supervisor-CJ6Zz6dz.js → provision-supervisor-DqvWXmAE.js} +4 -4
  49. package/dist/{provision-supervisor-CJ6Zz6dz.js.map → provision-supervisor-DqvWXmAE.js.map} +1 -1
  50. package/dist/{runtime-B2fjL9Oi.js → runtime-C55Zgo1R.js} +10 -10
  51. package/dist/{runtime-B2fjL9Oi.js.map → runtime-C55Zgo1R.js.map} +1 -1
  52. package/dist/{sandbox-profile-compat-BzVBFqrz.js → sandbox-profile-compat-DRr3JwDx.js} +2 -2
  53. package/dist/{sandbox-profile-compat-BzVBFqrz.js.map → sandbox-profile-compat-DRr3JwDx.js.map} +1 -1
  54. package/dist/{search-executor-zMumiak_.js → search-executor-ChOlRBgg.js} +3 -3
  55. package/dist/{search-executor-zMumiak_.js.map → search-executor-ChOlRBgg.js.map} +1 -1
  56. package/dist/{server-_4voDNrl.js → server-DkVBSN_S.js} +4 -4
  57. package/dist/{server-_4voDNrl.js.map → server-DkVBSN_S.js.map} +1 -1
  58. package/dist/{structural-rollout-DpLCXzaL.js → structural-rollout-C1LEk_zq.js} +3 -3
  59. package/dist/{structural-rollout-DpLCXzaL.js.map → structural-rollout-C1LEk_zq.js.map} +1 -1
  60. package/dist/{supervise-qAwXmPtD.js → supervise-CaUAxZZP.js} +86 -22
  61. package/dist/supervise-CaUAxZZP.js.map +1 -0
  62. package/dist/{supervisor-DHrUJhEZ.js → supervisor-VXuN2OHT.js} +29 -86
  63. package/dist/supervisor-VXuN2OHT.js.map +1 -0
  64. package/dist/testing.d.ts +1 -1
  65. package/dist/testing.js +13 -13
  66. package/dist/tui/index.d.ts +1 -1
  67. package/dist/tui/index.js +1 -1
  68. package/dist/{types-zWfqDjeL.d.ts → types-Cwrea-bU.d.ts} +2 -17
  69. package/dist/{types-CaOXqRTq.d.ts → types-DZ6L0Bv3.d.ts} +54 -58
  70. package/dist/workspace-store/index.d.ts +1 -82
  71. package/dist/workspace-store/index.js +1 -415
  72. package/dist/workspace-store/index.js.map +1 -1
  73. package/package.json +1 -1
  74. package/dist/driver-control-acknowledgers-C53ggdJ6.js.map +0 -1
  75. package/dist/profile-materialization-B1wm2SPT.d.ts +0 -94
  76. package/dist/supervise-qAwXmPtD.js.map +0 -1
  77. package/dist/supervisor-DHrUJhEZ.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"loop-runner-pmPIHFVY.js","names":[],"sources":["../src/loop-runner.ts"],"sourcesContent":["/**\n *\n * `runDelegatedLoop` — the configured delegated loop-runner.\n *\n * One typed entrypoint a worker agent (or a scheduled routine) calls to run a\n * disciplined loop in a chosen MODE, over agent-runtime's hardened engines:\n *\n * code → build-in-a-loop on the GENERIC recursive path (worktreeLoopRunner: author one\n * `AgentProfile` per harness → worktree-CLI leaves → `patchDelivered` gate)\n * review → caller-registered runner — a `code` runner with an approval gate over candidates\n * research → research-in-a-loop with valid-only KB growth (createKbGate)\n * audit → analyze trace/run data → findings (runAnalystLoop, caller-wired)\n * self-improve → caller-registered `improve(profile, options)` run\n *\n * It is intentionally a thin façade: the value is that EVERY product reuses the\n * one hardened engine instead of forking delegation logic. The dispatcher owns\n * mode routing, timing, fail-loud on an unregistered mode, and a uniform result\n * shape; each mode's engine is a pre-configured runner in the registry (build it\n * with the factories below, or inject your own / a stub).\n *\n * @experimental\n */\n\nimport type { AgentProfile } from '@tangle-network/agent-interface'\nimport { runAnalystLoop } from './analyst-loop'\nimport type { RunAnalystLoopOpts, RunAnalystLoopResult } from './analyst-loop/types'\nimport { ConfigError } from './errors'\nimport { type CreateKbGateOptions, createKbGate, type FactCandidate } from './mcp/kb-gate'\nimport {\n type AuthoredHarness,\n type Budget,\n createExecutorRegistry,\n definePersona,\n runPersonified,\n type WinnerStrategy,\n type WorktreeFanoutOptions,\n type WorktreePatchArtifact,\n worktreeFanout,\n} from './runtime'\n\n/** All valid delegated-loop mode names — used for validation and CLI surfaces. @experimental */\nexport const DELEGATED_LOOP_MODES = ['code', 'review', 'research', 'audit', 'self-improve'] as const\n\n/** @experimental */\nexport type DelegatedLoopMode = (typeof DELEGATED_LOOP_MODES)[number]\n\n/** Type guard — returns true when `value` is a valid `DelegatedLoopMode` string. @experimental */\nexport function isDelegatedLoopMode(value: unknown): value is DelegatedLoopMode {\n return typeof value === 'string' && (DELEGATED_LOOP_MODES as readonly string[]).includes(value)\n}\n\n/** @experimental A pre-configured loop for one mode. Returns the mode's raw\n * output; the dispatcher wraps it in a `DelegatedLoopResult`. */\nexport type DelegatedLoopRunner<T = unknown> = (signal: AbortSignal) => Promise<T>\n\n/** @experimental Mode → configured runner. Partial: only register the modes a\n * given product/routine actually uses. */\nexport type DelegatedLoopRegistry = Partial<Record<DelegatedLoopMode, DelegatedLoopRunner>>\n\n/** @experimental Uniform result — never throws from a registered runner; a\n * thrown engine becomes `{ ok: false, error }` so a routine can record + move on. */\nexport interface DelegatedLoopResult<T = unknown> {\n mode: DelegatedLoopMode\n ok: boolean\n output?: T\n error?: string\n durationMs: number\n}\n\n/** @experimental */\ninterface RunDelegatedLoopOptions {\n signal?: AbortSignal\n /** Clock override for deterministic tests. */\n now?: () => number\n}\n\n/**\n *\n * Dispatch a configured loop by mode. Fails loud (throws `ConfigError`) when no\n * runner is registered for the mode — a routine pointed at an unwired mode is a\n * config bug, not a silent no-op. A runner that throws is captured as\n * `{ ok: false }` so unattended runs record the failure rather than crash.\n *\n * @experimental\n */\nexport async function runDelegatedLoop<T = unknown>(\n mode: DelegatedLoopMode,\n registry: DelegatedLoopRegistry,\n options: RunDelegatedLoopOptions = {},\n): Promise<DelegatedLoopResult<T>> {\n const runner = registry[mode] as DelegatedLoopRunner<T> | undefined\n if (!runner) {\n throw new ConfigError(\n `runDelegatedLoop: no runner registered for mode '${mode}' (registered: ${\n Object.keys(registry).join(', ') || 'none'\n })`,\n )\n }\n const now = options.now ?? Date.now\n const signal = options.signal ?? new AbortController().signal\n const start = now()\n try {\n const output = await runner(signal)\n return { mode, ok: true, output, durationMs: now() - start }\n } catch (err) {\n return {\n mode,\n ok: false,\n error: err instanceof Error ? err.message : String(err),\n durationMs: now() - start,\n }\n }\n}\n\n/** @experimental Options for the local-repo `code` runner over the GENERIC recursive path. */\ninterface WorktreeLoopRunnerOptions {\n /** Exact profile carried by the personified root that owns this fanout. */\n rootProfile: AgentProfile\n /** Absolute path to the local git checkout each worktree is cut from. */\n repoRoot: string\n /** The instruction handed to every authored harness (composed under each profile's systemPrompt). */\n taskPrompt: string\n /** The supervisor-authored harness profiles — one fanout item (one worktree-CLI leaf) each. */\n harnesses: ReadonlyArray<AuthoredHarness>\n /** Conserved budget pool bounding the fanout (equal-k holds by construction). */\n budget: Budget\n /** Shell command run in each worktree to derive the tests-PASS signal. */\n testCmd?: string\n /** Shell command run in each worktree to derive the typecheck-PASS signal. */\n typecheckCmd?: string\n /** Which verification signals the deliverable REQUIRES present-and-passing (default none). */\n require?: ReadonlyArray<'tests' | 'typecheck'>\n /** Diff-size cap (lines). */\n maxDiffLines?: number\n /** Literal path prefixes the patch must not touch (the secret-floor is always on regardless). */\n forbiddenPaths?: string[]\n /** Winner-selection strategy among gated candidates. Default `highest-score`. */\n winnerStrategy?: WinnerStrategy\n /** Test seams forwarded to the worktree-CLI leaves so the runner drives offline. */\n runGit?: WorktreeFanoutOptions['runGit']\n runHarness?: WorktreeFanoutOptions['runHarness']\n runCommand?: WorktreeFanoutOptions['runCommand']\n}\n\n/**\n *\n * `code` mode on the GENERIC recursive path: author one `AgentProfile` per harness, run them as a\n * `worktreeFanout` (N `createWorktreeCliExecutor` leaves, each `gateOnDeliverable`) through\n * `runPersonified` on the keystone Supervisor. The sandbox-session counterpart that drives the in-box\n * harness over a `SandboxClient` is `detachedSessionDelegate` (`./mcp/delegates`); here there is no\n * `runAgentRounds` driver, no role-coupled delegate — the harness list is the fanout, the gate is\n * `patchDelivered`,\n * the winner is the shared valid-only selector (NOT `defaultSelectWinner`, whose non-valid fallback\n * would surface an ungated patch). Equal-k holds by the conserved budget pool. Returns the winning\n * patch artifact, or throws when no candidate is delivered (fail loud, never a vacuous done).\n *\n * @experimental\n */\nexport function worktreeLoopRunner(\n options: WorktreeLoopRunnerOptions,\n): DelegatedLoopRunner<WorktreePatchArtifact> {\n const shape = worktreeFanout<string>({\n repoRoot: options.repoRoot,\n taskPrompt: options.taskPrompt,\n harnesses: options.harnesses,\n ...(options.testCmd !== undefined ? { testCmd: options.testCmd } : {}),\n ...(options.typecheckCmd !== undefined ? { typecheckCmd: options.typecheckCmd } : {}),\n ...(options.require !== undefined ? { require: options.require } : {}),\n ...(options.maxDiffLines !== undefined ? { maxDiffLines: options.maxDiffLines } : {}),\n ...(options.forbiddenPaths !== undefined ? { forbiddenPaths: options.forbiddenPaths } : {}),\n ...(options.winnerStrategy !== undefined ? { winnerStrategy: options.winnerStrategy } : {}),\n ...(options.runGit ? { runGit: options.runGit } : {}),\n ...(options.runHarness ? { runHarness: options.runHarness } : {}),\n ...(options.runCommand ? { runCommand: options.runCommand } : {}),\n })\n // The persona's only role here is to carry the fanout shape onto the Supervisor; each item's\n // executor is BYO (the gated worktree-CLI leaf), so the registry only needs to pass BYO through.\n const persona = definePersona<WorktreePatchArtifact>({\n name: 'worktree-coder',\n root: { profile: options.rootProfile, harness: null },\n directive: 'deliver a minimal validated patch on a fresh worktree',\n context: { role: 'coder' },\n executors: { registry: createExecutorRegistry() },\n })\n return async (signal) => {\n const result = await runPersonified<string, WorktreePatchArtifact>({\n persona,\n shape,\n task: options.taskPrompt,\n budget: options.budget,\n signal,\n })\n if (result.kind !== 'winner' || result.out.kind !== 'done') {\n const blockers =\n result.kind === 'winner' && result.out.kind === 'blocked'\n ? result.out.blockers.join('; ')\n : `supervisor settled ${result.kind}`\n throw new Error(`worktreeLoopRunner: no delivered patch (${blockers})`)\n }\n return result.out.deliverable\n }\n}\n\n/** @experimental A fact rejected at the KB gate — surfaced, never dropped. */\nexport interface VetoedFact {\n candidate: FactCandidate\n vetoedBy?: string\n reason?: string\n}\n\n/** @experimental */\ninterface ResearchLoopResult {\n /** Facts that passed the fail-closed gate — safe to write to the KB. */\n accepted: FactCandidate[]\n /** Facts the gate vetoed in the final round — escalate, do not silently drop. */\n vetoed: VetoedFact[]\n /** Research rounds actually run. */\n rounds: number\n}\n\n/** @experimental Options for the default `research` runner. */\ninterface ResearchLoopRunnerOptions {\n /**\n * The research engine (the consumer's web/doc searcher + extractor). Called\n * each round with the prior round's vetoes so it can re-research the gaps.\n * Returns fact candidates carrying their grounding (`verbatimPassage` +\n * `sourceText`).\n */\n research: (round: number, vetoed: VetoedFact[]) => Promise<FactCandidate[]>\n /** Gate config (extra judges, self-artifact kinds, …). The floor is always on. */\n gate?: CreateKbGateOptions\n /** Max research rounds (correct-on-veto remediation). Default 1. */\n maxRounds?: number\n}\n\n/**\n * `research` mode — research-in-a-loop with valid-only KB growth.\n *\n * Each round: research → gate every candidate (fail-closed; passage MUST be in\n * the source) → accept the clean ones → re-research the vetoed ones next round,\n * up to `maxRounds`. Vetoed facts in the final round are RETURNED (escalate,\n * never silently dropped) so the caller audits vs retries.\n *\n * @experimental\n */\nexport function researchLoopRunner(\n o: ResearchLoopRunnerOptions,\n): DelegatedLoopRunner<ResearchLoopResult> {\n const gate = createKbGate(o.gate)\n const maxRounds = Math.max(1, Math.trunc(o.maxRounds ?? 1))\n return async (signal) => {\n const accepted: FactCandidate[] = []\n let vetoed: VetoedFact[] = []\n let rounds = 0\n for (let round = 0; round < maxRounds; round += 1) {\n if (signal.aborted) break\n rounds += 1\n const candidates = await o.research(round, vetoed)\n if (candidates.length === 0) break\n vetoed = []\n for (const c of candidates) {\n const v = await gate(c)\n if (v.accepted) accepted.push(c)\n else vetoed.push({ candidate: c, vetoedBy: v.vetoedBy, reason: v.reason })\n }\n if (vetoed.length === 0) break\n }\n return { accepted, vetoed, rounds }\n }\n}\n\n/**\n * `audit` mode — analyst loop over captured trace/run data.\n *\n * @experimental\n */\nexport function auditLoopRunner<TProposal = unknown, TEdit = unknown>(\n options: RunAnalystLoopOpts,\n): DelegatedLoopRunner<RunAnalystLoopResult<TProposal, TEdit>> {\n return async () => runAnalystLoop<TProposal, TEdit>(options)\n}\n"],"mappings":";;;;;AAyCA,MAAa,uBAAuB;CAAC;CAAQ;CAAU;CAAY;CAAS;AAAc;;AAM1F,SAAgB,oBAAoB,OAA4C;CAC9E,OAAO,OAAO,UAAU,YAAa,qBAA2C,SAAS,KAAK;AAChG;;;;;;;;;;AAoCA,eAAsB,iBACpB,MACA,UACA,UAAmC,CAAC,GACH;CACjC,MAAM,SAAS,SAAS;CACxB,IAAI,CAAC,QACH,MAAM,IAAI,YACR,oDAAoD,KAAK,iBACvD,OAAO,KAAK,QAAQ,CAAC,CAAC,KAAK,IAAI,KAAK,OACrC,EACH;CAEF,MAAM,MAAM,QAAQ,OAAO,KAAK;CAChC,MAAM,SAAS,QAAQ,UAAU,IAAI,gBAAgB,CAAC,CAAC;CACvD,MAAM,QAAQ,IAAI;CAClB,IAAI;EAEF,OAAO;GAAE;GAAM,IAAI;GAAM,QAAA,MADJ,OAAO,MAAM;GACD,YAAY,IAAI,IAAI;EAAM;CAC7D,SAAS,KAAK;EACZ,OAAO;GACL;GACA,IAAI;GACJ,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;GACtD,YAAY,IAAI,IAAI;EACtB;CACF;AACF;;;;;;AAoKA,SAAgB,gBACd,SAC6D;CAC7D,OAAO,YAAY,eAAiC,OAAO;AAC7D"}
1
+ {"version":3,"file":"loop-runner-DNWbTcVm.js","names":[],"sources":["../src/loop-runner.ts"],"sourcesContent":["/**\n *\n * `runDelegatedLoop` — the configured delegated loop-runner.\n *\n * One typed entrypoint a worker agent (or a scheduled routine) calls to run a\n * disciplined loop in a chosen MODE, over agent-runtime's hardened engines:\n *\n * code → build-in-a-loop on the GENERIC recursive path (worktreeLoopRunner: author one\n * `AgentProfile` per harness → worktree-CLI leaves → `patchDelivered` gate)\n * review → caller-registered runner — a `code` runner with an approval gate over candidates\n * research → research-in-a-loop with valid-only KB growth (createKbGate)\n * audit → analyze trace/run data → findings (runAnalystLoop, caller-wired)\n * self-improve → caller-registered `improve(profile, options)` run\n *\n * It is intentionally a thin façade: the value is that EVERY product reuses the\n * one hardened engine instead of forking delegation logic. The dispatcher owns\n * mode routing, timing, fail-loud on an unregistered mode, and a uniform result\n * shape; each mode's engine is a pre-configured runner in the registry (build it\n * with the factories below, or inject your own / a stub).\n *\n * @experimental\n */\n\nimport type { AgentProfile } from '@tangle-network/agent-interface'\nimport { runAnalystLoop } from './analyst-loop'\nimport type { RunAnalystLoopOpts, RunAnalystLoopResult } from './analyst-loop/types'\nimport { ConfigError } from './errors'\nimport { type CreateKbGateOptions, createKbGate, type FactCandidate } from './mcp/kb-gate'\nimport {\n type AuthoredHarness,\n type Budget,\n createExecutorRegistry,\n definePersona,\n runPersonified,\n type WinnerStrategy,\n type WorktreeFanoutOptions,\n type WorktreePatchArtifact,\n worktreeFanout,\n} from './runtime'\n\n/** All valid delegated-loop mode names — used for validation and CLI surfaces. @experimental */\nexport const DELEGATED_LOOP_MODES = ['code', 'review', 'research', 'audit', 'self-improve'] as const\n\n/** @experimental */\nexport type DelegatedLoopMode = (typeof DELEGATED_LOOP_MODES)[number]\n\n/** Type guard — returns true when `value` is a valid `DelegatedLoopMode` string. @experimental */\nexport function isDelegatedLoopMode(value: unknown): value is DelegatedLoopMode {\n return typeof value === 'string' && (DELEGATED_LOOP_MODES as readonly string[]).includes(value)\n}\n\n/** @experimental A pre-configured loop for one mode. Returns the mode's raw\n * output; the dispatcher wraps it in a `DelegatedLoopResult`. */\nexport type DelegatedLoopRunner<T = unknown> = (signal: AbortSignal) => Promise<T>\n\n/** @experimental Mode → configured runner. Partial: only register the modes a\n * given product/routine actually uses. */\nexport type DelegatedLoopRegistry = Partial<Record<DelegatedLoopMode, DelegatedLoopRunner>>\n\n/** @experimental Uniform result — never throws from a registered runner; a\n * thrown engine becomes `{ ok: false, error }` so a routine can record + move on. */\nexport interface DelegatedLoopResult<T = unknown> {\n mode: DelegatedLoopMode\n ok: boolean\n output?: T\n error?: string\n durationMs: number\n}\n\n/** @experimental */\ninterface RunDelegatedLoopOptions {\n signal?: AbortSignal\n /** Clock override for deterministic tests. */\n now?: () => number\n}\n\n/**\n *\n * Dispatch a configured loop by mode. Fails loud (throws `ConfigError`) when no\n * runner is registered for the mode — a routine pointed at an unwired mode is a\n * config bug, not a silent no-op. A runner that throws is captured as\n * `{ ok: false }` so unattended runs record the failure rather than crash.\n *\n * @experimental\n */\nexport async function runDelegatedLoop<T = unknown>(\n mode: DelegatedLoopMode,\n registry: DelegatedLoopRegistry,\n options: RunDelegatedLoopOptions = {},\n): Promise<DelegatedLoopResult<T>> {\n const runner = registry[mode] as DelegatedLoopRunner<T> | undefined\n if (!runner) {\n throw new ConfigError(\n `runDelegatedLoop: no runner registered for mode '${mode}' (registered: ${\n Object.keys(registry).join(', ') || 'none'\n })`,\n )\n }\n const now = options.now ?? Date.now\n const signal = options.signal ?? new AbortController().signal\n const start = now()\n try {\n const output = await runner(signal)\n return { mode, ok: true, output, durationMs: now() - start }\n } catch (err) {\n return {\n mode,\n ok: false,\n error: err instanceof Error ? err.message : String(err),\n durationMs: now() - start,\n }\n }\n}\n\n/** @experimental Options for the local-repo `code` runner over the GENERIC recursive path. */\ninterface WorktreeLoopRunnerOptions {\n /** Exact profile carried by the personified root that owns this fanout. */\n rootProfile: AgentProfile\n /** Absolute path to the local git checkout each worktree is cut from. */\n repoRoot: string\n /** The instruction handed to every authored harness (composed under each profile's systemPrompt). */\n taskPrompt: string\n /** The supervisor-authored harness profiles — one fanout item (one worktree-CLI leaf) each. */\n harnesses: ReadonlyArray<AuthoredHarness>\n /** Conserved budget pool bounding the fanout (equal-k holds by construction). */\n budget: Budget\n /** Shell command run in each worktree to derive the tests-PASS signal. */\n testCmd?: string\n /** Shell command run in each worktree to derive the typecheck-PASS signal. */\n typecheckCmd?: string\n /** Which verification signals the deliverable REQUIRES present-and-passing (default none). */\n require?: ReadonlyArray<'tests' | 'typecheck'>\n /** Diff-size cap (lines). */\n maxDiffLines?: number\n /** Literal path prefixes the patch must not touch (the secret-floor is always on regardless). */\n forbiddenPaths?: string[]\n /** Winner-selection strategy among gated candidates. Default `highest-score`. */\n winnerStrategy?: WinnerStrategy\n /** Test seams forwarded to the worktree-CLI leaves so the runner drives offline. */\n runGit?: WorktreeFanoutOptions['runGit']\n runHarness?: WorktreeFanoutOptions['runHarness']\n runCommand?: WorktreeFanoutOptions['runCommand']\n}\n\n/**\n *\n * `code` mode on the GENERIC recursive path: author one `AgentProfile` per harness, run them as a\n * `worktreeFanout` (N `createWorktreeCliExecutor` leaves, each `gateOnDeliverable`) through\n * `runPersonified` on the keystone Supervisor. The sandbox-session counterpart that drives the in-box\n * harness over a `SandboxClient` is `detachedSessionDelegate` (`./mcp/delegates`); here there is no\n * `runAgentRounds` driver, no role-coupled delegate — the harness list is the fanout, the gate is\n * `patchDelivered`,\n * the winner is the shared valid-only selector (NOT `defaultSelectWinner`, whose non-valid fallback\n * would surface an ungated patch). Equal-k holds by the conserved budget pool. Returns the winning\n * patch artifact, or throws when no candidate is delivered (fail loud, never a vacuous done).\n *\n * @experimental\n */\nexport function worktreeLoopRunner(\n options: WorktreeLoopRunnerOptions,\n): DelegatedLoopRunner<WorktreePatchArtifact> {\n const shape = worktreeFanout<string>({\n repoRoot: options.repoRoot,\n taskPrompt: options.taskPrompt,\n harnesses: options.harnesses,\n ...(options.testCmd !== undefined ? { testCmd: options.testCmd } : {}),\n ...(options.typecheckCmd !== undefined ? { typecheckCmd: options.typecheckCmd } : {}),\n ...(options.require !== undefined ? { require: options.require } : {}),\n ...(options.maxDiffLines !== undefined ? { maxDiffLines: options.maxDiffLines } : {}),\n ...(options.forbiddenPaths !== undefined ? { forbiddenPaths: options.forbiddenPaths } : {}),\n ...(options.winnerStrategy !== undefined ? { winnerStrategy: options.winnerStrategy } : {}),\n ...(options.runGit ? { runGit: options.runGit } : {}),\n ...(options.runHarness ? { runHarness: options.runHarness } : {}),\n ...(options.runCommand ? { runCommand: options.runCommand } : {}),\n })\n // The persona's only role here is to carry the fanout shape onto the Supervisor; each item's\n // executor is BYO (the gated worktree-CLI leaf), so the registry only needs to pass BYO through.\n const persona = definePersona<WorktreePatchArtifact>({\n name: 'worktree-coder',\n root: { profile: options.rootProfile, harness: null },\n directive: 'deliver a minimal validated patch on a fresh worktree',\n context: { role: 'coder' },\n executors: { registry: createExecutorRegistry() },\n })\n return async (signal) => {\n const result = await runPersonified<string, WorktreePatchArtifact>({\n persona,\n shape,\n task: options.taskPrompt,\n budget: options.budget,\n signal,\n })\n if (result.kind !== 'winner' || result.out.kind !== 'done') {\n const blockers =\n result.kind === 'winner' && result.out.kind === 'blocked'\n ? result.out.blockers.join('; ')\n : `supervisor settled ${result.kind}`\n throw new Error(`worktreeLoopRunner: no delivered patch (${blockers})`)\n }\n return result.out.deliverable\n }\n}\n\n/** @experimental A fact rejected at the KB gate — surfaced, never dropped. */\nexport interface VetoedFact {\n candidate: FactCandidate\n vetoedBy?: string\n reason?: string\n}\n\n/** @experimental */\ninterface ResearchLoopResult {\n /** Facts that passed the fail-closed gate — safe to write to the KB. */\n accepted: FactCandidate[]\n /** Facts the gate vetoed in the final round — escalate, do not silently drop. */\n vetoed: VetoedFact[]\n /** Research rounds actually run. */\n rounds: number\n}\n\n/** @experimental Options for the default `research` runner. */\ninterface ResearchLoopRunnerOptions {\n /**\n * The research engine (the consumer's web/doc searcher + extractor). Called\n * each round with the prior round's vetoes so it can re-research the gaps.\n * Returns fact candidates carrying their grounding (`verbatimPassage` +\n * `sourceText`).\n */\n research: (round: number, vetoed: VetoedFact[]) => Promise<FactCandidate[]>\n /** Gate config (extra judges, self-artifact kinds, …). The floor is always on. */\n gate?: CreateKbGateOptions\n /** Max research rounds (correct-on-veto remediation). Default 1. */\n maxRounds?: number\n}\n\n/**\n * `research` mode — research-in-a-loop with valid-only KB growth.\n *\n * Each round: research → gate every candidate (fail-closed; passage MUST be in\n * the source) → accept the clean ones → re-research the vetoed ones next round,\n * up to `maxRounds`. Vetoed facts in the final round are RETURNED (escalate,\n * never silently dropped) so the caller audits vs retries.\n *\n * @experimental\n */\nexport function researchLoopRunner(\n o: ResearchLoopRunnerOptions,\n): DelegatedLoopRunner<ResearchLoopResult> {\n const gate = createKbGate(o.gate)\n const maxRounds = Math.max(1, Math.trunc(o.maxRounds ?? 1))\n return async (signal) => {\n const accepted: FactCandidate[] = []\n let vetoed: VetoedFact[] = []\n let rounds = 0\n for (let round = 0; round < maxRounds; round += 1) {\n if (signal.aborted) break\n rounds += 1\n const candidates = await o.research(round, vetoed)\n if (candidates.length === 0) break\n vetoed = []\n for (const c of candidates) {\n const v = await gate(c)\n if (v.accepted) accepted.push(c)\n else vetoed.push({ candidate: c, vetoedBy: v.vetoedBy, reason: v.reason })\n }\n if (vetoed.length === 0) break\n }\n return { accepted, vetoed, rounds }\n }\n}\n\n/**\n * `audit` mode — analyst loop over captured trace/run data.\n *\n * @experimental\n */\nexport function auditLoopRunner<TProposal = unknown, TEdit = unknown>(\n options: RunAnalystLoopOpts,\n): DelegatedLoopRunner<RunAnalystLoopResult<TProposal, TEdit>> {\n return async () => runAnalystLoop<TProposal, TEdit>(options)\n}\n"],"mappings":";;;;;AAyCA,MAAa,uBAAuB;CAAC;CAAQ;CAAU;CAAY;CAAS;AAAc;;AAM1F,SAAgB,oBAAoB,OAA4C;CAC9E,OAAO,OAAO,UAAU,YAAa,qBAA2C,SAAS,KAAK;AAChG;;;;;;;;;;AAoCA,eAAsB,iBACpB,MACA,UACA,UAAmC,CAAC,GACH;CACjC,MAAM,SAAS,SAAS;CACxB,IAAI,CAAC,QACH,MAAM,IAAI,YACR,oDAAoD,KAAK,iBACvD,OAAO,KAAK,QAAQ,CAAC,CAAC,KAAK,IAAI,KAAK,OACrC,EACH;CAEF,MAAM,MAAM,QAAQ,OAAO,KAAK;CAChC,MAAM,SAAS,QAAQ,UAAU,IAAI,gBAAgB,CAAC,CAAC;CACvD,MAAM,QAAQ,IAAI;CAClB,IAAI;EAEF,OAAO;GAAE;GAAM,IAAI;GAAM,QAAA,MADJ,OAAO,MAAM;GACD,YAAY,IAAI,IAAI;EAAM;CAC7D,SAAS,KAAK;EACZ,OAAO;GACL;GACA,IAAI;GACJ,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;GACtD,YAAY,IAAI,IAAI;EACtB;CACF;AACF;;;;;;AAoKA,SAAgB,gBACd,SAC6D;CAC7D,OAAO,YAAY,eAAiC,OAAO;AAC7D"}
@@ -1,4 +1,4 @@
1
- import { n as DelegatedLoopResult, t as DelegatedLoopRegistry } from "./loop-runner-CWzG1bH4.js";
1
+ import { n as DelegatedLoopResult, t as DelegatedLoopRegistry } from "./loop-runner-C4EWEcXf.js";
2
2
  //#region src/loop-runner-bin.d.ts
3
3
  /** @experimental Parsed CLI invocation. */
4
4
  interface LoopRunnerCliArgs {
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { i as runDelegatedLoop, r as isDelegatedLoopMode, t as DELEGATED_LOOP_MODES } from "./loop-runner-pmPIHFVY.js";
2
+ import { i as runDelegatedLoop, r as isDelegatedLoopMode, t as DELEGATED_LOOP_MODES } from "./loop-runner-DNWbTcVm.js";
3
3
  //#region src/loop-runner-bin.ts
4
4
  /**
5
5
  *
package/dist/mcp/bin.js CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
- import { k as coordinationProfileToolPrefix, nn as readTraceContextFromEnv } from "../supervisor-DHrUJhEZ.js";
2
+ import { k as coordinationProfileToolPrefix, rn as readTraceContextFromEnv } from "../supervisor-VXuN2OHT.js";
3
3
  import { i as ConfigError } from "../errors-DodWX-cb.js";
4
- import { s as supervisorInstructions } from "../delegate-CCWE_AkU.js";
5
- import { i as FileDelegationStore, r as DelegationTaskQueue, t as createMcpServer } from "../server-_4voDNrl.js";
4
+ import { s as supervisorInstructions } from "../delegate-CtJIS-tp.js";
5
+ import { i as FileDelegationStore, r as DelegationTaskQueue, t as createMcpServer } from "../server-DkVBSN_S.js";
6
6
  //#region src/mcp/delegate-supervisor-provisioning.ts
7
7
  function trimmed(value) {
8
8
  const v = value?.trim();
@@ -1,4 +1,4 @@
1
- import { $m as CoderReviewer, Ah as DelegateUiAuditRoute, Bh as FeedbackRefersTo, Ch as SubmitOutput, Cp as McpServer, Dh as DelegateResearchConfig, Dp as DelegateHandlerOptions, Eh as DelegateResearchArgs, Ep as DelegateError, Fh as DelegationProgress, Fy as LocalHarness, Hh as UiAuditLensFilter, Ih as DelegationResultPayload, Lh as DelegationStatus, Mh as DelegationFeedbackSnapshot, My as RunLocalHarnessOptions, Nh as DelegationHistoryArgs, Ny as runLocalHarness, Oh as DelegateUiAuditArgs, Op as DelegateResult, Ov as QuestionUrgency, Ph as DelegationHistoryEntry, Py as LOCAL_HARNESSES, Qm as CoderReview, Rh as DelegationStatusResult, Sh as SubmitInput, Sp as resolveSpawnResourcePaths, St as SpawnRefusalCause, Th as DelegateCodeConfig, Tp as createMcpServer, Tv as QuestionOption, Uh as UiAuditorDelegationOutput, Vh as ResearchOutputShape, Xm as FeedbackStore, Ym as FeedbackEvent, Zm as CoderDelegate, _h as DelegationResumeDriver, _p as ResolvedSpawnResourcePath, ag as composeLoopTraceEmitters, ah as DelegationExecutor, bh as DelegationTaskQueue, bp as SpawnResourceReader, cg as FileDelegationStore, ch as SiblingSandboxExecutorOptions, cy as McpToolAnnotations, dg as CoderTask, dh as DetachedTurn, dt as CoordinationStats, eh as DelegateRunCtx, ev as GitRunner, fh as RunDetachedTurnOptions, ft as CoordinationTools, gh as DelegationResumeContext, gp as ResolveSpawnResourcePathsResult, gt as MakeWorkerAgent, hh as DelegationRecord, ig as DelegationTraceSpan, ih as detachedSessionDelegate, jh as DelegationError, kh as DelegateUiAuditConfig, kp as createDelegateHandler, lg as FileDelegationStoreOptions, lh as createFleetWorkspaceExecutor, ly as McpToolDescriptor, mh as DelegationArgs, ng as createPropagatingTraceEmitter, nh as DetachedWinnerSelection, np as CheckRead, nv as CoordinationEvent, og as DelegationPersistenceError, oh as FleetHandle, ov as ManagerWake, oy as JsonRpcMessage, ph as runDetachedTurn, pt as CoordinationToolsOptions, rg as readTraceContextFromEnv, rh as UiAuditorDelegate, sg as DelegationStore, sh as FleetWorkspaceExecutorOptions, sy as JsonRpcResponse, tg as TraceContext, th as DetachedSessionDelegateOptions, tv as AwaitWakeInput, ug as CoderOutput, uh as createSiblingSandboxExecutor, uy as McpTransport, vh as DelegationResumeTick, vp as SpawnResourceBytes, wh as DelegateCodeArgs, wp as McpServerOptions, wt as WorkerSpawnContext, wv as QuestionLevel, wy as AnalystRegistry, xh as DelegationTaskQueueOptions, xp as environmentReader, yh as DelegationRunContext, yp as SpawnResourceRead, yv as Question, zh as FeedbackRating } from "../types-CaOXqRTq.js";
1
+ import { $_ as CoordinationEvent, $m as DelegateRunCtx, Ah as DelegationError, Ay as runLocalHarness, Bh as ResearchOutputShape, Ch as DelegateCodeArgs, Cp as McpServer, Dh as DelegateUiAuditArgs, Dp as DelegateHandlerOptions, Eh as DelegateResearchConfig, Ep as DelegateError, Fh as DelegationResultPayload, Hh as UiAuditorDelegationOutput, Ih as DelegationStatus, Jm as FeedbackEvent, Lh as DelegationStatusResult, Mh as DelegationHistoryArgs, My as LocalHarness, Nh as DelegationHistoryEntry, Oh as DelegateUiAuditConfig, Op as DelegateResult, Ph as DelegationProgress, Q_ as AwaitWakeInput, Qm as CoderReviewer, Rh as FeedbackRating, Sh as SubmitOutput, Sp as resolveSpawnResourcePaths, St as SpawnRefusalCause, Sv as QuestionOption, Th as DelegateResearchArgs, Tp as createMcpServer, Tv as QuestionUrgency, Vh as UiAuditLensFilter, Xm as CoderDelegate, Ym as FeedbackStore, Z_ as GitRunner, Zm as CoderReview, _h as DelegationResumeTick, _p as ResolvedSpawnResourcePath, ag as DelegationPersistenceError, ah as FleetHandle, ay as McpToolAnnotations, bh as DelegationTaskQueueOptions, bp as SpawnResourceReader, cg as FileDelegationStoreOptions, ch as createFleetWorkspaceExecutor, dh as RunDetachedTurnOptions, dt as CoordinationStats, eg as TraceContext, eh as DetachedSessionDelegateOptions, fh as runDetachedTurn, ft as CoordinationTools, gh as DelegationResumeDriver, gp as ResolveSpawnResourcePathsResult, gt as MakeWorkerAgent, gv as Question, hh as DelegationResumeContext, ig as composeLoopTraceEmitters, ih as DelegationExecutor, iy as JsonRpcResponse, jh as DelegationFeedbackSnapshot, jy as LOCAL_HARNESSES, kh as DelegateUiAuditRoute, kp as createDelegateHandler, ky as RunLocalHarnessOptions, lg as CoderOutput, lh as createSiblingSandboxExecutor, mh as DelegationRecord, ng as readTraceContextFromEnv, nh as UiAuditorDelegate, np as CheckRead, og as DelegationStore, oh as FleetWorkspaceExecutorOptions, oy as McpToolDescriptor, ph as DelegationArgs, pt as CoordinationToolsOptions, rg as DelegationTraceSpan, rh as detachedSessionDelegate, rv as ManagerWake, ry as JsonRpcMessage, sg as FileDelegationStore, sh as SiblingSandboxExecutorOptions, sy as McpTransport, tg as createPropagatingTraceEmitter, th as DetachedWinnerSelection, ug as CoderTask, uh as DetachedTurn, vh as DelegationRunContext, vp as SpawnResourceBytes, wh as DelegateCodeConfig, wp as McpServerOptions, wt as WorkerSpawnContext, xh as SubmitInput, xp as environmentReader, xv as QuestionLevel, xy as AnalystRegistry, yh as DelegationTaskQueue, yp as SpawnResourceRead, zh as FeedbackRefersTo } from "../types-DZ6L0Bv3.js";
2
2
  import { HarnessType } from "@tangle-network/agent-interface";
3
3
  //#region src/mcp/kb-gate.d.ts
4
4
  /**
package/dist/mcp/index.js CHANGED
@@ -1,12 +1,12 @@
1
- import { Tr as LOCAL_HARNESSES, _t as runAgentRounds, bt as createSandboxForSpec, nn as readTraceContextFromEnv, tn as createPropagatingTraceEmitter, vt as assertBoxlessPromptOptions, wr as runWorktreeHarness } from "../supervisor-DHrUJhEZ.js";
1
+ import { Cr as LOCAL_HARNESSES, Sr as runWorktreeHarness, nn as createPropagatingTraceEmitter, rn as readTraceContextFromEnv, vt as runAgentRounds, xt as createSandboxForSpec, yt as assertBoxlessPromptOptions } from "../supervisor-VXuN2OHT.js";
2
2
  import { i as ConfigError, m as ValidationError } from "../errors-DodWX-cb.js";
3
3
  import { t as isRecord } from "../record-value-B6bwJxLa.js";
4
- import { X as deleteBoxSafe, st as sleep, ut as throwIfAborted } from "../sandbox-profile-compat-BzVBFqrz.js";
4
+ import { Y as deleteBoxSafe, lt as throwIfAborted, ot as sleep } from "../sandbox-profile-compat-DRr3JwDx.js";
5
5
  import { t as assertExecutableAgentProfile } from "../model-policy-CyCUwHed.js";
6
- import { _ as environmentReader, r as createCoordinationTools, v as resolveSpawnResourcePaths } from "../driver-control-acknowledgers-C53ggdJ6.js";
6
+ import { _ as environmentReader, r as createCoordinationTools, v as resolveSpawnResourcePaths } from "../driver-control-acknowledgers-MTrs8HAF.js";
7
7
  import { n as createStdioToolServer } from "../tool-server-XV4aJwoQ.js";
8
- import { ct as selectValidWinner, h as runCoderChecks } from "../runtime-B2fjL9Oi.js";
9
- import { a as composeLoopTraceEmitters, i as FileDelegationStore, n as createDelegateHandler, r as DelegationTaskQueue, t as createMcpServer } from "../server-_4voDNrl.js";
8
+ import { ct as selectValidWinner, h as runCoderChecks } from "../runtime-C55Zgo1R.js";
9
+ import { a as composeLoopTraceEmitters, i as FileDelegationStore, n as createDelegateHandler, r as DelegationTaskQueue, t as createMcpServer } from "../server-DkVBSN_S.js";
10
10
  import { agentProfileSchema } from "@tangle-network/agent-interface";
11
11
  import { randomUUID } from "node:crypto";
12
12
  //#region src/mcp/kb-gate.ts
@@ -1,4 +1,4 @@
1
- import { a as ProfileMaterializationContract, i as KnownAgentProfileMaterializationAxis, t as AgentProfileMaterializationAxis } from "./profile-materialization-B1wm2SPT.js";
1
+ import { a as ProfileMaterializationContract, i as KnownAgentProfileMaterializationAxis, t as AgentProfileMaterializationAxis } from "./profile-materialization-bPm0gGLO.js";
2
2
  import { AgentProfile } from "@tangle-network/agent-interface";
3
3
  //#region src/placement/index.d.ts
4
4
  /** The policy version every {@link PlacementRecord} carries. */
@@ -91,35 +91,11 @@ declare class PlacementRefusedError extends Error {
91
91
  * metadata. No harness, native tools, files, MCP, subagents, hooks or modes.
92
92
  */
93
93
  declare const loopTierProfileMaterialization: ProfileMaterializationContract;
94
- /** Options for {@link loopProfileRefusal}. */
95
- interface LoopProfileRefusalOptions {
96
- vacuousAxes?: readonly VacuousAxis[];
97
- inlineSkills?: boolean;
98
- }
99
- /** Options for {@link sandboxOnlyRequirements}. */
100
- interface SandboxOnlyRequirementsOptions extends LoopProfileRefusalOptions {
101
- requirements?: PlacementRequirements;
102
- profile?: AgentProfile | object;
103
- interactive: boolean;
104
- }
105
- /**
106
- * Every declared requirement only a sandbox can meet. A caller resuming work in the
107
- * loop it began in checks this is empty before it continues there.
108
- */
109
- declare function sandboxOnlyRequirements(options: SandboxOnlyRequirementsOptions): string[];
110
94
  /**
111
95
  * Choose where work runs: the cheapest tier every declared requirement allows.
112
96
  * Throws {@link PlacementRefusedError} when no tier can meet every requirement.
113
97
  */
114
98
  declare function placeExecution(input: PlaceExecutionInput): PlacementRecord;
115
- /** The record for a tier the author named: nothing was chosen or refused. */
116
- declare function authoredPlacement(chosen: ExecutionTier): PlacementRecord;
117
- /**
118
- * The record for work that resumes under the tier it began with. A retry reuses the
119
- * recorded tier and never re-decides, so one turn never runs on two tiers; a caller
120
- * resuming on `loop` first checks {@link sandboxOnlyRequirements} is empty.
121
- */
122
- declare function resumedPlacement(chosen: ExecutionTier): PlacementRecord;
123
99
  //#endregion
124
- export { type AgentProfileMaterializationAxis, ExecutionTier, type KnownAgentProfileMaterializationAxis, PLACEMENT_POLICY, PlaceExecutionInput, PlacementContext, PlacementRecord, PlacementRefusedError, PlacementRequirements, SandboxOnlyRequirementsOptions, VacuousAxis, authoredPlacement, loopTierProfileMaterialization, placeExecution, resumedPlacement, sandboxOnlyRequirements };
100
+ export { type AgentProfileMaterializationAxis, ExecutionTier, type KnownAgentProfileMaterializationAxis, PLACEMENT_POLICY, PlaceExecutionInput, PlacementContext, PlacementRecord, PlacementRefusedError, PlacementRequirements, VacuousAxis, loopTierProfileMaterialization, placeExecution };
125
101
  //# sourceMappingURL=placement.d.ts.map
package/dist/placement.js CHANGED
@@ -161,16 +161,6 @@ function sandboxRequirements(options) {
161
161
  function profileDefaultModel(profile) {
162
162
  return profile?.model?.default;
163
163
  }
164
- /**
165
- * Every declared requirement only a sandbox can meet. A caller resuming work in the
166
- * loop it began in checks this is empty before it continues there.
167
- */
168
- function sandboxOnlyRequirements(options) {
169
- return sandboxRequirements({
170
- ...options,
171
- requirements: options.requirements ?? {}
172
- });
173
- }
174
164
  /** Tools the work grants, each of which the loop can hold. */
175
165
  function toolRequirements(cfg) {
176
166
  const tools = [];
@@ -289,31 +279,7 @@ function declaredAxes(profile) {
289
279
  }
290
280
  return axes;
291
281
  }
292
- /** The record for a tier the author named: nothing was chosen or refused. */
293
- function authoredPlacement(chosen) {
294
- return {
295
- chosen,
296
- policy: PLACEMENT_POLICY,
297
- reason: `authored placement: ${chosen}`,
298
- coveredAxes: [],
299
- rejected: []
300
- };
301
- }
302
- /**
303
- * The record for work that resumes under the tier it began with. A retry reuses the
304
- * recorded tier and never re-decides, so one turn never runs on two tiers; a caller
305
- * resuming on `loop` first checks {@link sandboxOnlyRequirements} is empty.
306
- */
307
- function resumedPlacement(chosen) {
308
- return {
309
- chosen,
310
- policy: PLACEMENT_POLICY,
311
- reason: `resumed under the placement it began with: ${chosen}`,
312
- coveredAxes: [],
313
- rejected: []
314
- };
315
- }
316
282
  //#endregion
317
- export { PLACEMENT_POLICY, PlacementRefusedError, authoredPlacement, loopTierProfileMaterialization, placeExecution, resumedPlacement, sandboxOnlyRequirements };
283
+ export { PLACEMENT_POLICY, PlacementRefusedError, loopTierProfileMaterialization, placeExecution };
318
284
 
319
285
  //# sourceMappingURL=placement.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"placement.js","names":[],"sources":["../src/placement/index.ts"],"sourcesContent":["/**\n * Execution placement: choose the cheapest executor whose declared capabilities\n * cover the work, before any key, sandbox or model call.\n *\n * A pure decision over DECLARED data — the work's requirements and its resolved\n * {@link AgentProfile}. It never reads the prompt and does no I/O, so it is safe\n * in a Worker or Durable Object and costs nothing on the path to the first word.\n * Tiers, cheapest first:\n * - `inference`: one Router call, when nothing needs a tool, a file or a session;\n * - `loop`: the Runtime tool loop in the caller's own process (a Worker or the\n * Platform workflow worker), when the only tools are ones that loop holds;\n * - `sandbox`: anything that needs a harness, a filesystem, a shell, files, MCP\n * servers, subagents, hooks or an interactive session.\n * A requirement no tier can meet refuses the work by name\n * ({@link PlacementRefusedError}); a capability is never dropped silently, and a\n * pinned harness always runs in the sandbox.\n *\n * This is Platform's `agent-placement/2` (`selectAgentPlacement` and\n * `workerProfileRefusal`) moved here unchanged in semantics. Its reason strings\n * are kept byte-identical so a shadow comparison against Platform's copy reads\n * equal; only the tier name changes (`worker` → `loop`). `agent-placement/3`\n * adds two caller opt-ins, both off by default:\n * - {@link PlaceExecutionInput.vacuousAxes}: a deny-only `permissions` map asks\n * for nothing a tier lacks, so it counts as covered;\n * - {@link PlaceExecutionInput.inlineSkills}: profile skills count as covered on\n * `loop` when the caller inlines their content.\n */\n\nimport type { AgentProfile } from '@tangle-network/agent-interface'\nimport type {\n AgentProfileMaterializationAxis,\n ProfileMaterializationContract,\n} from '../agent/profile-materialization'\n\n/** The policy version every {@link PlacementRecord} carries. */\nexport const PLACEMENT_POLICY = 'agent-placement/3'\n\n/** Where one unit of agent work runs, cheapest first. */\nexport type ExecutionTier = 'inference' | 'loop' | 'sandbox'\n\n/** What was chosen, under which policy, why, and why each other tier was refused. */\nexport interface PlacementRecord {\n chosen: ExecutionTier\n policy: typeof PLACEMENT_POLICY\n reason: string\n /**\n * The profile's declared materialization axes the chosen tier carries. A tier that\n * cannot carry a declared axis is rejected, so on a fresh decision this is every\n * declared axis; an authored or resumed record makes no coverage decision and is empty.\n */\n coveredAxes: AgentProfileMaterializationAxis[]\n rejected: Array<{ placement: ExecutionTier; reason: string }>\n}\n\n/**\n * The declared requirements of the work, beside its profile. Field names follow\n * Platform's `agent.run` action, the first consumer; an app's chat turn usually\n * declares none of them.\n */\nexport interface PlacementRequirements {\n /** A pinned harness. Always runs in the sandbox. */\n harness?: string\n /** A model other than the profile's own needs the sandbox to choose between them. */\n model?: string\n source?: unknown\n /** Skill ids resolved into a box. Profile skills are `profile.resources.skills`. */\n skills?: unknown\n env?: unknown\n image?: unknown\n size?: unknown\n resources?: unknown\n fromSnapshot?: unknown\n fromSandboxId?: unknown\n resume?: unknown\n maxRounds?: number\n ask?: boolean\n /** Hub connections: tools the loop holds. */\n connections?: readonly string[]\n /** Owner-object grants: tools only the loop holds. */\n objects?: unknown\n /** Public HTTPS reads: a tool only the loop holds. */\n http?: unknown\n /** Tools the caller binds into the loop itself, such as an app's product tools. */\n tools?: readonly string[]\n}\n\n/** Which tiers this deployment has wired. */\nexport interface PlacementContext {\n interactive: boolean\n inferenceAvailable: boolean\n loopAvailable: boolean\n /** Why the loop cannot take this work, when it cannot. */\n loopUnavailableReason?: string\n}\n\n/** Profile axes a tier may count as covered when their declared value grants nothing. */\nexport type VacuousAxis = 'permissions'\n\nexport interface PlaceExecutionInput {\n requirements?: PlacementRequirements\n /** The RESOLVED profile: a named profile is read before the decision. */\n profile?: AgentProfile | object\n context: PlacementContext\n /**\n * Axes whose deny-only value counts as covered on `inference` and `loop`.\n * `permissions` qualifies when every rule is `deny`: a tier without native tools\n * already denies them. Deny-only `tools` are covered without opting in.\n */\n vacuousAxes?: readonly VacuousAxis[]\n /**\n * The caller inlines the profile's skill content into the loop's context, so\n * `resources.skills` (with a strict `resources.failOnError`) and the native `skill`\n * tool and permission that load them count as covered on `loop`. Off by default:\n * the skills then keep the work in the sandbox.\n */\n inlineSkills?: boolean\n}\n\n/** No tier can meet every declared requirement. Raised before any spend. */\nexport class PlacementRefusedError extends Error {\n constructor(readonly reason: string) {\n super(`placement cannot run this work — ${reason}`)\n this.name = 'PlacementRefusedError'\n }\n}\n\n/**\n * The profile axes `inference` and `loop` carry: identity, prompt, model controls and\n * metadata. No harness, native tools, files, MCP, subagents, hooks or modes.\n */\nexport const loopTierProfileMaterialization: ProfileMaterializationContract = {\n name: 'placement-loop-tier',\n axes: [\n 'name',\n 'description',\n 'version',\n 'tags',\n 'systemPrompt',\n 'appendSystemPrompt',\n 'instructions',\n 'modelDefault',\n 'modelSmall',\n 'modelProvider',\n 'modelReasoningEffort',\n 'modelMaxVisibleOutputTokens',\n 'modelMaxTotalOutputTokens',\n 'modelMetadata',\n 'metadata',\n ],\n}\n\n/** Compound profile properties and the axis each child maps to. */\nconst COMPOUND_FIELDS: Record<string, Record<string, AgentProfileMaterializationAxis>> = {\n prompt: {\n systemPrompt: 'systemPrompt',\n appendSystemPrompt: 'appendSystemPrompt',\n instructions: 'instructions',\n },\n model: {\n default: 'modelDefault',\n small: 'modelSmall',\n provider: 'modelProvider',\n reasoningEffort: 'modelReasoningEffort',\n maxVisibleOutputTokens: 'modelMaxVisibleOutputTokens',\n maxReasoningTokens: 'modelMaxReasoningTokens',\n maxTotalOutputTokens: 'modelMaxTotalOutputTokens',\n metadata: 'modelMetadata',\n },\n resources: {\n files: 'files',\n tools: 'resourceTools',\n skills: 'skills',\n agents: 'resourceAgents',\n commands: 'commands',\n instructions: 'resourceInstructions',\n failOnError: 'resourceFailOnError',\n },\n}\n\n/** Top-level profile properties that are themselves an axis (the rest are compounds). */\nconst TOP_LEVEL_AXES: ReadonlySet<string> = new Set([\n 'name',\n 'description',\n 'version',\n 'tags',\n 'harness',\n 'seats',\n 'permissions',\n 'tools',\n 'mcp',\n 'connections',\n 'subagents',\n 'hooks',\n 'modes',\n 'confidential',\n 'metadata',\n 'extensions',\n])\n\n/** Options for {@link loopProfileRefusal}. */\ninterface LoopProfileRefusalOptions {\n vacuousAxes?: readonly VacuousAxis[]\n inlineSkills?: boolean\n}\n\n/**\n * Why `inference` and `loop` cannot run this profile, or undefined when they can.\n * A profile that needs a harness, files, harness built-in tools, MCP servers,\n * subagents, hooks or modes needs a sandbox; the loop never drops one silently.\n * Platform's `workerProfileRefusal`, read through {@link loopTierProfileMaterialization}.\n */\nfunction loopProfileRefusal(\n profile: object,\n options: LoopProfileRefusalOptions = {},\n): string | undefined {\n const fields = profile as Record<string, unknown>\n const vacuousPermissions = options.vacuousAxes?.includes('permissions') === true\n const inlineSkills = options.inlineSkills === true\n const carried = new Set<string>(loopTierProfileMaterialization.axes)\n if (inlineSkills) carried.add('skills')\n const childCovered = (field: string, child: string, value: unknown): boolean => {\n const axis = COMPOUND_FIELDS[field]?.[child]\n if (axis === undefined) return false\n // A strict failure policy is honoured by an inliner that fails closed; a best-effort\n // one would have to report a skipped skill, which the loop has no channel for.\n if (axis === 'resourceFailOnError') return inlineSkills && value === true\n return carried.has(axis)\n }\n const reasons: string[] = []\n for (const [field, value] of Object.entries(fields)) {\n if (value === undefined) continue\n if (field === 'tools') continue\n if (\n field === 'permissions' &&\n vacuousPermissions &&\n permissionsGrantNothing(value, inlineSkills)\n )\n continue\n if (TOP_LEVEL_AXES.has(field) && carried.has(field)) continue\n const children = COMPOUND_FIELDS[field]\n if (children && Object.values(children).some((axis) => carried.has(axis))) continue\n reasons.push(`profile ${field} needs a sandbox`)\n }\n for (const field of Object.keys(COMPOUND_FIELDS)) {\n const value = fields[field]\n if (typeof value !== 'object' || value === null) continue\n // A compound the loop carries none of was refused whole above.\n if (!Object.values(COMPOUND_FIELDS[field] ?? {}).some((axis) => carried.has(axis))) continue\n for (const [child, inner] of Object.entries(value)) {\n if (inner !== undefined && !childCovered(field, child, inner)) {\n reasons.push(`profile ${field}.${child} is not supported by the worker`)\n }\n }\n }\n // Harness built-in tools (shell, edit, browser) exist only in a sandbox. A profile may\n // still switch them off. The `skill` tool only loads skills, which an inliner supplies.\n const tools = fields.tools\n if (typeof tools === 'object' && tools !== null) {\n const enabled = Object.entries(tools)\n .filter(([name, on]) => on === true && !(inlineSkills && name === 'skill'))\n .map(([name]) => name)\n if (enabled.length > 0) reasons.push(`profile tools ${enabled.join(', ')} need a sandbox`)\n }\n return reasons.length > 0 ? reasons.join('; ') : undefined\n}\n\n/** True when every permission rule denies, or allows only `skill` under inlined skills. */\nfunction permissionsGrantNothing(value: unknown, inlineSkills: boolean): boolean {\n if (typeof value !== 'object' || value === null) return false\n return Object.entries(value).every(([tool, rule]) => {\n if (inlineSkills && tool === 'skill') return true\n if (rule === 'deny') return true\n if (typeof rule !== 'object' || rule === null) return false\n return Object.values(rule).every((inner) => inner === 'deny')\n })\n}\n\n/** Requirement fields that only mean something inside a provisioned box. */\nconst SANDBOX_FIELDS = [\n 'source',\n 'skills',\n 'env',\n 'image',\n 'size',\n 'resources',\n 'fromSnapshot',\n 'fromSandboxId',\n 'resume',\n] as const satisfies ReadonlyArray<keyof PlacementRequirements>\n\ninterface RequirementOptions extends LoopProfileRefusalOptions {\n requirements: PlacementRequirements\n profile?: object\n interactive: boolean\n}\n\n/** Every declared requirement only a sandbox can meet, in field order. */\nfunction sandboxRequirements(options: RequirementOptions): string[] {\n const { requirements: cfg, profile } = options\n const needs: string[] = []\n if (options.interactive) needs.push('interactive run needs a session')\n if (cfg.harness !== undefined) needs.push(`harness ${cfg.harness} is pinned`)\n for (const field of SANDBOX_FIELDS) {\n if (cfg[field] !== undefined) needs.push(`${field} needs a sandbox`)\n }\n if (cfg.maxRounds !== undefined && cfg.maxRounds > 1)\n needs.push('maxRounds > 1 needs a multi-round session')\n if (cfg.ask === true) needs.push('ask needs a session to answer into')\n const refusal = profile ? loopProfileRefusal(profile, options) : undefined\n if (refusal) needs.push(refusal)\n // Inference and the loop run the profile's own model and refuse another; only the\n // sandbox decides between a requested model and the profile's.\n const profileModel = profileDefaultModel(profile)\n if (cfg.model !== undefined && profileModel !== undefined && cfg.model !== profileModel) {\n needs.push(\n `the action model ${cfg.model} differs from the profile model ${String(profileModel)}`,\n )\n }\n return needs\n}\n\nfunction profileDefaultModel(profile: object | undefined): unknown {\n return (profile as { model?: { default?: unknown } } | undefined)?.model?.default\n}\n\n/** Options for {@link sandboxOnlyRequirements}. */\nexport interface SandboxOnlyRequirementsOptions extends LoopProfileRefusalOptions {\n requirements?: PlacementRequirements\n profile?: AgentProfile | object\n interactive: boolean\n}\n\n/**\n * Every declared requirement only a sandbox can meet. A caller resuming work in the\n * loop it began in checks this is empty before it continues there.\n */\nexport function sandboxOnlyRequirements(options: SandboxOnlyRequirementsOptions): string[] {\n return sandboxRequirements({ ...options, requirements: options.requirements ?? {} })\n}\n\n/** Tools the work grants, each of which the loop can hold. */\nfunction toolRequirements(cfg: PlacementRequirements): string[] {\n const tools: string[] = []\n if ((cfg.connections ?? []).length > 0) tools.push('connections need Hub tools')\n if (cfg.objects !== undefined) tools.push('objects need object tools')\n if (cfg.http !== undefined) tools.push('http needs a fetch tool')\n if ((cfg.tools ?? []).length > 0) tools.push(`tools ${cfg.tools?.join(', ')} need the loop`)\n return tools\n}\n\n/** Grants only the loop holds: a sandbox cannot honour them. */\nfunction loopOnlyGrants(cfg: PlacementRequirements): string[] {\n return [\n ...(cfg.objects !== undefined ? ['objects'] : []),\n ...(cfg.http !== undefined ? ['http'] : []),\n ]\n}\n\n/** True when the profile declares skills that only an inliner on the loop can carry. */\nfunction declaresProfileSkills(profile: object | undefined): boolean {\n const fields = (profile ?? {}) as {\n resources?: { skills?: unknown }\n tools?: Record<string, unknown>\n permissions?: Record<string, unknown>\n }\n const skills = fields.resources?.skills\n return (\n (Array.isArray(skills) && skills.length > 0) ||\n fields.tools?.skill === true ||\n (fields.permissions?.skill !== undefined && fields.permissions.skill !== 'deny')\n )\n}\n\n/**\n * Choose where work runs: the cheapest tier every declared requirement allows.\n * Throws {@link PlacementRefusedError} when no tier can meet every requirement.\n */\nexport function placeExecution(input: PlaceExecutionInput): PlacementRecord {\n const cfg = input.requirements ?? {}\n const { context, profile } = input\n const opts: LoopProfileRefusalOptions = {\n ...(input.vacuousAxes ? { vacuousAxes: input.vacuousAxes } : {}),\n ...(input.inlineSkills ? { inlineSkills: true } : {}),\n }\n const sandboxNeeds = sandboxRequirements({\n requirements: cfg,\n profile,\n interactive: context.interactive,\n ...opts,\n })\n const tools = toolRequirements(cfg)\n if (input.inlineSkills && declaresProfileSkills(profile))\n tools.push('skills are inlined by the loop')\n const loopOnly = loopOnlyGrants(cfg)\n const coveredAxes = profile ? declaredAxes(profile) : []\n const record = (\n chosen: ExecutionTier,\n reason: string,\n rejected: PlacementRecord['rejected'],\n ): PlacementRecord => ({ chosen, policy: PLACEMENT_POLICY, reason, coveredAxes, rejected })\n\n if (sandboxNeeds.length > 0) {\n const reason = sandboxNeeds.join('; ')\n if (loopOnly.length > 0) {\n throw new PlacementRefusedError(\n `${loopOnly.join(' and ')} run only in the worker, but ${reason}`,\n )\n }\n return record('sandbox', reason, [\n { placement: 'inference', reason },\n { placement: 'loop', reason },\n ])\n }\n\n // Past the sandbox check the profile is one the loop (and a single Router call) can\n // honour, so only granted tools separate the two.\n if (tools.length > 0) {\n const reason = `${tools.join('; ')}, which the worker holds without a sandbox`\n if (!context.loopAvailable) {\n const unavailable =\n context.loopUnavailableReason ?? 'worker placement is not wired on this deployment'\n if (loopOnly.length > 0) {\n throw new PlacementRefusedError(\n `${loopOnly.join(' and ')} need the worker, but ${unavailable}`,\n )\n }\n // Inlined skills are the one loop need a sandbox also meets, natively.\n return record('sandbox', unavailable, [\n { placement: 'inference', reason: tools.join('; ') },\n { placement: 'loop', reason: unavailable },\n ])\n }\n return record('loop', reason, [\n { placement: 'inference', reason: tools.join('; ') },\n { placement: 'sandbox', reason: 'costs more and no declared requirement needs it' },\n ])\n }\n\n if (context.inferenceAvailable) {\n return record('inference', 'no declared requirement needs tools, files or a session', [\n { placement: 'loop', reason: 'no declared tool needs a loop' },\n { placement: 'sandbox', reason: 'costs more and no declared requirement needs it' },\n ])\n }\n const unwired = 'inference is not wired on this deployment'\n if (context.loopAvailable) {\n return record('loop', `${unwired}; one tool-free worker turn`, [\n { placement: 'inference', reason: unwired },\n { placement: 'sandbox', reason: 'costs more and no declared requirement needs it' },\n ])\n }\n return record('sandbox', unwired, [\n { placement: 'inference', reason: unwired },\n { placement: 'loop', reason: 'worker placement is not wired on this deployment' },\n ])\n}\n\n/** The materialization axes a profile declares, in its own field order. Read from the\n * same field map as {@link loopProfileRefusal}, so the module has no runtime import. */\nfunction declaredAxes(profile: object): AgentProfileMaterializationAxis[] {\n const axes: AgentProfileMaterializationAxis[] = []\n for (const [field, value] of Object.entries(profile)) {\n if (value === undefined) continue\n if (TOP_LEVEL_AXES.has(field)) axes.push(field as AgentProfileMaterializationAxis)\n const children = COMPOUND_FIELDS[field]\n if (!children || typeof value !== 'object' || value === null) continue\n for (const [child, inner] of Object.entries(value)) {\n const axis = children[child]\n if (axis !== undefined && inner !== undefined) axes.push(axis)\n }\n }\n return axes\n}\n\n/** The record for a tier the author named: nothing was chosen or refused. */\nexport function authoredPlacement(chosen: ExecutionTier): PlacementRecord {\n return {\n chosen,\n policy: PLACEMENT_POLICY,\n reason: `authored placement: ${chosen}`,\n coveredAxes: [],\n rejected: [],\n }\n}\n\n/**\n * The record for work that resumes under the tier it began with. A retry reuses the\n * recorded tier and never re-decides, so one turn never runs on two tiers; a caller\n * resuming on `loop` first checks {@link sandboxOnlyRequirements} is empty.\n */\nexport function resumedPlacement(chosen: ExecutionTier): PlacementRecord {\n return {\n chosen,\n policy: PLACEMENT_POLICY,\n reason: `resumed under the placement it began with: ${chosen}`,\n coveredAxes: [],\n rejected: [],\n }\n}\nexport type {\n AgentProfileMaterializationAxis,\n KnownAgentProfileMaterializationAxis,\n} from '../agent/profile-materialization'\n"],"mappings":"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"}
1
+ {"version":3,"file":"placement.js","names":[],"sources":["../src/placement/index.ts"],"sourcesContent":["/**\n * Execution placement: choose the cheapest executor whose declared capabilities\n * cover the work, before any key, sandbox or model call.\n *\n * A pure decision over DECLARED data — the work's requirements and its resolved\n * {@link AgentProfile}. It never reads the prompt and does no I/O, so it is safe\n * in a Worker or Durable Object and costs nothing on the path to the first word.\n * Tiers, cheapest first:\n * - `inference`: one Router call, when nothing needs a tool, a file or a session;\n * - `loop`: the Runtime tool loop in the caller's own process (a Worker or the\n * Platform workflow worker), when the only tools are ones that loop holds;\n * - `sandbox`: anything that needs a harness, a filesystem, a shell, files, MCP\n * servers, subagents, hooks or an interactive session.\n * A requirement no tier can meet refuses the work by name\n * ({@link PlacementRefusedError}); a capability is never dropped silently, and a\n * pinned harness always runs in the sandbox.\n *\n * This is Platform's `agent-placement/2` (`selectAgentPlacement` and\n * `workerProfileRefusal`) moved here unchanged in semantics. Its reason strings\n * are kept byte-identical so a shadow comparison against Platform's copy reads\n * equal; only the tier name changes (`worker` → `loop`). `agent-placement/3`\n * adds two caller opt-ins, both off by default:\n * - {@link PlaceExecutionInput.vacuousAxes}: a deny-only `permissions` map asks\n * for nothing a tier lacks, so it counts as covered;\n * - {@link PlaceExecutionInput.inlineSkills}: profile skills count as covered on\n * `loop` when the caller inlines their content.\n */\n\nimport type { AgentProfile } from '@tangle-network/agent-interface'\nimport type {\n AgentProfileMaterializationAxis,\n ProfileMaterializationContract,\n} from '../agent/profile-materialization'\n\n/** The policy version every {@link PlacementRecord} carries. */\nexport const PLACEMENT_POLICY = 'agent-placement/3'\n\n/** Where one unit of agent work runs, cheapest first. */\nexport type ExecutionTier = 'inference' | 'loop' | 'sandbox'\n\n/** What was chosen, under which policy, why, and why each other tier was refused. */\nexport interface PlacementRecord {\n chosen: ExecutionTier\n policy: typeof PLACEMENT_POLICY\n reason: string\n /**\n * The profile's declared materialization axes the chosen tier carries. A tier that\n * cannot carry a declared axis is rejected, so on a fresh decision this is every\n * declared axis; an authored or resumed record makes no coverage decision and is empty.\n */\n coveredAxes: AgentProfileMaterializationAxis[]\n rejected: Array<{ placement: ExecutionTier; reason: string }>\n}\n\n/**\n * The declared requirements of the work, beside its profile. Field names follow\n * Platform's `agent.run` action, the first consumer; an app's chat turn usually\n * declares none of them.\n */\nexport interface PlacementRequirements {\n /** A pinned harness. Always runs in the sandbox. */\n harness?: string\n /** A model other than the profile's own needs the sandbox to choose between them. */\n model?: string\n source?: unknown\n /** Skill ids resolved into a box. Profile skills are `profile.resources.skills`. */\n skills?: unknown\n env?: unknown\n image?: unknown\n size?: unknown\n resources?: unknown\n fromSnapshot?: unknown\n fromSandboxId?: unknown\n resume?: unknown\n maxRounds?: number\n ask?: boolean\n /** Hub connections: tools the loop holds. */\n connections?: readonly string[]\n /** Owner-object grants: tools only the loop holds. */\n objects?: unknown\n /** Public HTTPS reads: a tool only the loop holds. */\n http?: unknown\n /** Tools the caller binds into the loop itself, such as an app's product tools. */\n tools?: readonly string[]\n}\n\n/** Which tiers this deployment has wired. */\nexport interface PlacementContext {\n interactive: boolean\n inferenceAvailable: boolean\n loopAvailable: boolean\n /** Why the loop cannot take this work, when it cannot. */\n loopUnavailableReason?: string\n}\n\n/** Profile axes a tier may count as covered when their declared value grants nothing. */\nexport type VacuousAxis = 'permissions'\n\nexport interface PlaceExecutionInput {\n requirements?: PlacementRequirements\n /** The RESOLVED profile: a named profile is read before the decision. */\n profile?: AgentProfile | object\n context: PlacementContext\n /**\n * Axes whose deny-only value counts as covered on `inference` and `loop`.\n * `permissions` qualifies when every rule is `deny`: a tier without native tools\n * already denies them. Deny-only `tools` are covered without opting in.\n */\n vacuousAxes?: readonly VacuousAxis[]\n /**\n * The caller inlines the profile's skill content into the loop's context, so\n * `resources.skills` (with a strict `resources.failOnError`) and the native `skill`\n * tool and permission that load them count as covered on `loop`. Off by default:\n * the skills then keep the work in the sandbox.\n */\n inlineSkills?: boolean\n}\n\n/** No tier can meet every declared requirement. Raised before any spend. */\nexport class PlacementRefusedError extends Error {\n constructor(readonly reason: string) {\n super(`placement cannot run this work — ${reason}`)\n this.name = 'PlacementRefusedError'\n }\n}\n\n/**\n * The profile axes `inference` and `loop` carry: identity, prompt, model controls and\n * metadata. No harness, native tools, files, MCP, subagents, hooks or modes.\n */\nexport const loopTierProfileMaterialization: ProfileMaterializationContract = {\n name: 'placement-loop-tier',\n axes: [\n 'name',\n 'description',\n 'version',\n 'tags',\n 'systemPrompt',\n 'appendSystemPrompt',\n 'instructions',\n 'modelDefault',\n 'modelSmall',\n 'modelProvider',\n 'modelReasoningEffort',\n 'modelMaxVisibleOutputTokens',\n 'modelMaxTotalOutputTokens',\n 'modelMetadata',\n 'metadata',\n ],\n}\n\n/** Compound profile properties and the axis each child maps to. */\nconst COMPOUND_FIELDS: Record<string, Record<string, AgentProfileMaterializationAxis>> = {\n prompt: {\n systemPrompt: 'systemPrompt',\n appendSystemPrompt: 'appendSystemPrompt',\n instructions: 'instructions',\n },\n model: {\n default: 'modelDefault',\n small: 'modelSmall',\n provider: 'modelProvider',\n reasoningEffort: 'modelReasoningEffort',\n maxVisibleOutputTokens: 'modelMaxVisibleOutputTokens',\n maxReasoningTokens: 'modelMaxReasoningTokens',\n maxTotalOutputTokens: 'modelMaxTotalOutputTokens',\n metadata: 'modelMetadata',\n },\n resources: {\n files: 'files',\n tools: 'resourceTools',\n skills: 'skills',\n agents: 'resourceAgents',\n commands: 'commands',\n instructions: 'resourceInstructions',\n failOnError: 'resourceFailOnError',\n },\n}\n\n/** Top-level profile properties that are themselves an axis (the rest are compounds). */\nconst TOP_LEVEL_AXES: ReadonlySet<string> = new Set([\n 'name',\n 'description',\n 'version',\n 'tags',\n 'harness',\n 'seats',\n 'permissions',\n 'tools',\n 'mcp',\n 'connections',\n 'subagents',\n 'hooks',\n 'modes',\n 'confidential',\n 'metadata',\n 'extensions',\n])\n\n/** Options for {@link loopProfileRefusal}. */\ninterface LoopProfileRefusalOptions {\n vacuousAxes?: readonly VacuousAxis[]\n inlineSkills?: boolean\n}\n\n/**\n * Why `inference` and `loop` cannot run this profile, or undefined when they can.\n * A profile that needs a harness, files, harness built-in tools, MCP servers,\n * subagents, hooks or modes needs a sandbox; the loop never drops one silently.\n * Platform's `workerProfileRefusal`, read through {@link loopTierProfileMaterialization}.\n */\nfunction loopProfileRefusal(\n profile: object,\n options: LoopProfileRefusalOptions = {},\n): string | undefined {\n const fields = profile as Record<string, unknown>\n const vacuousPermissions = options.vacuousAxes?.includes('permissions') === true\n const inlineSkills = options.inlineSkills === true\n const carried = new Set<string>(loopTierProfileMaterialization.axes)\n if (inlineSkills) carried.add('skills')\n const childCovered = (field: string, child: string, value: unknown): boolean => {\n const axis = COMPOUND_FIELDS[field]?.[child]\n if (axis === undefined) return false\n // A strict failure policy is honoured by an inliner that fails closed; a best-effort\n // one would have to report a skipped skill, which the loop has no channel for.\n if (axis === 'resourceFailOnError') return inlineSkills && value === true\n return carried.has(axis)\n }\n const reasons: string[] = []\n for (const [field, value] of Object.entries(fields)) {\n if (value === undefined) continue\n if (field === 'tools') continue\n if (\n field === 'permissions' &&\n vacuousPermissions &&\n permissionsGrantNothing(value, inlineSkills)\n )\n continue\n if (TOP_LEVEL_AXES.has(field) && carried.has(field)) continue\n const children = COMPOUND_FIELDS[field]\n if (children && Object.values(children).some((axis) => carried.has(axis))) continue\n reasons.push(`profile ${field} needs a sandbox`)\n }\n for (const field of Object.keys(COMPOUND_FIELDS)) {\n const value = fields[field]\n if (typeof value !== 'object' || value === null) continue\n // A compound the loop carries none of was refused whole above.\n if (!Object.values(COMPOUND_FIELDS[field] ?? {}).some((axis) => carried.has(axis))) continue\n for (const [child, inner] of Object.entries(value)) {\n if (inner !== undefined && !childCovered(field, child, inner)) {\n reasons.push(`profile ${field}.${child} is not supported by the worker`)\n }\n }\n }\n // Harness built-in tools (shell, edit, browser) exist only in a sandbox. A profile may\n // still switch them off. The `skill` tool only loads skills, which an inliner supplies.\n const tools = fields.tools\n if (typeof tools === 'object' && tools !== null) {\n const enabled = Object.entries(tools)\n .filter(([name, on]) => on === true && !(inlineSkills && name === 'skill'))\n .map(([name]) => name)\n if (enabled.length > 0) reasons.push(`profile tools ${enabled.join(', ')} need a sandbox`)\n }\n return reasons.length > 0 ? reasons.join('; ') : undefined\n}\n\n/** True when every permission rule denies, or allows only `skill` under inlined skills. */\nfunction permissionsGrantNothing(value: unknown, inlineSkills: boolean): boolean {\n if (typeof value !== 'object' || value === null) return false\n return Object.entries(value).every(([tool, rule]) => {\n if (inlineSkills && tool === 'skill') return true\n if (rule === 'deny') return true\n if (typeof rule !== 'object' || rule === null) return false\n return Object.values(rule).every((inner) => inner === 'deny')\n })\n}\n\n/** Requirement fields that only mean something inside a provisioned box. */\nconst SANDBOX_FIELDS = [\n 'source',\n 'skills',\n 'env',\n 'image',\n 'size',\n 'resources',\n 'fromSnapshot',\n 'fromSandboxId',\n 'resume',\n] as const satisfies ReadonlyArray<keyof PlacementRequirements>\n\ninterface RequirementOptions extends LoopProfileRefusalOptions {\n requirements: PlacementRequirements\n profile?: object\n interactive: boolean\n}\n\n/** Every declared requirement only a sandbox can meet, in field order. */\nfunction sandboxRequirements(options: RequirementOptions): string[] {\n const { requirements: cfg, profile } = options\n const needs: string[] = []\n if (options.interactive) needs.push('interactive run needs a session')\n if (cfg.harness !== undefined) needs.push(`harness ${cfg.harness} is pinned`)\n for (const field of SANDBOX_FIELDS) {\n if (cfg[field] !== undefined) needs.push(`${field} needs a sandbox`)\n }\n if (cfg.maxRounds !== undefined && cfg.maxRounds > 1)\n needs.push('maxRounds > 1 needs a multi-round session')\n if (cfg.ask === true) needs.push('ask needs a session to answer into')\n const refusal = profile ? loopProfileRefusal(profile, options) : undefined\n if (refusal) needs.push(refusal)\n // Inference and the loop run the profile's own model and refuse another; only the\n // sandbox decides between a requested model and the profile's.\n const profileModel = profileDefaultModel(profile)\n if (cfg.model !== undefined && profileModel !== undefined && cfg.model !== profileModel) {\n needs.push(\n `the action model ${cfg.model} differs from the profile model ${String(profileModel)}`,\n )\n }\n return needs\n}\n\nfunction profileDefaultModel(profile: object | undefined): unknown {\n return (profile as { model?: { default?: unknown } } | undefined)?.model?.default\n}\n\n/** Tools the work grants, each of which the loop can hold. */\nfunction toolRequirements(cfg: PlacementRequirements): string[] {\n const tools: string[] = []\n if ((cfg.connections ?? []).length > 0) tools.push('connections need Hub tools')\n if (cfg.objects !== undefined) tools.push('objects need object tools')\n if (cfg.http !== undefined) tools.push('http needs a fetch tool')\n if ((cfg.tools ?? []).length > 0) tools.push(`tools ${cfg.tools?.join(', ')} need the loop`)\n return tools\n}\n\n/** Grants only the loop holds: a sandbox cannot honour them. */\nfunction loopOnlyGrants(cfg: PlacementRequirements): string[] {\n return [\n ...(cfg.objects !== undefined ? ['objects'] : []),\n ...(cfg.http !== undefined ? ['http'] : []),\n ]\n}\n\n/** True when the profile declares skills that only an inliner on the loop can carry. */\nfunction declaresProfileSkills(profile: object | undefined): boolean {\n const fields = (profile ?? {}) as {\n resources?: { skills?: unknown }\n tools?: Record<string, unknown>\n permissions?: Record<string, unknown>\n }\n const skills = fields.resources?.skills\n return (\n (Array.isArray(skills) && skills.length > 0) ||\n fields.tools?.skill === true ||\n (fields.permissions?.skill !== undefined && fields.permissions.skill !== 'deny')\n )\n}\n\n/**\n * Choose where work runs: the cheapest tier every declared requirement allows.\n * Throws {@link PlacementRefusedError} when no tier can meet every requirement.\n */\nexport function placeExecution(input: PlaceExecutionInput): PlacementRecord {\n const cfg = input.requirements ?? {}\n const { context, profile } = input\n const opts: LoopProfileRefusalOptions = {\n ...(input.vacuousAxes ? { vacuousAxes: input.vacuousAxes } : {}),\n ...(input.inlineSkills ? { inlineSkills: true } : {}),\n }\n const sandboxNeeds = sandboxRequirements({\n requirements: cfg,\n profile,\n interactive: context.interactive,\n ...opts,\n })\n const tools = toolRequirements(cfg)\n if (input.inlineSkills && declaresProfileSkills(profile))\n tools.push('skills are inlined by the loop')\n const loopOnly = loopOnlyGrants(cfg)\n const coveredAxes = profile ? declaredAxes(profile) : []\n const record = (\n chosen: ExecutionTier,\n reason: string,\n rejected: PlacementRecord['rejected'],\n ): PlacementRecord => ({ chosen, policy: PLACEMENT_POLICY, reason, coveredAxes, rejected })\n\n if (sandboxNeeds.length > 0) {\n const reason = sandboxNeeds.join('; ')\n if (loopOnly.length > 0) {\n throw new PlacementRefusedError(\n `${loopOnly.join(' and ')} run only in the worker, but ${reason}`,\n )\n }\n return record('sandbox', reason, [\n { placement: 'inference', reason },\n { placement: 'loop', reason },\n ])\n }\n\n // Past the sandbox check the profile is one the loop (and a single Router call) can\n // honour, so only granted tools separate the two.\n if (tools.length > 0) {\n const reason = `${tools.join('; ')}, which the worker holds without a sandbox`\n if (!context.loopAvailable) {\n const unavailable =\n context.loopUnavailableReason ?? 'worker placement is not wired on this deployment'\n if (loopOnly.length > 0) {\n throw new PlacementRefusedError(\n `${loopOnly.join(' and ')} need the worker, but ${unavailable}`,\n )\n }\n // Inlined skills are the one loop need a sandbox also meets, natively.\n return record('sandbox', unavailable, [\n { placement: 'inference', reason: tools.join('; ') },\n { placement: 'loop', reason: unavailable },\n ])\n }\n return record('loop', reason, [\n { placement: 'inference', reason: tools.join('; ') },\n { placement: 'sandbox', reason: 'costs more and no declared requirement needs it' },\n ])\n }\n\n if (context.inferenceAvailable) {\n return record('inference', 'no declared requirement needs tools, files or a session', [\n { placement: 'loop', reason: 'no declared tool needs a loop' },\n { placement: 'sandbox', reason: 'costs more and no declared requirement needs it' },\n ])\n }\n const unwired = 'inference is not wired on this deployment'\n if (context.loopAvailable) {\n return record('loop', `${unwired}; one tool-free worker turn`, [\n { placement: 'inference', reason: unwired },\n { placement: 'sandbox', reason: 'costs more and no declared requirement needs it' },\n ])\n }\n return record('sandbox', unwired, [\n { placement: 'inference', reason: unwired },\n { placement: 'loop', reason: 'worker placement is not wired on this deployment' },\n ])\n}\n\n/** The materialization axes a profile declares, in its own field order. Read from the\n * same field map as {@link loopProfileRefusal}, so the module has no runtime import. */\nfunction declaredAxes(profile: object): AgentProfileMaterializationAxis[] {\n const axes: AgentProfileMaterializationAxis[] = []\n for (const [field, value] of Object.entries(profile)) {\n if (value === undefined) continue\n if (TOP_LEVEL_AXES.has(field)) axes.push(field as AgentProfileMaterializationAxis)\n const children = COMPOUND_FIELDS[field]\n if (!children || typeof value !== 'object' || value === null) continue\n for (const [child, inner] of Object.entries(value)) {\n const axis = children[child]\n if (axis !== undefined && inner !== undefined) axes.push(axis)\n }\n }\n return axes\n}\n\nexport type {\n AgentProfileMaterializationAxis,\n KnownAgentProfileMaterializationAxis,\n} from '../agent/profile-materialization'\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
- import { A as verifyMaterializedWorkspace, B as omitTopLevelDigest, E as readVerifiedArtifact, F as canonicalCandidateDigest$1, I as canonicalCandidateDocument, L as deepFreezeCandidate, O as verifyBytes, P as canonicalCandidateBytes$1, R as embeddedCandidateArtifact, S as artifactCacheKey, T as readMaterializedWorkspaceFiles, U as hasControlCharacter, V as sha256Bytes$1, Z as errorMessage, b as isWellFormedUnicode, d as readCandidateGitHubResource, f as verifyCandidateCode, j as verifyWorkspaceSnapshotArtifacts, k as verifyMaterializedProfileWorkspace, m as verifyTaskOutcomePatch, p as verifyTaskCheckout, v as persistCandidateOutputArtifact, y as assertExactObjectKeys, z as immutableCandidateValue } from "./sandbox-profile-compat-BzVBFqrz.js";
2
- import { _ as parseAgentCandidateProfileActivation, c as buildLaunch, f as exactProfileExecutorFiles, h as modelRoutes, l as candidateEntrypointReceipt, m as materializeAgentCandidateProfilePlan, n as PI_CANDIDATE_SESSION_DIR_ENV, r as absoluteExecutionCwd, s as assertCandidateProfileExecutionSupport, t as PI_CANDIDATE_AGENT_DIR_ENV, u as candidateProfileAgentPaths, x as requirePublicCandidateConfigValue } from "./profile-Dpei_Sq0.js";
1
+ import { A as verifyWorkspaceSnapshotArtifacts, B as sha256Bytes$1, D as verifyBytes, E as readVerifiedArtifact, F as canonicalCandidateDocument, H as hasControlCharacter, I as deepFreezeCandidate, L as embeddedCandidateArtifact, N as canonicalCandidateBytes$1, O as verifyMaterializedProfileWorkspace, P as canonicalCandidateDigest$1, R as immutableCandidateValue, S as artifactCacheKey, T as readMaterializedWorkspaceFiles, X as errorMessage, b as isWellFormedUnicode, d as readCandidateGitHubResource, f as verifyCandidateCode, k as verifyMaterializedWorkspace, m as verifyTaskOutcomePatch, p as verifyTaskCheckout, v as persistCandidateOutputArtifact, y as assertExactObjectKeys, z as omitTopLevelDigest } from "./sandbox-profile-compat-DRr3JwDx.js";
2
+ import { _ as parseAgentCandidateProfileActivation, c as buildLaunch, f as exactProfileExecutorFiles, h as modelRoutes, l as candidateEntrypointReceipt, m as materializeAgentCandidateProfilePlan, n as PI_CANDIDATE_SESSION_DIR_ENV, r as absoluteExecutionCwd, s as assertCandidateProfileExecutionSupport, t as PI_CANDIDATE_AGENT_DIR_ENV, u as candidateProfileAgentPaths, x as requirePublicCandidateConfigValue } from "./profile-DToH0XEs.js";
3
3
  import { i as redactProtectedValue, n as createProtectedRecordRedactor, r as redactProtectedReason, t as assertNoProtectedBytes } from "./protected-redaction-DFCeo4vg.js";
4
4
  import { agentCandidateArtifactRefSchema, agentCandidateBenchmarkResultEvidenceSchema, agentCandidateBenchmarkSuiteSchema, agentCandidateBenchmarkTaskSchema, agentCandidateBundleSchema, agentCandidateContainerSchema, agentCandidateExecutionLimitsSchema, agentCandidateExecutionPlanEvidenceSchema, agentCandidateExecutionPlanMaterialSchema, agentCandidateMaterializationReceiptSchema, agentCandidateModelAccessNetworkSchema, agentCandidateModelSettlementEvidenceSchema, agentCandidateProfilePlanEvidenceSchema, agentCandidateResolvedModelSchema, agentCandidateRunCellSchema, agentCandidateRunReceiptSchema, agentCandidateTaskOutcomeEvidenceSchema, agentCandidateTaskOutcomeSpecSchema, agentCandidateTerminationSchema, agentCandidateWorkspaceManifestMaterialSchema, agentCandidateWorkspaceSnapshotEvidenceSchema, sha256DigestSchema } from "@tangle-network/agent-interface";
5
5
  import { REDACTION_VERSION, isLlmSpan } from "@tangle-network/agent-eval";
@@ -4085,4 +4085,4 @@ function assertEnvironmentDisjoint(publicEnv, traceEnv) {
4085
4085
  //#endregion
4086
4086
  export { verifiedResourceTextByDigest as C, AGENT_CANDIDATE_EXECUTION_SUPPORT as S, sealTerminalDigest as _, consumePreparedCandidateExecution as a, sealAgentCandidateModelSettlement as b, withinCandidateCleanupDeadline as c, assertTerminalMatchesClaim as d, assertTerminalMatchesStaged as f, requireStagedTerminal as g, rejectedStage as h, beginPreparedCandidateDisposal as i, assertRecoveryMatchesStaged as l, rejectedFinish as m, executePreparedAgentCandidate as n, candidateClaimFileInternals as o, recoveredTerminalRecord as p, assertPreparedCandidateIntegrity as r, candidateCleanupDeadline as s, prepareAgentCandidateExecution as t, assertTerminalAllowedInPhase as u, terminalRecord as v, verifyAgentCandidateBundle as w, exactProcessProviderAsCandidateExecutor as x, createProtectedAgentCandidateModelPort as y };
4087
4087
 
4088
- //# sourceMappingURL=prepare-bXkvPMJA.js.map
4088
+ //# sourceMappingURL=prepare-FzJoS68l.js.map