import { SupervisorResult } from "../contracts/result/supervisor-result.type.mjs"; import { SupervisorInput } from "../contracts/supervisor/supervisor-input.type.mjs"; import { SupervisorSnapshot } from "../contracts/supervisor/supervisor-snapshot.type.mjs"; import { SupervisorExecuteOptions } from "../contracts/supervisor/supervisor-execute-options.type.mjs"; import { SupervisorConfig } from "../contracts/supervisor/supervisor-config.type.mjs"; import { ResolvedIntentEntry } from "./entries.mjs"; import { SupervisorEmitter } from "./emitter.mjs"; import { SupervisorStreamController } from "./supervisor-stream.mjs"; //#region ../ai/src/supervisor/execution.d.ts type SupervisorExecutionParams = { config: SupervisorConfig; entries: Map; signature: string; emitter: SupervisorEmitter; input: SupervisorInput; runId: string; options?: SupervisorExecuteOptions; streamController?: SupervisorStreamController>; resumeFrom?: SupervisorSnapshot; }; /** * Per-call driver that owns the full lifecycle of one supervisor run. * * **Role.** Short-lived state container and phase orchestrator — * mirrors `agent/Execution` and `workflow/runWorkflow`, one level up. * * **Responsibility.** * - Owns: the iteration loop, per-iteration dispatch (single or * fan-out), evaluate scheduling, usage aggregation across router + * every branch + evaluate, snapshot collection, event emission * through all three tiers, KV-store checkpointing, final result * assembly (state validation against the output schema → typed data). * - Does NOT own: how child agents produce responses (delegated via * `agent.execute` / `workflow.execute`), the routing decision * itself (delegated to `decide.ts`), snapshot persistence mechanics * (delegated to `snapshot.ts`), the stream queue plumbing * (delegated to `supervisor-stream.ts`). * * `execute()` never throws — every unexpected failure funnels into * `this.error` and is returned on `result.error` with an appropriate * `SupervisorFailedError` / `MaxIterationsError` / `SupervisorRoutingError` * / `SupervisorCancelledError`. * * @example * // Inside supervisor.execute() — never constructed by user code directly: * return new SupervisorExecution(params).run(); */ declare class SupervisorExecution { private readonly config; private readonly entries; private readonly signature; private readonly emitter; private readonly input; private readonly runId; private readonly options?; private readonly streamController?; private readonly resumeFrom?; private readonly maxIterations; private readonly logger; private readonly logModule; /** * Supervisor-level middleware stack — `config.middleware` (default * empty). Each entry's optional `supervisor` hook map fires once * around the whole run via `runPipeline(..., "supervisor", ...)` in * {@link run}; entries without that hook map are skipped by the * pipeline. */ private readonly middleware; /** * Per-run shared-state bag threaded through every `supervisor`-level * hook (`before` / `after` / `onError`) of this one run. Fresh `Map` * per `SupervisorExecution` so two concurrent runs of the same * supervisor get isolated bags — mirrors the agent pipeline. */ private readonly middlewareState; private readonly snapshots; private readonly childReports; private readonly usage; private readonly startedAtIso; private readonly startPerf; private iteration; private carriedFeedback?; /** * Per-intent `next` directive (Q24 / Stage 4d) collected at the * end of an iteration after evaluate hasn't already steered. When * set, `decideDispatch` consumes it on the next iteration's start — * skipping the router entirely. Cleared after consumption. * * Only the dispatch variant is stored; an `END` collection * terminates the iteration loop directly inside `runIteration`. */ private carriedNextDispatch?; private terminatedBy; private status; private cancelledAtIso?; private error?; private data?; private lastDispatchIntents; /** * Per-execute typed accumulator. Initialized from `config.state` * (default `{}`) at construction; rehydrated from the last * snapshot's `state` on resume; mutated in-place as each iteration's * intents strip-merge their outputs into it. */ private state; /** * Per-iteration artifacts bag (Phase 5 / decisions §35). Tools * dispatched within an iteration mutate `ctx.artifacts` — which * points at this object. After the iteration's branches settle and * their slices merge into state, this bag validates against * `config.artifactsSchema` (if set) and merges via * `config.finalizeArtifacts` or auto-spread, then resets to `{}` * for the next iteration. The reset is crucial — long runs and * orchestrator sessions never accumulate raw artifacts here. */ private currentArtifacts; /** * Frozen copy of the iteration's `currentArtifacts` bag captured at * merge time — BEFORE `finalizeArtifacts` (or auto-spread) reshaped * it into state (Phase 8 / decisions §38). Surfaced on the iteration * snapshot's `artifacts` field for forensic / telemetry consumers * that want the raw tool contributions. * * Reset to `{}` at the start of every iteration so a snapshot built * for an iteration whose tools wrote nothing carries an empty bag, * not a stale carry-over. */ private capturedIterationArtifacts; /** * Classifier (Phase 7 / decisions §37) forensic record. Set on iter * 0 when `SupervisorConfig.classifier` is configured AND the run * started fresh (resumes don't re-fire classifier — same as ack). * Surfaced on `SupervisorReport.classifier` and threaded into * `ctx.classifier` on RouteContext / DispatchContext / * EvaluateContext from iter 0 onward. */ private classifierSnapshot?; /** * Iter-0 dispatch decision pre-computed by the classifier (Phase 7). * When set, `decideDispatch` short-circuits and uses this directly * with `source: "classifier"`. Cleared after consumption. */ private carriedClassifierDispatch?; /** Set true by classifier refine returning END to halt before any dispatch. */ private classifierHalted; /** * Receptionist forensic record. Set when an `ackAgent` was * configured AND the run started fresh (resumes don't re-fire ack). * Surfaced on `SupervisorReport.ack`. */ private ackSnapshot?; /** * Read-only request-scoped context surfaced on every `ctx.context`. * Shallow-copied + frozen at construction so callbacks see a stable * snapshot of the caller's bag and can't mutate the original. * Always present — defaults to a frozen `{}` when no context was * passed. NOT persisted in snapshots. */ private readonly context; /** * Prior conversation messages threaded through every callback context * (`ctx.history`) and forwarded verbatim to dispatched agents (and the * receptionist `ack` agent) as `agent.execute(input, { history })`. * Frozen reference so callbacks see a stable view; not deep-cloned — * conversation messages are treated as immutable by convention. NOT * persisted in snapshots (re-supply on `resume()`). */ private readonly history; /** * Resolved natural-language objective from `SupervisorConfig.goal`. * Materialized to plain text at construction (string passes through; * `SystemPromptContract` is `.resolve()`-d). `undefined` when the * supervisor was configured without a goal. */ private readonly goal; constructor(params: SupervisorExecutionParams); /** * Resolve the history slice forwarded to a child execution (router / * dispatched agent / ack). Precedence: * * 1. Per-entry `history` callback — full override; whatever it * returns goes through (after defensive copy). * 2. `SupervisorConfig.historyWindow.` — last-N slice of the * caller-supplied history. * 3. Default — full history for `router`/`agents`, empty for `ack` * (receptionists rarely benefit from scroll-back). * * Always returns a fresh `Message[]` (the agent layer * mutates by reference internally, e.g. via `messages.push(...)`). */ private resolveHistoryFor; /** * Apply only the global `historyWindow.agents` slice — used by the * recursive `ctx.intents.X.execute()` re-entry path where no * `RouteContext` is available to feed the per-entry slicer. */ private applyAgentsWindow; /** * Entry point. Wraps the core run (`runCore`) in the * `supervisor`-level middleware pipeline, then emits the terminal * `supervisor.cancelled` / `supervisor.error` / `supervisor.completed` * events and closes the stream (if any) with the post-pipeline result * — so a middleware that short-circuits or transforms the final * result still produces a well-formed public outcome. Returns the * uniform `{ data, report, usage, error }` shape. Never throws. */ run(): Promise>; /** * Build the `supervisor`-level middleware context — the stable * identity of this run plus the per-run shared-state bag every hook * sees. Constructed once per run, before the pipeline `before` hooks * fire. Mirrors the agent's `buildExecuteContext`, one level up. */ private buildSupervisorContext; /** * Inner body wrapped by the `supervisor`-level pipeline. Emits the * `supervisor.starting` event, drives the iteration loop, absorbs * every iteration-loop failure into `this.error` (so the run never * throws from here), and returns the assembled `SupervisorResult`. * `supervisor`-level `after` hooks receive this result, with `error` * populated when the loop failed; `before` hooks can short-circuit * before this ever runs. */ private runCore; /** * Drive the iteration loop until a terminal condition fires: * `END` / `satisfied:true` / `maxIterations` / signal abort / * routing error. Between-iteration cancellation is guaranteed — * the signal is checked before every iteration starts. */ private runIterationLoop; /** * Run one iteration end-to-end: decide → dispatch → evaluate → * snapshot. Returns `true` when the loop should continue to the * next iteration, `false` when this iteration terminated the run * (success or satisfied-verdict). Failures throw — the loop's * outer catch converts them into typed errors on the result. */ private runIteration; /** * Resolve the dispatch decision for this iteration — defers to * `decide.ts`. When `carriedFeedback.reassignTo` is set the * supervisor overrides the router/route decision with an * evaluator-forced dispatch (design §2 — "Evaluate can override * router"). */ private decideDispatch; /** * Dispatch every intent named by the decision in parallel. Per- * branch errors don't abort siblings — they're recorded on the * branch snapshot and let evaluate (or default termination logic) * decide the response. * * `capFanOut` runs here as well as in `decide.ts` — this is the one * chokepoint every dispatch source funnels through (router/route * decisions, `evaluate.reassignTo`, classifier picks, per-intent * `next` unions), so the width bound holds even for the paths that * build a `DispatchDecision` without going through `normalize()`. * Idempotent for already-normalized decisions. */ private dispatchBranches; /** * Execute a single branch — resolve the input, invoke the * agent / workflow / callback, apply the per-intent `output` * transformer, and produce an immutable `AgentBranchSnapshot`. */ private dispatchOne; /** * Dispatch a callback intent as a top-level branch — produces an * `AgentBranchSnapshot` and pushes the synthesized callback report * onto the supervisor's recursive children. Delegates the actual * callback invocation to {@link runCallback} so nested * `ctx.intents.X.execute()` calls can reuse the same machinery. * * Each branch dispatch starts with a fresh per-branch call stack — * sibling fan-out branches don't share cycle-detection state, so * branch A and branch B both invoking the same intent isn't a * cycle. The branch's own intent name is seeded onto the stack so * a callback that re-enters itself via `ctx.intents.X.execute()` trips * cycle detection on the first recursion. */ private dispatchCallback; /** * Run a callback intent and produce its leaf report + final * output. Used both for top-level branch dispatch (via * {@link dispatchCallback}) and for nested `dispatch.byName` * recursion. The synthesized report is appended to `reportSink`, * which is either `this.childReports` (top-level) or the calling * callback's own `children[]` (nested) — that's what gives the * unified report tree its compositional shape. * * Usage on the report rolls up children's usage; the callback * itself contributes zero (it's dev code, no token spend). */ private runCallback; /** * Build a {@link DispatchContext} with a typed `intents` map of * `IntentRunner` closures, each closing over the supplied call * stack and report sink. Cycle detection uses the call stack — * re-entering an intent already on it throws * `SupervisorFailedError` with code `SUPERVISOR_DISPATCH_CYCLE` * and the offending chain in the message. * * Replaces the Phase-3.3 `ctx.dispatch.byName` plumbing with * property-access on a typed map (Q5/Q6) — autocomplete, no typo * crashes, `.execute()` matches every other primitive's verb. */ private seedDispatchContext; /** * Backing implementation for `ctx.intents.X.execute(input?)`. * Looks up the named intent in the supervisor's registry, asserts * the call wouldn't close a cycle, and runs the dispatchable * through the same machinery a top-level branch would — except * the resulting report nests under the calling callback's * `children[]` rather than the supervisor's top-level child list, * and only the final output is returned (no snapshot). */ private runIntent; /** * Backing implementation for `ctx.intents.X.stream(input?)` (Phase 6 * / decisions §36). Streaming sibling of {@link runIntent} — same * cycle protection, same auto-merge of supervisor-level concerns, * but routes through the unit's `.stream()` method when available * and bubbles deltas as `supervisor.agent.streaming` under the * **calling callback's** intent name (not the dispatched intent's). */ private streamIntent; /** * Backing implementation for `ctx.run(executable, input, options?)` * (Phase 6 / decisions §36). Runs an inline / un-registered * executable under supervision: auto-merges `signal`, `toolCtx`, * `history` defaults; nests the resulting report under the * calling callback's `children[]`. Per-call options REPLACE auto- * defaults — standard Warlock convention. * * Cycle protection by executable `name` matches the registered- * intent path so a callback that recurses on the same agent trips * the same error, regardless of whether the agent was looked up * via `ctx.intents.X.execute()` or passed inline. */ private runInline; /** * Backing implementation for `ctx.stream(executable, input, options?)` * (Phase 6 / decisions §36). Streaming sibling of {@link runInline}. * Routes through the executable's native `.stream()` method, * subscribes to delta events, and bubbles them as * `supervisor.agent.streaming` under the calling callback's intent * name. The returned `StreamContract` is the executable's own — * iteration and `.result` work identically. * * Cycle protection on entry mirrors {@link runInline}; release runs * after `.result` settles so a same-callback recursion is caught * regardless of which path closed the cycle. */ private streamInline; /** * Shared wiring for both `ctx.intents.X.stream()` and * `ctx.stream(...)`. Subscribes to the executable's stream, re- * emits deltas as `supervisor.agent.streaming` under the calling * callback's intent name, and pushes the inner report onto the * reportSink once `.result` settles. The returned StreamContract * is the executable's own — the framework attaches handlers * transparently via `.on(...)`. */ private streamSupervisedExecutable; /** * Build the options object passed into an inline `.execute()` / * `.stream()` call. Auto-merges supervisor-level defaults * (`signal`, `toolCtx`, `history` window) under the caller's * options. Per-call values REPLACE the auto-defaults — when the * dev passes `signal: undefined` they explicitly opt out. */ private mergeInlineOptions; /** * Coerce an arbitrary inline input into the shape the underlying * executable expects. Agents take `string`; workflows + supervisors * take whatever they declared. We safe-stringify objects only when * passing to an agent — workflow / supervisor calls hand the value * through unchanged so structured inputs work. */ private coerceInlineInput; /** * Invoke the underlying dispatchable unit. Agents and workflows * both satisfy `ExecutableContract` so the call shape * is uniform; the `type` discriminator picks which options get * threaded through (e.g. per-call stream event bubbling for * agents, which we wire inline so child agent tokens surface as * `supervisor.agent.streaming`). */ private invokeUnit; /** * Build the input string passed to a branch's child execution. * Default: pass the supervisor's original `ctx.input` through * unchanged. The per-intent `entry.input` override is the escape * hatch for the rare case where the agent's user message itself * must vary per intent. * * Q17 lock: dropped `composeAgentInput` + `defaultComposeAgentInput`. * Their three jobs (carry original / prior outputs / feedback) all * have cleaner homes in the new model — original is the input * itself, prior outputs are state (Stage 4b), feedback is a * router-only signal (Q18). */ private resolveBranchInput; /** * Strip-merge the agent/workflow's raw output against the per-intent * `output` schema (Q11/Q13). Returns the validated slice that: * * 1. Lands on `IterationSnapshot.result[intent].output` (so * consumers see the same shape that hit state). * 2. Shallow-merges into `this.state` (handled by the caller). * * When `entry.output` is omitted the agent's full `data` (or `text` * fallback for unstructured agents) flows through unvalidated — but * is NOT auto-merged into state. State contribution is opt-in via * declaring the slice schema. * * Validation failure surfaces as a per-branch error on the * snapshot; sibling branches still run. */ private applyOutputSchema; /** * Fire the receptionist (`ack`) — runs in parallel with phase A on * iteration 0 only. Accepts three shapes: * * - `AckEntry` — `{ agent, placeholders?, input?, output? }`. LLM * form. Streams tokens via `supervisor.ack.streaming`; report * node pushes onto `childReports[]`. * - `AckRunEntry` — `{ run, output? }`. Pure-code callback. Settles * without an LLM call. No streaming events; just `.completed`. * - `AckCallback` — bare `(ctx) => slice` shorthand for the * pure-code form when no schema is declared. * * Failures are recorded but never abort the run — the receptionist * tripping doesn't stop the specialist from doing the actual job. * The returned outcome is what `mergeAckIntoState` consumes. */ private runAck; /** * Pure-code receptionist path — invokes the callback, strip-validates * the return value (when an `output` schema is declared), records the * snapshot, emits `supervisor.ack.completed`, returns the outcome. * No streaming events fire (callbacks settle synchronously from the * supervisor's POV). */ private runAckCallback; /** * Agent-driven receptionist path — invokes the agent, streams tokens * via `supervisor.ack.streaming`, captures the report node, strip- * validates against `output` (when declared), records the snapshot, * emits `supervisor.ack.completed`. */ private runAckAgent; /** * Probe the ack promise non-blockingly. Yields one macrotask cycle * (`setImmediate`) so an already-resolved ack wins via microtask * priority; if the probe returns first, the slice is abandoned — * warning logged, error captured on `report.ack`, run completes * regardless. Specialists own the actual answer; the receptionist * was just a reassuring preview. */ private settleAck; /** * Merge the receptionist's strip-validated slice into state. Called * from `settleAck` BEFORE branch merges so specialists override the * receptionist on key collision — the receptionist hedges, the * specialist commits. */ private mergeAckIntoState; /** * Single funnel for "shallow-merge a model-influenced slice into * `this.state`". Wraps the shared {@link mergeSafely} guard so no * merge site can assign `__proto__` / `constructor` / `prototype` * onto the run's state object, and logs when something tried. * * Every slice reaching state is model- or tool-influenced (agent * outputs validated against a DEVELOPER-supplied schema, which may * legitimately be permissive: `z.record()`, `.passthrough()`, * `z.any()`), so the key names are untrusted input even when the * values are shaped. */ private mergeIntoState; /** Shared logging for refused prototype-tampering keys. */ private warnOnUnsafeKeys; /** * Run the iter-0 classifier prelude (Phase 7 / decisions §37). * Resolves the configured classifier (agent / callback / entry * form), invokes it, runs the optional `refine` post-process hook, * and either: * * - sets `carriedClassifierDispatch` so the upcoming * `decideDispatch` short-circuits to the chosen intent, OR * - sets `classifierHalted = true` so `runIteration` terminates * before any dispatch (refine returned `END`). * * Captures the full forensic record on `classifierSnapshot` — * surfaced on `SupervisorReport.classifier` and threaded into * `ctx.classifier` on every downstream context. * * Errors in the classifier OR the refine hook abort the run with * a `SupervisorFailedError` so issues surface loudly instead of * silently falling through to router/route. */ private runClassifier; /** * Resolve the configured classifier into a callable that returns * `{ output, usage }`. Handles the four accepted shapes — bare * agent / bare callback / agent-entry / run-entry. Pure shape * normalization; no side effects. */ private invokeClassifier; /** * Invoke a classifier agent with the supervisor's standard wiring * — placeholders, input override, history slicing, signal, * streaming bubble. Output schema validation belongs to the agent * itself; we just pull the typed `data` (or fall back to parsing * `text`) and assert the locked `intent` field. */ private invokeClassifierAgent; /** * Coerce an agent's output into the locked classifier shape. * Accepts a typed object with `intent` (the canonical case) or a * plain string (interpreted as the intent name with no reasoning). * Throws `SupervisorFailedError` if neither shape matches. */ private coerceClassifierOutput; /** * Build the read-only context passed to a classifier callback / agent * resolvers. No dispatch helpers — registered intents haven't fired * yet; pre-running them from the classifier would be confusing. */ private buildClassifierContext; /** * Build the refine context — extends ClassifierContext with the * classifier's just-resolved output plus `run` / `stream` so the * refine hook can spin up secondary classifiers / validators * inline (Phase 6 features). */ private buildClassifierRefineContext; /** * Pull the optional `refine` hook off whichever classifier-config * shape was supplied. Bare-callback and bare-agent forms have no * refine; only entry forms do. */ private resolveRefineHook; /** * Interpret a refine return value into actionable bits — final * classifier output to dispatch, slice-to-merge, halted/refined * flags, or an error. See {@link ClassifierRefineResult} for the * accepted shapes. */ private interpretRefineResult; /** * Run the `evaluate` callback (when configured) after the * iteration's branches settle and outputs have merged into state. * Errors in the callback surface as `SupervisorFailedError` so a * buggy evaluate doesn't silently swallow the whole run. * * Phase 3.4 (Stage 4b) — `EvaluateContext.state` carries the * post-merge accumulator so verdicts can be state-aware. Q9 * lifted the router-only restriction; evaluate now runs in both * router and route modes. */ private runEvaluate; /** * Merge each branch's output into supervisor `state` in * `decision.intents` order — Q15 conflict rule: last intent in * the array wins on key collisions. Errored branches don't * contribute. Non-object outputs (primitives, null) are skipped * with a warning log; they can't shallow-merge into an object. * * For agent/workflow intents: merging is opt-in via declaring an * `output` schema (the strip-merge gate). Without a schema, the * raw output stays on the branch snapshot but doesn't pollute * state. For callback intents: their return is already strip-merged * (or pass-through) inside `runCallback` — we just merge what's on * the branch snapshot. */ private mergeBranchesIntoState; /** * Merge the iteration's accumulated `currentArtifacts` bag into * supervisor state (Phase 5 / decisions §35). Runs once per * iteration after branch slices land and before evaluate. * * Order of operations: * * 1. **Empty-bag fast path** — if no tool wrote anything, skip * validation and merge entirely; reset the bag for the next * iteration is also a no-op (already empty). * 2. **Schema validation** — when `config.artifactsSchema` is set, * validate the bag against it. Failure aborts the iteration via * a thrown `SchemaValidationError`; the iteration loop's outer * catch surfaces it on `result.error`. Validation is opt-in * (no schema → no validation cost). * 3. **Merge** — `config.finalizeArtifacts` when supplied, else * auto-spread `state = { ...state, ...artifacts }`. Replace * semantics under auto-spread; `finalizeArtifacts` carries * full responsibility for concat / dedupe / cross-iteration * accumulation when configured. * 4. **Reset** — `currentArtifacts = {}`. The next iteration's * tool calls start with a fresh empty bag; long runs never * accumulate raw artifacts here. */ private mergeArtifactsIntoState; /** * Collect each branch's `intent.next(ctx)` directive after state * merge (Stage 4d / Q24). Iterates `decision.intents` order so * union resolution is deterministic. * * Rules: * - Errored branch → silent (treated as if no `next` defined). * - Branch with no `next` → silent; abstains (does NOT drag the * iteration to the router). * - Branch returns `END` → supreme; terminates immediately and * discards other branches' opinions. * - Branch returns `string` or `string[]` → contributes to the * union of unique intent names. Validated against the * supervisor's registry; unknown keys throw `SupervisorFailedError`. * - All branches silent → returns `undefined`; caller falls back * to router/route. */ private collectIntentNext; /** * Finalize the supervisor result: validate accumulated state * against the output schema and build the public `SupervisorResult`. * Assemble-only — event emission and stream close happen in * `run()` around this call. */ private finalize; /** * Build the typed `data` at finalize. Stage 4c — single mode: * * - When `config.output` is declared, validate the accumulated * `state` against it and return the validated value (Q8). * `result.data` always matches the schema, or `result.error` * carries the validation issues. * - When `config.output` is omitted, return the raw state object. * * Validation failure surfaces as `SchemaValidationError` on * `result.error`; the run is still considered semantically * "completed" (intents ran, evaluate said done) but the typed * data slot is empty. */ private buildTypedData; /** * Record a snapshot for an iteration whose first decision was * `END` — no dispatch, no evaluate, just the decision record. Keeps * the snapshot log uniform so a late-route-to-END still appears in * the forensic history rather than vanishing. */ private recordTerminalDecisionSnapshot; /** * Write the current run state to the configured KV store (if any). * Persistence failures surface as `supervisor.error` events and * logged warnings but never abort the run — checkpoint best-effort * by design, matching `workflow` semantics. */ private checkpoint; /** * Between-iteration cancellation check. Called at the top of * every iteration; signal abort here means the loop exits before * any routing happens. */ private throwIfCancelled; /** * Aggregate one usage record (typically a branch or a router call) * into both the run-wide total and the iteration-local total. */ private aggregateUsage; /** * Fan an event out through the three-tier emitter AND mirror it * into the stream controller when streaming. Event names map 1:1 * to stream event types so consumers iterating the stream see the * exact same surface as `.on()` / `options.on` handlers. */ private emit; private logEvent; } //#endregion export { SupervisorExecution }; //# sourceMappingURL=execution.d.mts.map