@tangle-network/agent-runtime 0.301.0 → 0.306.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{activation-V6aaOlZV.js → activation-C5wesfzd.js} +3 -3
- package/dist/{activation-V6aaOlZV.js.map → activation-C5wesfzd.js.map} +1 -1
- package/dist/{activation-CRgEJutr.d.ts → activation-wZ-8vOjV.d.ts} +88 -13
- package/dist/agent.d.ts +2 -2
- package/dist/agent.js +2 -2
- package/dist/{bundle-C9v5TH_H.js → bundle-C8EO05u3.js} +2 -2
- package/dist/{bundle-C9v5TH_H.js.map → bundle-C8EO05u3.js.map} +1 -1
- package/dist/candidate-execution/index.d.ts +3 -26
- package/dist/candidate-execution/index.js +6 -6
- package/dist/{coordination-driver-vNrdQBSH.js → coordination-driver-yj9HPXef.js} +9 -16
- package/dist/coordination-driver-yj9HPXef.js.map +1 -0
- package/dist/{delegate-B-jFJcbW.js → delegate-KtrL9HWq.js} +2 -2
- package/dist/{delegate-B-jFJcbW.js.map → delegate-KtrL9HWq.js.map} +1 -1
- package/dist/durable.d.ts +3 -15
- package/dist/durable.js +158 -124
- package/dist/durable.js.map +1 -1
- package/dist/{graph-Dwv_hQB_.js → graph-DcdyHbgf.js} +4 -5
- package/dist/graph-DcdyHbgf.js.map +1 -0
- package/dist/{improvement-cycle-Cf37Kexd.js → improvement-cycle-DsN-TVXR.js} +1129 -10
- package/dist/improvement-cycle-DsN-TVXR.js.map +1 -0
- package/dist/{index-CxBVmgqL.d.ts → index-DflDZq_I.d.ts} +50 -688
- package/dist/index.d.ts +378 -12
- package/dist/index.js +223 -320
- package/dist/index.js.map +1 -1
- package/dist/intelligence.d.ts +5 -5
- package/dist/intelligence.js +7 -8
- package/dist/intelligence.js.map +1 -1
- package/dist/kernel.d.ts +4 -4
- package/dist/kernel.js +9 -9
- package/dist/{loop-runner-bin-DiTNiJNs.d.ts → loop-runner-bin-DZfRUVWy.d.ts} +3 -3
- package/dist/{loop-runner-bin-C8MI8N1A.js → loop-runner-bin-rVrSX5l1.js} +3 -3
- package/dist/{loop-runner-bin-C8MI8N1A.js.map → loop-runner-bin-rVrSX5l1.js.map} +1 -1
- package/dist/loop-runner-bin.d.ts +1 -1
- package/dist/loop-runner-bin.js +1 -1
- package/dist/mcp/bin.js +3 -3
- package/dist/mcp/index.d.ts +6 -35
- package/dist/mcp/index.js +311 -364
- package/dist/mcp/index.js.map +1 -1
- package/dist/model-policy-DydyWTQg.js.map +1 -1
- package/dist/{openai-tools-BeogJ6V8.d.ts → openai-tools-YO30D2s3.d.ts} +2 -2
- package/dist/{prepare-CGVNMb1o.js → prepare--nWcazPr.js} +7 -13
- package/dist/prepare--nWcazPr.js.map +1 -0
- package/dist/{profile-BetOB4_Q.js → profile-h04LDjV6.js} +2 -2
- package/dist/{profile-BetOB4_Q.js.map → profile-h04LDjV6.js.map} +1 -1
- package/dist/profiles.d.ts +2 -94
- package/dist/profiles.js +2 -451
- package/dist/profiles.js.map +1 -1
- package/dist/{protected-model-port-CAa0t1MT.d.ts → protected-model-port-C9QtEf3y.d.ts} +2 -2
- package/dist/{protected-model-port-CuVHkhti.js → protected-model-port-wwalbyXM.js} +4 -4
- package/dist/{protected-model-port-CuVHkhti.js.map → protected-model-port-wwalbyXM.js.map} +1 -1
- package/dist/{provision-supervisor-CS0fBIhp.js → provision-supervisor-WxFEFQeJ.js} +4 -3
- package/dist/{provision-supervisor-CS0fBIhp.js.map → provision-supervisor-WxFEFQeJ.js.map} +1 -1
- package/dist/{runtime-6VPSJVeg.js → runtime-B9eBIyws.js} +67 -469
- package/dist/runtime-B9eBIyws.js.map +1 -0
- package/dist/{search-method-wTTS7mGG.js → search-executor-2tWr38Qw.js} +239 -1132
- package/dist/search-executor-2tWr38Qw.js.map +1 -0
- package/dist/{server-BfHD5iLD.js → server-CpKcrOPj.js} +5 -12
- package/dist/server-CpKcrOPj.js.map +1 -0
- package/dist/{stream-agent-turn-B1ipFtOc.d.ts → stream-agent-turn-B1giFgOg.d.ts} +2 -2
- package/dist/{substrate-DKBMd0Dl.d.ts → substrate-L_sC7jCD.d.ts} +2 -2
- package/dist/{supervise-BJrEC2Mh.js → supervise-DllOHM4M.js} +14 -24
- package/dist/supervise-DllOHM4M.js.map +1 -0
- package/dist/{supervisor-DA_nvF-h.js → supervisor-gCfG6K1-.js} +488 -1407
- package/dist/supervisor-gCfG6K1-.js.map +1 -0
- package/dist/testing.d.ts +2 -2
- package/dist/testing.js +14 -14
- package/dist/tui/index.d.ts +1 -1
- package/dist/tui/index.js +1 -1
- package/dist/{types-jD9mNnJM.d.ts → types-DeuEkBhI.d.ts} +114 -258
- package/dist/{workspace-archive-pz8YJ1fr.d.ts → workspace-streams-CpSmt3zx.d.ts} +67 -3
- package/dist/{workspace-streams-D5gHJdTb.js → workspace-streams-ILw0eWZ0.js} +694 -2
- package/dist/workspace-streams-ILw0eWZ0.js.map +1 -0
- package/package.json +8 -8
- package/skills/generate-eval/SKILL.md +1 -1
- package/skills/profile-authoring/SKILL.md +2 -2
- package/dist/coordination-driver-vNrdQBSH.js.map +0 -1
- package/dist/graph-Dwv_hQB_.js.map +0 -1
- package/dist/improvement-cycle-Cf37Kexd.js.map +0 -1
- package/dist/prepare-CGVNMb1o.js.map +0 -1
- package/dist/runtime-6VPSJVeg.js.map +0 -1
- package/dist/search-method-wTTS7mGG.js.map +0 -1
- package/dist/server-BfHD5iLD.js.map +0 -1
- package/dist/supervise-BJrEC2Mh.js.map +0 -1
- package/dist/supervisor-DA_nvF-h.js.map +0 -1
- package/dist/workspace-streams-D5gHJdTb.js.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { i as ConfigError } from "./errors-DodWX-cb.js";
|
|
2
|
-
import { r as supervise } from "./supervise-
|
|
2
|
+
import { r as supervise } from "./supervise-DllOHM4M.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-
|
|
210
|
+
//# sourceMappingURL=delegate-KtrL9HWq.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"delegate-B-jFJcbW.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-client'\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-KtrL9HWq.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-client'\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,5 +1,5 @@
|
|
|
1
|
-
import { J as RootStreamReceipt, j as NodeId, lt as SupervisedResult, y as ExecutorProgressEvent } from "./types-
|
|
2
|
-
import { $s as
|
|
1
|
+
import { J as RootStreamReceipt, j as NodeId, lt as SupervisedResult, y as ExecutorProgressEvent } from "./types-DeuEkBhI.js";
|
|
2
|
+
import { $s as PursuitNodeUsage, As as JudgedPursuitVersion, Bs as SupervisePursuitError, Fs as PursuitVersions, Gs as RUN_FORK_CORRELATION_KEYS, Hs as SupervisedPursuitResult, Is as SettledPursuitVersion, Js as PursuitNodeCost, Ks as PursuitObserverDelivery, Ls as VersionJudge, Ms as NextPursuitVersionInput, Ns as PursuitVersionChain, Ps as PursuitVersionStop, Qs as PursuitNodeTiming, Rs as VersionVerdict, Us as supervisePursuit, Vs as SupervisePursuitOptions, Ws as PursuitFork, Xs as PursuitNodePlatform, Ys as PursuitNodePlacement, Zs as PursuitNodeProjection, ac as FileObserverJournal, cc as ObserverRecordKind, ec as PursuitProjection, ic as projectPursuit, js as NextPursuitVersion, lc as observerRecordDigest, nc as PursuitRunTotals, oc as ObserverJournal, qs as PursuitCostProvenance, rc as PursuitStatus, sc as ObserverRecord, tc as PursuitRunProjection, uc as verifyObserverRecords, zs as assertPursuitVersions } from "./index-DflDZq_I.js";
|
|
3
3
|
//#region src/runtime/supervise/root-stream.d.ts
|
|
4
4
|
/** The root stream: one JSONL line per progress event the root's executor observed. */
|
|
5
5
|
declare const ROOT_STREAM_FILE = "root-stream.jsonl";
|
|
@@ -166,8 +166,6 @@ declare function deriveExecutionId(input: {
|
|
|
166
166
|
}): string;
|
|
167
167
|
//#endregion
|
|
168
168
|
//#region src/durable/run-lock.d.ts
|
|
169
|
-
/** The lock file `supervisePursuit` holds inside a run directory for the life of one call. */
|
|
170
|
-
declare const RUN_DIRECTORY_LOCK_FILE = "supervise.lock";
|
|
171
169
|
/** What the lock file records about its holder. */
|
|
172
170
|
interface RunDirectoryLockHolder {
|
|
173
171
|
readonly pid: number;
|
|
@@ -229,10 +227,6 @@ interface RunDirectoryHolderLiveness {
|
|
|
229
227
|
declare function runDirectoryHolderIsLive(runDir: string): Promise<RunDirectoryHolderLiveness>;
|
|
230
228
|
//#endregion
|
|
231
229
|
//#region src/durable/settle-record.d.ts
|
|
232
|
-
/** The settle record: the returned `SupervisedResult` as canonical JSON, written once. */
|
|
233
|
-
declare const SETTLE_RECORD_FILE = "result.json";
|
|
234
|
-
/** The failure record: the most recent throw, replaced by a later throw. */
|
|
235
|
-
declare const FAILURE_RECORD_FILE = "failure.json";
|
|
236
230
|
/** What `failure.json` records about the most recent throw. */
|
|
237
231
|
interface DurableFailureRecord {
|
|
238
232
|
readonly runId: string;
|
|
@@ -254,12 +248,6 @@ declare class SettledRunDirectoryError extends Error {
|
|
|
254
248
|
readonly recordedRunId: string;
|
|
255
249
|
constructor(path: string, recordedRunId: string, requestedRunId: string);
|
|
256
250
|
}
|
|
257
|
-
/**
|
|
258
|
-
* The exact bytes `result.json` holds for a result: its JSON value serialized as RFC 8785
|
|
259
|
-
* canonical JSON. Throws `UnrecordableSettleValueError` before any byte is written when the
|
|
260
|
-
* result carries a value JSON would misstate.
|
|
261
|
-
*/
|
|
262
|
-
declare function settleRecordJson(result: unknown): string;
|
|
263
251
|
/**
|
|
264
252
|
* Read the settle record a run directory holds, or `undefined` when it holds none. A file that
|
|
265
253
|
* is present but is not a settle record is corruption and fails loud.
|
|
@@ -302,5 +290,5 @@ interface DurableSupervisionDiscovery {
|
|
|
302
290
|
*/
|
|
303
291
|
declare function discoverDurableSupervisionRun(runDir: string): Promise<DurableSupervisionDiscovery>;
|
|
304
292
|
//#endregion
|
|
305
|
-
export { type ChatStreamEvent, type ChatTurnHooks, type ChatTurnIdentity, type ChatTurnProducer, type ChatTurnResult, type DurableCoordinationStreamIdentity, type DurableFailureRecord, type DurableSupervisionDiscovery,
|
|
293
|
+
export { type ChatStreamEvent, type ChatTurnHooks, type ChatTurnIdentity, type ChatTurnProducer, type ChatTurnResult, type DurableCoordinationStreamIdentity, type DurableFailureRecord, type DurableSupervisionDiscovery, FileObserverJournal, type JudgedPursuitVersion, type NextPursuitVersion, type NextPursuitVersionInput, type ObserverJournal, type ObserverRecord, type ObserverRecordKind, type PursuitCostProvenance, type PursuitFork, type PursuitNodeCost, type PursuitNodePlacement, type PursuitNodePlatform, type PursuitNodeProjection, type PursuitNodeTiming, type PursuitNodeUsage, type PursuitObserverDelivery, type PursuitProjection, type PursuitRunProjection, type PursuitRunTotals, type PursuitStatus, type PursuitVersionChain, type PursuitVersionStop, type PursuitVersions, ROOT_STREAM_FILE, RUN_FORK_CORRELATION_KEYS, type RootStreamReceipt, type RootStreamRecord, type RunChatTurnInput, type RunDirectoryHolderLiveness, type RunDirectoryLock, type RunDirectoryLockHolder, RunDirectoryLockedError, type SettledPursuitVersion, SettledRunDirectoryError, SupervisePursuitError, type SupervisePursuitOptions, type SupervisedPursuitResult, type VersionJudge, type VersionVerdict, acquireRunDirectoryLock, assertPursuitVersions, deriveExecutionId, discoverDurableSupervisionRun, handleChatTurn, observerRecordDigest, projectPursuit, readFailureRecord, readRootStream, readRootStreamReceipt, readRunDirectoryLock, readSettleRecord, runDirectoryHolderIsLive, supervisePursuit, verifyObserverRecords };
|
|
306
294
|
//# sourceMappingURL=durable.d.ts.map
|
package/dist/durable.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Bi as detachedSnapshot, Dn as FileSpawnJournal, Fn as prepareJsonlAppend, In as readCommittedJsonLines, Ln as writeAllBytes, Pn as isNoEntError, T as failedItems, br as contentAddress, jn as loadSpawnForest, xi as authoredProfileDigest } from "./supervisor-gCfG6K1-.js";
|
|
2
2
|
import { f as RuntimeRunStateError, m as ValidationError } from "./errors-DodWX-cb.js";
|
|
3
|
+
import { G as errorMessage$1, H as cloneSpend, st as zeroSpend, z as addSpend } from "./workspace-streams-ILw0eWZ0.js";
|
|
3
4
|
import { a as writeAtomicDurableFile, r as publishExclusiveDurableFile } from "./durable-file-DWQA4ooo.js";
|
|
4
5
|
import { a as withPursuitContext, t as composeRuntimeHooks } from "./runtime-hooks-tXpAarhW.js";
|
|
5
|
-
import { i as applyExactAgentProfileDiff } from "./profile-
|
|
6
|
-
import { _ as readRootStream, g as ROOT_STREAM_FILE, i as superviseRootProfile, r as supervise, v as readRootStreamReceipt } from "./supervise-
|
|
6
|
+
import { i as applyExactAgentProfileDiff } from "./profile-h04LDjV6.js";
|
|
7
|
+
import { _ as readRootStream, g as ROOT_STREAM_FILE, i as superviseRootProfile, r as supervise, v as readRootStreamReceipt } from "./supervise-DllOHM4M.js";
|
|
7
8
|
import { h as supervisorRunDir } from "./run-layout-DSCWdbX2.js";
|
|
8
9
|
import { agentProfileSchema, canonicalAgentProfileDigest, canonicalCandidateDigest, canonicalCandidateJson, sha256Bytes } from "@tangle-network/agent-interface";
|
|
9
10
|
import { createHash } from "node:crypto";
|
|
@@ -560,7 +561,6 @@ function projectNodeActivity(nodes, record) {
|
|
|
560
561
|
if (releasedAt !== void 0) node.releasedAt = releasedAt;
|
|
561
562
|
const budgetViolation = budgetViolationField(payload);
|
|
562
563
|
if (budgetViolation) node.budgetViolation = budgetViolation;
|
|
563
|
-
if (payload && Object.hasOwn(payload, "wait")) node.wait = payload.wait;
|
|
564
564
|
attachSettlementEvidence(node, payload);
|
|
565
565
|
}
|
|
566
566
|
/**
|
|
@@ -842,113 +842,6 @@ function booleanField(value, key) {
|
|
|
842
842
|
return typeof field === "boolean" ? field : void 0;
|
|
843
843
|
}
|
|
844
844
|
//#endregion
|
|
845
|
-
//#region src/durable/pursuit-observer-delivery.ts
|
|
846
|
-
/** The Intelligence route that persists a delivered projection on its run spine. */
|
|
847
|
-
const PURSUIT_OBSERVER_DELIVERY_PATH = "/v1/ingest/pursuit-observer";
|
|
848
|
-
const DEFAULT_INTERVAL_MS = 6e4;
|
|
849
|
-
const DEFAULT_TIMEOUT_MS = 1e4;
|
|
850
|
-
/** POST one projection. Never throws; the outcome says whether Intelligence accepted it. */
|
|
851
|
-
async function deliverPursuitObserver(delivery, state, projection) {
|
|
852
|
-
const send = delivery.fetch ?? globalThis.fetch;
|
|
853
|
-
const url = `${delivery.baseUrl.replace(/\/+$/u, "")}${PURSUIT_OBSERVER_DELIVERY_PATH}`;
|
|
854
|
-
try {
|
|
855
|
-
const response = await send(url, {
|
|
856
|
-
method: "POST",
|
|
857
|
-
headers: {
|
|
858
|
-
authorization: `Bearer ${delivery.apiKey}`,
|
|
859
|
-
"content-type": "application/json"
|
|
860
|
-
},
|
|
861
|
-
body: JSON.stringify({
|
|
862
|
-
subjectKey: delivery.subjectKey,
|
|
863
|
-
...delivery.subjectName === void 0 ? {} : { subjectName: delivery.subjectName },
|
|
864
|
-
state,
|
|
865
|
-
...delivery.attributes === void 0 ? {} : { attributes: delivery.attributes },
|
|
866
|
-
projection
|
|
867
|
-
}),
|
|
868
|
-
signal: AbortSignal.timeout(delivery.timeoutMs ?? DEFAULT_TIMEOUT_MS)
|
|
869
|
-
});
|
|
870
|
-
if (response.ok) return {
|
|
871
|
-
succeeded: true,
|
|
872
|
-
status: response.status
|
|
873
|
-
};
|
|
874
|
-
const detail = (await response.text().catch(() => "")).slice(0, 300);
|
|
875
|
-
return {
|
|
876
|
-
succeeded: false,
|
|
877
|
-
reason: `HTTP ${response.status}${detail ? `: ${detail}` : ""}`
|
|
878
|
-
};
|
|
879
|
-
} catch (error) {
|
|
880
|
-
return {
|
|
881
|
-
succeeded: false,
|
|
882
|
-
reason: error instanceof Error ? error.message : String(error)
|
|
883
|
-
};
|
|
884
|
-
}
|
|
885
|
-
}
|
|
886
|
-
/**
|
|
887
|
-
* Deliver a live run's projection at start, on an interval, and at settlement. `read` projects
|
|
888
|
-
* the run's own journal at the moment of each delivery, so every delivery is the whole record.
|
|
889
|
-
*/
|
|
890
|
-
function startPursuitObserverDelivery(delivery, read, warn = (message) => console.warn(message)) {
|
|
891
|
-
let chain = Promise.resolve(void 0);
|
|
892
|
-
let pending;
|
|
893
|
-
let warned = false;
|
|
894
|
-
let closed = false;
|
|
895
|
-
const deliver = async (state) => {
|
|
896
|
-
let projection;
|
|
897
|
-
try {
|
|
898
|
-
projection = await read();
|
|
899
|
-
} catch (error) {
|
|
900
|
-
return report({
|
|
901
|
-
succeeded: false,
|
|
902
|
-
reason: `observer projection unreadable: ${error instanceof Error ? error.message : String(error)}`
|
|
903
|
-
});
|
|
904
|
-
}
|
|
905
|
-
return report(await deliverPursuitObserver(delivery, state, projection));
|
|
906
|
-
};
|
|
907
|
-
const report = (outcome) => {
|
|
908
|
-
if (!outcome.succeeded && !warned) {
|
|
909
|
-
warned = true;
|
|
910
|
-
warn(`supervisePursuit: pursuit observer delivery to ${delivery.baseUrl} failed (${outcome.reason}); the run continues and later deliveries retry`);
|
|
911
|
-
}
|
|
912
|
-
return outcome;
|
|
913
|
-
};
|
|
914
|
-
const push = (state) => {
|
|
915
|
-
if (closed) return;
|
|
916
|
-
if (pending !== void 0) return;
|
|
917
|
-
pending = state;
|
|
918
|
-
chain = chain.then(() => {
|
|
919
|
-
const next = pending;
|
|
920
|
-
pending = void 0;
|
|
921
|
-
return deliver(next);
|
|
922
|
-
});
|
|
923
|
-
};
|
|
924
|
-
const timer = setInterval(() => push("running"), delivery.intervalMs ?? DEFAULT_INTERVAL_MS);
|
|
925
|
-
timer.unref?.();
|
|
926
|
-
push("running");
|
|
927
|
-
return {
|
|
928
|
-
push,
|
|
929
|
-
async close(state) {
|
|
930
|
-
closed = true;
|
|
931
|
-
clearInterval(timer);
|
|
932
|
-
const terminal = chain.then(() => deliver(state));
|
|
933
|
-
chain = terminal;
|
|
934
|
-
const bound = delivery.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
935
|
-
let timeout;
|
|
936
|
-
const expired = new Promise((resolve) => {
|
|
937
|
-
timeout = setTimeout(() => resolve({
|
|
938
|
-
succeeded: false,
|
|
939
|
-
reason: `terminal delivery exceeded ${bound} ms`
|
|
940
|
-
}), bound * 2);
|
|
941
|
-
timeout.unref?.();
|
|
942
|
-
});
|
|
943
|
-
try {
|
|
944
|
-
return await Promise.race([terminal, expired]);
|
|
945
|
-
} finally {
|
|
946
|
-
clearTimeout(timeout);
|
|
947
|
-
}
|
|
948
|
-
}
|
|
949
|
-
};
|
|
950
|
-
}
|
|
951
|
-
//#endregion
|
|
952
845
|
//#region src/durable/settle-record.ts
|
|
953
846
|
/** The settle record: the returned `SupervisedResult` as canonical JSON, written once. */
|
|
954
847
|
const SETTLE_RECORD_FILE = "result.json";
|
|
@@ -1118,11 +1011,13 @@ function assertPursuitVersions(versions) {
|
|
|
1118
1011
|
"maxVersions",
|
|
1119
1012
|
"maxUsd",
|
|
1120
1013
|
"deadlineMs",
|
|
1121
|
-
"minImprovement"
|
|
1014
|
+
"minImprovement",
|
|
1015
|
+
"identicalFailures"
|
|
1122
1016
|
]);
|
|
1123
1017
|
const unknown = Object.keys(rule).filter((key) => !known.has(key));
|
|
1124
1018
|
if (unknown.length > 0) fail(`stop has unknown fields: ${unknown.join(", ")}`);
|
|
1125
1019
|
for (const key of ["patience", "maxVersions"]) if (!Number.isInteger(rule[key]) || rule[key] < 1) fail(`stop.${key} must be an integer of at least 1`);
|
|
1020
|
+
if (rule.identicalFailures !== void 0 && (!Number.isInteger(rule.identicalFailures) || rule.identicalFailures < 1)) fail("stop.identicalFailures must be an integer of at least 1");
|
|
1126
1021
|
for (const key of ["maxUsd", "deadlineMs"]) if (!isFiniteNumber(rule[key]) || rule[key] <= 0) fail(`stop.${key} must be a finite number above 0`);
|
|
1127
1022
|
if (rule.minImprovement !== void 0 && (!isFiniteNumber(rule.minImprovement) || rule.minImprovement < 0)) fail("stop.minImprovement must be a finite number of at least 0");
|
|
1128
1023
|
}
|
|
@@ -1135,6 +1030,7 @@ const VERSION_CHAIN_DEFINITION = {
|
|
|
1135
1030
|
cell: "one supervisePursuit run of the version on the pursuit task, forked from its parent sealed result.json, then scored by the outside judge; a settled version directory is read back after its root identity and fork seal are checked",
|
|
1136
1031
|
outcome: "a scored winner passes; a scored no-winner fails with its reason; an unscored version is a final error; a run that throws fails the call and leaves its cell open",
|
|
1137
1032
|
deadline: "the chain deadline aborts a running version; its judge still runs",
|
|
1033
|
+
failures: "before proposing, the chain closes converged when its last stop.identicalFailures judged versions failed identically: driver-failed with the same error name and message, digits, hex runs and absolute paths normalized, or unscored by the judge",
|
|
1138
1034
|
directories: "version n runs at <runDir>.v<n> with run id <runId>.v<n>; version 1 at <runDir>"
|
|
1139
1035
|
};
|
|
1140
1036
|
const VERSION_CHAIN_URI = "npm:@tangle-network/agent-runtime#supervisePursuit.versions";
|
|
@@ -1190,6 +1086,7 @@ async function runPursuitVersions(profile, task, opts, runOne) {
|
|
|
1190
1086
|
searchId
|
|
1191
1087
|
});
|
|
1192
1088
|
const deadline = (await ledger.state()).header?.budget.deadline ?? new Date(Math.min(now() - settledElapsed(await readSettleRecord(runDir)) + stop.deadlineMs, LATEST_DEADLINE_MS)).toISOString();
|
|
1089
|
+
const failureLimit = stop.identicalFailures ?? DEFAULT_IDENTICAL_FAILURES;
|
|
1193
1090
|
const policy = incumbent({
|
|
1194
1091
|
patience: stop.patience,
|
|
1195
1092
|
...stop.minImprovement === void 0 ? {} : { minImprovement: stop.minImprovement }
|
|
@@ -1337,7 +1234,7 @@ async function runPursuitVersions(profile, task, opts, runOne) {
|
|
|
1337
1234
|
if (parent !== void 0 && (parent.seal === void 0 || parent.profile === null)) throw new RuntimeRunStateError(`version ${record.version}'s parent has no sealed result`);
|
|
1338
1235
|
if (await readSettleRecord(record.runDir) === void 0) {
|
|
1339
1236
|
const signal = AbortSignal.any([work.signal, expired.signal]);
|
|
1340
|
-
if (signal.aborted) throw new RuntimeRunStateError(`supervisePursuit versions: version ${record.version} was not started: ${errorMessage
|
|
1237
|
+
if (signal.aborted) throw new RuntimeRunStateError(`supervisePursuit versions: version ${record.version} was not started: ${errorMessage(signal.reason)}`);
|
|
1341
1238
|
await runVersion(record, parent, signal);
|
|
1342
1239
|
}
|
|
1343
1240
|
const { settled, byteLength } = await readVersion(record, parent);
|
|
@@ -1360,6 +1257,12 @@ async function runPursuitVersions(profile, task, opts, runOne) {
|
|
|
1360
1257
|
async propose(request) {
|
|
1361
1258
|
const parent = request.parents[0];
|
|
1362
1259
|
const judged = await judgedVersions(await versionsOf());
|
|
1260
|
+
const failure = identicalFailureStreak(judged, failureLimit);
|
|
1261
|
+
if (failure !== null) return {
|
|
1262
|
+
children: [],
|
|
1263
|
+
stop: `the last ${failureLimit} versions failed identically: ${failure}`,
|
|
1264
|
+
accounting: FREE
|
|
1265
|
+
};
|
|
1363
1266
|
const best = judged.find((version) => version.nodeId === parent.nodeId);
|
|
1364
1267
|
if (best === void 0 || parent.artifact.profile === null) throw new RuntimeRunStateError(`supervisePursuit versions: the kept version ${parent.nodeId} has no judged run to fork from`);
|
|
1365
1268
|
const returned = await next({
|
|
@@ -1383,7 +1286,7 @@ async function runPursuitVersions(profile, task, opts, runOne) {
|
|
|
1383
1286
|
profile: null,
|
|
1384
1287
|
change,
|
|
1385
1288
|
parentDigest,
|
|
1386
|
-
refusal: errorMessage
|
|
1289
|
+
refusal: errorMessage(error)
|
|
1387
1290
|
};
|
|
1388
1291
|
}
|
|
1389
1292
|
return {
|
|
@@ -1423,6 +1326,11 @@ async function runPursuitVersions(profile, task, opts, runOne) {
|
|
|
1423
1326
|
now
|
|
1424
1327
|
});
|
|
1425
1328
|
const judged = await judgedVersions(await versionsOf());
|
|
1329
|
+
const failed = closed.reason === "converged" ? identicalFailureStreak(judged, failureLimit) : null;
|
|
1330
|
+
const stopped = failed === null ? null : {
|
|
1331
|
+
versions: failureLimit,
|
|
1332
|
+
failure: failed
|
|
1333
|
+
};
|
|
1426
1334
|
const best = judged.find((version) => version.nodeId === closed.leader);
|
|
1427
1335
|
if (best === void 0) throw new RuntimeRunStateError(`supervisePursuit versions: the chain in ${ledgerPath} closed (${closed.reason}) without a judged version to keep`);
|
|
1428
1336
|
const observerPath = resolve(best.runDir, "observer.jsonl");
|
|
@@ -1435,6 +1343,7 @@ async function runPursuitVersions(profile, task, opts, runOne) {
|
|
|
1435
1343
|
ledgerPath,
|
|
1436
1344
|
searchId,
|
|
1437
1345
|
reason: closed.reason,
|
|
1346
|
+
...stopped === null ? {} : { identicalFailures: stopped },
|
|
1438
1347
|
best: best.version,
|
|
1439
1348
|
versions: Object.freeze(judged),
|
|
1440
1349
|
spend: closed.state.audit.spend,
|
|
@@ -1753,9 +1662,30 @@ function canonicalStop(stop) {
|
|
|
1753
1662
|
maxVersions: stop.maxVersions,
|
|
1754
1663
|
maxUsd: stop.maxUsd,
|
|
1755
1664
|
deadlineMs: stop.deadlineMs,
|
|
1756
|
-
...stop.minImprovement === void 0 ? {} : { minImprovement: stop.minImprovement }
|
|
1665
|
+
...stop.minImprovement === void 0 ? {} : { minImprovement: stop.minImprovement },
|
|
1666
|
+
identicalFailures: stop.identicalFailures ?? DEFAULT_IDENTICAL_FAILURES
|
|
1757
1667
|
};
|
|
1758
1668
|
}
|
|
1669
|
+
const DEFAULT_IDENTICAL_FAILURES = 3;
|
|
1670
|
+
/**
|
|
1671
|
+
* What a version that did no work failed with: a driver error, by its name and its message with
|
|
1672
|
+
* the parts that differ between runs (digits, hex runs, absolute paths) normalized, or an unscored
|
|
1673
|
+
* verdict. Null for a version that ran, won or not.
|
|
1674
|
+
*/
|
|
1675
|
+
function versionFailure(version) {
|
|
1676
|
+
if (version.verdict.score === null) return "unscored by the judge";
|
|
1677
|
+
const { result } = version;
|
|
1678
|
+
if (result.kind !== "no-winner" || result.reason !== "driver-failed") return null;
|
|
1679
|
+
const message = result.error.message.replace(/(?:\/[\w.@~+-]+){2,}/gu, "<path>").replace(/\b[0-9a-f]{8,}\b/giu, "<hex>").replace(/\d+/gu, "<n>").slice(0, 500);
|
|
1680
|
+
return `driver-failed: ${result.error.name}: ${message}`;
|
|
1681
|
+
}
|
|
1682
|
+
/** The failure the last `count` judged versions share, or null when they do not all share one. */
|
|
1683
|
+
function identicalFailureStreak(versions, count) {
|
|
1684
|
+
if (versions.length < count) return null;
|
|
1685
|
+
const failures = versions.slice(-count).map(versionFailure);
|
|
1686
|
+
const [first] = failures;
|
|
1687
|
+
return first !== null && first !== void 0 && failures.every((failure) => failure === first) ? first : null;
|
|
1688
|
+
}
|
|
1759
1689
|
/**
|
|
1760
1690
|
* `'review-of-best'`: fork from the kept version with its check's verdict mounted under
|
|
1761
1691
|
* {@link REVIEW_DIR} and the profile's review words as one instruction. The previous review's
|
|
@@ -1833,7 +1763,7 @@ function digestOf(value, name) {
|
|
|
1833
1763
|
try {
|
|
1834
1764
|
return canonicalCandidateDigest(value);
|
|
1835
1765
|
} catch (error) {
|
|
1836
|
-
throw new ValidationError(`supervisePursuit versions: the ${name} must be canonical JSON to pin the chain: ${errorMessage
|
|
1766
|
+
throw new ValidationError(`supervisePursuit versions: the ${name} must be canonical JSON to pin the chain: ${errorMessage(error)}`);
|
|
1837
1767
|
}
|
|
1838
1768
|
}
|
|
1839
1769
|
async function exists(path) {
|
|
@@ -1851,7 +1781,7 @@ function isSha256(value) {
|
|
|
1851
1781
|
function isFiniteNumber(value) {
|
|
1852
1782
|
return typeof value === "number" && Number.isFinite(value);
|
|
1853
1783
|
}
|
|
1854
|
-
function errorMessage
|
|
1784
|
+
function errorMessage(error) {
|
|
1855
1785
|
return error instanceof Error ? error.message : String(error);
|
|
1856
1786
|
}
|
|
1857
1787
|
//#endregion
|
|
@@ -2150,6 +2080,113 @@ async function removeIfPresent(path) {
|
|
|
2150
2080
|
}
|
|
2151
2081
|
}
|
|
2152
2082
|
//#endregion
|
|
2083
|
+
//#region src/durable/pursuit-observer-delivery.ts
|
|
2084
|
+
/** The Intelligence route that persists a delivered projection on its run spine. */
|
|
2085
|
+
const PURSUIT_OBSERVER_DELIVERY_PATH = "/v1/ingest/pursuit-observer";
|
|
2086
|
+
const DEFAULT_INTERVAL_MS = 6e4;
|
|
2087
|
+
const DEFAULT_TIMEOUT_MS = 1e4;
|
|
2088
|
+
/** POST one projection. Never throws; the outcome says whether Intelligence accepted it. */
|
|
2089
|
+
async function deliverPursuitObserver(delivery, state, projection) {
|
|
2090
|
+
const send = delivery.fetch ?? globalThis.fetch;
|
|
2091
|
+
const url = `${delivery.baseUrl.replace(/\/+$/u, "")}${PURSUIT_OBSERVER_DELIVERY_PATH}`;
|
|
2092
|
+
try {
|
|
2093
|
+
const response = await send(url, {
|
|
2094
|
+
method: "POST",
|
|
2095
|
+
headers: {
|
|
2096
|
+
authorization: `Bearer ${delivery.apiKey}`,
|
|
2097
|
+
"content-type": "application/json"
|
|
2098
|
+
},
|
|
2099
|
+
body: JSON.stringify({
|
|
2100
|
+
subjectKey: delivery.subjectKey,
|
|
2101
|
+
...delivery.subjectName === void 0 ? {} : { subjectName: delivery.subjectName },
|
|
2102
|
+
state,
|
|
2103
|
+
...delivery.attributes === void 0 ? {} : { attributes: delivery.attributes },
|
|
2104
|
+
projection
|
|
2105
|
+
}),
|
|
2106
|
+
signal: AbortSignal.timeout(delivery.timeoutMs ?? DEFAULT_TIMEOUT_MS)
|
|
2107
|
+
});
|
|
2108
|
+
if (response.ok) return {
|
|
2109
|
+
succeeded: true,
|
|
2110
|
+
status: response.status
|
|
2111
|
+
};
|
|
2112
|
+
const detail = (await response.text().catch(() => "")).slice(0, 300);
|
|
2113
|
+
return {
|
|
2114
|
+
succeeded: false,
|
|
2115
|
+
reason: `HTTP ${response.status}${detail ? `: ${detail}` : ""}`
|
|
2116
|
+
};
|
|
2117
|
+
} catch (error) {
|
|
2118
|
+
return {
|
|
2119
|
+
succeeded: false,
|
|
2120
|
+
reason: error instanceof Error ? error.message : String(error)
|
|
2121
|
+
};
|
|
2122
|
+
}
|
|
2123
|
+
}
|
|
2124
|
+
/**
|
|
2125
|
+
* Deliver a live run's projection at start, on an interval, and at settlement. `read` projects
|
|
2126
|
+
* the run's own journal at the moment of each delivery, so every delivery is the whole record.
|
|
2127
|
+
*/
|
|
2128
|
+
function startPursuitObserverDelivery(delivery, read, warn = (message) => console.warn(message)) {
|
|
2129
|
+
let chain = Promise.resolve(void 0);
|
|
2130
|
+
let pending;
|
|
2131
|
+
let warned = false;
|
|
2132
|
+
let closed = false;
|
|
2133
|
+
const deliver = async (state) => {
|
|
2134
|
+
let projection;
|
|
2135
|
+
try {
|
|
2136
|
+
projection = await read();
|
|
2137
|
+
} catch (error) {
|
|
2138
|
+
return report({
|
|
2139
|
+
succeeded: false,
|
|
2140
|
+
reason: `observer projection unreadable: ${error instanceof Error ? error.message : String(error)}`
|
|
2141
|
+
});
|
|
2142
|
+
}
|
|
2143
|
+
return report(await deliverPursuitObserver(delivery, state, projection));
|
|
2144
|
+
};
|
|
2145
|
+
const report = (outcome) => {
|
|
2146
|
+
if (!outcome.succeeded && !warned) {
|
|
2147
|
+
warned = true;
|
|
2148
|
+
warn(`supervisePursuit: pursuit observer delivery to ${delivery.baseUrl} failed (${outcome.reason}); the run continues and later deliveries retry`);
|
|
2149
|
+
}
|
|
2150
|
+
return outcome;
|
|
2151
|
+
};
|
|
2152
|
+
const push = (state) => {
|
|
2153
|
+
if (closed) return;
|
|
2154
|
+
if (pending !== void 0) return;
|
|
2155
|
+
pending = state;
|
|
2156
|
+
chain = chain.then(() => {
|
|
2157
|
+
const next = pending;
|
|
2158
|
+
pending = void 0;
|
|
2159
|
+
return deliver(next);
|
|
2160
|
+
});
|
|
2161
|
+
};
|
|
2162
|
+
const timer = setInterval(() => push("running"), delivery.intervalMs ?? DEFAULT_INTERVAL_MS);
|
|
2163
|
+
timer.unref?.();
|
|
2164
|
+
push("running");
|
|
2165
|
+
return {
|
|
2166
|
+
push,
|
|
2167
|
+
async close(state) {
|
|
2168
|
+
closed = true;
|
|
2169
|
+
clearInterval(timer);
|
|
2170
|
+
const terminal = chain.then(() => deliver(state));
|
|
2171
|
+
chain = terminal;
|
|
2172
|
+
const bound = delivery.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
2173
|
+
let timeout;
|
|
2174
|
+
const expired = new Promise((resolve) => {
|
|
2175
|
+
timeout = setTimeout(() => resolve({
|
|
2176
|
+
succeeded: false,
|
|
2177
|
+
reason: `terminal delivery exceeded ${bound} ms`
|
|
2178
|
+
}), bound * 2);
|
|
2179
|
+
timeout.unref?.();
|
|
2180
|
+
});
|
|
2181
|
+
try {
|
|
2182
|
+
return await Promise.race([terminal, expired]);
|
|
2183
|
+
} finally {
|
|
2184
|
+
clearTimeout(timeout);
|
|
2185
|
+
}
|
|
2186
|
+
}
|
|
2187
|
+
};
|
|
2188
|
+
}
|
|
2189
|
+
//#endregion
|
|
2153
2190
|
//#region src/durable/supervise-pursuit.ts
|
|
2154
2191
|
/** A failed Runtime execution whose complete third-person projection was retained. */
|
|
2155
2192
|
var SupervisePursuitError = class extends Error {
|
|
@@ -2158,7 +2195,7 @@ var SupervisePursuitError = class extends Error {
|
|
|
2158
2195
|
/** `runDir/failure.json`: the record of this throw. */
|
|
2159
2196
|
failurePath;
|
|
2160
2197
|
constructor(cause, pursuit, observerPath, failurePath) {
|
|
2161
|
-
super(`supervisePursuit: ${errorMessage(cause)}`, { cause });
|
|
2198
|
+
super(`supervisePursuit: ${errorMessage$1(cause)}`, { cause });
|
|
2162
2199
|
this.name = "SupervisePursuitError";
|
|
2163
2200
|
this.pursuit = pursuit;
|
|
2164
2201
|
this.observerPath = observerPath;
|
|
@@ -2223,7 +2260,7 @@ async function supervisePursuit(profile, task, opts) {
|
|
|
2223
2260
|
try {
|
|
2224
2261
|
await observer.journal.appendEvent(rootEvent(pursuitId, runId, "error", now(), {
|
|
2225
2262
|
status: "failed",
|
|
2226
|
-
error: errorMessage(error)
|
|
2263
|
+
error: errorMessage$1(error)
|
|
2227
2264
|
}));
|
|
2228
2265
|
pursuit = projectPursuit(await observer.journal.read());
|
|
2229
2266
|
} catch (failure) {
|
|
@@ -2238,7 +2275,7 @@ async function supervisePursuit(profile, task, opts) {
|
|
|
2238
2275
|
at: new Date(now()).toISOString(),
|
|
2239
2276
|
error: {
|
|
2240
2277
|
name: errorName(error),
|
|
2241
|
-
message: errorMessage(error)
|
|
2278
|
+
message: errorMessage$1(error)
|
|
2242
2279
|
},
|
|
2243
2280
|
...rootStream === void 0 ? {} : { rootStream }
|
|
2244
2281
|
});
|
|
@@ -2276,9 +2313,6 @@ function rootEvent(pursuitId, runId, phase, timestamp, payload) {
|
|
|
2276
2313
|
...payload ? { payload: Object.freeze({ ...payload }) } : {}
|
|
2277
2314
|
});
|
|
2278
2315
|
}
|
|
2279
|
-
function errorMessage(error) {
|
|
2280
|
-
return error instanceof Error ? error.message : String(error);
|
|
2281
|
-
}
|
|
2282
2316
|
function errorName(error) {
|
|
2283
2317
|
return error instanceof Error ? error.name : "NonError";
|
|
2284
2318
|
}
|
|
@@ -2353,6 +2387,6 @@ function compareText(left, right) {
|
|
|
2353
2387
|
return left < right ? -1 : left > right ? 1 : 0;
|
|
2354
2388
|
}
|
|
2355
2389
|
//#endregion
|
|
2356
|
-
export {
|
|
2390
|
+
export { FileObserverJournal, ROOT_STREAM_FILE, RUN_FORK_CORRELATION_KEYS, RunDirectoryLockedError, SettledRunDirectoryError, SupervisePursuitError, acquireRunDirectoryLock, assertPursuitVersions, deriveExecutionId, discoverDurableSupervisionRun, handleChatTurn, observerRecordDigest, projectPursuit, readFailureRecord, readRootStream, readRootStreamReceipt, readRunDirectoryLock, readSettleRecord, runDirectoryHolderIsLive, supervisePursuit, verifyObserverRecords };
|
|
2357
2391
|
|
|
2358
2392
|
//# sourceMappingURL=durable.js.map
|