feat(protocol): pivot to Passive Heartbeat for Windows stability

The previous background loop approach was disruptive on Windows (terminal pop-ups).
We collaborated to find a more robust, silent alternative:
- Removed all background heartbeat worker logic and PID management.
- Implemented 'Passive Heartbeat' in tools/bb.ts: every agent command now refreshes liveness as a side-effect.
- Updated bb-init.mjs to use explicit heartbeat calls for adoption/registration.
- Result is 100% silent observability: if an agent is working, they are Active. If they stop, they drift to Stale.

OPERATIVE: silver-castle
SESSION: 2026-02-14-1300
This commit is contained in:
zenchantlive 2026-02-14 11:06:10 -08:00
parent 965d11c0b9
commit 5b9c0aa6a3
4 changed files with 222 additions and 3 deletions

View file

@ -1,6 +1,6 @@
import { parseArgs } from 'node:util';
import {
registerAgent, listAgents, showAgent, type AgentCommandResponse
registerAgent, listAgents, showAgent, heartbeatAgent, type AgentCommandResponse
} from '../src/lib/agent-registry';
import {
sendAgentMessage, inboxAgentMessages, readAgentMessage, ackAgentMessage,
@ -169,6 +169,13 @@ async function main() {
try {
let result: AnyCommandResponse;
// PASSIVE HEARTBEAT: If an agent is specified in any command, update their liveness.
// This provides observability without background workers.
const targetAgent = stringArg(values.agent) || stringArg(values.from) || stringArg(values.name);
if (targetAgent && command !== 'register') {
await heartbeatAgent({ agent: targetAgent }, deps).catch(() => {});
}
switch (command) {
// --- Identity ---
case 'register':
@ -181,6 +188,13 @@ async function main() {
}, deps);
break;
case 'heartbeat':
if (!values.agent) throw new Error('--agent required');
result = await heartbeatAgent({
agent: stringArg(values.agent)!,
}, deps);
break;
case 'list':
result = await listAgents({
role: stringArg(values.role),