/** * Drumbeat daemon core (DEC-086, slice D2): the dumb, always-on anchor that * `/loop` provides only on Claude. It scans the registry and, for each stalled * agent, calls a **relauncher adapter** (concrete adapters — local-tmux, * remote, headless — are slice D3/D4). It performs no LLM work and never * relaunches by itself; it just detects + dispatches + caps the relances. */ import type { H2AReflexiveDecision } from "@sentropic/h2a"; import { type H2ADrumbeatFinding, type ScanDrumbeatOptions } from "./scan.js"; import { type H2ADrumbeatEntry } from "./registry.js"; import type { ReflexiveDecider } from "./deciders.js"; /** Adapter that actually re-invokes a stalled agent (slice D3/D4). */ export interface H2ARelauncher { /** Returns true if a relance was issued (counts against the cap). */ relance(finding: H2ADrumbeatFinding): boolean | Promise; } /** A relauncher that only logs — the D2 default until real adapters land. */ export declare function loggingRelauncher(write: (line: string) => void): H2ARelauncher; export interface DrumbeatTickOptions extends ScanDrumbeatOptions { /** D4: optional pre-scan hook, used by watch to consume remote resume inboxes. */ beforeScan?: () => readonly string[] | Promise; /** Hook for entries that hit the relance cap → escalate to PRINCIPAL (D7). */ onExhausted?: (entry: H2ADrumbeatEntry) => void | Promise; now?: number; /** D5: reflexive watchdog. Consulted once `relanceCount >= deciderAfter`. */ decider?: ReflexiveDecider; /** D5: minimum relanceCount before consulting the decider (default 1). */ deciderAfter?: number; /** D5: apply the decision (true) or just log it and do the safe default (false). */ enforce?: boolean; /** D5: label recorded in the decision log (default "logging"). */ deciderLabel?: string; /** D5: effect hooks (cli.ts wires escalation), keeping watch.ts store-free. */ onEscalate?: (finding: H2ADrumbeatFinding, decision: H2AReflexiveDecision) => void | Promise; onReroute?: (finding: H2ADrumbeatFinding, decision: H2AReflexiveDecision) => void | Promise; /** * WP-7: store root for the reach-guard. When provided, each finding is * checked via `reachGuard` before reaching: phantom / ambiguous / malformed * targets are SKIPPED (never relanced). When absent the guard is bypassed * and the tick behaves exactly as today — backward-compatible. */ root?: string; /** * WP-7: optional log sink for reach-guard skip messages. Defaults to a * no-op; pass a `(line: string) => void` to surface the skip reason. */ log?: (line: string) => void; /** * Gov D2/D4: the instance id of the agent running this drumbeat. When * absent, BOTH governance checks (D2 conductor-ownership, D4 cross-workspace * CoI advisory) are SKIPPED entirely — fully backward-compatible; all * existing callers that do not pass this option are unaffected. */ selfInstance?: string; } export interface DrumbeatTickResult { readonly relanced: readonly string[]; readonly exhausted: readonly string[]; } /** One scan→dispatch pass (no timers) — the testable unit of the loop. */ export declare function drumbeatTick(root: string, relauncher: H2ARelauncher, options?: DrumbeatTickOptions): Promise; export interface DrumbeatWatchOptions extends DrumbeatTickOptions { /** Beat interval in ms. Default 30s. */ intervalMs?: number; /** Stop the loop. */ signal?: AbortSignal; onTick?: (result: DrumbeatTickResult) => void | Promise; } /** * Run the drumbeat loop until `signal` aborts. Each beat: scan → relance via * the adapter → cap. The anchor that codex/agy/gemini lack. */ export declare function runDrumbeatWatch(root: string, relauncher: H2ARelauncher, options?: DrumbeatWatchOptions): Promise; //# sourceMappingURL=watch.d.ts.map