feat(hands,jobs): watchdog ring with a neighbour agent, daily heartbeat and silence alarm
This commit is contained in:
1 parent
0475e09c81
commit
f6cb17acaf
9 files changed
+1029
No files matched your search
@@ -204,6 +204,67 @@ publishes no reply, so the only voice in it is the `say` tool.
|
||||
Switches: `досуг: выкл` as the first line of the file for every day, and
|
||||
`LEISURE=off` in `.env` to drop the job entirely.
|
||||
|
||||
## The watchdog ring (`beaver_agent/jobs/heartbeat.py`)
|
||||
|
||||
An agreement with a foreign agent on someone else's home server: both print
|
||||
one line a day into a vibegram room of their own, and both watch the other's
|
||||
silence. If the neighbour goes quiet past the threshold, *their* human is the
|
||||
one who needs to hear about it, so the alarm goes to the master thread as a
|
||||
spawned triage job and the dispatcher decides whether to wake the owner.
|
||||
|
||||
The room is a **second, independent** `Vibegram` client (`VIBEGRAM_HB`), not
|
||||
the one the dispatcher's `vibegram` tool and the triage job share. `/api/pending`
|
||||
is a cursor on the hub's side: one client for both rooms would mean the triage
|
||||
job eats the heartbeats, and the heartbeats drown in conversation. The model
|
||||
gets no tool for this room - the watchdog is a mechanism.
|
||||
|
||||
The point of a watchdog is that it does not lie: an instrument that quietly
|
||||
fails to watch is worse than no instrument, because it looks healthy. So there
|
||||
are no defaults anywhere in it.
|
||||
|
||||
- `отметка` (17:12 local) prints `hb <nick> at=… boot=… every=… src=…`. The
|
||||
period is not a constant: it is derived from that job's own registered cron
|
||||
(`run.job.cron`). A schedule that does not reduce to "every N hours" gets
|
||||
`every=unknown src=none` - lying with a number is worse than refusing.
|
||||
- `сторож` (17:42 local) asks for an explicit window (`pending(limit=25)`),
|
||||
finds the newest `hb claude-kai …`, and compares its age against
|
||||
`every * 1.5` where `every` comes **from the neighbour's own line**. The
|
||||
clock it trusts is `seen` - when *we* saw the line - not the `at=` inside it,
|
||||
which is the neighbour's statement about himself.
|
||||
- Every run prints `ack claude-kai last=… seen=… res=… win=… armed=… thr=…`
|
||||
into the room. Anything the run could not measure says so out loud there:
|
||||
`every=unknown` in his line falls back to the last known period and marks the
|
||||
threshold `thr=36h-fallback`; a period never read once is `thr=unknown` and
|
||||
no alarm is taken.
|
||||
- Never having seen a beat is not an alarm, it is `armed=no` - there is nothing
|
||||
to watch yet. Seven days of `armed=no` in a row tells the human once, so an
|
||||
unfinished ring does not quietly become permanent.
|
||||
- If the window's highest event id went **backwards** since the last run, the
|
||||
window is looking at the past: `win=stale`, and an alarm in that run is
|
||||
forbidden. Same for a hub that did not answer at all.
|
||||
- The alarm names all three possible causes - the neighbour's machine, his
|
||||
cron, or the channel (the room itself) - and if our own write into the room
|
||||
went through in that same run while neither `hb` nor `ack` came back from
|
||||
him, it says to suspect the channel before the neighbour's death. It repeats
|
||||
at most once a day, not once a run.
|
||||
|
||||
State between runs is `мета/бобер/.состояние/сторож.json`; a lost file means
|
||||
the ring disarms itself and starts the seven-day count again.
|
||||
|
||||
Setup is two variables in `.env` and one manual step:
|
||||
|
||||
```bash
|
||||
# join the watchdog room with the invite code the neighbour sends
|
||||
python3 scripts/vibegram_join.py <join-code> --nick <nick>
|
||||
# put the token into .env as VIBEGRAM_HB_TOKEN, then
|
||||
docker compose restart gateway
|
||||
```
|
||||
|
||||
`VIBEGRAM_HB_NICK` is optional - without it the nick is `VIBEGRAM_NICK`. With
|
||||
no `VIBEGRAM_HB_TOKEN` both jobs do nothing and the log says once at startup
|
||||
that the watchdog is not configured. The neighbour's nick is the `PEER`
|
||||
constant in `beaver_agent/hands/heartbeat.py`.
|
||||
|
||||
## Policy (`beaver_agent/policy.py`)
|
||||
|
||||
`bypassPermissions` everywhere; the boundary is `PreToolUse` rules declared
|
||||
|
||||
Reference in new issue
Block a user