import type { LoopState, LoopMode, RegisterResult } from "./types.js"; import type { IDispatchAdapter } from "./dispatch-adapter.js"; export { shouldCancelLoop, DISPATCH_MARKERS } from "./cancellation.js"; export declare class LoopCoordinator { private adapter; private opts?; private loops; private _advancing; private _workerToOrigin; /** Map of workerTaskId → terminated listener callback, for cleanup on cancel/fail */ private _workerListeners; /** Completions that arrived while the _advancing re-entrancy guard was held, keyed by originSessionId. */ private _pendingCompletions; /** Timer reference for the stale-lock sweeper interval, cleared on dispose. */ private _advancingSweeper; /** Counter of stale locks detected and swept by the sweeper. */ private _staleLockCount; constructor(adapter: IDispatchAdapter, opts?: { delayMs?: number; roundTimeoutMs?: number; persist?: (loops: Map) => void; } | undefined); private _persist; /** * Periodic sweeper that detects and releases stale _advancing locks. * Runs every SWEEPER_INTERVAL_MS. A lock is stale if it has been held * longer than ADVANCING_LOCK_TIMEOUT_MS without being released, which * would indicate an exception escaped the try block without the finally * block executing. */ private _sweepStaleLocks; /** * Register a fire-once terminated listener on the loop's active worker task. * When the worker completes, the listener triggers onWorkerCompleted, which * chains into _advanceFromSummarizing to continue the push-driven loop. */ private _registerWorkerListener; /** * Remove a pending terminated listener for a worker task that is being * cancelled or has errored (prevents callback leakage). */ private _cleanupWorkerListener; /** * Self-driving push-chain step: read summary, advance round count, then * either finalize (all rounds done) or dispatch the next round and register * its listener. This runs inside the same _advancing critical section as * onWorkerCompleted, so no external idle event is needed. */ private _advanceFromSummarizing; /** * Self-kickoff for loops in the activating phase. When register() creates a * loop with phase="activating", or reSubscribeListeners() recovers an * activating loop after restart, this method transitions it to dispatching * and dispatches the first round. This eliminates the previous dependency on * onOriginIdle for first-round kickoff. */ private _kickoffFromActivating; register(input: { originSessionId: string; agent: string; prompt: string; mode: LoopMode; iterations: number; objective?: string; }): RegisterResult; onOriginIdle(originSessionId: string): Promise; onWorkerCompleted(workerTaskId: string): Promise; requestCancel(originSessionId: string): void; cancelNow(sessionId: string): Promise; shouldCancelOnUserMessage(sessionId: string, messageText: string): boolean; isActiveLoopOrigin(sessionId: string): boolean; isLoopSession(sessionId: string): boolean; getLoopState(originSessionId: string): LoopState | undefined; getAllLoopStates(): Map; getNonTerminalLoops(): LoopState[]; /** * Walk up the parentLoopId chain and return all ancestor LoopStates, * ordered from nearest ancestor to root. */ getLoopAncestors(originSessionId: string): LoopState[]; /** * Collect all descendant loops (children, grandchildren, etc.) * of the given originSessionId recursively. Returns a flat array. */ getLoopDescendants(originSessionId: string): LoopState[]; /** * Returns the current state of the _advancing lock map for health monitoring. * - `activeLocks`: number of locks currently held (non-stale) * - `staleLocks`: cumulative count of stale locks detected and swept */ getAdvancingLockState(): { activeLocks: number; staleLocks: number; }; failSession(sessionId: string, reason: string): Promise; restoreState(state: LoopState): void; /** * Re-subscribe terminated listeners and advance loops after a restart. * * Called after reconcile + restoreState has loaded persisted loops and * reconciled their phase against dispatch task state. For each non-terminal * loop: * * - `awaiting_worker` with a completed worker → triggers onWorkerCompleted * to advance through the push chain (summary → next round or finalize). * - `awaiting_worker` with a still-running worker → re-subscribes the * terminated listener so the push chain resumes when the worker finishes. * - `summarizing` → calls _advanceFromSummarizing to resume the push chain * (the worker completed during the restart window). * * Each transition runs inside the _advancing re-entrancy guard to stay * serialised with normal push-chain operations. */ reSubscribeListeners(): Promise; dispose(): void; } //# sourceMappingURL=coordinator.d.ts.map