/** * Heartbeat is a steady liveness ticker. While a long, otherwise-silent agent * step runs, it invokes a callback at a fixed BASE cadence with the elapsed * time since start, so a progress sink can show the run is alive (and for how * long). It owns no rendering and no transport: it only answers "still working, * N ms in" on a clock. Per-surface throttling (e.g. Telegram's 15s edit window) * is the sink's concern, not the ticker's. * * Pure and deterministic: the clock and the interval timer are injected, so the * ticking is unit-tested by firing a fake timer by hand — the same pattern * PreviewSession uses. The live binding wraps setInterval. */ /** The repeating-timer seam. start() schedules fn every ms; stop() cancels it. */ export interface IntervalTimer { start(fn: () => void, ms: number): void; stop(): void; } export interface HeartbeatDeps { /** Called on each tick with the ms elapsed since start(). */ emit: (elapsedMs: number) => void; /** Clock, injectable for tests. Default Date.now. */ now?: () => number; /** Repeating timer, injectable for tests. Default setInterval-backed. */ timer?: IntervalTimer; /** Base tick cadence in ms. Default 1000. */ intervalMs?: number; } export declare class Heartbeat { private readonly emit; private readonly now; private readonly timer; private readonly intervalMs; private startedAt; constructor(deps: HeartbeatDeps); /** Begin ticking. Records the start time the elapsed value is measured from. */ start(): void; /** Stop ticking. A timer that fires after this is a no-op. */ stop(): void; private tick; }