@tangle-network/agent-runtime 0.344.0 → 0.349.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 (82) hide show
  1. package/dist/{activation-C5rFWXGj.js → activation-tNGW73Y9.js} +4 -4
  2. package/dist/{activation-C5rFWXGj.js.map → activation-tNGW73Y9.js.map} +1 -1
  3. package/dist/{activation-CGtGr0qu.d.ts → activation-wBipBkCf.d.ts} +2 -2
  4. package/dist/agent.d.ts +1 -1
  5. package/dist/agent.js +3 -3
  6. package/dist/{bundle-DKQtOO9d.js → bundle-1xaJEgjq.js} +2 -2
  7. package/dist/{bundle-DKQtOO9d.js.map → bundle-1xaJEgjq.js.map} +1 -1
  8. package/dist/candidate-execution/index.js +4 -4
  9. package/dist/{coordination-manager-T98ZbsEt.js → coordination-manager-B2Z_MmL9.js} +22 -19
  10. package/dist/coordination-manager-B2Z_MmL9.js.map +1 -0
  11. package/dist/{delegate-CjdtLf1v.js → delegate-Dr-63jFl.js} +2 -2
  12. package/dist/{delegate-CjdtLf1v.js.map → delegate-Dr-63jFl.js.map} +1 -1
  13. package/dist/durable.d.ts +7 -2
  14. package/dist/durable.js +129 -117
  15. package/dist/durable.js.map +1 -1
  16. package/dist/{graph-kgzTaNMA.js → graph-DPejYB3C.js} +6 -5
  17. package/dist/graph-DPejYB3C.js.map +1 -0
  18. package/dist/{improve-DevAd83a.js → improve-DwSahPi1.js} +7 -7
  19. package/dist/{improve-DevAd83a.js.map → improve-DwSahPi1.js.map} +1 -1
  20. package/dist/index.d.ts +3 -3
  21. package/dist/index.js +9 -9
  22. package/dist/intelligence.d.ts +2 -2
  23. package/dist/intelligence.js +6 -6
  24. package/dist/kernel.d.ts +2 -2
  25. package/dist/kernel.js +11 -11
  26. package/dist/{loop-runner-DsVqEv8O.d.ts → loop-runner-C6pGq-2l.d.ts} +2 -2
  27. package/dist/{loop-runner-BM509Zmp.js → loop-runner-CP6MfO1b.js} +2 -2
  28. package/dist/{loop-runner-BM509Zmp.js.map → loop-runner-CP6MfO1b.js.map} +1 -1
  29. package/dist/loop-runner-bin.d.ts +1 -1
  30. package/dist/loop-runner-bin.js +1 -1
  31. package/dist/mcp/bin.js +3 -3
  32. package/dist/mcp/index.d.ts +1 -1
  33. package/dist/mcp/index.js +6 -6
  34. package/dist/mcp/index.js.map +1 -1
  35. package/dist/model-policy-CyCUwHed.js.map +1 -1
  36. package/dist/{prepare-TEEHSJwq.js → prepare-B9wWffjO.js} +3 -3
  37. package/dist/{prepare-TEEHSJwq.js.map → prepare-B9wWffjO.js.map} +1 -1
  38. package/dist/{profile-6OLpbjvV.js → profile-JLJd2wFG.js} +2 -2
  39. package/dist/{profile-6OLpbjvV.js.map → profile-JLJd2wFG.js.map} +1 -1
  40. package/dist/profiles.d.ts +1 -1
  41. package/dist/{provision-supervisor-CysVhSqV.js → provision-supervisor-C4v2vqDo.js} +6 -6
  42. package/dist/{provision-supervisor-CysVhSqV.js.map → provision-supervisor-C4v2vqDo.js.map} +1 -1
  43. package/dist/{durable-file-DLcrzrjL.js → record-events-D93DkpAx.js} +100 -3
  44. package/dist/record-events-D93DkpAx.js.map +1 -0
  45. package/dist/{run-cancellation-CEDiqfhC.js → run-cancellation-CzFhQGMa.js} +11 -9
  46. package/dist/run-cancellation-CzFhQGMa.js.map +1 -0
  47. package/dist/{runtime-B_93oWEv.js → runtime-CsvGCQpl.js} +448 -55
  48. package/dist/runtime-CsvGCQpl.js.map +1 -0
  49. package/dist/{sandbox-profile-compat-_fEtHPCj.js → sandbox-profile-compat-DtuJWFrM.js} +2 -2
  50. package/dist/sandbox-profile-compat-DtuJWFrM.js.map +1 -0
  51. package/dist/{search-executor-t05_9Wwk.js → search-executor-CCb82Umc.js} +5 -5
  52. package/dist/search-executor-CCb82Umc.js.map +1 -0
  53. package/dist/{server-DjrVWzH2.js → server-CKPwZU-I.js} +4 -4
  54. package/dist/{server-DjrVWzH2.js.map → server-CKPwZU-I.js.map} +1 -1
  55. package/dist/{structural-rollout-CpJknIaL.js → structural-rollout-DylC_fwU.js} +3 -3
  56. package/dist/{structural-rollout-CpJknIaL.js.map → structural-rollout-DylC_fwU.js.map} +1 -1
  57. package/dist/{supervise-BuVVC7Bl.js → supervise-BGfm2GlY.js} +293 -87
  58. package/dist/supervise-BGfm2GlY.js.map +1 -0
  59. package/dist/{supervisor-D3M9ab1v.js → supervisor-wS9Ehz_x.js} +454 -187
  60. package/dist/supervisor-wS9Ehz_x.js.map +1 -0
  61. package/dist/testing.d.ts +2 -2
  62. package/dist/testing.js +13 -13
  63. package/dist/{top-snapshot-BGYI6G6K.js → top-snapshot-CflmaDgk.js} +6 -4
  64. package/dist/top-snapshot-CflmaDgk.js.map +1 -0
  65. package/dist/tui/bin.js +2 -2
  66. package/dist/tui/index.d.ts +1 -1
  67. package/dist/tui/index.js +2 -2
  68. package/dist/{types-DztCeYd6.d.ts → types-C8BGOCBn.d.ts} +286 -34
  69. package/package.json +11 -11
  70. package/skills/agent-graphs/SKILL.md +40 -2
  71. package/skills/graph-designer/SKILL.md +6 -0
  72. package/skills/profile-authoring/SKILL.md +1 -1
  73. package/dist/coordination-manager-T98ZbsEt.js.map +0 -1
  74. package/dist/durable-file-DLcrzrjL.js.map +0 -1
  75. package/dist/graph-kgzTaNMA.js.map +0 -1
  76. package/dist/run-cancellation-CEDiqfhC.js.map +0 -1
  77. package/dist/runtime-B_93oWEv.js.map +0 -1
  78. package/dist/sandbox-profile-compat-_fEtHPCj.js.map +0 -1
  79. package/dist/search-executor-t05_9Wwk.js.map +0 -1
  80. package/dist/supervise-BuVVC7Bl.js.map +0 -1
  81. package/dist/supervisor-D3M9ab1v.js.map +0 -1
  82. package/dist/top-snapshot-BGYI6G6K.js.map +0 -1
@@ -1,5 +1,5 @@
1
1
  import { i as ConfigError } from "./errors-DodWX-cb.js";
2
- import { t as supervise } from "./supervise-BuVVC7Bl.js";
2
+ import { t as supervise } from "./supervise-BGfm2GlY.js";
3
3
  import { agentProfileSchema } from "@tangle-network/agent-interface";
4
4
  import { computeFindingId, makeFinding } from "@tangle-network/agent-eval";
5
5
  //#region src/runtime/supervise/authoring.ts
