Commit graph

3 commits

Author SHA1 Message Date
bob-boat
95ff60ce94 mcp-watcher: safety-poll fallback for dropped inotify events
Adds a periodic timer (default 30s) that calls drain() unconditionally,
covering the case where chokidar/inotify silently drops an IN_MODIFY
event. Observed twice in production: ping appended to inbox, file mtime
updated, but no event delivered to the watcher; a sibling-file touch
unblocked it. Root cause is Linux inotify under brief idle gaps + atomic
writes — not consistently reliable on its own.

drain() is already idempotent (HWM comparison short-circuits when
nothing's new), so the steady-state overhead is one stat + JSON parse
per poll cycle. Event-driven path remains the primary; the poll just
masks the rare miss within the cycle interval.

- safetyPollMs option: default 30_000, set to 0 to disable
- stop() clears the interval before closing chokidar
- Two new tests: safety-poll delivers when fs-event never fires;
  safetyPollMs:0 truly disables the timer

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 12:54:26 -04:00
bob
c757af3909 Fix env var: CLAUDE_CONFIG_DIR not CLAUDE_HOME
Foreman verified by 'strings' on the claude binary that the
sandbox-isolation env var is CLAUDE_CONFIG_DIR, not CLAUDE_HOME
(2026-05-06 sandbox spike).

identity.ts now resolves in this order:
  1. PING_AGENT_IDENTITY
  2. $CLAUDE_CONFIG_DIR/ping-agent  (preferred — what Claude Code reads)
  3. $CLAUDE_HOME/ping-agent         (compat for the previously-documented var)
  4. ~/.ping-agent

Two new tests cover the precedence (CLAUDE_CONFIG_DIR > CLAUDE_HOME)
and the fallback path (CLAUDE_HOME still works when CLAUDE_CONFIG_DIR
is unset). Existing tests updated to use CLAUDE_CONFIG_DIR. 37 tests
pass.

README + install.sh: sandbox launch examples updated to set
CLAUDE_CONFIG_DIR.

Note: agent-ping's identity-resolution PR (#1, merged) has the same
bug and should also be patched. Filing a follow-up.
2026-05-06 18:05:22 -03:00
bob
c22558c67a Layer 2 MCP Watcher v0 scaffold
Per spec/agent-watcher.md §4. TypeScript/Node implementation living in
mcp-watcher/ subdirectory, parallel to Layer 1 Collector at repo root.

What lands:
- Core MCP server (src/server.ts) with experimental['claude/channel']:{}
  + tools:{} capability declarations, stdio transport, channel-event
  notifier wired through the inbox watcher.
- Identity resolution mirroring agent-ping's layered model
  (PING_AGENT_IDENTITY env, $CLAUDE_HOME/ping-agent, ~/.ping-agent).
- Inbox reader with HWM tracking, sentinel deferral (warn-after-3),
  atomic HWM writes via tmp+rename.
- chokidar-backed file watcher with coalesced drain, urgent-first
  ordering, recentEvents map for tool sender lookup.
- Three reply tools (ack / respond / mark_handled) with cross-host
  write discipline (writes to local inbox files; Syncthing replicates).
- Sentinel file (.<agent>.watcher-active) for hook coexistence per
  spec §4.3 — agent-ping hook stands down when the watcher is in
  charge of delivery on this host. Sentinel + hwm in .stignore.
- 35 unit tests passing (vitest): inbox parsing, HWM round-trip,
  sentinel deferral semantics, identity layers, tool I/O, watcher
  drain + ordering + restart-from-hwm.
- install.sh (Angus-executed, rule-2 compliant) installs deps,
  builds, symlinks ~/.local/bin/agent-watcher-mcp, prints mcp.json
  registration snippet for paste.
- README documents launch flag, sandbox CLAUDE_HOME pattern,
  hook coexistence, observability, v2 limitations.

Not yet:
- Integration test against a real Claude Code session — gated on
  Angus spinning up a sandbox CC session on the VPS with
  CLAUDE_HOME=~/.claude-sandbox.
- agent-ping hook update to read the sentinel and stand down.
  Separate small PR against agent-ping.

Interface contract with Layer 1: the inbox JSONL line shape from
inbox.ts::PingEvent matches inbox.Event in the Collector — bit-
identical reads regardless of source.
2026-05-06 17:44:57 -03:00