swarm: publish each agent's turn-state header on its own subject
The swarm can already tell whether an agent is alive — the `agent-status`
KV bucket republishes once a minute — but not what it is doing right now.
A header bar wants the second thing, and a minute-old answer to "is this
agent thinking" is the wrong answer most of the time it is read.
`hive-agent` now publishes a turn-state header to
`$SWARM.agent-state.<hive>.<agent>`, a core subject beside the terminal
rows it already sends. It goes out **on transition, not on a timer**: the
publisher watches the event bus, rebuilds the header, and sends only when
the serialised result differs from the last one it sent — so a second
periodic writer, which is the problem this exists to fix, is not what
replaces the bucket.
The payload is the published contract a swarm-level renderer is written
against, so the test asserts on the serialised JSON keys rather than on
Rust field names. Two fields deliberately depart from the per-agent web
UI's `StateSnapshot`: `turn_state_since` is an ISO 8601 UTC string rather
than unix seconds, matching the sibling `$SWARM.term` subject's stamp, and
`agent_state` carries the swarm's own `AgentState` vocabulary rather than
a `paused` boolean, so a reader can compare actual against wanted without
translating. `turn_state` and `agent_state` stay two separate fields:
neither vocabulary contains the other's values.
Swarm-side, `GET /api/agents/{name}/state/stream` relays the subject as
SSE, resolving the agent's hive at request time exactly as the terminal
stream does and passing the bytes through without parsing them.
The broker grant is a second `--agent-publish-subject` rather than a
widening of the existing one, so the terminal family and the header family
stay independently revocable, and a `module-eval` arm pins the rendered
flag and its argument together — the doubled dollar included, since a
single one expands to nothing in `ExecStart` and yields a grant that
matches nothing.
Refs #3802
This commit is contained in:
parent
065f93f037
commit
1ea3d87d7a
7 changed files with 683 additions and 0 deletions
|
|
@ -403,6 +403,36 @@ take the connection down with it, so the harness drops such a row's body before
|
|||
sending and leaves a marker in its place; the summary, level and icon still
|
||||
arrive. A row that's too large even without its body is logged and skipped.
|
||||
|
||||
The second thing an agent publishes is its **turn-state header**, on
|
||||
`$SWARM.agent-state.<hive>.<agent>` — same shape of subject, same grant
|
||||
mechanics, same lack of retention. It carries what a header bar wants: what the
|
||||
turn loop is doing (`turn_state`, plus `turn_state_since` as an ISO 8601 UTC
|
||||
stamp), which model (`model` and the resolved id the last turn actually ran on),
|
||||
the context budget and the last turn's context and cost token blocks, and
|
||||
`agent_state`.
|
||||
|
||||
`agent_state` reuses the swarm's own wanted-state vocabulary
|
||||
(`up`/`offline`/`paused`/`destroyed`) so a reader can compare what an agent _is_
|
||||
against what the swarm declared it should be without translating between two
|
||||
spellings. ⚠️ From inside the container only two of those four are sayable: the
|
||||
harness reports `up`, or `paused` when the pause marker is present. `offline` and
|
||||
`destroyed` are hive-c0re's observations — a stopped agent publishes nothing and
|
||||
a destroyed one doesn't exist — so a view that needs the full four-state picture
|
||||
takes them from the `agent-status` bucket and uses this subject to sharpen the
|
||||
rest.
|
||||
|
||||
Headers go out **on transition, not on a timer**: the harness rebuilds the
|
||||
header whenever its event bus moves and publishes only when the result differs
|
||||
from what it last sent. That's the whole point of the subject — the
|
||||
`agent-status` bucket already republishes once a minute, which is far too slow
|
||||
for "is this agent thinking right now". The cost of a core subject is that a
|
||||
subscriber attaching mid-idle sees nothing until the next change, so a renderer
|
||||
opens with the bucket's snapshot and lets this stream refine it.
|
||||
|
||||
Swarm-side, `GET /api/agents/<name>/state/stream` relays that subject as SSE,
|
||||
resolving the agent's hive at request time exactly as the terminal stream does.
|
||||
The payload passes through opaquely — the controller never parses a header.
|
||||
|
||||
### Swarm-wide forge webhooks
|
||||
|
||||
At startup the controller registers two Forgejo hooks pointing at
|
||||
|
|
|
|||
Loading…
Reference in a new issue