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> |
||
|---|---|---|
| cmd/agent-watcher | ||
| docs/channels | ||
| examples | ||
| internal | ||
| mcp-watcher | ||
| spec | ||
| systemd | ||
| .gitignore | ||
| go.mod | ||
| go.sum | ||
| INSTALL.md | ||
| install.sh | ||
| README.md | ||
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.