feat(dashboard): 60s base refresh with event-driven immediate updates

Raise the status dashboard base refresh from 10s to 60s, and refresh
immediately (debounced 1.5s) on events between ticks:
- a real chat message arrives in a registered room (index.ts onMessage)
- an agent starts/finishes a run, i.e. activity moves between rooms
  (GroupQueue.setOnActivityChange fired on activeCount changes)

requestImmediateStatusUpdate() drives updateStatus out-of-band via a coalescing
trigger. The existing re-entrancy guard means the base periodic refresh does not
run while an edit-retry is in flight; a refresh requested during that window is
remembered and runs once afterward. Adds createCoalescingTrigger with tests.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Codex
2026-08-21 12:53:39 +09:00
parent 9108174380
commit 8600213bcc
5 changed files with 122 additions and 4 deletions

View File

@@ -59,12 +59,28 @@ export class GroupQueue {
return state;
}
private onActivityChange: (() => void) | null = null;
setProcessMessagesFn(
fn: (groupJid: string, context: GroupRunContext) => Promise<boolean>,
): void {
this.processMessagesFn = fn;
}
/** Notified when a group starts or finishes a run (activeCount changes). */
setOnActivityChange(fn: () => void): void {
this.onActivityChange = fn;
}
private notifyActivityChange(): void {
if (!this.onActivityChange) return;
try {
this.onActivityChange();
} catch (err) {
logger.debug({ err }, 'onActivityChange callback threw');
}
}
/** Limit concurrency after restart to avoid API rate-limit storms. */
enterRecoveryMode(): void {
this.recoveryMode = true;
@@ -528,6 +544,7 @@ export class GroupQueue {
transitionRunPhase(state, groupJid, 'running_messages', { runId, reason });
assertRunPhaseInvariants(state, groupJid);
this.activeCount++;
this.notifyActivityChange();
logger.info(
{
@@ -580,6 +597,7 @@ export class GroupQueue {
resetRunState(state, groupJid);
assertRunPhaseInvariants(state, groupJid);
this.activeCount--;
this.notifyActivityChange();
this.drainGroup(groupJid);
}
}
@@ -592,6 +610,7 @@ export class GroupQueue {
assertRunPhaseInvariants(state, groupJid);
this.activeCount++;
this.activeTaskCount++;
this.notifyActivityChange();
logger.info(
{
@@ -628,6 +647,7 @@ export class GroupQueue {
assertRunPhaseInvariants(state, groupJid);
this.activeCount--;
this.activeTaskCount--;
this.notifyActivityChange();
this.drainGroup(groupJid);
}
}