/**
 * DAG Scheduler v2 — unified scheduling for Product + Automation modes.
 *
 * Single-track model: one algorithm, no compat shims, no legacy paths.
 * Product mode = flat DAG (kind:"agent", no conditions, no loops) — strict subset.
 *
 * Spec: docs/dag-scheduler-v2-spec.md
 */
import type { ProductTaskStatus } from "../protocol/wire/acp-agent-management.js";
/**
 * Loop safety hard ceiling (plan D15): a loop controller's `maxIterations` is required and may
 * never exceed this. Unattended/triggered runs must not be able to declare an effectively
 * unbounded loop, so a value above the cap is rejected fail-loud at construction — not clamped.
 */
export declare const LOOP_MAX_ITERATIONS_HARD_CAP = 1000;
/** Unified node status; "skipped" is a terminal success (intentionally not run). */
export type NodeStatus = ProductTaskStatus;
/**
 * Fully-materialized runtime node (constructed from NodeDef, never partial).
 * Internal to the scheduler — do not mutate directly outside DagScheduler.
 */
export interface Node {
    id: string;
    kind: string;
    params: Record<string, unknown>;
    status: NodeStatus;
    outPorts: string[];
    joinPolicy: "all" | "any";
    firedPorts?: string[];
    output?: unknown;
    error?: string;
    startedAt?: number;
    completedAt?: number;
    runIndex: number;
    maxIterations?: number;
    iteration?: number;
}
/** Declarative input for the constructor. Edges derived from dependsOn. */
export interface NodeDef {
    id: string;
    kind?: string;
    params?: Record<string, unknown>;
    dependsOn?: string[];
    outPorts?: string[];
    joinPolicy?: "all" | "any";
    maxIterations?: number;
}
export interface Edge {
    from: string;
    to: string;
    fromPort?: string;
    loopBack?: boolean;
}
export declare class DagScheduler {
    private nodes;
    private edges;
    constructor(defs: NodeDef[]);
    getNode(id: string): Node | undefined;
    getAllNodes(): Node[];
    /**
     * Ready = pending + all forward incoming edges satisfied (or no incoming edges).
     * Spec §3.1.
     */
    getReadyNodes(): Node[];
    isFinished(): boolean;
    /** All nodes completed or skipped (skipped = intentionally not run, counts as success). */
    isAllCompleted(): boolean;
    getBlockedNodes(): Node[];
    getProgress(): {
        total: number;
        completed: number;
        running: number;
        failed: number;
        pending: number;
        paused: number;
        skipped: number;
    };
    markRunning(id: string): void;
    /**
     * Mark completed. firedPorts omitted → all outPorts fire (agent/tool nodes).
     * Triggers skip propagation (§3.2).
     */
    markCompleted(id: string, out: {
        output?: unknown;
        firedPorts?: string[];
    }): void;
    markFailed(id: string, error: string): void;
    markPaused(id: string): void;
    resumePaused(id: string): void;
    markSkipped(id: string): void;
    pauseAll(): void;
    resumeAll(): void;
    /**
     * Loop controller decision (§3.3).
     * If again && iterations remain → reset loop body, fire "loop" port.
     * Otherwise → fire "done" port (exit).
     */
    decideLoop(loopId: string, again: boolean): void;
    addNode(def: NodeDef): void;
    /** Add multiple nodes atomically. All-or-nothing. */
    addNodes(defs: NodeDef[]): void;
    /**
     * Remove a pending node. Cannot remove running or completed nodes.
     * Removes all edges incident to this node.
     */
    removeNode(id: string): void;
    /**
     * Reset a failed node to pending. Optionally patches params (e.g. revised prompt).
     */
    retryNode(id: string, paramsPatch?: Record<string, unknown>): void;
    /** Merge patch into node.params. Node must be pending. */
    updateNodeParams(id: string, patch: Record<string, unknown>): void;
    /** Add an explicit edge. Validates no cycle. Validates ports exist on source. */
    addEdge(edge: Edge): void;
    removeEdge(from: string, to: string, fromPort?: string): void;
    serialize(): SerializedNode[];
    /**
     * Restore status from persisted snapshot.
     * Running nodes become pending (need re-execution after crash).
     * Missing fields use defaults.
     */
    restore(data: SerializedNode[]): void;
    /** §3.1 edge predicate: source completed AND this port fired. */
    private edgeSatisfied;
    /** An edge is "dead" if the source is skipped, OR source completed but did NOT fire this port. */
    private edgeDead;
    private incomingForwardEdges;
    private isBlockedByFailed;
    /** A pending node is "skippable" if all its forward edges are dead. */
    private isSkippable;
    /**
     * §3.2 Skip propagation.
     * After markCompleted/markFailed/markSkipped, repeatedly mark pending nodes
     * whose incoming edges are all dead as skipped.
     * Product mode (no conditional edges) → no-op.
     */
    private propagateSkips;
    /**
     * §3.3 Reset all loop-body nodes back to pending for next iteration.
     */
    private resetLoopScope;
    /**
     * Compute the loop body: nodes reachable from the loop controller's "loop" outPort
     * via non-loopBack forward edges, up to (but not including) the loop controller itself.
     * The controller's "done" port leads OUT of the loop and is not part of the body.
     */
    private loopBodyNodes;
    private validateDag;
    private requireNode;
}
export interface SerializedNode {
    id: string;
    kind: string;
    params: Record<string, unknown>;
    dependsOn: string[];
    outPorts: string[];
    joinPolicy: "all" | "any";
    status: NodeStatus;
    firedPorts?: string[];
    output?: unknown;
    error?: string;
    startedAt?: number;
    completedAt?: number;
    runIndex: number;
    maxIterations?: number;
    iteration?: number;
}
