12 Commits

Author SHA1 Message Date
chahinebrini
6fa94116a4 fix(hub): offene Asks sichtbar machen + Erinnerungsflut stoppen (TSK-0406)
Am 09.08. stellte sich heraus, dass fuenf Asks auf pending standen, zwei
seit einer Woche. Vier Agenten waren blockiert — darunter einer, der einen
fertigen Windows-Build fuer einen externen Tester nicht ausliefern durfte.
Der Architekt hat die ganze Zeit das Board gepollt und nichts davon gesehen.
Aufgefallen ist es nur, weil ein Agent den Stau selbst benannt hat.

TEIL 1 — SICHTBARKEIT (Umsetzung: codex)
Asks sind ein eigener, BLOCKIERENDER Kanal, den keine der Oberflaechen
erwaehnte, die ein Architekt regelmaessig ansieht:
- core/status.ts nennt jetzt Anzahl, IDs und Alter des aeltesten Asks.
  Das Alter ist der eigentliche Alarm: "einer offen" ist normal,
  "seit sechs Tagen offen" ist ein Notfall.
- /health liefert pendingAsks + oldestAskAgeSec — der billigste Statuscheck
  des Architekten; was dort nicht steht, existiert fuer ihn nicht.
- Der Watchdog kannte Asks gar nicht. Er meldete brav "silent for 16m",
  also das Symptom, waehrend die Ursache unsichtbar blieb. Jetzt alarmiert
  er bei ueberfaelligen Asks mit steigender Dringlichkeit, und die
  irrefuehrende "silent"-Meldung wird bei einem offenen Ask ersetzt durch
  "wartet seit Xh auf ASK-NNNN — bitte antworten, nicht neu starten".
  Genau diese Verwechslung fuehrte dazu, dass wartende Agenten fuer tot
  gehalten und ihre Sessions neu gestartet wurden.
- watch --await-ask weckt den Architekten bei neuen Asks (mit --new-only).
- Das Board zeigt offene Asks mit Alter und wartendem Agenten.

TEIL 2 — DIE FLUT (Umsetzung: claude)
Der Watchdog erinnerte einen nicht beanspruchten Task unbegrenzt im
Basisintervall weiter — auch an Agenten, von denen er WUSSTE, dass sie
nicht im work-Loop sind. Ergebnis: ueber 1500 ungelesene Nachrichten fuer
einen einzigen Agenten, dessen Loop daran nicht mehr anlief. Ein Alarm,
der den Empfaenger handlungsunfaehig macht, ist schlimmer als kein Alarm.
- Ist der Assignee nachweislich aus dem Loop ausgestiegen (nicht: nie
  gesehen), wird KEINE Erinnerung mehr in sein Postfach geschrieben.
  Eine Nachricht ist ein dauerhaftes Artefakt; in ein Postfach zu
  schreiben, das niemand liest, baut nur den Rueckstau auf, der spaeter
  seinen eigenen Loop blockiert. Stattdessen genau EIN Hinweis an den
  Architekten — ein Session-Neustart ist eine menschliche Entscheidung.
- Das ephemere SSE-Reemit bleibt in beiden Zweigen: es kostet nichts und
  erreicht womoeglich einen Agenten, der gerade neu verbindet.
- Fuer erreichbare Agenten waechst der Abstand exponentiell (Basis x 2^n,
  gedeckelt bei 8x) statt konstant zu bleiben.
- Der Zaehler wird zurueckgesetzt, sobald der Task beansprucht wird, damit
  ein spaeteres Reopen nicht in einem halb stummgeschalteten Zustand
  startet.

Der bestehende Test "does not spam" pruefte, dass nach erneutem Ablauf der
BASIS-Schwelle eine zweite Erinnerung kommt — also genau das Verhalten,
das die Flut erzeugt hat. Er ist auf den Backoff angepasst; seine Absicht
(Anti-Spam) gilt jetzt ueber die Lebensdauer eines Tasks statt nur ueber
ein Cooldown-Fenster.

