/** * * BrowserRestartDetector * * Watches a stream of `mcp__playwright__browser_*` tool invocations * (synthetic, fed in by tests or by the sub-agent-progress signal) * and emits a halt signal when the model repeatedly tears down * and re-spawns the browser context. * * The detector is intentionally simple and dependency-free: * - `record(event)` appends one event to a ring buffer. * - `shouldHalt()` returns true when >= `maxRestarts` close->navigate * pairs have occurred within `windowMs` of each other. * - `diagnostic()` renders the user-facing message. * * It does NOT call playwright MCP. It does NOT spawn processes. It * only inspects the synthetic event log. * * R2 (per PRD): server-restart is a different MCP-level event; * only `browser_close` followed by `browser_navigate` (or * `browser_navigate` followed by `browser_close`, in either order) * within `windowMs` counts as a restart. A standalone * `browser_install` is not a tab close. * * Window: pairs are evaluated per (close_ts, navigate_ts) delta. The * detector uses a sliding window of `windowMs` (default 30s, the * PRD R4 default). Older events fall out of the window. * * Test seam: the test suite feeds a hand-rolled sequence of * `BrowserEvent` objects; production will call `record` from the * sub-agent-progress signal when it sees an * `mcp__playwright__browser_*` invocation. */ export type BrowserEvent = { /** The tool name as seen by the LLM dispatcher (e.g. `browser_close`). */ readonly tool: string; /** ISO8601 timestamp. */ readonly ts: string; }; export type BrowserRestartDetectorOptions = { /** Maximum number of close->navigate pairs before halt. Default 3 (PRD AC1). */ readonly maxRestarts?: number; /** Window in milliseconds within which a close must be followed by a navigate. Default 30_000. */ readonly windowMs?: number; /** When false, the detector is a no-op (PRD AC6: --no-restart-detector). Default true. */ readonly enabled?: boolean; }; export declare class BrowserRestartDetector { private readonly maxRestarts; private readonly windowMs; private readonly enabled; private readonly events; constructor(options?: BrowserRestartDetectorOptions); /** * Record a single browser event. Returns true when this event * caused a restart count to increment (i.e. it was a navigate * that paired with a prior close inside the window). Returns * false for inert events. */ record(event: BrowserEvent): boolean; /** * Count of close->navigate pairs detected within the window. A * pair is one `browser_close` followed by a `browser_navigate` * within `windowMs` of each other. */ restartCount(): number; shouldHalt(): boolean; /** * Returns the human-readable diagnostic the user will see when * `shouldHalt` flips to true. Format: AC1 contract. */ diagnostic(): string; }