No description
Find a file
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
cmd/agent-watcher Collector milestone 5: end-to-end integration tests 2026-05-06 16:23:34 -04:00
docs/channels Add Channels reference docs snapshot for Layer 2 implementation 2026-05-06 16:04:32 -04:00
examples Address Bob's notes 2 and 3 with documenting comments 2026-05-06 16:47:40 -04:00
internal Address Bob's notes 2 and 3 with documenting comments 2026-05-06 16:47:40 -04:00
mcp-watcher mcp-watcher: safety-poll fallback for dropped inotify events 2026-05-23 12:54:26 -04:00
spec Initial: README, spec/agent-watcher.md v1 (signed off by Bob 2026-05-06), .gitignore 2026-05-06 14:36:45 -04:00
systemd Collector milestone 6: packaging — install.sh, systemd unit, docs 2026-05-06 16:26:16 -04:00
.gitignore Initial: README, spec/agent-watcher.md v1 (signed off by Bob 2026-05-06), .gitignore 2026-05-06 14:36:45 -04:00
go.mod Collector milestone 4: config loader + main wiring (binary builds) 2026-05-06 16:22:03 -04:00
go.sum Collector milestone 4: config loader + main wiring (binary builds) 2026-05-06 16:22:03 -04:00
INSTALL.md Collector milestone 6: packaging — install.sh, systemd unit, docs 2026-05-06 16:26:16 -04:00
install.sh Collector milestone 6: packaging — install.sh, systemd unit, docs 2026-05-06 16:26:16 -04:00
README.md Collector milestone 6: packaging — install.sh, systemd unit, docs 2026-05-06 16:26:16 -04:00

agent-watcher

Push-delivery layer for agent-ping. The "secondary nervous system" for Claude Code agents on this network.

agent-ping queues messages in inbox files; agent-watcher notices them (and other external events) and wakes the recipient agent without a human in the loop.

Two layers:

  • Collector (this repo, Go) — small daemon under systemd --user. Always on, brain-blind. Converts external events (HTTP webhooks, drop-folder file arrivals) into ping inbox writes. Runs whether or not any agent is alive.
  • MCP Watcher (Python, in progress) — Claude Code MCP subprocess declared in each agent's mcp.json. Watches the agent's inbox via inotify and surfaces events into the live session via Channels. Provides reply tools (ack, respond, mark_handled).

Filesystem is the queue. OpenBrain is not involved.

Spec

spec/agent-watcher.md. Read that for architecture, decisions, scope.

Channels reference docs (snapshot of Anthropic's official docs, used by Layer 2): docs/channels/.

Status

Layer Lane Status
Spec v1 Signed off by Bob 2026-05-06
Layer 1: Collector Foreman / Go v0 working: 43 tests passing. End-to-end exercised; binary builds. systemd unit + INSTALL.md ready.
Layer 2: MCP Watcher Bob / Python In progress — sandbox CC session being set up on VPS for testing.

Install (Layer 1)

git clone https://git.botbought.ai/foreman/agent-watcher ~/agent-watcher
cd ~/agent-watcher
./install.sh

Then edit ~/.config/agent-watcher/collector.yaml and systemctl --user restart agent-watcher.

See INSTALL.md for verify steps, troubleshooting, and the loginctl enable-linger step required to keep the daemon running across logouts.

Per CLAUDE.md rule #2, Angus runs the install commands — agents do not modify their own configuration.

Quick reference (Layer 1)

Inputs                          Output
─────────                       ──────
HTTP POST → port 18790      ┐
              (routed via   │   <recipient>.inbox  (JSONL, ping-shaped)
               YAML table)  │   identical format to
                            ├─→ what `ping <recipient> <payload>` writes;
File drop in                │   the existing UserPromptSubmit hook and the
~/Nyx/workspace/incoming/   │   future MCP Watcher consume the stream
*.json                      ┘   without distinguishing source.

/health on the same webhook port returns {received, emitted, errors, uptime_sec} for journalctl correlation.