From 0de8c372339b3841a92b3e7e259429f2f77ca527 Mon Sep 17 00:00:00 2001 From: iris Date: Thu, 2 Jul 2026 14:43:12 +0200 Subject: [PATCH] docs(turn-loop): document ack_until tool + [msg #id] markers in recv/wake prompt ack_until was added to the messaging group by the ack_until feature but was missing from the turn-loop.md MCP surface documentation entirely. Add it to: - Messaging group signature list - recv bullet: mention that each row is prefixed with [msg #] so agents know what id to pass to ack_until - New ack_until bullet explaining recipient-scoped bulk-triage use case - Wake prompt section: mention the [msg #] prefix and its purpose --- docs/turn-loop.md | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/docs/turn-loop.md b/docs/turn-loop.md index 92678187..e2759e19 100644 --- a/docs/turn-loop.md +++ b/docs/turn-loop.md @@ -253,10 +253,13 @@ agent's durable bind-mounted dir. CLAUDE.md auto-load walks upward from `/state` — drop a per-agent CLAUDE.md there if you want long-term hints that survive destroy/recreate. -The wake prompt is intentionally minimal: just the popped message's -`from`/`body`, plus an inline `({unread} more pending — drain via -…)` hint when `unread > 0`. Claude drives any further `recv`/`send` -itself via the embedded MCP server. +The wake prompt is intentionally minimal: the popped message's +`from`/`body`, prefixed with a `[msg #]` broker-row-id marker +(so the agent knows what id to pass to `ack_until` for bulk triage; +transient pings show no marker because their sentinel id 0 has nothing +to ack), plus an inline `({unread} more pending — drain via …)` hint +when `unread > 0`. Claude drives any further `recv`/`send` itself via +the embedded MCP server. Whenever hive-c0re starts / restarts / rebuilds a container, it also drops a `system` message into the agent's inbox via @@ -502,7 +505,7 @@ preset (`AGENT_DEFAULT`) includes `messaging`, `meta`, `inbox`, and **Messaging** (`messaging` group): `send(to, body, in_reply_to?)`, `recv(wait_seconds?, max?)`, `ask(question, options?, multi?, -ttl_seconds?, to?)`, `answer(id, answer)`. +ttl_seconds?, to?)`, `answer(id, answer)`, `ack_until(up_to)`. - `send` — message a peer (logical name) or the operator (`to: "operator"`). Use `to: ""` to address the topology @@ -518,7 +521,9 @@ ttl_seconds?, to?)`, `answer(id, answer)`. immediately. Positive value parks the turn up to that many seconds (cap 180) — incoming messages wake instantly. `max` (default 1, cap 32) drains up to N rows; `wait_seconds` applies to the first, then - drains up to `max` total. **Graceful shutdown**: when the harness + drains up to `max` total. Each returned row is prefixed with + `[msg #]` (broker row id; note the highest id seen, then pass + it to `ack_until` to bulk-triage the batch). **Graceful shutdown**: when the harness receives a stop signal, the inbox becomes fenced and `recv` returns an explicit `from: "graceful-stop"` message instead of an empty inbox. This unmissably directs the agent to flush durable state (`/state` @@ -534,6 +539,13 @@ ttl_seconds?, to?)`, `answer(id, answer)`. checkboxes; `ttl_seconds` auto-cancels with answer `[expired]`. - `answer` — respond to a `question_asked` event routed to this agent. Strict authorisation: only the declared target can answer. +- `ack_until(up_to)` — bulk-mark inbox rows handled: every row with + broker id `<= up_to` is stamped as acked in a single UPDATE. + Recipient-scoped (agents can only ack their own rows). Use when a + restart redelivers a large backlog of already-handled messages: read + the highest `[msg #N]` from the set you've actually processed, then + `ack_until(N)` to prevent re-pop. Acked rows never redeliver. + Transient pings (sentinel id 0) have nothing to ack and show no marker. **Inbox** (`inbox` group): `get_loose_ends(agent?)`, `cancel_loose_end(kind, id)`, `remind(message, delay_seconds? |