// Sentinel file: signals the agent-ping UserPromptSubmit hook to stand // down while the watcher is delivering pings via Channels (spec §4.3). // // The file lives at `pings/..watcher-active` and is local-only // (must be in .stignore — otherwise one host's watcher silences another // host's hook). // // Lifecycle: // - On startup: write the file with the current PID + timestamp. // - On graceful shutdown (SIGINT/SIGTERM): remove it. // - On crash: file is left behind. The hook checks file age + PID // liveness to ignore stale sentinels. (See agent-ping hook update.) import { writeFileSync, unlinkSync, mkdirSync, existsSync } from "node:fs"; import { dirname } from "node:path"; export interface SentinelHandle { release(): void; } export function claimSentinel(path: string): SentinelHandle { mkdirSync(dirname(path), { recursive: true }); const body = JSON.stringify({ pid: process.pid, started_at: new Date().toISOString(), }); writeFileSync(path, body + "\n", "utf8"); let released = false; return { release() { if (released) return; released = true; try { if (existsSync(path)) unlinkSync(path); } catch { // best-effort } }, }; }