add ack_until: bulk-ack inbox messages by id + surface msg ids in wake prompts and recv (closes #2125)

This commit is contained in:
damocles 2026-07-02 11:45:12 +02:00 committed by mara
commit 34374fd10a
8 changed files with 221 additions and 16 deletions

View file

@ -9,21 +9,36 @@ use crate::turn::TurnOutcome;
use crate::turn_stats::TurnStatRow;
/// Assemble the per-turn wake prompt string. The role/tools/etc. live in the
/// system prompt; this is just the wake signal body. `unread` is the inbox
/// depth after this message was popped. `redelivered` prepends a "may already
/// be handled" banner.
/// system prompt; this is just the wake signal body. `id` is the broker row
/// id, rendered as a `[msg #<id>]` marker so the agent can reference it in
/// `ack_until` (transient pings carry the sentinel 0 and render without it).
/// `unread` is the inbox depth after this message was popped. `redelivered`
/// prepends a "may already be handled" banner.
#[must_use]
pub fn format_wake_prompt(from: &str, body: &str, unread: u64, redelivered: bool) -> String {
pub fn format_wake_prompt(
id: i64,
from: &str,
body: &str,
unread: u64,
redelivered: bool,
) -> String {
let banner = if redelivered { REDELIVERY_HINT } else { "" };
let tag = if id > 0 {
format!("[msg #{id}] ")
} else {
String::new()
};
let pending = if unread == 0 {
String::new()
} else {
format!(
"\n\n({unread} more message(s) pending in your inbox — call `mcp__hyperhive__recv` \
with `max: {unread}` to drain them all in one round-trip before acting.)"
with `max: {unread}` to drain them all in one round-trip before acting. If the \
backlog is stale/already handled, `ack_until(up_to: <highest [msg #N] seen>)` \
clears everything up to that id in one call instead.)"
)
};
format!("{banner}Incoming message from `{from}`:\n---\n{body}\n---{pending}")
format!("{banner}{tag}Incoming message from `{from}`:\n---\n{body}\n---{pending}")
}
/// Current time as a Unix timestamp (seconds). Returns 0 on any error.