316 Tests gruen, tsc --noEmit sauber.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-09 12:29:09 +02:00
chahinebrini
c2e19b3371 feat(watch): --ignore-from — Notifier nicht vom Watchdog zumüllen lassen
Der Architekten-Message-Notifier (`watch --await-message claude`) feuerte bei
JEDER Nachricht — auch bei den Watchdog-Erinnerungen, die alle paar Minuten
eintreffen ("review waiting 5m/10m/15m…"). Real erlebt: dreimal hintereinander
geweckt worden, ohne dass etwas Neues passiert war. Ein Signal, das im Takt
seiner eigenen Erinnerungen feuert, entwertet sich selbst.

`--ignore-from <agents>` blendet Absender aus; message-Events tragen dafür
jetzt `from` (bisher nur im title). Armung des Architekten künftig mit
`--ignore-from agenthub` — echte Agenten-Post weckt weiterhin sofort, die
Watchdog-Erinnerungen sieht man beim Pollen ohnehin.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 19:47:11 +02:00
chahinebrini
f5680c6982 feat(server): agent-health page + realtime auto-wake — SSE Last-Event-ID replay (durable SQLite event log), polling fallback wake on assign/message, /agent-health with reachability ampel + test-message delivery tracking + transport badge (TSK-0230)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 01:20:20 +02:00
chahinebrini
9551c90597 fix(realtime): discovery self-heal, polling fallback, message ack semantics, CRLF SSE parser
- probe configured server URL (~1s) before remote commands; on failure
  re-discover via LAN discovery, persist and use the resolved URL
- poll tryClaim/trySurfaceMessages every ~4s during the SSE wait in CLI
  work and MCP waitForTask — a lost SSE frame costs seconds, never sleep
- work/MCP no longer auto-mark messages read: surfacing sets delivered,
  explicit ack/read required; loops still wake only on unread (no spin)
- parseSSEBuffer accepts CRLF frame separators and joins multi data: lines
- bump version to 0.10.1 (package.json, CLI --version, MCP server)
2026-07-23 16:53:33 +02:00
chahinebrini
7922eeb8e8 chore(agenthub): snapshot pre-existing orphaned WIP (single-instance/mDNS + claimedBy + message-read) as batch baseline 2026-07-12 00:46:47 +02:00
chahinebrini
1e29a7b9b9 feat(watch): --new-only for --await-review — re-armable architect review-notifier without backlog spin
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-01 01:53:11 +02:00
chahinebrini
ed6c5b01d0 feat(messaging): read receipts + ack convention
Closes the fire-and-forget gap the user hit: the sender now sees when a message
is read. When markMessageRead fires, the read event carries the reader, and the
watch stream renders it as a receipt:

  AgentHub: ✓ Read         MSG-0002  read by windows-claude

Plus an ack convention in the implementer guide: when you act on a message-
request, send a brief "on it …" + a result message — so the sender sees progress,
not silence. (Board-side activity feed with messages is folded into TSK-0032.)

Bump 0.8.0 -> 0.8.1.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 04:04:04 +02:00
chahinebrini
07afa717ac feat(messaging): agent messaging — agenthub_message / agenthub_inbox (cross-agent via MCP)
Direct messages between agents through the hub — architect↔agent and agent↔agent
questions, clarifications and pings that handoffs/memories don't cover. Built as
MCP tools so kimi + codex get it too (not Claude-only), plus REST + CLI.

- Message entity (MSG-NNNN): schema, counter, messages/ dir, messageService
  (createMessage / listInbox / listMessages / markMessageRead).
- REST: POST /messages, GET /messages[?agent&unread], POST /messages/:id/read,
  with SSE emit. fsWatch + statusRefresh wired ('message' events; status skips them).
- MCP: agenthub_message {from,to,text,taskId?} + agenthub_inbox {agent,unreadOnly?}.
  agenthub_work now surfaces + drains unread messages and wakes on message events,
  so an agent sees messages inside its work loop. (18 tools total.)
- CLI: `agenthub message <to> <text> --from <agent>` + `agenthub inbox --agent`.
  watch stream shows "Message" events (architect-visible).
- Architect-visible by design: the hub stays the coordination point.

Bump 0.7.5 -> 0.8.0.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 03:20:38 +02:00
chahinebrini
ec378131e4 feat(watch): --await-review — architect review-queue notifier
Closes the architect-side gap: the auto-claim daemon wakes implementers on
new tasks, but the architect had no signal when an implementer submitted.

