The status snapshot (.agenthub/status/latest.md) was only refreshed on an explicit `status --update`, so it drifted stale and misled agents that follow golden rule #2 ("read status/latest.md") — e.g. an implementer seeing an old task list and picking the wrong task. - src/server/statusRefresh.ts: subscribe to the event bus and regenerate the snapshot on every task/handoff/decision/memory change (REST and filesystem-watch paths), debounced 300 ms. Presence ('agent') events are skipped; the snapshot lives outside the watched dirs so it can't feed back into the watcher. - wired into startServer lifecycle (start + stop on close). - test: status/latest.md reflects a new task shortly after POST /tasks. 117/117 green. Bump 0.3.0 -> 0.3.1. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
41 lines
1.4 KiB
TypeScript
41 lines
1.4 KiB
TypeScript
import { eventBus } from './events.js';
|
|
import type { AgentHubEvent } from './events.js';
|
|
import { updateStatus } from '../core/services/statusService.js';
|
|
|
|
/**
|
|
* Keep `.agenthub/status/latest.md` fresh. Agents are told (golden rule #2) to
|
|
* read the status snapshot, but it was only regenerated on an explicit
|
|
* `status --update` — so it drifted stale and misled agents (e.g. showing an
|
|
* old task list). This subscribes to the in-process event bus and regenerates
|
|
* the snapshot on every entity change, from both the REST and filesystem-watch
|
|
* paths, debounced so a burst collapses to one write.
|
|
*
|
|
* Presence ('agent') events are ephemeral and don't affect status. Writing the
|
|
* snapshot lives in `.agenthub/status/` — outside the watched entity dirs — so
|
|
* it never feeds back into the watcher.
|
|
*/
|
|
const DEBOUNCE_MS = 300;
|
|
|
|
export function startStatusAutoRefresh(cwd: string): () => void {
|
|
let timer: NodeJS.Timeout | undefined;
|
|
|
|
const onChange = (event: AgentHubEvent) => {
|
|
if (event.type === 'agent') return;
|
|
if (timer) clearTimeout(timer);
|
|
timer = setTimeout(() => {
|
|
timer = undefined;
|
|
try {
|
|
updateStatus(cwd);
|
|
} catch {
|
|
// best-effort — a failed status refresh must never break the server
|
|
}
|
|
}, DEBOUNCE_MS);
|
|
};
|
|
|
|
eventBus.on('change', onChange);
|
|
return () => {
|
|
if (timer) clearTimeout(timer);
|
|
eventBus.off('change', onChange);
|
|
};
|
|
}
|