/** * TaskDAG — Directed Acyclic Graph of tasks with fork/join semantics. * * Replaces the Director's flat `awaitTasks()` with a proper dependency graph. * Each task has explicit dependencies; the DAG resolves which tasks are * runnable at any moment and manages blocking/unblocking as tasks complete. * * Key features: * - Fork: one parent spawns multiple children that run in parallel * - Join: a task can wait for multiple children before continuing * - Dynamic: tasks can be added dynamically; the graph re-evaluates runnable set * - Cycle detection: inserting a dependency that would create a cycle throws * - Priority queue: within a "runnable" set, tasks are ordered by priority * - Deadlock detection: if no tasks are runnable and no tasks are complete, * the DAG is in deadlock — agents are notified * * @module task-dag */ /** Represents a node in the DAG. */ export interface DAGNode { id: string; description: string; role?: string; priority: number; status: DAGNodeStatus; deps: string[]; dependents: string[]; result?: unknown; error?: string; spawnedAt?: string; completedAt?: string; assignedTo?: string; tags: string[]; } export type DAGNodeStatus = 'pending' | 'ready' | 'running' | 'done' | 'failed' | 'skipped'; /** Event emitted by the DAG on state changes. */ export type DAGEdgeEvent = { type: 'node:ready'; nodeId: string; deps: string[]; } | { type: 'node:started'; nodeId: string; assignedTo: string; } | { type: 'node:completed'; nodeId: string; result: unknown; blockers: string[]; } | { type: 'node:failed'; nodeId: string; error: string; blockers: string[]; } | { type: 'node:skipped'; nodeId: string; reason: string; } | { type: 'deadlock'; blocked: string[]; } | { type: 'graph:done'; allDone: boolean; }; export type DAGEdgeHandler = (event: DAGEdgeEvent) => void; /** Callback invoked when the DAG produces a set of runnable tasks. */ export type RunnablesHandler = (nodes: DAGNode[]) => void; export declare class TaskDAG { private readonly nodes; private readonly handlers; private readonly runnablesHandlers; private runnableCache; /** * Add a task node. Dependencies are validated for cycles. * Throws if adding a dep would create a cycle. */ addNode(id: string, description: string, deps?: string[], opts?: { role?: string; priority?: number; tags?: string[]; }): void; /** * Remove a node and all edges to/from it. * Skips any dependents that would become dangling. */ removeNode(id: string): void; /** * Mark a task as running. Returns true if the transition was valid * (task was in 'ready' state), false otherwise. */ start(id: string, assignedTo: string): boolean; /** * Mark a task as completed. Unblocks all dependents; they become 'ready' * if all their deps are done. */ complete(id: string, result: unknown): void; /** * Mark a task as failed. Unblocks dependents but they remain 'pending' * (they may still be runnable if other deps succeeded). */ fail(id: string, error: string): void; /** * Skip a task (e.g., it was deemed unnecessary by an earlier step). * Treats it as done for dependency purposes. */ skip(id: string, reason: string): void; getNode(id: string): DAGNode | undefined; getAll(): DAGNode[]; getReady(): DAGNode[]; getRunning(): DAGNode[]; getPending(): DAGNode[]; getDone(): DAGNode[]; getFailed(): DAGNode[]; getCompleted(): DAGNode[]; isDone(): boolean; isFailed(): boolean; /** All tasks that are currently blocked (pending but not ready). */ getBlocked(): DAGNode[]; /** Topological sort — tasks in dependency order. */ getTopologicalOrder(): DAGNode[]; /** Check for deadlock: no runnable tasks but not done. */ hasDeadlock(): boolean; /** Stats snapshot for reporting. */ stats(): { total: number; pending: number; ready: number; running: number; done: number; failed: number; skipped: number; progress: number; }; onEvent(handler: DAGEdgeHandler): () => void; onRunnable(handler: RunnablesHandler): () => void; private _transition; private _emit; private _emitReady; private invalidateCache; /** * DFS cycle detection. Adding edge (id → dep) creates a cycle if * there already exists a path from dep to id. */ private _wouldCycle; } //# sourceMappingURL=task-dag.d.ts.map