/** * @fileoverview Dependency-aware DAG execution for workflow scripts. * * Adapted from open-multi-agent's `task/queue.ts` (dependency-aware task queue * with cascade failure/skip), reshaped to this engine's authoring model: * * - Pure topology + cascade logic lives here; the caller injects the executor * (`runWave`), so concurrency stays gated by the run's shared limiter and the * `agent()` callSeq ordering remains resume-deterministic. * - Nodes run in deterministic WAVES (all currently-ready nodes, in declaration * order) — NOT a completion-timing promise-graph. `callIndex` is assigned * synchronously at the top of `agent()`, so stable wave membership yields a * reproducible callSeq, which is what makes resume replay correctly. A * timing-driven graph would scramble callSeq and break resume. * - When a node fails, every node that (transitively) depends on it is SKIPPED * rather than left blocked forever (cascade skip), so a wave never deadlocks * on a dead upstream. */ export type DagNodeStatus = "pending" | "running" | "done" | "failed" | "skipped"; export interface DagNode { /** Unique id within this `dag()` call. */ readonly id: string; /** Ids this node waits for. All must reach "done" before it runs. */ readonly dependsOn?: readonly string[]; /** Work to perform once dependencies are satisfied. Receives done deps' results by id. */ readonly run: (deps: Readonly>) => Promise | T; } export interface DagOutcome { /** Results of nodes that reached "done", keyed by id. */ readonly results: Record; readonly status: Record; /** Error messages for failed nodes, keyed by id. */ readonly errors: Record; /** Skipped nodes mapped to the failed/skipped dependency id that caused the skip. */ readonly skipped: Record; /** True when every node reached "done". */ readonly ok: boolean; } /** * One settled node result from a wave. Recoverable failures are returned as * `{ ok: false }`; non-recoverable failures (token budget / agent-limit) must be * THROWN by `runWave` so they halt the whole run, exactly like `parallel()`. */ export type WaveResult = { readonly id: string; readonly ok: true; readonly value: T; } | { readonly id: string; readonly ok: false; readonly error: string; }; export interface DagWaveItem { readonly node: DagNode; /** Results of this node's already-"done" dependencies, keyed by id. */ readonly deps: Record; } export type RunWave = (batch: ReadonlyArray>) => Promise>>; /** Thrown for malformed graphs (bad id, missing dep, duplicate id, cycle). */ export declare class DagValidationError extends Error { constructor(message: string); } /** Validate node shape, dependency references, uniqueness, and acyclicity. */ export declare function validateDag(nodes: ReadonlyArray>): void; /** * Execute a DAG in deterministic waves. `runWave` runs one wave of ready nodes * and returns their settled results; the caller owns concurrency/limiter and may * throw to abort the whole run on a non-recoverable failure. */ export declare function runDag(nodes: ReadonlyArray>, runWave: RunWave): Promise>;