import type { BackgroundJobBoard, BackgroundJobLaunchInput, BackgroundJobRecord, BackgroundJobStatusInput, ContextFile } from './background-job-board'; import type { BackgroundJobStore } from './background-job-store'; import type { TaskOutputState } from './task'; type TerminalStateListener = (taskID: string) => void; /** * BackgroundJobCoordinator owns the lifecycle policy for background jobs. * It sits between the board and its consumers, providing: * - Subscription interface for terminal state notifications (replaces fire-and-forget) * - Lifecycle policy: determines when jobs are terminal, when closes should be deferred * - Single-writer contract: coordinator is the sole writer to the board * * The board's guards prevent silent overwrites. The coordinator adds: * - Centralized notification with guaranteed delivery * - Re-checks board state before notifying (handles races) */ export declare class BackgroundJobCoordinator implements BackgroundJobStore { private readonly board; private terminalStateListeners; private readonly deferredIdleCloses; constructor(board: BackgroundJobBoard); addTerminalStateListener(listener: TerminalStateListener): void; removeTerminalStateListener(listener: TerminalStateListener): void; /** * Handle terminal state from board. Re-checks board state to handle races. * This is the centralized lifecycle policy. */ private handleTerminalState; /** * Evaluate close policy. Returns true if session should close now. * Mutates deferred state: adds to deferred set if running, removes if not. */ deferIfRunning(sessionId: string): boolean; /** * Retry closing a deferred session. Called when a background job completes. * Returns true if the session should now close. */ retryDeferredClose(sessionId: string): boolean; /** * Clear deferred close state for a session being deleted. */ clearDeferredClose(sessionId: string): void; registerLaunch(input: BackgroundJobLaunchInput): BackgroundJobRecord; updateStatus(input: BackgroundJobStatusInput): BackgroundJobRecord | undefined; updateFromStatusOutput(output: string): BackgroundJobRecord | undefined; markRunningFromLiveSession(taskID: string, now?: number): BackgroundJobRecord | undefined; markReconciled(taskID: string, now?: number): BackgroundJobRecord | undefined; markCancelled(taskID: string, reason?: string, now?: number, options?: { force?: boolean; }): BackgroundJobRecord | undefined; get(taskID: string): BackgroundJobRecord | undefined; field(taskID: string, key: K): BackgroundJobRecord[K] | undefined; isRunning(taskID: string): boolean; isTerminalUnreconciled(taskID: string): boolean; getResultSummary(taskID: string): string | undefined; getLastLiveBusyAt(taskID: string): number | undefined; getParentSessionID(taskID: string): string | undefined; getState(taskID: string): TaskOutputState | 'reconciled' | undefined; resolve(parentSessionID: string, taskIDOrAlias: string): BackgroundJobRecord | undefined; resolveReusable(parentSessionID: string, taskIDOrAlias: string, agent?: string): BackgroundJobRecord | undefined; resolveRecoverable(parentSessionID: string, taskIDOrAlias: string, agent?: string): BackgroundJobRecord | undefined; markUsed(parentSessionID: string, key: string, now?: number): void; taskIDs(): Set; addContext(taskID: string, files: ContextFile[]): void; list(parentSessionID?: string): BackgroundJobRecord[]; hasRunning(parentSessionID: string): boolean; hasTerminalUnreconciled(parentSessionID: string): boolean; hasConvergenceSignals(taskID: string, threshold?: number): boolean; formatForPrompt(parentSessionID: string, now?: number): string | undefined; clearParent(parentSessionID: string): void; drop(taskID: string): void; } export {};