import type { TaskHandle, TaskScheduler } from '../../types/agent/scheduler.js'; import type { TaskId } from '../../types/ids/index.js'; /** * Waiting on a delegated worker, bounded by two different questions. * * A wall clock alone is the wrong instrument. It has to be long enough to * serve as the outer bound for a child doing real work — an hour, here — and * that is far too long to notice a child that wedged in its second minute. * The same number cannot be both "how long is too long" and "how quiet is too * quiet", so this keeps them apart: * * - the **wall-clock bound** counts elapsed time and is never refreshed. It exists * for a worker that stays busy forever. * - the **idle bound** counts time since the worker last did anything, and * resets whenever it does. It exists for a worker that stopped. * * Whichever fires first ends the wait, and the result says WHICH — because * "it went quiet" and "it ran too long" are different diagnoses and lead to * different next moves. Telling a caller its worker timed out when the worker * was making steady progress is the failure this replaces. * * The idle bound is only armed when the gateway can report progress. * `onTaskProgress` is optional on the contract, because hosts implement * `TaskScheduler` and not all of them can observe their children — so a gateway * without it is bounded by the wall clock alone, exactly as before. That is a * real degradation and it is deliberately visible in the result rather than * silent: `idleBoundArmed` says whether the quieter half was ever watching. */ export type WaitOutcome = { readonly kind: 'completed'; readonly handle: TaskHandle; } | { readonly kind: 'timeout'; /** Which clock ran out. */ readonly cause: 'idle' | 'wall'; readonly elapsedMs: number; /** False when the gateway cannot report progress, so only the wall clock applied. */ readonly idleBoundArmed: boolean; }; export interface WaitBounds { /** Elapsed-time ceiling, never refreshed. */ readonly wallMs: number; /** * Time-without-progress ceiling, refreshed on every progress signal. * * Omit to bound by the turn clock alone. */ readonly idleMs?: number; } /** * Await a task under both bounds. * * Note what this does NOT do: it does not cancel the worker. A wait that ran * out is a statement about the waiter, not about the work — the child keeps * going, its completion still reaches the inbox, and the supervisor is still * told what it produced. Killing a child because a parent stopped waiting was * never asked for, and losing an eight-minute worker's output because a * two-minute clock expired is the exact shape of the bug this whole area has * been unpicking. */ export declare function waitForTaskWithBounds(gateway: TaskScheduler, taskId: TaskId, bounds: WaitBounds, now?: () => number): Promise; /** What to tell the model, in the words that fit what actually happened. */ export declare function describeWaitTimeout(outcome: Extract): string; //# sourceMappingURL=wait-with-idle-bound.d.ts.map