`agenthub watch --await-review` exits (after printing) the moment a task
enters `review` — including any task already in review on connect (initial
check) — so the architect can run it in the background and be re-invoked by
the harness the instant a submission needs a verdict.

- watch.ts: awaitReview option, initial fetchReviewTasks() check + SSE exit
  on task→review.
- index.ts: --await-review flag.
- tests: returns immediately on already-pending review; exits on a review
  transition while watching. 121/121 green.

Bump 0.4.0 -> 0.5.0.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-27 18:53:40 +02:00
chahinebrini
45070222d0 feat: zero-config auto-connect + agent presence (join-announce)
Agents now connect to the hub with no manual env/flag, and announce
themselves on join — restoring the agreed onboarding flow.

Auto-connect (resolveContext):
- An initialized project WITHOUT a configured serverUrl now falls
  through to LAN auto-discovery (previously it went straight to local
  mode, so agents in an initialized repo never found the server). The
  discovered URL is persisted to the project config, so the next
  command is instant.
- IP-change self-heal: when a configured server is unreachable,
  runRemote re-discovers the live server and updates the config so the
  next command reconnects automatically.

Presence (join-announce):
- POST /announce broadcasts an ephemeral 'agent/joined' event (not
  written to disk). New `agenthub hello --agent <name> --role <role>`
  posts it; watchers print "AgentHub: <agent> joined (<role>)".
- AgentHubEvent extended with type 'agent' + actions 'joined'/'left';
  formatEvent renders presence lines; remoteClient.announce() added.

Tests: formatEvent agent lines + /announce SSE e2e. 109/109 green.
Bump 0.1.2 -> 0.2.0 (CLI --version too).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-27 16:33:03 +02:00
chahinebrini
ff79cbb639 feat(cli): brand all AgentHub console output with "AgentHub:" prefix
Every user-facing line — the watch stream and each command's success
line, on both the local and --server (remote) paths — now carries an
"AgentHub:" prefix so it's recognizable in any agent's console
(Claude / Codex / Kimi), independent of the board.

- watch: formatEvent rewritten to verb-based, branded lines
  ("AgentHub: Task received <id>  <title>  [status]",
   "AgentHub: Task done <id>  by <agent>", "AgentHub: Handoff …"),
  plus an "AgentHub: connected" line on stream start.
- task/handoff/decision/memory/delegate/init + server-listening lines
  branded on the local command path.
- cli/index.ts: same branding on the --server remote path (the path
  agents actually hit), so CLI line and SSE stream now match.
- tests: formatEvent assertions updated to the branded format
  (regex-tolerant of column padding). 106/106 green.

codex + kimi stay implementers by convention (role=implementer +
assignedTo) — no schema change. Bump 0.1.1 -> 0.1.2.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-27 16:09:15 +02:00
chahinebrini
4e5e5ae6bb feat(realtime): SSE event channel + agenthub watch (TSK-0006 part 1)
- src/server/events.ts: in-process EventEmitter bus (AgentHubEventBus);
  singleton per server process, unlimited listeners for SSE fan-out.
- src/server/routes.ts: GET /events SSE endpoint — keepalive every 25 s,
  optional ?role= server-side filter, reply.hijack() for clean streaming;
  eventBus.publish() called on every successful mutating route (POST tasks/
  handoffs/decisions/memory, PATCH tasks/:id).
- src/cli/commands/watch.ts: SSE client using Node fetch + ReadableStream
  reader; exports parseSSEBuffer + formatEvent for testability; --once flag
  exits 0 after first event (turn-based agent use case); --role for client-
  side filtering (additive to server-side filter).
- src/cli/index.ts: register agenthub watch command via resolveContext.
- tests/sse.test.ts: 18 new tests (74 total, all green) — unit tests for
  parseSSEBuffer + formatEvent, integration tests for eventBus on each
  mutation type, E2E SSE stream + role-filter tests over real HTTP.

Limitation (documented): only REST mutations through the server emit events.
Local-CLI writes (direct file+SQLite) are invisible to subscribers — a
filesystem-watch increment is deferred to a later phase.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 00:16:16 +02:00