@@ -207,4 +207,4 @@ async function delegate(intent, opts) {
207
207
  //#endregion
208
208
  export { defaultProfileRichnessThresholds as a, assessAuthoredProfile as i, delegate as n, profileRichnessFinding as o, asAuthoredProfile as r, supervisorInstructions as s, defaultDelegateBudget as t };
209
209
 
210
- //# sourceMappingURL=delegate-CjdtLf1v.js.map
210
+ //# sourceMappingURL=delegate-Dr-63jFl.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"delegate-CjdtLf1v.js","names":[],"sources":["../src/runtime/supervise/authoring.ts","../src/runtime/supervise/delegate.ts"],"sourcesContent":["/**\n *\n * The supervisor's intelligence is AUTHORING the agents it spawns — not pressing buttons.\n *\n * Every agent here is three things: instructions (system prompt), tools, and a model — its\n * `AgentProfile`. The supervisor's job is to WRITE those profiles: read the task, decompose it,\n * and for each sub-task author a tailored worker recipe. `supervisorInstructions` is the how-to the\n * supervisor reads; canonical Runtime executors materialize the resulting profile.\n *\n * The skill is the single OPTIMIZABLE surface: edit it → the supervisor designs better agents.\n * That is the self-improvement lever (the prompt/skill lever), not the execution plumbing.\n *\n * @experimental\n */\n\nimport { type AnalystFinding, computeFindingId, makeFinding } from '@tangle-network/agent-eval'\nimport {\n type AgentProfile,\n type AgentProfilePrompt,\n agentProfileSchema,\n} from '@tangle-network/agent-interface'\n\n/** What the supervisor AUTHORS per sub-task: one complete canonical profile whose name and\n * task-specific system prompt are present. Every other `AgentProfile` axis is preserved exactly. */\nexport type AuthoredProfile = AgentProfile & {\n readonly name: string\n readonly prompt: AgentProfilePrompt & { readonly systemPrompt: string }\n}\n\n/** Narrow an untyped `spawn_worker` profile argument to an `AuthoredProfile`, or null if the\n * supervisor failed to author one (empty/placeholder profile — a skill violation worth catching). */\nexport function asAuthoredProfile(raw: unknown): AuthoredProfile | null {\n const parsed = agentProfileSchema.safeParse(raw)\n if (!parsed.success) return null\n const systemPrompt = parsed.data.prompt?.systemPrompt\n if (typeof systemPrompt !== 'string' || systemPrompt.trim().length === 0) return null\n return {\n ...parsed.data,\n name:\n typeof parsed.data.name === 'string' && parsed.data.name.length > 0\n ? parsed.data.name\n : 'worker',\n prompt: { ...parsed.data.prompt, systemPrompt },\n }\n}\n\n/** The supervisor skill: an explicit profile-authoring instruction, never an implicit Runtime\n * policy. Editing this text changes how a profile designs the descendants it spawns. */\nexport function supervisorInstructions(opts?: { goal?: string }): string {\n return [\n 'Your delegation craft is AUTHORING: a spawned worker is exactly as good as the profile you write.',\n '',\n 'For the task you are given:',\n '1. DECOMPOSE it into the smallest set of sub-tasks a single focused worker can each deliver.',\n '2. For EACH sub-task, AUTHOR a worker by calling spawn_worker with a COMPLETE `profile`:',\n ' • name and description: who this specialist is and why it exists.',\n ' • prompt.systemPrompt: rich instructions for THIS sub-task — exact output, process, evidence, and what \"done\" means.',\n ' • model.default, model.reasoningEffort, and harness: choose the execution system deliberately when the task benefits from it.',\n ' • tools, mcp, resources.skills/files/instructions, hooks, subagents, permissions, and modes: grant or attach every capability the worker needs; omit an axis only when it is intentionally unnecessary.',\n ' • tools.agent_runtime_coordination_spawn_worker: true ONLY when this child should author and drive descendants. Add only the other agent_runtime_coordination_<verb> tools it will call, such as observe_agent or steer_agent.',\n ' • A child with spawn_worker MUST carry this complete profile-authoring instruction as an immutable resources.skills entry with resources.failOnError: true, so it can author its own descendants from the same contract.',\n ' • metadata may describe the work, but it never grants recursion or selects a Runtime execution path.',\n ' NEVER spawn a worker with an empty profile. The quality of the worker IS the quality of the profile you write.',\n '3. End your turn while your workers run: Runtime wakes you with each settled worker. A settlement says valid:true only if the deployable check passed.',\n '4. If a worker did NOT deliver, AUTHOR A NEW profile whose prompt.systemPrompt names the SPECIFIC failure and how to fix it — never just retry the same profile.',\n \"5. read_journal to re-read YOUR OWN record before you decide the next move: every spawn you made, every settle, every question and answer, every steer, every analyst finding — oldest first, this node only, including what you did before a restart. Use it to see what you already tried instead of trying it again. It is paged: pass the returned nextRow as the next call's sinceRow, narrow with kinds, and raise limit/maxBytes only as far as you will actually read. A truncated:true page means a bound cut it short — keep paging before you conclude you have read everything.\",\n '6. AUTHOR YOUR OWN LENS when the questions you can already ask of a settled trace do not cover the failure you are chasing: define_analyst takes an id, a description, an area, the question in your own words, the instructions for answering it with trace evidence, and the smallest toolGroup that can answer it (model is the seat it runs on; omit it for the run default). It is DATA, never code. Then run_analyst it on any settled worker like a lens the run shipped with, and read the finding. list_analysts shows what you have. Define a lens when you need a different question asked — not a second copy of a question already on the menu.',\n '7. EVERY refusal you get back carries a `reason` naming the exact unmet condition. Read it and change that condition — a spawn refused for budget-exhausted needs a smaller budget or the refund of a settled worker, an invalid-profile needs the named field fixed, a submit_result refused because the check THREW is a broken check to report, not a result to resubmit. Never repeat a call that was refused without changing what it was refused for.',\n '8. ask_parent ONLY when you genuinely cannot decide, and then READ ITS OUTCOME. \"queued-for-parent\" means an inbox above you now holds the question. \"no-parent\" means no inbox above you is configured to receive it: the question is still on the run record for anyone watching, but nothing will route an answer back to you, so do not block. Decide it with answer_question, or answer_question with deferReason to record that it stays open, and carry on — a blocking question left undecided also refuses your stop.',\n '9. Once the work is delivered, end your turn with nothing left running.',\n ...(opts?.goal ? ['', `The goal: ${opts.goal}`] : []),\n ].join('\\n')\n}\n\n// ── Profile-richness gate ────────────────────────────────────────────────────\n//\n// The supervisor's product is the worker PROFILE it authors. The failure mode the existing\n// gates miss: `asAuthoredProfile` / `local-harness` only reject a FULLY EMPTY system prompt —\n// a two-sentence stub passes. `assessAuthoredProfile` OBSERVES the authored artifact (it reads\n// no judge verdict, so it steers cleanly past `assertTraceDerivedFindings`) and flags THIN:\n// a short/few-line system prompt, OR no tools, OR no skills, OR no MCP when the task needs one.\n// It emits a real `AnalystFinding` so it rides the SAME coordination bus the driver pulls via\n// receives in its next wake — the supervisor can self-correct and re-author richer.\n\n/** Thresholds below which a system prompt is treated as a thin stub. Tunable per call. */\nexport interface ProfileRichnessThresholds {\n /** A prompt shorter than this many characters is thin (default 600). */\n readonly minSystemPromptChars: number\n /** A prompt with fewer than this many non-blank lines is thin (default 6). */\n readonly minSystemPromptLines: number\n}\n\n/** Default thresholds for `ProfileRichnessThresholds` — 600 chars / 6 lines minimum system prompt. */\nexport const defaultProfileRichnessThresholds: ProfileRichnessThresholds = {\n minSystemPromptChars: 600,\n minSystemPromptLines: 6,\n}\n\n/** Per-field verdict on one authored profile — the raw material the bench renders + scores. */\nexport interface ProfileRichness {\n readonly name: string\n /** The resolved system prompt (canonical `prompt.systemPrompt`, the sandbox `prompt.system`\n * convention, or a bare-string prompt — whichever the author used). */\n readonly systemPrompt: string\n readonly systemPromptChars: number\n readonly systemPromptLines: number\n readonly sentenceCount: number\n readonly hasDescription: boolean\n readonly hasTools: boolean\n readonly hasSkills: boolean\n readonly hasMcp: boolean\n readonly hasSubagents: boolean\n /** 0..1 — fraction of richness signals present (prompt-depth + the four levers). */\n readonly richness: number\n /** True when the supervisor authored a stub instead of a real profile. */\n readonly thin: boolean\n /** The specific reasons it is thin (empty when rich) — used in the finding's action. */\n readonly reasons: string[]\n}\n\n/** Read the system prompt from any authored shape: canonical `prompt.systemPrompt`, the sandbox\n * `prompt.system` convention, or a bare-string `prompt`. */\nfunction resolveSystemPrompt(profile: AgentProfile): string {\n const pr = (profile as { prompt?: unknown }).prompt\n if (typeof pr === 'string') return pr\n if (pr && typeof pr === 'object') {\n const o = pr as { systemPrompt?: unknown; system?: unknown }\n if (typeof o.systemPrompt === 'string') return o.systemPrompt\n if (typeof o.system === 'string') return o.system\n }\n return ''\n}\n\n/** OBSERVE one authored `AgentProfile` and score its richness (no judge verdict is read). The task\n * context (`needsMcp`) lets a domain say \"this work needs a data/tool MCP\" so a missing MCP counts. */\nexport function assessAuthoredProfile(\n profile: AgentProfile,\n opts?: { needsMcp?: boolean; thresholds?: Partial<ProfileRichnessThresholds> },\n): ProfileRichness {\n const th = { ...defaultProfileRichnessThresholds, ...(opts?.thresholds ?? {}) }\n const systemPrompt = resolveSystemPrompt(profile)\n const trimmed = systemPrompt.trim()\n const systemPromptChars = trimmed.length\n const systemPromptLines = trimmed\n ? trimmed.split('\\n').filter((l) => l.trim().length > 0).length\n : 0\n const sentenceCount = trimmed\n ? (trimmed.match(/[.!?](\\s|$)/g) ?? []).length || (trimmed ? 1 : 0)\n : 0\n const hasDescription =\n typeof profile.description === 'string' && profile.description.trim().length > 0\n const tools = (profile as { tools?: Record<string, unknown> }).tools\n const hasTools = !!tools && Object.keys(tools).length > 0\n const skills = (profile.resources as { skills?: unknown[] } | undefined)?.skills\n const hasSkills = Array.isArray(skills) && skills.length > 0\n const mcp = (profile as { mcp?: Record<string, unknown> }).mcp\n const hasMcp = !!mcp && Object.keys(mcp).length > 0\n const subagents = (profile as { subagents?: Record<string, unknown> }).subagents\n const hasSubagents = !!subagents && Object.keys(subagents).length > 0\n\n const reasons: string[] = []\n const promptThin =\n systemPromptChars < th.minSystemPromptChars || systemPromptLines < th.minSystemPromptLines\n if (promptThin)\n reasons.push(\n `system prompt is thin (${systemPromptChars} chars, ${systemPromptLines} lines; need ≥${th.minSystemPromptChars} chars and ≥${th.minSystemPromptLines} lines)`,\n )\n if (!hasTools)\n reasons.push('no tools granted (a worker can only act through the tools you grant it)')\n if (!hasSkills) reasons.push('no skills attached (no reusable how-to notes injected)')\n if (opts?.needsMcp && !hasMcp) reasons.push('no MCP server, but the task needs data/tool access')\n\n // Richness = fraction of signals present. Prompt-depth is one signal; the four levers are the rest.\n const signals = [!promptThin, hasTools, hasSkills, hasDescription, opts?.needsMcp ? hasMcp : true]\n const richness = signals.filter(Boolean).length / signals.length\n // THIN ⟺ the prompt is a stub OR the worker has no levers at all (no tools AND no skills AND no mcp).\n const thin = promptThin || (!hasTools && !hasSkills && !hasMcp)\n\n return {\n name: profile.name ?? 'worker',\n systemPrompt,\n systemPromptChars,\n systemPromptLines,\n sentenceCount,\n hasDescription,\n hasTools,\n hasSkills,\n hasMcp,\n hasSubagents,\n richness,\n thin,\n reasons,\n }\n}\n\n/** Turn a {@link ProfileRichness} verdict into a bus-routable `AnalystFinding` (area `profile-quality`).\n * Severity scales with thinness; the recommended action names the MISSING lever so the supervisor can\n * re-author. `subject` = the worker name so per-worker findings diff cleanly across re-authors. */\nexport function profileRichnessFinding(\n richness: ProfileRichness,\n opts?: { analystId?: string; runId?: string },\n): AnalystFinding {\n const analyst_id = opts?.analystId ?? 'profile-richness'\n const subject = richness.name\n const claim = richness.thin\n ? `Worker \"${richness.name}\" was authored as a THIN profile: ${richness.reasons.join('; ')}.`\n : `Worker \"${richness.name}\" was authored as a rich profile (richness ${(richness.richness * 100).toFixed(0)}%).`\n const severity: AnalystFinding['severity'] = richness.thin\n ? richness.richness < 0.25\n ? 'high'\n : 'medium'\n : 'info'\n return makeFinding({\n analyst_id,\n severity,\n area: 'profile-quality',\n claim,\n subject,\n confidence: 0.9,\n evidence_refs: [\n {\n kind: 'metric',\n uri: `profile:${subject}`,\n excerpt: `chars=${richness.systemPromptChars} lines=${richness.systemPromptLines} tools=${richness.hasTools} skills=${richness.hasSkills} mcp=${richness.hasMcp} richness=${richness.richness.toFixed(2)}`,\n },\n ],\n ...(richness.thin\n ? { recommended_action: `Re-author \"${richness.name}\" with: ${richness.reasons.join('; ')}.` }\n : {}),\n id_basis: computeFindingId({\n analyst_id,\n area: 'profile-quality',\n subject,\n claim: `richness:${richness.thin ? 'thin' : 'rich'}`,\n }),\n })\n}\n","/**\n *\n * `delegate` — the one generic delegation verb. You hand it an INTENT (what you want done) and it\n * hands that intent to a default AUTHORING supervisor: a router-brained supervisor whose standing\n * instruction is `supervisorInstructions()` (the authoring-agent-profiles skill). The supervisor\n * DECOMPOSES the intent and AUTHORS the worker profile it needs per sub-task — there is NO hardcoded\n * coder/researcher profile here. That is the whole point: `delegate('fix the failing test', …)` and\n * `delegate('research X and cite sources', …)` route through the SAME front door; the supervisor\n * writes a code-shaped or research-shaped worker on its own.\n *\n * It is a thin wrapper over `supervise()` — the one front door — so the conserved-budget pool, the\n * completion oracle (`deliverable`), the coordination toolbox, and equal-compute accounting all come\n * for free; nothing is hand-rolled. The result is `supervise()`'s `SupervisedResult` returned\n * UNCHANGED, so its `spentTotal` (`{ iterations, tokens, usd, ms }`) rides straight back to the\n * caller on BOTH paths — a `winner` carries the delivered worker's spend, a `no-winner` carries the\n * spend incurred before it failed. That cost channel means a `delegate()` caller always learns what\n * the delegation actually spent.\n *\n * @experimental\n */\n\nimport { ConfigError } from '../../errors'\nimport type { RouterTransportConfig } from '../router-http-transport'\nimport type { DeliverableSpec } from './completion-gate'\nimport type { ExecutorConfig } from './runtime'\nimport { supervise } from './supervise'\nimport type { SupervisorProfile } from './supervisor-agent'\nimport type { Budget, SupervisedResult } from './types'\n\n/** The conserved pool a `delegate()` call applies when the caller does not pass its own `budget`.\n * A modest token ceiling + a small iteration ceiling — generous enough for a few-worker decompose,\n * bounded enough that an unsupervised intent cannot run away. Callers override via `opts.budget`. */\nexport const defaultDelegateBudget: Budget = { maxIterations: 50, maxTokens: 200_000 }\n\n/** Inputs to {@link delegate}. The intent is the first positional arg; everything here is optional\n * with explicit execution identity, so the common call names one exact supervisor profile. */\nexport interface DelegateOptions<Out = unknown> {\n /** The completion oracle (settled ⟺ delivered) the authored workers settle against. Strongly\n * recommended — without it the supervisor trusts a worker's self-report. For a code intent,\n * `patchDelivered()` is the canonical example; for a free-form answer, a content check. */\n readonly deliverable?: DeliverableSpec<Out>\n /** WHERE the authored workers run — the worker-execution backend (`router-tools` / `sandbox` /\n * `cli-worktree` / …). The supervisor authors the worker PROFILE; this is the substrate it runs\n * on. Provide this OR `makeWorkerAgent`-style wiring through `supervise()` is unavailable. */\n readonly backend?: ExecutorConfig\n /** The conserved compute pool for the whole delegation. Defaults to {@link defaultDelegateBudget}. */\n readonly budget?: Budget\n /** Exact executable authoring supervisor. Model, prompt, harness, and provider live here. */\n readonly supervisorProfile: SupervisorProfile\n /** Router endpoint/auth for a `cli-base` supervisor; contains no behavioral settings. */\n readonly router: RouterTransportConfig\n /** Restrict the run to this subset of models (forwarded to `supervise()`). */\n readonly allowedModels?: readonly string[]\n readonly runId?: string\n}\n\n/**\n * Delegate an INTENT to a default authoring supervisor and return its `SupervisedResult` unchanged.\n *\n * The supervisor authors + spawns whatever worker the intent needs over the conserved-budget pool;\n * `result.spentTotal` reports what the whole delegation actually cost. A `winner` result carries the\n * authored worker's delivered output; a `no-winner` result names why (never a fabricated success).\n */\nexport async function delegate<Out = unknown>(\n intent: string,\n opts: DelegateOptions<Out>,\n): Promise<SupervisedResult<Out>> {\n if (typeof intent !== 'string' || intent.trim().length === 0) {\n throw new ConfigError('delegate: `intent` must be a non-empty string')\n }\n return supervise(opts.supervisorProfile, intent, {\n budget: opts.budget ?? defaultDelegateBudget,\n ...(opts.backend ? { backend: opts.backend } : {}),\n ...(opts.deliverable ? { deliverable: opts.deliverable as DeliverableSpec<unknown> } : {}),\n router: opts.router,\n ...(opts.allowedModels ? { allowedModels: opts.allowedModels } : {}),\n ...(opts.runId ? { runId: opts.runId } : {}),\n }) as Promise<SupervisedResult<Out>>\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"delegate-Dr-63jFl.js","names":[],"sources":["../src/runtime/supervise/authoring.ts","../src/runtime/supervise/delegate.ts"],"sourcesContent":["/**\n *\n * The supervisor's intelligence is AUTHORING the agents it spawns — not pressing buttons.\n *\n * Every agent here is three things: instructions (system prompt), tools, and a model — its\n * `AgentProfile`. The supervisor's job is to WRITE those profiles: read the task, decompose it,\n * and for each sub-task author a tailored worker recipe. `supervisorInstructions` is the how-to the\n * supervisor reads; canonical Runtime executors materialize the resulting profile.\n *\n * The skill is the single OPTIMIZABLE surface: edit it → the supervisor designs better agents.\n * That is the self-improvement lever (the prompt/skill lever), not the execution plumbing.\n *\n * @experimental\n */\n\nimport { type AnalystFinding, computeFindingId, makeFinding } from '@tangle-network/agent-eval'\nimport {\n type AgentProfile,\n type AgentProfilePrompt,\n agentProfileSchema,\n} from '@tangle-network/agent-interface'\n\n/** What the supervisor AUTHORS per sub-task: one complete canonical profile whose name and\n * task-specific system prompt are present. Every other `AgentProfile` axis is preserved exactly. */\nexport type AuthoredProfile = AgentProfile & {\n readonly name: string\n readonly prompt: AgentProfilePrompt & { readonly systemPrompt: string }\n}\n\n/** Narrow an untyped `spawn_worker` profile argument to an `AuthoredProfile`, or null if the\n * supervisor failed to author one (empty/placeholder profile — a skill violation worth catching). */\nexport function asAuthoredProfile(raw: unknown): AuthoredProfile | null {\n const parsed = agentProfileSchema.safeParse(raw)\n if (!parsed.success) return null\n const systemPrompt = parsed.data.prompt?.systemPrompt\n if (typeof systemPrompt !== 'string' || systemPrompt.trim().length === 0) return null\n return {\n ...parsed.data,\n name:\n typeof parsed.data.name === 'string' && parsed.data.name.length > 0\n ? parsed.data.name\n : 'worker',\n prompt: { ...parsed.data.prompt, systemPrompt },\n }\n}\n\n/** The supervisor skill: an explicit profile-authoring instruction, never an implicit Runtime\n * policy. Editing this text changes how a profile designs the descendants it spawns. */\nexport function supervisorInstructions(opts?: { goal?: string }): string {\n return [\n 'Your delegation craft is AUTHORING: a spawned worker is exactly as good as the profile you write.',\n '',\n 'For the task you are given:',\n '1. DECOMPOSE it into the smallest set of sub-tasks a single focused worker can each deliver.',\n '2. For EACH sub-task, AUTHOR a worker by calling spawn_worker with a COMPLETE `profile`:',\n ' • name and description: who this specialist is and why it exists.',\n ' • prompt.systemPrompt: rich instructions for THIS sub-task — exact output, process, evidence, and what \"done\" means.',\n ' • model.default, model.reasoningEffort, and harness: choose the execution system deliberately when the task benefits from it.',\n ' • tools, mcp, resources.skills/files/instructions, hooks, subagents, permissions, and modes: grant or attach every capability the worker needs; omit an axis only when it is intentionally unnecessary.',\n ' • tools.agent_runtime_coordination_spawn_worker: true ONLY when this child should author and drive descendants. Add only the other agent_runtime_coordination_<verb> tools it will call, such as observe_agent or steer_agent.',\n ' • A child with spawn_worker MUST carry this complete profile-authoring instruction as an immutable resources.skills entry with resources.failOnError: true, so it can author its own descendants from the same contract.',\n ' • metadata may describe the work, but it never grants recursion or selects a Runtime execution path.',\n ' NEVER spawn a worker with an empty profile. The quality of the worker IS the quality of the profile you write.',\n '3. End your turn while your workers run: Runtime wakes you with each settled worker. A settlement says valid:true only if the deployable check passed.',\n '4. If a worker did NOT deliver, AUTHOR A NEW profile whose prompt.systemPrompt names the SPECIFIC failure and how to fix it — never just retry the same profile.',\n \"5. read_journal to re-read YOUR OWN record before you decide the next move: every spawn you made, every settle, every question and answer, every steer, every analyst finding — oldest first, this node only, including what you did before a restart. Use it to see what you already tried instead of trying it again. It is paged: pass the returned nextRow as the next call's sinceRow, narrow with kinds, and raise limit/maxBytes only as far as you will actually read. A truncated:true page means a bound cut it short — keep paging before you conclude you have read everything.\",\n '6. AUTHOR YOUR OWN LENS when the questions you can already ask of a settled trace do not cover the failure you are chasing: define_analyst takes an id, a description, an area, the question in your own words, the instructions for answering it with trace evidence, and the smallest toolGroup that can answer it (model is the seat it runs on; omit it for the run default). It is DATA, never code. Then run_analyst it on any settled worker like a lens the run shipped with, and read the finding. list_analysts shows what you have. Define a lens when you need a different question asked — not a second copy of a question already on the menu.',\n '7. EVERY refusal you get back carries a `reason` naming the exact unmet condition. Read it and change that condition — a spawn refused for budget-exhausted needs a smaller budget or the refund of a settled worker, an invalid-profile needs the named field fixed, a submit_result refused because the check THREW is a broken check to report, not a result to resubmit. Never repeat a call that was refused without changing what it was refused for.',\n '8. ask_parent ONLY when you genuinely cannot decide, and then READ ITS OUTCOME. \"queued-for-parent\" means an inbox above you now holds the question. \"no-parent\" means no inbox above you is configured to receive it: the question is still on the run record for anyone watching, but nothing will route an answer back to you, so do not block. Decide it with answer_question, or answer_question with deferReason to record that it stays open, and carry on — a blocking question left undecided also refuses your stop.',\n '9. Once the work is delivered, end your turn with nothing left running.',\n ...(opts?.goal ? ['', `The goal: ${opts.goal}`] : []),\n ].join('\\n')\n}\n\n// ── Profile-richness gate ────────────────────────────────────────────────────\n//\n// The supervisor's product is the worker PROFILE it authors. The failure mode the existing\n// gates miss: `asAuthoredProfile` / `local-harness` only reject a FULLY EMPTY system prompt —\n// a two-sentence stub passes. `assessAuthoredProfile` OBSERVES the authored artifact (it reads\n// no judge verdict, so it steers cleanly past `assertTraceDerivedFindings`) and flags THIN:\n// a short/few-line system prompt, OR no tools, OR no skills, OR no MCP when the task needs one.\n// It emits a real `AnalystFinding` so it rides the SAME coordination bus the driver pulls via\n// receives in its next wake — the supervisor can self-correct and re-author richer.\n\n/** Thresholds below which a system prompt is treated as a thin stub. Tunable per call. */\nexport interface ProfileRichnessThresholds {\n /** A prompt shorter than this many characters is thin (default 600). */\n readonly minSystemPromptChars: number\n /** A prompt with fewer than this many non-blank lines is thin (default 6). */\n readonly minSystemPromptLines: number\n}\n\n/** Default thresholds for `ProfileRichnessThresholds` — 600 chars / 6 lines minimum system prompt. */\nexport const defaultProfileRichnessThresholds: ProfileRichnessThresholds = {\n minSystemPromptChars: 600,\n minSystemPromptLines: 6,\n}\n\n/** Per-field verdict on one authored profile — the raw material the bench renders + scores. */\nexport interface ProfileRichness {\n readonly name: string\n /** The resolved system prompt (canonical `prompt.systemPrompt`, the sandbox `prompt.system`\n * convention, or a bare-string prompt — whichever the author used). */\n readonly systemPrompt: string\n readonly systemPromptChars: number\n readonly systemPromptLines: number\n readonly sentenceCount: number\n readonly hasDescription: boolean\n readonly hasTools: boolean\n readonly hasSkills: boolean\n readonly hasMcp: boolean\n readonly hasSubagents: boolean\n /** 0..1 — fraction of richness signals present (prompt-depth + the four levers). */\n readonly richness: number\n /** True when the supervisor authored a stub instead of a real profile. */\n readonly thin: boolean\n /** The specific reasons it is thin (empty when rich) — used in the finding's action. */\n readonly reasons: string[]\n}\n\n/** Read the system prompt from any authored shape: canonical `prompt.systemPrompt`, the sandbox\n * `prompt.system` convention, or a bare-string `prompt`. */\nfunction resolveSystemPrompt(profile: AgentProfile): string {\n const pr = (profile as { prompt?: unknown }).prompt\n if (typeof pr === 'string') return pr\n if (pr && typeof pr === 'object') {\n const o = pr as { systemPrompt?: unknown; system?: unknown }\n if (typeof o.systemPrompt === 'string') return o.systemPrompt\n if (typeof o.system === 'string') return o.system\n }\n return ''\n}\n\n/** OBSERVE one authored `AgentProfile` and score its richness (no judge verdict is read). The task\n * context (`needsMcp`) lets a domain say \"this work needs a data/tool MCP\" so a missing MCP counts. */\nexport function assessAuthoredProfile(\n profile: AgentProfile,\n opts?: { needsMcp?: boolean; thresholds?: Partial<ProfileRichnessThresholds> },\n): ProfileRichness {\n const th = { ...defaultProfileRichnessThresholds, ...(opts?.thresholds ?? {}) }\n const systemPrompt = resolveSystemPrompt(profile)\n const trimmed = systemPrompt.trim()\n const systemPromptChars = trimmed.length\n const systemPromptLines = trimmed\n ? trimmed.split('\\n').filter((l) => l.trim().length > 0).length\n : 0\n const sentenceCount = trimmed\n ? (trimmed.match(/[.!?](\\s|$)/g) ?? []).length || (trimmed ? 1 : 0)\n : 0\n const hasDescription =\n typeof profile.description === 'string' && profile.description.trim().length > 0\n const tools = (profile as { tools?: Record<string, unknown> }).tools\n const hasTools = !!tools && Object.keys(tools).length > 0\n const skills = (profile.resources as { skills?: unknown[] } | undefined)?.skills\n const hasSkills = Array.isArray(skills) && skills.length > 0\n const mcp = (profile as { mcp?: Record<string, unknown> }).mcp\n const hasMcp = !!mcp && Object.keys(mcp).length > 0\n const subagents = (profile as { subagents?: Record<string, unknown> }).subagents\n const hasSubagents = !!subagents && Object.keys(subagents).length > 0\n\n const reasons: string[] = []\n const promptThin =\n systemPromptChars < th.minSystemPromptChars || systemPromptLines < th.minSystemPromptLines\n if (promptThin)\n reasons.push(\n `system prompt is thin (${systemPromptChars} chars, ${systemPromptLines} lines; need ≥${th.minSystemPromptChars} chars and ≥${th.minSystemPromptLines} lines)`,\n )\n if (!hasTools)\n reasons.push('no tools granted (a worker can only act through the tools you grant it)')\n if (!hasSkills) reasons.push('no skills attached (no reusable how-to notes injected)')\n if (opts?.needsMcp && !hasMcp) reasons.push('no MCP server, but the task needs data/tool access')\n\n // Richness = fraction of signals present. Prompt-depth is one signal; the four levers are the rest.\n const signals = [!promptThin, hasTools, hasSkills, hasDescription, opts?.needsMcp ? hasMcp : true]\n const richness = signals.filter(Boolean).length / signals.length\n // THIN ⟺ the prompt is a stub OR the worker has no levers at all (no tools AND no skills AND no mcp).\n const thin = promptThin || (!hasTools && !hasSkills && !hasMcp)\n\n return {\n name: profile.name ?? 'worker',\n systemPrompt,\n systemPromptChars,\n systemPromptLines,\n sentenceCount,\n hasDescription,\n hasTools,\n hasSkills,\n hasMcp,\n hasSubagents,\n richness,\n thin,\n reasons,\n }\n}\n\n/** Turn a {@link ProfileRichness} verdict into a bus-routable `AnalystFinding` (area `profile-quality`).\n * Severity scales with thinness; the recommended action names the MISSING lever so the supervisor can\n * re-author. `subject` = the worker name so per-worker findings diff cleanly across re-authors. */\nexport function profileRichnessFinding(\n richness: ProfileRichness,\n opts?: { analystId?: string; runId?: string },\n): AnalystFinding {\n const analyst_id = opts?.analystId ?? 'profile-richness'\n const subject = richness.name\n const claim = richness.thin\n ? `Worker \"${richness.name}\" was authored as a THIN profile: ${richness.reasons.join('; ')}.`\n : `Worker \"${richness.name}\" was authored as a rich profile (richness ${(richness.richness * 100).toFixed(0)}%).`\n const severity: AnalystFinding['severity'] = richness.thin\n ? richness.richness < 0.25\n ? 'high'\n : 'medium'\n : 'info'\n return makeFinding({\n analyst_id,\n severity,\n area: 'profile-quality',\n claim,\n subject,\n confidence: 0.9,\n evidence_refs: [\n {\n kind: 'metric',\n uri: `profile:${subject}`,\n excerpt: `chars=${richness.systemPromptChars} lines=${richness.systemPromptLines} tools=${richness.hasTools} skills=${richness.hasSkills} mcp=${richness.hasMcp} richness=${richness.richness.toFixed(2)}`,\n },\n ],\n ...(richness.thin\n ? { recommended_action: `Re-author \"${richness.name}\" with: ${richness.reasons.join('; ')}.` }\n : {}),\n id_basis: computeFindingId({\n analyst_id,\n area: 'profile-quality',\n subject,\n claim: `richness:${richness.thin ? 'thin' : 'rich'}`,\n }),\n })\n}\n","/**\n *\n * `delegate` — the one generic delegation verb. You hand it an INTENT (what you want done) and it\n * hands that intent to a default AUTHORING supervisor: a router-brained supervisor whose standing\n * instruction is `supervisorInstructions()` (the authoring-agent-profiles skill). The supervisor\n * DECOMPOSES the intent and AUTHORS the worker profile it needs per sub-task — there is NO hardcoded\n * coder/researcher profile here. That is the whole point: `delegate('fix the failing test', …)` and\n * `delegate('research X and cite sources', …)` route through the SAME front door; the supervisor\n * writes a code-shaped or research-shaped worker on its own.\n *\n * It is a thin wrapper over `supervise()` — the one front door — so the conserved-budget pool, the\n * completion oracle (`deliverable`), the coordination toolbox, and equal-compute accounting all come\n * for free; nothing is hand-rolled. The result is `supervise()`'s `SupervisedResult` returned\n * UNCHANGED, so its `spentTotal` (`{ iterations, tokens, usd, ms }`) rides straight back to the\n * caller on BOTH paths — a `winner` carries the delivered worker's spend, a `no-winner` carries the\n * spend incurred before it failed. That cost channel means a `delegate()` caller always learns what\n * the delegation actually spent.\n *\n * @experimental\n */\n\nimport { ConfigError } from '../../errors'\nimport type { RouterTransportConfig } from '../router-http-transport'\nimport type { DeliverableSpec } from './completion-gate'\nimport type { ExecutorConfig } from './runtime'\nimport { supervise } from './supervise'\nimport type { SupervisorProfile } from './supervisor-agent'\nimport type { Budget, SupervisedResult } from './types'\n\n/** The conserved pool a `delegate()` call applies when the caller does not pass its own `budget`.\n * A modest token ceiling + a small iteration ceiling — generous enough for a few-worker decompose,\n * bounded enough that an unsupervised intent cannot run away. Callers override via `opts.budget`. */\nexport const defaultDelegateBudget: Budget = { maxIterations: 50, maxTokens: 200_000 }\n\n/** Inputs to {@link delegate}. The intent is the first positional arg; everything here is optional\n * with explicit execution identity, so the common call names one exact supervisor profile. */\nexport interface DelegateOptions<Out = unknown> {\n /** The completion oracle (settled ⟺ delivered) the authored workers settle against. Strongly\n * recommended — without it the supervisor trusts a worker's self-report. For a code intent,\n * `patchDelivered()` is the canonical example; for a free-form answer, a content check. */\n readonly deliverable?: DeliverableSpec<Out>\n /** WHERE the authored workers run — the worker-execution backend (`router-tools` / `sandbox` /\n * `cli-worktree` / …). The supervisor authors the worker PROFILE; this is the substrate it runs\n * on. Provide this OR `makeWorkerAgent`-style wiring through `supervise()` is unavailable. */\n readonly backend?: ExecutorConfig\n /** The conserved compute pool for the whole delegation. Defaults to {@link defaultDelegateBudget}. */\n readonly budget?: Budget\n /** Exact executable authoring supervisor. Model, prompt, harness, and provider live here. */\n readonly supervisorProfile: SupervisorProfile\n /** Router endpoint/auth for a `cli-base` supervisor; contains no behavioral settings. */\n readonly router: RouterTransportConfig\n /** Restrict the run to this subset of models (forwarded to `supervise()`). */\n readonly allowedModels?: readonly string[]\n readonly runId?: string\n}\n\n/**\n * Delegate an INTENT to a default authoring supervisor and return its `SupervisedResult` unchanged.\n *\n * The supervisor authors + spawns whatever worker the intent needs over the conserved-budget pool;\n * `result.spentTotal` reports what the whole delegation actually cost. A `winner` result carries the\n * authored worker's delivered output; a `no-winner` result names why (never a fabricated success).\n */\nexport async function delegate<Out = unknown>(\n intent: string,\n opts: DelegateOptions<Out>,\n): Promise<SupervisedResult<Out>> {\n if (typeof intent !== 'string' || intent.trim().length === 0) {\n throw new ConfigError('delegate: `intent` must be a non-empty string')\n }\n return supervise(opts.supervisorProfile, intent, {\n budget: opts.budget ?? defaultDelegateBudget,\n ...(opts.backend ? { backend: opts.backend } : {}),\n ...(opts.deliverable ? { deliverable: opts.deliverable as DeliverableSpec<unknown> } : {}),\n router: opts.router,\n ...(opts.allowedModels ? { allowedModels: opts.allowedModels } : {}),\n ...(opts.runId ? { runId: opts.runId } : {}),\n }) as Promise<SupervisedResult<Out>>\n}\n"],"mappings":"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"}
package/dist/durable.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { $l as PursuitVersions, Cu as FileObserverJournal, D as RetainedPendingCause, Dd as SelectSeat, Du as observerRecordDigest, Eu as ObserverRecordKind, Gf as CoordinationAuthentication, Hd as DeadlineCheckIn, Jf as CoordinationReadAccess, Jl as JudgedPursuitVersion, Km as ToolLoopCompactionOptions, Mv as DownMessageAuthorizationInput, Qf as CoordinationRejectReason, Ql as PursuitVersionStop, Rv as MapOnSettleRoute, Su as projectPursuit, Td as SeatSelectionInput, Tu as ObserverRecord, V as NodeId, Xl as NextPursuitVersionInput, Yf as CoordinationTransportOptions, Yl as NextPursuitVersion, Zf as CoordinationHttpAudit, Zl as PursuitVersionChain, _u as PursuitNodeUsage, au as SupervisePursuitError, bu as PursuitRunTotals, cd as AuthorizedSpawnContext, cu as superviseGraphPursuit, dd as WorkerSlots, df as ResolveDriveHarness, du as PursuitObserverDelivery, eu as SettledPursuitVersion, fu as PursuitCostProvenance, gu as PursuitNodeTiming, gv as EscalateQuestion, hu as PursuitNodeProjection, it as SupervisedResult, iu as SuperviseGraphPursuitOptions, jv as ContinuityMode, kv as AuthorizedDownMessage, lf as DriveHarnessOwnerContext, lu as supervisePursuit, mu as PursuitNodePlatform, nd as RUN_FORK_CORRELATION_KEYS, nf as readRootStream, nu as VersionVerdict, ou as SupervisePursuitOptions, ov as WorkerWatchOptions, pu as PursuitNodeCost, qf as CoordinationPublicAddress, rf as readRootStreamReceipt, rt as RootStreamReceipt, ru as assertPursuitVersions, sd as AuthorizedSpawn, su as SupervisedPursuitResult, td as PursuitFork, tf as RootStreamRecord, tu as VersionJudge, uu as RunReopen, vu as PursuitProjection, wd as SeatSelection, wu as ObserverJournal, wy as AnalyzeOnSettleRoute, xu as PursuitStatus, yf as ManagerWakePolicy, yu as PursuitRunProjection, yv as QuestionEscalationOutcome } from "./types-DztCeYd6.js";
1
+ import { $d as DeadlineCheckIn, Au as PursuitRunProjection, By as AnalyzeOnSettleRoute, Cu as PursuitCostProvenance, D as RetainedPendingCause, Df as ResolveDriveHarness, Du as PursuitNodeTiming, Eu as PursuitNodeProjection, Fd as SeatSelection, Ff as ManagerWakePolicy, Fu as ObserverJournal, Fv as QuestionEscalationOutcome, Gv as AuthorizedDownMessage, Id as SeatSelectionInput, Iu as ObserverRecord, Jv as DownMessageAuthorizationInput, Lu as ObserverRecordKind, Mu as PursuitStatus, Mv as EscalateQuestion, Nu as projectPursuit, Ou as PursuitNodeUsage, Pu as FileObserverJournal, Rd as SelectSeat, Ru as observerRecordDigest, Sd as WorkerSlots, Su as PursuitObserverDelivery, Sv as WorkerWatchOptions, Tf as DriveHarnessOwnerContext, Tu as PursuitNodePlatform, V as NodeId, _f as RootStreamRecord, _u as SupervisePursuitOptions, au as JudgedPursuitVersion, bu as supervisePursuit, ch as ToolLoopCompactionOptions, cu as PursuitVersionChain, dp as CoordinationTransportOptions, du as SettledPursuitVersion, ef as ParkedRun, ey as MapOnSettleRoute, fd as PursuitFork, fu as VersionJudge, gu as SupervisePursuitError, hu as SuperviseGraphPursuitOptions, if as readRunParking, it as SupervisedResult, ju as PursuitRunTotals, ku as PursuitProjection, lp as CoordinationPublicAddress, lu as PursuitVersionStop, mp as CoordinationRejectReason, mu as assertPursuitVersions, nf as RunParkingPolicy, ou as NextPursuitVersion, pd as RUN_FORK_CORRELATION_KEYS, pp as CoordinationHttpAudit, pu as VersionVerdict, qv as ContinuityMode, rf as RunParkingRecord, rt as RootStreamReceipt, sp as CoordinationAuthentication, su as NextPursuitVersionInput, tf as RunParkedError, up as CoordinationReadAccess, uu as PursuitVersions, vd as AuthorizedSpawn, vf as readRootStream, vu as SupervisedPursuitResult, wu as PursuitNodeCost, xu as RunReopen, yd as AuthorizedSpawnContext, yf as readRootStreamReceipt, yu as superviseGraphPursuit } from "./types-C8BGOCBn.js";
2
2
  import { n as RuntimeDecisionKind, r as RuntimeDecisionPoint, t as RuntimeDecisionEvidenceRef } from "./runtime-hooks-nsqxSUFL.js";
3
3
  import { LockIdentity } from "@tangle-network/agent-eval/lock-holder";
4
4
  //#region src/durable/chat-engine.d.ts
@@ -137,6 +137,8 @@ declare function deriveExecutionId(input: {
137
137
  /** What the lock file records about its holder. */
138
138
  interface RunDirectoryLockHolder {
139
139
  readonly pid: number;
140
+ /** Monotonic acquisition generation; absent on legacy lock records. */
141
+ readonly generation?: number;
140
142
  /** ISO instant the holder took the lock. */
141
143
  readonly startedAt: string;
142
144
  readonly runId: string;
@@ -151,7 +153,10 @@ interface RunDirectoryLockHolder {
151
153
  }
152
154
  /** A held lock. `release()` removes the file; it is safe to call more than once. */
153
155
  interface RunDirectoryLock extends RunDirectoryLockHolder {
156
+ readonly generation: number;
154
157
  readonly path: string;
158
+ /** Bind durable writes to this acquisition, including work queued before release. */
159
+ run<T>(operation: () => T): T;
155
160
  release(): Promise<void>;
156
161
  }
157
162
  /** The directory is held by a live process. `holder` is what that process recorded. */
@@ -261,5 +266,5 @@ interface DurableSupervisionDiscovery {
261
266
  */
262
267
  declare function discoverDurableSupervisionRun(runDir: string): Promise<DurableSupervisionDiscovery>;
263
268
  //#endregion
264
- export { type AnalyzeOnSettleRoute, type AuthorizedDownMessage, type AuthorizedSpawn, type AuthorizedSpawnContext, type ChatStreamEvent, type ChatTurnHooks, type ChatTurnIdentity, type ChatTurnProducer, type ChatTurnResult, type ContinuityMode, type CoordinationAuthentication, type CoordinationHttpAudit, type CoordinationPublicAddress, type CoordinationReadAccess, type CoordinationRejectReason, type CoordinationTransportOptions, type DeadlineCheckIn, type DownMessageAuthorizationInput, type DriveHarnessOwnerContext, type DurableCoordinationStreamIdentity, type DurableFailureRecord, type DurableSupervisionDiscovery, type EscalateQuestion, FileObserverJournal, type JudgedPursuitVersion, type ManagerWakePolicy, type MapOnSettleRoute, type NextPursuitVersion, type NextPursuitVersionInput, type ObserverJournal, type ObserverRecord, type ObserverRecordKind, type PursuitCostProvenance, type PursuitFork, type PursuitNodeCost, type PursuitNodePlatform, type PursuitNodeProjection, type PursuitNodeTiming, type PursuitNodeUsage, type PursuitObserverDelivery, type PursuitProjection, type PursuitRunProjection, type PursuitRunTotals, type PursuitStatus, type PursuitVersionChain, type PursuitVersionStop, type PursuitVersions, type QuestionEscalationOutcome, RUN_FORK_CORRELATION_KEYS, type ResolveDriveHarness, type RetainedPendingCause, type RootStreamRecord, type RunChatTurnInput, type RunDirectoryHolderLiveness, type RunDirectoryLock, type RunDirectoryLockHolder, RunDirectoryLockedError, type RunReopen, type RuntimeDecisionEvidenceRef, type RuntimeDecisionKind, type RuntimeDecisionPoint, type SeatSelection, type SeatSelectionInput, type SelectSeat, type SettledPursuitVersion, SettledRunDirectoryError, type SuperviseGraphPursuitOptions, SupervisePursuitError, type SupervisePursuitOptions, type SupervisedPursuitResult, type ToolLoopCompactionOptions, type VersionJudge, type VersionVerdict, type WorkerSlots, type WorkerWatchOptions, acquireRunDirectoryLock, assertPursuitVersions, deriveExecutionId, discoverDurableSupervisionRun, handleChatTurn, observerRecordDigest, projectPursuit, readFailureRecord, readRootStream, readRootStreamReceipt, readRunDirectoryLock, readSettleRecord, runDirectoryHolderIsLive, superviseGraphPursuit, supervisePursuit };
269
+ export { type AnalyzeOnSettleRoute, type AuthorizedDownMessage, type AuthorizedSpawn, type AuthorizedSpawnContext, type ChatStreamEvent, type ChatTurnHooks, type ChatTurnIdentity, type ChatTurnProducer, type ChatTurnResult, type ContinuityMode, type CoordinationAuthentication, type CoordinationHttpAudit, type CoordinationPublicAddress, type CoordinationReadAccess, type CoordinationRejectReason, type CoordinationTransportOptions, type DeadlineCheckIn, type DownMessageAuthorizationInput, type DriveHarnessOwnerContext, type DurableCoordinationStreamIdentity, type DurableFailureRecord, type DurableSupervisionDiscovery, type EscalateQuestion, FileObserverJournal, type JudgedPursuitVersion, type ManagerWakePolicy, type MapOnSettleRoute, type NextPursuitVersion, type NextPursuitVersionInput, type ObserverJournal, type ObserverRecord, type ObserverRecordKind, type ParkedRun, type PursuitCostProvenance, type PursuitFork, type PursuitNodeCost, type PursuitNodePlatform, type PursuitNodeProjection, type PursuitNodeTiming, type PursuitNodeUsage, type PursuitObserverDelivery, type PursuitProjection, type PursuitRunProjection, type PursuitRunTotals, type PursuitStatus, type PursuitVersionChain, type PursuitVersionStop, type PursuitVersions, type QuestionEscalationOutcome, RUN_FORK_CORRELATION_KEYS, type ResolveDriveHarness, type RetainedPendingCause, type RootStreamRecord, type RunChatTurnInput, type RunDirectoryHolderLiveness, type RunDirectoryLock, type RunDirectoryLockHolder, RunDirectoryLockedError, RunParkedError, type RunParkingPolicy, type RunParkingRecord, type RunReopen, type RuntimeDecisionEvidenceRef, type RuntimeDecisionKind, type RuntimeDecisionPoint, type SeatSelection, type SeatSelectionInput, type SelectSeat, type SettledPursuitVersion, SettledRunDirectoryError, type SuperviseGraphPursuitOptions, SupervisePursuitError, type SupervisePursuitOptions, type SupervisedPursuitResult, type ToolLoopCompactionOptions, type VersionJudge, type VersionVerdict, type WorkerSlots, type WorkerWatchOptions, acquireRunDirectoryLock, assertPursuitVersions, deriveExecutionId, discoverDurableSupervisionRun, handleChatTurn, observerRecordDigest, projectPursuit, readFailureRecord, readRootStream, readRootStreamReceipt, readRunDirectoryLock, readRunParking, readSettleRecord, runDirectoryHolderIsLive, superviseGraphPursuit, supervisePursuit };
265
270
  //# sourceMappingURL=durable.d.ts.map
package/dist/durable.js CHANGED
@@ -1,18 +1,18 @@
1
- import { a as syncDurableDirectory, i as publishExclusiveDurableFile, o as writeAtomicDurableFile } from "./durable-file-DLcrzrjL.js";
2
- import { Bi as assertValidBudget, C as FileSpawnJournal, Mi as contentAddress, Qr as authoredProfileDigest, _t as readCommittedJsonLines, ct as loadSpawnForest, hr as isFiniteNumber, ji as detachedSnapshot, mr as failedItems, mt as isNoEntError, pt as appendCommittedJsonLine, r as assertResumeLimits } from "./supervisor-D3M9ab1v.js";
1
+ import { a as publishExclusiveDurableFile, c as captureRunWriteContext, d as runGenerationPath, f as runWithWriteClaim, l as fenceRunWrite, o as syncDurableDirectory, s as writeAtomicDurableFile, t as spawnJournalRecordEvents, u as mutateRunClaim } from "./record-events-D93DkpAx.js";
2
+ import { Ct as readCommittedJsonLines, Ri as detachedSnapshot, Sr as isFiniteNumber, ai as authoredProfileDigest, bt as isNoEntError, i as RunParkedError, k as FileSpawnJournal, mt as loadSpawnForest, qi as assertValidBudget, r as assertResumeLimits, s as readRunParking, xr as failedItems, yt as appendCommittedJsonLine, zi as contentAddress } from "./supervisor-wS9Ehz_x.js";
3
3
  import { f as RuntimeRunStateError, m as ValidationError } from "./errors-DodWX-cb.js";
4
- import { W as addSpend, X as errorMessage, ft as zeroSpend, q as cloneSpend } from "./sandbox-profile-compat-_fEtHPCj.js";
4
+ import { W as addSpend, X as errorMessage, ft as zeroSpend, q as cloneSpend } from "./sandbox-profile-compat-DtuJWFrM.js";
5
5
  import { a as withPursuitContext, t as composeRuntimeHooks } from "./runtime-hooks-tXpAarhW.js";
6
- import { a as applyExactAgentProfileDiff } from "./profile-6OLpbjvV.js";
7
- import { a as superviseRootProfile, ot as readRootStream, st as readRootStreamReceipt, t as supervise } from "./supervise-BuVVC7Bl.js";
8
- import { a as runCancelRequestFile, o as runCancellationFile, v as supervisorRunDir, x as workerCancellationsDir } from "./run-cancellation-CEDiqfhC.js";
9
- import { i as superviseAgentGraph } from "./graph-kgzTaNMA.js";
6
+ import { a as applyExactAgentProfileDiff } from "./profile-JLJd2wFG.js";
7
+ import { a as superviseRootProfile, ct as readRootStream, lt as readRootStreamReceipt, t as supervise } from "./supervise-BGfm2GlY.js";
8
+ import { a as runCancelRequestFile, o as runCancellationFile, v as supervisorRunDir, x as workerCancellationsDir } from "./run-cancellation-CzFhQGMa.js";
9
+ import { i as superviseAgentGraph } from "./graph-DPejYB3C.js";
10
10
  import { agentProfileSchema, canonicalAgentProfileDigest, canonicalCandidateDigest, canonicalCandidateJson, sha256Bytes } from "@tangle-network/agent-interface";
11
11
  import { isRecord } from "@tangle-network/agent-core";
12
12
  import { createHash } from "node:crypto";
13
- import { existsSync, mkdirSync, readFileSync, readdirSync, renameSync } from "node:fs";
13
+ import { closeSync, existsSync, fstatSync, mkdirSync, openSync, readFileSync, readdirSync, renameSync, unlinkSync } from "node:fs";
14
14
  import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
15
- import { access, mkdir, open, readFile, realpath, rm, rmdir, unlink } from "node:fs/promises";
15
+ import { access, readFile, realpath } from "node:fs/promises";
16
16
  import { SearchRecorder, developmentClaim, incumbent, openSearchLedger, parseSearchLedgerLine, runSearch, uniform } from "@tangle-network/agent-eval/campaign";
17
17
  import { pathToFileURL } from "node:url";
18
18
  import { setTimeout as setTimeout$1 } from "node:timers/promises";
@@ -197,6 +197,7 @@ function deriveExecutionId(input) {
197
197
  var FileObserverJournal = class {
198
198
  path;
199
199
  pursuitId;
200
+ writeContext = captureRunWriteContext();
200
201
  tail = Promise.resolve();
201
202
  initialized = false;
202
203
  sequence = 0;
@@ -240,7 +241,7 @@ var FileObserverJournal = class {
240
241
  return Promise.reject(error);
241
242
  }
242
243
  let result;
243
- const operation = this.tail.then(async () => {
244
+ const operation = this.tail.then(() => this.writeContext(async () => {
244
245
  this.assertComplete();
245
246
  try {
246
247
  await this.initialize();
@@ -273,7 +274,7 @@ var FileObserverJournal = class {
273
274
  this.sequence = record.sequence;
274
275
  this.previousDigest = record.digest;
275
276
  result = record;
276
- });
277
+ }));
277
278
  this.tail = operation.then(() => void 0, () => void 0);
278
279
  return operation.then(() => {
279
280
  if (!result) throw new Error("FileObserverJournal: append completed without a record");
@@ -2052,7 +2053,7 @@ var RunDirectoryLockedError = class extends Error {
2052
2053
  async function acquireRunDirectoryLock(runDir, runId, now = Date.now) {
2053
2054
  const dir = resolve(runDir);
2054
2055
  const path = resolve(dir, RUN_DIRECTORY_LOCK_FILE);
2055
- await mkdir(dir, { recursive: true });
2056
+ mkdirSync(dir, { recursive: true });
2056
2057
  const { pid, ...identity } = readLockHolderIdentity(process.pid);
2057
2058
  const owner = {
2058
2059
  pid,
@@ -2060,14 +2061,25 @@ async function acquireRunDirectoryLock(runDir, runId, now = Date.now) {
2060
2061
  runId,
2061
2062
  ...identity
2062
2063
  };
2063
- return withMutationGuard(path, async () => {
2064
- const existing = await readLockFile(path);
2064
+ return mutateRunClaim(path, () => {
2065
+ const existing = readLockFile(path);
2065
2066
  if (existing.state === "holder" && !holderIsDead(existing)) throw new RunDirectoryLockedError(path, existing.holder);
2066
- if (existing.state !== "missing") await removeIfPresent(path);
2067
+ let previousGeneration = 0;
2068
+ try {
2069
+ previousGeneration = Number(readFileSync(runGenerationPath(dir), "utf8"));
2070
+ if (!Number.isSafeInteger(previousGeneration) || previousGeneration < 1) throw new Error("invalid run directory generation counter");
2071
+ } catch (error) {
2072
+ if (!isNoEntError(error)) throw error;
2073
+ }
2074
+ const generation = Math.max(previousGeneration, existing.state === "holder" ? existing.holder.generation ?? 0 : 0) + 1;
2075
+ if (!Number.isSafeInteger(generation)) throw new Error("run directory generation exhausted");
2076
+ writeAtomicDurableFile(runGenerationPath(dir), `${generation}\n`);
2077
+ if (existing.state !== "missing") removeIfPresent(path);
2067
2078
  const written = {};
2068
2079
  const published = publishExclusiveDurableFile(path, (file) => {
2069
2080
  written.holder = Object.freeze({
2070
2081
  ...owner,
2082
+ generation,
2071
2083
  lock: lockIdentity(file)
2072
2084
  });
2073
2085
  return `${JSON.stringify(written.holder)}\n`;
@@ -2077,7 +2089,16 @@ async function acquireRunDirectoryLock(runDir, runId, now = Date.now) {
2077
2089
  let released = false;
2078
2090
  return Object.freeze({
2079
2091
  ...holder,
2092
+ generation,
2080
2093
  path,
2094
+ run: (operation) => {
2095
+ if (holder.lock === void 0) throw new Error("run lock has no inode identity");
2096
+ return runWithWriteClaim({
2097
+ path,
2098
+ generation,
2099
+ lock: holder.lock
2100
+ }, operation);
2101
+ },
2081
2102
  release: async () => {
2082
2103
  if (released) return;
2083
2104
  await releaseHolder(path, holder);
@@ -2114,16 +2135,16 @@ async function runDirectoryHolderIsLive(runDir) {
2114
2135
  holder: existing.holder
2115
2136
  };
2116
2137
  }
2117
- async function readLockFile(path) {
2138
+ function readLockFile(path) {
2118
2139
  let text;
2119
2140
  let current;
2120
2141
  try {
2121
- const file = await open(path, "r");
2142
+ const file = openSync(path, "r");
2122
2143
  try {
2123
- current = lockIdentity(await file.stat({ bigint: true }));
2124
- text = await file.readFile("utf8");
2144
+ current = lockIdentity(fstatSync(file, { bigint: true }));
2145
+ text = readFileSync(file, "utf8");
2125
2146
  } finally {
2126
- await file.close();
2147
+ closeSync(file);
2127
2148
  }
2128
2149
  } catch (error) {
2129
2150
  if (isNoEntError(error)) return { state: "missing" };
@@ -2137,7 +2158,7 @@ async function readLockFile(path) {
2137
2158
  throw new Error(`supervisePursuit: ${path} is not a readable lock file; remove it by hand`, { cause });
2138
2159
  }
2139
2160
  const record = parsed;
2140
- if (typeof record !== "object" || record === null || typeof record.pid !== "number" || !Number.isInteger(record.pid) || record.pid <= 0 || typeof record.startedAt !== "string" || typeof record.runId !== "string" || record.processStart !== void 0 && typeof record.processStart !== "string") throw new Error(`supervisePursuit: ${path} is not a readable lock file; remove it by hand`);
2161
+ if (typeof record !== "object" || record === null || typeof record.pid !== "number" || !Number.isInteger(record.pid) || record.pid <= 0 || typeof record.startedAt !== "string" || typeof record.runId !== "string" || record.processStart !== void 0 && typeof record.processStart !== "string" || record.generation !== void 0 && (!Number.isSafeInteger(record.generation) || record.generation < 1)) throw new Error(`supervisePursuit: ${path} is not a readable lock file; remove it by hand`);
2141
2162
  const { pid, startedAt, runId, processStart } = record;
2142
2163
  const verdictRecord = parseLockHolderRecord(parsed);
2143
2164
  const { pid: _pid, ...identity } = verdictRecord ?? { pid };
@@ -2147,6 +2168,7 @@ async function readLockFile(path) {
2147
2168
  pid,
2148
2169
  startedAt,
2149
2170
  runId,
2171
+ ...record.generation === void 0 ? {} : { generation: record.generation },
2150
2172
  ...processStart === void 0 ? {} : { processStart },
2151
2173
  ...identity
2152
2174
  }),
@@ -2160,36 +2182,18 @@ function holderIsDead(existing) {
2160
2182
  }
2161
2183
  /** Remove the lock only while it still names this holder; a reclaimed lock belongs to its new owner. */
2162
2184
  async function releaseHolder(path, holder) {
2163
- await withMutationGuard(path, async () => {
2164
- const existing = await readLockFile(path);
2185
+ const deadline = performance.now() + 1e3;
2186
+ while (existsSync(`${path}.guard`) && performance.now() < deadline) await setTimeout$1(10);
2187
+ mutateRunClaim(path, () => {
2188
+ const existing = readLockFile(path);
2165
2189
  if (existing.state !== "holder") return;
2166
- if (existing.holder.pid !== holder.pid || existing.holder.startedAt !== holder.startedAt || existing.holder.runId !== holder.runId || existing.holder.processStart !== holder.processStart) return;
2167
- await removeIfPresent(path);
2168
- }, true);
2169
- }
2170
- /** Serialize stale reclamation and release; an abandoned guard requires operator recovery. */
2171
- async function withMutationGuard(path, mutate, waitForRelease = false) {
2172
- const guard = `${path}.guard`;
2173
- const deadline = performance.now() + (waitForRelease ? 1e3 : 0);
2174
- for (;;) try {
2175
- await mkdir(guard);
2176
- break;
2177
- } catch (cause) {
2178
- if (waitForRelease && cause.code === "EEXIST" && performance.now() < deadline) {
2179
- await setTimeout$1(10);
2180
- continue;
2181
- }
2182
- throw new Error(`supervisePursuit: cannot mutate ${path}; inspect ${guard} and remove it only after confirming no lock mutation is active`, { cause });
2183
- }
2184
- try {
2185
- return await mutate();
2186
- } finally {
2187
- await rmdir(guard);
2188
- }
2190
+ if (existing.holder.pid !== holder.pid || existing.holder.startedAt !== holder.startedAt || existing.holder.runId !== holder.runId || existing.holder.processStart !== holder.processStart || existing.holder.generation !== holder.generation || existing.holder.lock?.dev !== holder.lock?.dev || existing.holder.lock?.ino !== holder.lock?.ino || existing.holder.lock?.birthtimeNs !== holder.lock?.birthtimeNs) return;
2191
+ removeIfPresent(path);
2192
+ });
2189
2193
  }
2190
- async function removeIfPresent(path) {
2194
+ function removeIfPresent(path) {
2191
2195
  try {
2192
- await unlink(path);
2196
+ unlinkSync(path);
2193
2197
  } catch (error) {
2194
2198
  if (!isNoEntError(error)) throw error;
2195
2199
  }
@@ -2373,15 +2377,17 @@ async function reopenSettledRun(input) {
2373
2377
  archivedAs
2374
2378
  } }
2375
2379
  });
2376
- setCancellationAside(runDir, operationId);
2377
- await rm(settlePath);
2378
- syncDurableDirectory(runDir);
2380
+ fenceRunWrite(() => {
2381
+ setCancellationAside(runDir, operationId);
2382
+ unlinkSync(settlePath);
2383
+ syncDurableDirectory(runDir);
2384
+ });
2379
2385
  }
2380
2386
  /** The next free `settles/<n>.json`, or the archive that already holds these bytes (a reopen that
2381
2387
  * crashed after archiving and before its journal row). */
2382
2388
  async function archiveName(runDir, settled) {
2383
2389
  const dir = resolve(runDir, SETTLE_ARCHIVE_DIR);
2384
- mkdirSync(dir, { recursive: true });
2390
+ fenceRunWrite(() => mkdirSync(dir, { recursive: true }));
2385
2391
  const numbers = readdirSync(dir).map((name) => /^(\d+)\.json$/u.exec(name)?.[1]).filter((n) => n !== void 0).map(Number).sort((a, b) => a - b);
2386
2392
  for (const n of numbers) if (await readBytes(join(dir, `${n}.json`)) === settled) return `${SETTLE_ARCHIVE_DIR}/${n}.json`;
2387
2393
  return `${SETTLE_ARCHIVE_DIR}/${(numbers.at(-1) ?? 0) + 1}.json`;
@@ -2523,70 +2529,76 @@ async function runDurablePursuit(opts, execute, settled, reopen) {
2523
2529
  const runId = opts.runId ?? "supervise";
2524
2530
  const now = opts.now ?? Date.now;
2525
2531
  const lock = await acquireRunDirectoryLock(runDir, runId, now);
2526
- let delivery;
2527
- let terminalState = "failed";
2528
- try {
2529
- const observer = createFileObserverHooks(observerPath, pursuitId);
2530
- if (reopen !== void 0) await reopen(observer.journal);
2531
- else {
2532
- const prior = await readSettleRecord(runDir);
2533
- if (prior !== void 0) throw new SettledRunDirectoryError(settlePath, prior.tree.root, runId);
2534
- }
2535
- await observer.journal.appendEvent(rootEvent(pursuitId, runId, "before", now()));
2536
- delivery = opts.observerDelivery === void 0 ? void 0 : startPursuitObserverDelivery(opts.observerDelivery, async () => projectPursuit(await observer.journal.read()));
2537
- let result;
2532
+ return lock.run(async () => {
2533
+ let delivery;
2534
+ let terminalState = "failed";
2538
2535
  try {
2539
- result = await execute({
2540
- hooks: withPursuitContext(pursuitId, composeRuntimeHooks(observer.hooks, opts.hooks)),
2541
- steerDir: opts.steerDir ?? supervisorRunDir(runDir, runId)
2542
- });
2543
- } catch (error) {
2544
- let pursuit;
2545
- let observerError;
2546
- try {
2547
- await observer.journal.appendEvent(rootEvent(pursuitId, runId, "error", now(), {
2548
- status: "failed",
2549
- error: errorMessage(error)
2550
- }));
2551
- pursuit = projectPursuit(await observer.journal.read());
2552
- } catch (failure) {
2553
- observerError = failure;
2536
+ const observer = createFileObserverHooks(observerPath, pursuitId);
2537
+ if (reopen !== void 0) await reopen(observer.journal);
2538
+ else {
2539
+ const prior = await readSettleRecord(runDir);
2540
+ if (prior !== void 0) throw new SettledRunDirectoryError(settlePath, prior.tree.root, runId);
2554
2541
  }
2555
- if (observerError !== void 0 || pursuit === void 0) throw new Error("supervisePursuit: Runtime failed and durable observer completeness could not be proven", { cause: new AggregateError(observerError === void 0 ? [error] : [error, observerError]) });
2542
+ await observer.journal.appendEvent(rootEvent(pursuitId, runId, "before", now()));
2543
+ delivery = opts.observerDelivery === void 0 ? void 0 : startPursuitObserverDelivery(opts.observerDelivery, async () => projectPursuit(await observer.journal.read()));
2544
+ let result;
2556
2545
  try {
2557
- const rootStream = await readRootStreamReceipt(runDir);
2558
- await writeFailureRecord(runDir, {
2559
- runId,
2560
- pursuitId,
2561
- at: new Date(now()).toISOString(),
2562
- error: {
2563
- name: errorName(error),
2564
- message: errorMessage(error)
2565
- },
2566
- ...rootStream === void 0 ? {} : { rootStream }
2546
+ result = await execute({
2547
+ hooks: withPursuitContext(pursuitId, composeRuntimeHooks(observer.hooks, opts.hooks)),
2548
+ steerDir: opts.steerDir ?? supervisorRunDir(runDir, runId)
2567
2549
  });
2568
- } catch (recordError) {
2569
- throw new Error(`supervisePursuit: Runtime failed and the failure record ${failurePath} could not be written`, { cause: new AggregateError([error, recordError]) });
2550
+ } catch (error) {
2551
+ if (error instanceof RunParkedError && error.runId === runId) {
2552
+ terminalState = "running";
2553
+ throw error;
2554
+ }
2555
+ let pursuit;
2556
+ let observerError;
2557
+ try {
2558
+ await observer.journal.appendEvent(rootEvent(pursuitId, runId, "error", now(), {
2559
+ status: "failed",
2560
+ error: errorMessage(error)
2561
+ }));
2562
+ pursuit = projectPursuit(await observer.journal.read());
2563
+ } catch (failure) {
2564
+ observerError = failure;
2565
+ }
2566
+ if (observerError !== void 0 || pursuit === void 0) throw new Error("supervisePursuit: Runtime failed and durable observer completeness could not be proven", { cause: new AggregateError(observerError === void 0 ? [error] : [error, observerError]) });
2567
+ try {
2568
+ const rootStream = await readRootStreamReceipt(runDir);
2569
+ await writeFailureRecord(runDir, {
2570
+ runId,
2571
+ pursuitId,
2572
+ at: new Date(now()).toISOString(),
2573
+ error: {
2574
+ name: errorName(error),
2575
+ message: errorMessage(error)
2576
+ },
2577
+ ...rootStream === void 0 ? {} : { rootStream }
2578
+ });
2579
+ } catch (recordError) {
2580
+ throw new Error(`supervisePursuit: Runtime failed and the failure record ${failurePath} could not be written`, { cause: new AggregateError([error, recordError]) });
2581
+ }
2582
+ throw new SupervisePursuitError(error, pursuit, observerPath, failurePath);
2570
2583
  }
2571
- throw new SupervisePursuitError(error, pursuit, observerPath, failurePath);
2572
- }
2573
- try {
2574
- await writeSettleRecord(runDir, settled(result));
2575
- } catch (cause) {
2576
- throw new Error(`supervisePursuit: Runtime settled but the settle record ${settlePath} could not be written`, { cause });
2584
+ try {
2585
+ await writeSettleRecord(runDir, settled(result));
2586
+ } catch (cause) {
2587
+ throw new Error(`supervisePursuit: Runtime settled but the settle record ${settlePath} could not be written`, { cause });
2588
+ }
2589
+ await observer.journal.appendEvent(rootEvent(pursuitId, runId, "after", now(), { status: "done" }));
2590
+ terminalState = "done";
2591
+ return Object.freeze({
2592
+ result,
2593
+ pursuit: projectPursuit(await observer.journal.read()),
2594
+ observerPath,
2595
+ settlePath
2596
+ });
2597
+ } finally {
2598
+ await delivery?.close(terminalState);
2599
+ await lock.release();
2577
2600
  }
2578
- await observer.journal.appendEvent(rootEvent(pursuitId, runId, "after", now(), { status: "done" }));
2579
- terminalState = "done";
2580
- return Object.freeze({
2581
- result,
2582
- pursuit: projectPursuit(await observer.journal.read()),
2583
- observerPath,
2584
- settlePath
2585
- });
2586
- } finally {
2587
- await delivery?.close(terminalState);
2588
- await lock.release();
2589
- }
2601
+ });
2590
2602
  }
2591
2603
  function rootEvent(pursuitId, runId, phase, timestamp, payload) {
2592
2604
  return Object.freeze({
@@ -2629,12 +2641,12 @@ async function discoverDurableSupervisionRun(runDir) {
2629
2641
  rootsBegunAt.set(record.root, typeof record.at === "string" ? record.at : null);
2630
2642
  continue;
2631
2643
  }
2632
- if (record.kind !== "event") continue;
2633
- const event = record.event;
2634
- if (!isRecord(event) || event.kind !== "spawned") continue;
2635
- if (!Object.hasOwn(event, "ownedTreeRoot")) continue;
2636
- if (typeof event.ownedTreeRoot !== "string" || event.ownedTreeRoot.length === 0) throw new Error(`${spawnJournalPath}: spawned event ownedTreeRoot must be a non-empty string when present`);
2637
- nestedRoots.add(event.ownedTreeRoot);
2644
+ for (const event of spawnJournalRecordEvents(record)) {
2645
+ if (!isRecord(event) || event.kind !== "spawned") continue;
2646
+ if (!Object.hasOwn(event, "ownedTreeRoot")) continue;
2647
+ if (typeof event.ownedTreeRoot !== "string" || event.ownedTreeRoot.length === 0) throw new Error(`${spawnJournalPath}: spawned event ownedTreeRoot must be a non-empty string when present`);
2648
+ nestedRoots.add(event.ownedTreeRoot);
2649
+ }
2638
2650
  }
2639
2651
  const streams = /* @__PURE__ */ new Map();
2640
2652
  for await (const record of readCommittedJsonLines(coordinationLogPath, { allowMissing: true })) {
@@ -2670,6 +2682,6 @@ function compareText(left, right) {
2670
2682
  return left < right ? -1 : left > right ? 1 : 0;
2671
2683
  }
2672
2684
  //#endregion
2673
- export { FileObserverJournal, RUN_FORK_CORRELATION_KEYS, RunDirectoryLockedError, SettledRunDirectoryError, SupervisePursuitError, acquireRunDirectoryLock, assertPursuitVersions, deriveExecutionId, discoverDurableSupervisionRun, handleChatTurn, observerRecordDigest, projectPursuit, readFailureRecord, readRootStream, readRootStreamReceipt, readRunDirectoryLock, readSettleRecord, runDirectoryHolderIsLive, superviseGraphPursuit, supervisePursuit };
2685
+ export { FileObserverJournal, RUN_FORK_CORRELATION_KEYS, RunDirectoryLockedError, RunParkedError, SettledRunDirectoryError, SupervisePursuitError, acquireRunDirectoryLock, assertPursuitVersions, deriveExecutionId, discoverDurableSupervisionRun, handleChatTurn, observerRecordDigest, projectPursuit, readFailureRecord, readRootStream, readRootStreamReceipt, readRunDirectoryLock, readRunParking, readSettleRecord, runDirectoryHolderIsLive, superviseGraphPursuit, supervisePursuit };
2674
2686
 
2675
2687
  //# sourceMappingURL=durable.js.map