/** * Proactive AI-credential refresh scheduler. Holds a single timer that fires * `PROACTIVE_AI_REFRESH_MARGIN_MS` before the token expires and re-arms against * the new expiry via the caller-supplied `refresh`. Pure logic — the caller * threads the `host.refresh_credential` side-effect. Resilience model * (transient bounded-retry, terminal stop, stale-guard, non-amplification on a * shared seat) is documented in `runner/CLAUDE.md` § AI Credential Mediation. * * @docLink packages/runner/dev-guide#ai-credential-mediation */ /** * Safety margin between the scheduled refresh and the token's declared * expiry. Default 5 minutes: long enough that any in-flight prompt at * fire time still has a usable token, short enough that a server clock * skew of a few seconds does not push the fire-time past expiry. */ export declare const PROACTIVE_AI_REFRESH_MARGIN_MS: number; /** * Stale-schedule guard window. After the bounded transient retries are * exhausted the scheduler arms a single long timer this far out so a blip * during a proactive fire cannot leave refresh idle indefinitely — the * worst case becomes one degraded window, after which a fresh refresh is * attempted. Also the threshold {@link AiCredentialRefreshScheduler.ensureArmed} * uses to decide a previously-armed scheduler has gone stale. */ export declare const STALE_REARM_MS: number; /** * Node/Bun `setTimeout` ceiling. A delay above this is **silently collapsed to * 1 ms** (the only trace is a `TimeoutOverflowWarning` on stderr), which turns * a far-future expiry into a re-mint hot loop rather than one fire near expiry. */ export declare const MAX_TIMER_DELAY_MS: number; /** * Longest delay this scheduler ever hands to `setTimeout`. Kept a margin below * {@link MAX_TIMER_DELAY_MS} rather than at the boundary so no rounding or * clock skew can reach the ceiling; anything longer is served as a chain of * re-arms that fires nothing until the real delay has elapsed. */ export declare const MAX_REARM_MS: number; /** * Outcome of a proactive refresh attempt, returned by the caller-supplied * {@link AiCredentialRefreshDeps.refresh}: * * - `ok` — a fresh credential was minted; `expiresAt` is its ISO expiry, or * `null` for a static token (no further refresh needed). * - `transient` — a mediator/transport blip; the scheduler re-arms a bounded * retry and ultimately a stale-schedule guard. * - `terminal` — the credential/account is genuinely unusable; the scheduler * stops re-arming and leaves the reactive 401 path as the safety net. */ export type AiRefreshOutcome = { status: "ok"; expiresAt: string | null; } | { status: "transient"; } | { status: "terminal"; }; /** * Caller-supplied side effects the scheduler invokes. * * - `refresh()` runs the actual mint round-trip and classifies the result * into an {@link AiRefreshOutcome}. * - `log()` receives operator-facing single-line strings (info level). * - `logError()` receives failure strings that MUST surface at error level * (visible in `skaile session logs` and the debug panel). Defaults to * `log` when omitted. * - `now()` is dependency-injected for deterministic timer tests; defaults * to `Date.now`. * - `setTimeoutImpl` / `clearTimeoutImpl` are injected for fake-timer * tests; default to the host's `setTimeout` / `clearTimeout`. */ export interface AiCredentialRefreshDeps { refresh: () => Promise; log: (line: string) => void; logError?: (line: string) => void; now?: () => number; setTimeoutImpl?: (handler: () => void, ms: number) => { unref?: () => void; }; clearTimeoutImpl?: (handle: { unref?: () => void; }) => void; marginMs?: number; } export interface AiCredentialRefreshScheduler { /** * Arm the timer for `expiresAtIso - marginMs`. Cancels any prior pending * fire. `null` / `undefined` cancels without re-arming (static PAT * semantics) and marks the credential static. If the fire-time has * already passed, runs `refresh()` on the next tick. Records the * credential as healthy (resets the transient-retry counter and the * stale clock). */ schedule(expiresAtIso: string | null | undefined): void; /** * Re-arm hook for the configure/wake path. Kicks a fresh refresh on the next * tick when the scheduler is idle (prior terminal failure), stale (no * successful fire in {@link STALE_REARM_MS}), or currently parked on the long * stale-schedule guard — so a container waking during a degraded window * recovers eagerly instead of waiting out the guard. No-op when a healthy * refresh timer is already pending or the credential is a static PAT. */ ensureArmed(): void; /** Cancel any pending fire. Idempotent. */ cancel(): void; /** * Whether a timer is currently armed. Exposed for tests; production * callers should not need this. */ isPending(): boolean; } /** * Build a scheduler bound to the supplied dependencies. One scheduler * per session — the runner constructs it once at session_init and * disposes via `cancel()` during cleanup. */ export declare function createAiCredentialRefreshScheduler(deps: AiCredentialRefreshDeps): AiCredentialRefreshScheduler; //# sourceMappingURL=ai-credential-refresh.d.ts.map