feat(pulse): notListening — Agenten mit offener Arbeit, die nicht im Loop sitzen

Der Fall, der den Menschen zum Postman macht: eine Task ist an einen Agenten
adressiert, aber er sitzt nicht in agenthub_work. Er ist dann NICHT dormant
(er lebt womöglich und hat gerade erst mit dem Hub gesprochen), hört aber
nicht zu — die Arbeit bleibt liegen, ohne dass irgendwo ein Fehler erscheint.

Real erlebt: TSK-0288 lag offen an codex adressiert, codex war `active`
(75 s zuvor gesehen) mit inLoop=false. Der Architekt hat es nicht bemerkt und
dem CEO stattdessen erzählt, die Task werde "automatisch" geclaimt. Genau
diese Erwartungs-statt-Prüfung soll das Signal künftig unmöglich machen.

`/architect/pulse` liefert dazu `notListening` mit Agent, Anzahl wartender
Tasks und einem Klartext-Hinweis.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
chahinebrini 2026-07-29 20:17:40 +02:00
parent c2e19b3371
commit 6a4cd32312

View File

@ -120,6 +120,21 @@ export async function registerRoutes(app: FastifyInstance, cwd: string): Promise
loopExitAgoSec: agent.loopExitAgoSec, loopExitAgoSec: agent.loopExitAgoSec,
loopExitReason: agent.loopExitReason, loopExitReason: agent.loopExitReason,
})), })),
// Der Fall, der den Menschen sonst zum Postman macht: eine Task ist an
// einen Agenten adressiert, aber er sitzt nicht im Work-Loop. Er ist dann
// nicht "dormant" (er lebt vielleicht sogar), hoert aber nicht zu — die
// Arbeit bleibt liegen, ohne dass irgendwo ein Fehler erscheint. Real
// erlebt: codex war `active` (75s zuvor gesehen) mit inLoop=false,
// waehrend TSK-0288 offen an ihn adressiert dalag.
notListening: health.agents
.filter((agent) => !agent.inLoop && agent.pendingCount > 0)
.map((agent) => ({
name: agent.name,
waitingTasks: agent.pendingCount,
taskId: agent.waitingTaskId,
lastSeenAgoSec: agent.lastSeenAgoSec,
hint: `${agent.name} hat offene Arbeit, sitzt aber nicht im Work-Loop — anstossen oder Session neu starten.`,
})),
messages: allMessages.slice(0, messageLimit).map(({ id, from, taskId, createdAt }) => ({ messages: allMessages.slice(0, messageLimit).map(({ id, from, taskId, createdAt }) => ({
id, from, taskId, createdAt, id, from, taskId, createdAt,
})), })),