/** * DAG orchestrator — 2.7.0 slice-dag-dispatcher MVP (slice 1.2.a). * * The pure planner. Given a `SliceDag`, the orchestrator decides which * slices to dispatch next (in topological order), drives the join barrier, * and rolls back on any leaf failure. * * Design: * - **Pure dispatcher contract**: the orchestrator never calls an LLM * directly; it returns `DispatchSpec[]` per topological level and lets * the caller (the CLI / peaks-code LLM) execute the per-IDE tool calls. * This keeps the orchestrator testable end-to-end with mock sub-agents. * - **Join barrier**: between levels, the orchestrator awaits the * results of all dispatched leaves and refuses to advance if any * leaf failed. * - **Failure rollback**: a leaf failure cancels all other in-flight * slices in the same level. The orchestrator surfaces a `DagRollback` * event so the caller can re-plan. * - **Contract broadcast**: when a slice finishes successfully, the * orchestrator writes its public contract to disk via `writeContract`, * and the next dispatch's prompt is built with `formatContractInjection`. * * Why this lives in `services/code/`: * `peaks-code` is the natural caller. The CLI surface (`peaks sub-agent * dispatch --from-dag`) consumes the planner's output but the planner * itself is a library. */ import { type SliceDag, type Wave, type WaveArtifact, type WaveOptions } from '../dispatch/slice-dag.js'; import { type SliceContract } from '../dispatch/contract-store.js'; export interface DispatchSpec { readonly sliceId: string; readonly role: string; readonly label: string | null; readonly prompt: string; /** Optional contract block to splice into the prompt. */ readonly contractBlock: string; } export interface RunDagOptions { readonly projectRoot: string; readonly sessionId: string; /** * Reserved for slice 1.3 — when the per-IDE `awaitBatch` join barrier * moves into the orchestrator, this will cap how long `runDag` waits * for a single leaf before issuing `cancel`. Unused in 1.2 MVP * (per-IDE await is delegated to the LLM-side runner); declared here * so the public surface stays stable across the 1.2 → 1.3 transition. */ readonly timeoutMs?: number; /** Test seam: replace the runner (default: a no-op that returns `done`). */ readonly runSlice?: (spec: DispatchSpec) => Promise; /** Test seam: replace the contract writer (default: `writeContract`). */ readonly writeContractFn?: (sliceId: string, publicSurface: PublicSurface) => SliceContract; /** * Pre-existing contracts from upstream slices (e.g. already completed in * a prior `peaks sub-agent dispatch --from-dag` invocation). The * orchestrator splices these into downstream dispatch prompts via * dispatch prompt 自动注入 A 契约". */ readonly existingContracts?: readonly SliceContract[]; } export type SliceOutcome = { readonly status: 'done'; readonly publicSurface: PublicSurface; } | { readonly status: 'failed'; readonly reason: string; } | { readonly status: 'cancelled'; }; export interface PublicSurface { readonly exports: readonly string[]; readonly types: readonly string[]; readonly publicSignatures: readonly string[]; readonly broadcastTo?: readonly string[]; } export interface DagRunResult { readonly dagHash: string; readonly completed: readonly string[]; readonly failed: readonly { sliceId: string; reason: string; }[]; readonly cancelled: readonly string[]; readonly contracts: readonly SliceContract[]; } /** Thrown when the DAG has no feasible plan (validation error). */ export declare class DagPlanError extends Error { readonly code: "DAG_PLAN_ERROR"; constructor(message: string); } /** * Build a single dispatch spec for `sliceId` given the current contract set. * * When `node.prompt` is set on the DAG node, that wins verbatim (the * caller controls per-slice prompt content). Otherwise we fall back to * `defaultPrompt`, then to a structured MVP prompt covering: the slice's * role + id + label, the injected ancestor-contract block (if any), the * MVP scope statement, and the explicit handoff protocol the LLM-side * runner must follow (write contract, then re-invoke for the next * topological level). */ export declare function buildDispatchSpec(dag: SliceDag, sliceId: string, contracts: readonly SliceContract[], defaultPrompt?: string): DispatchSpec; /** * Run a slice DAG end-to-end. Returns a summary; throws `DagPlanError` * on validation failure; any leaf failure causes an in-flight cancel * signal but the function still resolves with the partial result so the * caller can re-plan. */ export declare function runDag(dag: SliceDag, opts: RunDagOptions): Promise; /** * v2.15.0 follow-up — G12: layered DAG runner (foundation-first, * business-parallel, upstreamSync-priority). Behavior: * * 1. Foundation slices run first (topological order). * 2. As foundation slices complete, business slices that depend ONLY * on the completed foundation subset can start — they do NOT wait * for ALL foundation slices to complete. This is the layered- * parallelism speedup (saves 2-3 days on a 1-week+ requirement). * 3. upstreamSync slices take priority within their topological * level (handled by `topologicalLevels` priority sort). * 4. Cancel-on-fail semantics are preserved: any leaf failure * cancels in-flight siblings and breaks the loop. * * Pure planner; same I/O contract as `runDag`. The caller (CLI / * peaks-code LLM) executes the per-IDE tool calls. */ export declare function runLayeredDag(dag: SliceDag, opts: RunDagOptions): Promise; /** * Plan a slice DAG into ordered dispatch Waves. * * Each topological level is split into chunks of at most * `opts.maxConcurrency` slice IDs (default 6). Wave ordering follows * topological level order; within each level, slices preserve * `topologicalLevels`'s foundation / upstreamSync / id-asc priority sort. */ export declare function planDispatchWaves(dag: SliceDag, opts?: WaveOptions): readonly Wave[]; /** * Backward-compat wrapper: flatten `planDispatchWaves(dag)` into a flat * sequence of stub `DispatchSpec`s. The original `runDag` / * `runLayeredDag` do NOT consume this; the wrapper exists only so * external callers that imported `planDispatch(dag)` keep their shape. */ export declare function planDispatch(dag: SliceDag): readonly DispatchSpec[]; /** * Run a single Wave: dispatch `wave.slices` in parallel, return a * `WaveArtifact` envelope on success. On any per-slice failure or * cancellation, REJECTS with `DagPlanError` — caller treats the * rejection as a barrier violation and rolls back siblings. * * `passArtifacts`: when true, the returned envelope includes each * slice's public surface (callers splice into downstream Wave prompts). * * `priorArtifacts` is reserved for future artifact-injection. This MVP * accepts the shape but does not yet splice it into the prompt. */ export declare function runWaveWithArtifacts(dag: SliceDag, wave: Wave, runner: (spec: DispatchSpec) => Promise, priorArtifacts?: readonly WaveArtifact[], opts?: WaveOptions): Promise;