From 28502708294fd8ca4da399f8a5b34074c917820e Mon Sep 17 00:00:00 2001 From: damocles Date: Sun, 30 Aug 2026 01:18:17 +0200 Subject: [PATCH] hive-c0re/hive-sh4re: remove the ask/answer wire protocol + core routing --- docs/agent-hierarchy.md | 21 +- docs/approvals.md | 28 +-- docs/conventions.md | 57 +---- docs/persistence.md | 28 +-- docs/terminal-rendering.md | 11 - docs/web-ui/agent.md | 13 +- hive-agent-mcp/README.md | 4 +- hive-agent-mcp/src/mcp/args.rs | 13 +- hive-agent-mcp/src/mcp/render.rs | 22 +- hive-agent/prompts/system.md | 6 +- hive-agent/src/main.rs | 6 +- hive-agent/src/prompt.rs | 2 +- hive-agent/src/stream_enrich.rs | 67 +---- hive-c0re/src/coordinator.rs | 17 +- hive-c0re/src/main.rs | 5 +- hive-c0re/src/questions.rs | 276 --------------------- hive-c0re/src/socket_server/mod.rs | 169 ++++++++----- hive-c0re/src/stores/mod.rs | 10 +- hive-c0re/src/stores/operator_questions.rs | 216 ---------------- hive-c0re/src/stores/power.rs | 4 +- hive-core-agent-sock/src/lib.rs | 22 -- hive-sh4re/src/inbox.rs | 10 - hive-sh4re/src/manager.rs | 21 -- 23 files changed, 177 insertions(+), 851 deletions(-) delete mode 100644 hive-c0re/src/questions.rs delete mode 100644 hive-c0re/src/stores/operator_questions.rs diff --git a/docs/agent-hierarchy.md b/docs/agent-hierarchy.md index c54b1ca4..ff08bcf2 100644 --- a/docs/agent-hierarchy.md +++ b/docs/agent-hierarchy.md @@ -94,7 +94,7 @@ umount-old / mount-new / restart-cascade step. | `request_init_config` (spawn a new child) | any agent, child added under self | | config change via forge PR (any descendant's config) | any ancestor | | `get_logs` (any descendant) | any ancestor | -| moderate questions / reminders (cancel any open thread of a descendant) | any ancestor | +| moderate reminders (cancel any open thread of a descendant) | any ancestor | | `send` / `recv` routing | parent ↔ same-parent siblings ↔ self ↔ descendants; explicit allow-list for anyone else | | `request_update_meta_inputs` (bump meta lock) | root agents only (today: just `manager`) | @@ -149,16 +149,17 @@ other agents don't: - **Scattered ownership checks** — a handful of independent manager-only overrides exist across `hive-c0re` today: loose-ends visibility (manager sees hive-wide, sub-agents only their own), - "manager can cancel any question/reminder" overrides on the owner - check, `destroy` refusing to act on the manager, and crash-watch - skipping the manager (it auto-restarts via systemd instead of going - through the crash-watch loop). Each is planned to become an + `destroy` refusing to act on the manager, and crash-watch skipping + the manager (it auto-restarts via systemd instead of going through + the crash-watch loop). Each is planned to become an ancestor/descendant check instead of a manager-name check — see the - module docs for `loose_ends.rs`, `stores/operator_questions.rs`, - `stores/broker.rs`, `actions.rs`, and `workers/crash_watch.rs` for - the current owner-check logic in each. (Reminder cancellation has - since moved fully in-agent — see the note on - `CancelLooseEndKind::Reminder` in `hive-c0re/src/questions.rs`.) + module docs for `loose_ends.rs`, `stores/broker.rs`, `actions.rs`, + and `workers/crash_watch.rs` for the current owner-check logic in + each. (Question/answer routing and its own manager-override cancel + path — formerly `hive-c0re/src/questions.rs` and + `stores/operator_questions.rs` — has been removed entirely; reminder + cancellation is now handled fully in-agent, see the note on + `CancelLooseEndKind::Reminder` in `hive-c0re/src/socket_server/mod.rs`.) None of the above is a stable interface — treat the module doc comments as the source of truth for exactly which checks exist today. diff --git a/docs/approvals.md b/docs/approvals.md index 88f7c63b..b3ddda62 100644 --- a/docs/approvals.md +++ b/docs/approvals.md @@ -598,12 +598,6 @@ renders only for the root agent. The root agent's approval-gating behaviour comes from its CLAUDE.md / agent-specific instructions, not the system prompt template. -Any agent (root or not) can also ask a structured question of the -operator or a peer agent via the `ask`/`answer` MCP tools, independent -of the approval flow above — see -`docs/conventions.md#question-routing-ask--answer` for the routing -rules, `ttl_seconds` expiry, and cancellation. - ## Helper events to the submitting agent `Coordinator::notify_submitter(approval_id, &HelperEvent)` routes the @@ -635,18 +629,6 @@ root agent. Variants (`hive_sh4re::manager::HelperEvent`): - `NeedsUpdate { agent }` — sub-agent's recorded flake rev is stale. The root agent calls `update(name)` to rebuild — idempotent, no approval required. -- `QuestionAnswered { id, question, answer, answerer }` — peer - `Answer` request (answerer = agent name) or ttl watchdog expiry - (answerer = `"ttl-watchdog"`, answer = `"[expired]"`). The dashboard's - own `/answer-question/{id}` trigger (answerer = `"operator"`) is - gone — removed along with the rest of the dashboard's ask/answer - surface, so an operator-targeted question now has no path to an - answer at all. -- `QuestionAsked { id, asker, question, options, multi }` — - fired when an agent calls `Ask { to: Some(), ... }`. - The recipient responds via `Answer { id, answer }` and the - asker sees the matching `QuestionAnswered`. - The remaining lower-urgency lifecycle notices — `Rebuilt`, `Killed`, `Destroyed`, `NeedsLogin`, `LoggedIn`, `ConfigReady` — are "FYI, check when convenient" events with no reason to drive an immediate turn, so @@ -668,11 +650,11 @@ or the dashboard `↻ R3BU1LD` button when the lock didn't move). When set, bootstrap container yields the exact tree that was referenced. To add a new lifecycle notice: if it needs to drive an immediate turn -(a live conversation like `Ask`/`Answer`, or something genuinely -urgent), add a `HelperEvent` variant + call sites + update -`prompts/system.md`'s message-event list. If it's "FYI, check when -convenient," call `push_todo`/`push_todo_submitter` directly instead — -no new wire type needed. +(something genuinely urgent, like `ContainerCrash`), add a +`HelperEvent` variant + call sites + update `prompts/system.md`'s +message-event list. If it's "FYI, check when convenient," call +`push_todo`/`push_todo_submitter` directly instead — no new wire type +needed. ## Auto-update on startup diff --git a/docs/conventions.md b/docs/conventions.md index 472c6075..7b06bcc2 100644 --- a/docs/conventions.md +++ b/docs/conventions.md @@ -171,46 +171,6 @@ cutoff so a later `AckTurn` doesn't double-update and a stale redelivery tag can't outlive its row. The operator-side sibling is the dashboard's "mark all read" (unbounded, per-agent). -### Question routing (Ask / Answer) - -`AgentRequest::Ask` (and the manager-flavour mirror) surfaces a -structured question that either lands in the operator-question queue -or in a peer agent's inbox. The recipient is the `to` field: - -- `to = None` or `to = Some("operator")` — routes to the - operator-question queue. ⚠️ The dashboard's whole ask/answer - surface (the UI that used to render this queue and let the operator - reply) has been removed — an operator-targeted question currently - has no reader anywhere; this routing case is being removed too as - the same effort continues. The legacy `AskOperator` variant - collapses into this case. -- `to = Some()` — peer Q&A. The target agent receives a - `HelperEvent::QuestionAsked { id, asker, question, options, multi }` - in their inbox. They reply via `AgentRequest::Answer` (or - `ManagerRequest::Answer` if they're the manager); the answer - threads back to the asker as a `HelperEvent::QuestionAnswered` - event. - -Shape fields are uniform across both targets: - -- `options` is advisory — a list is never enforced; peer-agent - recipients see it in their `QuestionAsked` event and can return any - string regardless. -- `multi = true` is a hint that the answerer should treat `options` as - a multi-select rather than pick-one. The answer comes back as a - single string with selections joined by `", "`. -- `ttl_seconds` auto-cancels with answer `[expired]` (and `answerer: - "ttl-watchdog"`) when the wait becomes moot. `None` = wait - indefinitely or until manual cancel. - -Response shape is always `QuestionQueued { id }` — the asker stores -the id and correlates the asynchronous answer event when it lands. -Authorisation on `Answer`: only the question's `target` agent, or the -operator, is permitted to reply; an answer attempt from anyone else -fails the wire-side check. (The operator's only path to invoke this -was the now-removed dashboard — see the note above; the -authorisation rule itself is unchanged.) - ### Loose-ends wire shape `LooseEnd` is the per-row response shape for `GetLooseEnds` (both @@ -220,15 +180,12 @@ privileged bot, etc.) can land later without breaking existing handlers. Each row carries enough context that the caller renders it directly as a bulleted list, no follow-up fetch needed. -Per-flavour scoping is uniform across the three variants: +Per-flavour scoping is uniform across the two cancellable variants: - **agent-flavour** `GetLooseEnds` only surfaces rows the calling agent has standing in. `Approval` rows only appear when the calling agent is the manager (sub-agents don't submit - approvals). `Question` rows surface where the agent is `asker` - OR `target` (the routing semantics from the Ask/Answer - subsection above). `Reminder` rows are scoped to `owner == - self`. + approvals). `Reminder` rows are scoped to `owner == self`. - **manager-flavour** `GetLooseEnds` lists every pending row in the swarm — full audit view. @@ -240,9 +197,6 @@ Per-variant fields: blurb shown on the dashboard card. `commit_ref` is the kind-specific payload (see `docs/approvals.md::Approval kinds (wire shapes)`). -- `Question { id, asker, target?, question, age_seconds }` — - `target = None` = operator-routed (dashboard); `Some(agent)` = - peer-to-peer thread. - `Reminder { id, owner, message, due_at, age_seconds }` — `due_at` is the absolute time the scheduler is targeting (RFC 3339 on the wire, see *Timestamps on the wire* below); clients @@ -265,10 +219,9 @@ unsynchronised wall clock, etc.) so the bulleted list never shows nonsense ages. `CancelLooseEnd { kind, id }` is the matching write surface. The -`kind` enum (`Question` / `Reminder` / `Approval`) selects which -underlying store the dispatcher reaches into. `Question` and -`Reminder` cancel from either surface subject to ownership -checks (asker for the question, scheduler for the reminder). +`kind` enum (`Reminder` / `Approval`) selects which underlying store +the dispatcher reaches into. `Reminder` cancels from either surface +subject to an ownership check (the scheduling agent). `Approval` is manager-only — sub-agents don't submit approvals so they have nothing of their own to withdraw; their wire surface returns a clear error if they try. Cancelling an approval diff --git a/docs/persistence.md b/docs/persistence.md index e0e65ba9..b7f3ae00 100644 --- a/docs/persistence.md +++ b/docs/persistence.md @@ -20,8 +20,8 @@ keeps its state, purging it doesn't.** Beyond that: -- **Approvals and questions are kept forever** — they're audit trails, - not caches. Nothing about them ever ages out. +- **Approvals are kept forever** — they're an audit trail, not a + cache. Nothing about them ever ages out. - **Broker messages**: acked ones vacuum after 30 days; anything undelivered or delivered-but-not-yet-acked is always kept, however old. @@ -55,20 +55,11 @@ power-intent registry: ⚠️ The `mcp__hyperhive__remind` queue is **not** here any more: it moved to a harness-local, per-agent store as part of the loose-ends-v2 migration — see [`/harness/` contents - below](#state-dirs-per-agent) for where reminders (and todos, and - the questions mirror) actually live now. + below](#state-dirs-per-agent) for where reminders (and todos) + actually live now. - `approvals` — the queue. `agent / kind (merge_config_pr | spawn | init_config | update_meta_inputs | schedule_prompt) / commit_ref / requested_at / status / resolved_at / note`. -- `operator_questions` — `ask` / `answer` queue (despite the - table name, stores both operator-targeted + agent-to-agent - questions since the `ask` rename). - `asker / question / options_json / multi / asked_at / - deadline_at (ttl) / answered_at / answer / target`. `target IS - NULL` = operator path (dashboard); `target = ''` = peer - Q&A (`HelperEvent::QuestionAsked` pushed into target's inbox, - answered via `Answer` request). Migrated via `ALTER TABLE ADD - COLUMN` against `pragma_table_info`. - `scheduled_prompts` — recurring + one-shot prompt queue. `owner / body / interval_seconds (NULL = one-shot) / next_fire_at_unix / created_at_unix / source ("operator" or @@ -98,9 +89,8 @@ Retention: rows are always kept — the harness `ack_turn`s only after a successful turn, so an unacked row can still be requeued via `requeue_inflight` on a crash. -- Approvals and questions are kept indefinitely — both are - audit trails. `actions::destroy` and answered questions stay - visible to anything that queries by id. +- Approvals are kept indefinitely — an audit trail. `actions::destroy` + rows stay visible to anything that queries by id. - Scheduled prompts: one-shot rows are deleted on fire by the worker; recurring rows live until the operator cancels them (`cancel_schedule` MCP / dashboard ✗) which tombstones via @@ -301,9 +291,9 @@ Under `/var/lib/hyperhive/agents//`: hours; non-terminal (still-running) tasks are never deleted. This used to be a host-side `hive-c0re` vacuum, moved in-container for the same privsep-ownership reason as the events vacuum above. - - `hyperhive-state.sqlite` — consolidated loose-ends-v2 store: todos, - reminders, and a questions mirror, one small table each in a single - file (in-container daemons — `hive-bash-daemon`, `hive-matrix-daemon`, + - `hyperhive-state.sqlite` — consolidated loose-ends-v2 store: todos + and reminders, one small table each in a single file (in-container + daemons — `hive-bash-daemon`, `hive-matrix-daemon`, `hive-forge-notify` — upsert keyed todos here over the harness's in-agent socket, `HIVE_AGENT_SOCKET`; the harness merges them into `get_loose_ends` output and clears a row on `mark_todo_done`). diff --git a/docs/terminal-rendering.md b/docs/terminal-rendering.md index f1da4c63..d8d43be4 100644 --- a/docs/terminal-rendering.md +++ b/docs/terminal-rendering.md @@ -180,8 +180,6 @@ tools (Read, Write, etc.) keep their name as-is. | **Core hyperhive** | | | `send*` | rich renderer: `send* → to · NL` (default-open body) | | `recv*` | `recv*()` · `recv* wait Ns` · `recv* max N` | -| `ask*` | rich renderer: `ask* → to` (no inline answer form — see [Inline ask-operator answer](#inline-ask-operator-answer)) | -| `answer*` | rich renderer: `answer* #id` | | `remind*` | `remind* +Xm "preview"` or `remind* at HH:MMZ "preview"` | | `set_status*` | `set_status* "text"` | | `get_loose_ends*` | `get_loose_ends*()` or `get_loose_ends* [agent]` | @@ -242,15 +240,6 @@ This keeps less-frequent tools that don't have a specific `fmt_tool_use` case from dumping raw JSON. Common matrix and hyperhive tools have their own cases and skip this path. -## Inline ask-operator answer - -An `mcp__hyperhive__ask(to: "operator", ...)` row has no inline -answer form in this terminal — it renders like any other tool call. -The dashboard's own question-surfacing UI that used to let the -operator answer from the Y3R C4LL tab is gone (hyperhive#3721); there -is currently no UI path to answer an operator-targeted `ask()` at -all. - ## Dashboard side (not covered here) The main dashboard's message-flow pane is a different diff --git a/docs/web-ui/agent.md b/docs/web-ui/agent.md index c7654775..6d802daa 100644 --- a/docs/web-ui/agent.md +++ b/docs/web-ui/agent.md @@ -170,14 +170,11 @@ from the harness-local store (same effect as `cancel_loose_end(kind: "todo")`, just from the web UI instead of the agent's own tool calls). The todos flyout is the only per-agent flyout — there is no separate -"loose-ends" or "tasks" list. There is also no inline answer form for -`ask` tool calls in this terminal: an `ask` renders like any other -tool call (see -[`terminal-rendering.md`](../terminal-rendering.md#inline-ask-operator-answer)). -The dashboard's own Y3R C4LL questions pane that used to let the -operator answer from there is gone (hyperhive#3721) — an -operator-targeted `ask()` currently has no UI path to an answer at -all until the removal finishes or a replacement lands. +"loose-ends" or "tasks" list. There used to be a note here about the +`ask`/`answer` MCP tools having no inline answer form in this +terminal — that whole mechanism (the tools, the dashboard's questions +pane, the wire protocol) has since been removed entirely, so there's +nothing left to render a form for. ## Live view diff --git a/hive-agent-mcp/README.md b/hive-agent-mcp/README.md index 482d9fba..171992cc 100644 --- a/hive-agent-mcp/README.md +++ b/hive-agent-mcp/README.md @@ -9,7 +9,7 @@ would hit. HTTP is the sole transport; there is no stdio mode here. ## When to use it This is where the core hyperhive tool surface lives: `send`, `recv`, -`ask`/`answer`, `remind`, `get_loose_ends`, `set_status`, +`remind`, `get_loose_ends`, `set_status`, `get_agent_meta`, lifecycle (`kill`/`start`/`restart`/`update` on direct children), scheduling, and the approval-request tools. Reach for this crate when you're adding or changing a built-in tool rather @@ -25,7 +25,7 @@ or their own daemon instead of living here. in-agent socket the `hive-agent` harness serves. - **`client.rs`** — socket client to the hyperhive broker. - **`send_allow.rs`** — enforces the per-agent - `hyperhive.allowedRecipients` allow-list on `send`/`ask`. + `hyperhive.allowedRecipients` allow-list on `send`. - **`paths.rs`** — socket + state path resolution shared with the harness's own `paths.rs` conventions. diff --git a/hive-agent-mcp/src/mcp/args.rs b/hive-agent-mcp/src/mcp/args.rs index f64ac080..5a68820c 100644 --- a/hive-agent-mcp/src/mcp/args.rs +++ b/hive-agent-mcp/src/mcp/args.rs @@ -159,14 +159,13 @@ pub struct UpdateArgs { #[derive(Debug, serde::Deserialize, schemars::JsonSchema)] pub struct CancelLooseEndArgs { - /// Which kind of thread to cancel — `"question"` for an open - /// `ask` that's still waiting on an answer, `"reminder"` for a - /// scheduled `remind` that hasn't fired yet, or `"todo"` for a - /// loose-ends-v2 todo (bash/matrix/forge). Use the `kind` - /// field straight off the `get_loose_ends` row. + /// Which kind of thread to cancel — `"reminder"` for a scheduled + /// `remind` that hasn't fired yet, `"approval"` for a pending + /// approval you submitted, or `"todo"` for a loose-ends-v2 todo + /// (bash/matrix/forge). Use the `kind` field straight off the + /// `get_loose_ends` row. pub kind: String, - /// Row id from the matching `get_loose_ends` entry (or the - /// `question_queued` reply when you submitted it). + /// Row id from the matching `get_loose_ends` entry. pub id: i64, } diff --git a/hive-agent-mcp/src/mcp/render.rs b/hive-agent-mcp/src/mcp/render.rs index bb6b3466..39e3f7f8 100644 --- a/hive-agent-mcp/src/mcp/render.rs +++ b/hive-agent-mcp/src/mcp/render.rs @@ -194,19 +194,6 @@ fn render_one_loose_end(out: &mut String, t: &hive_sh4re::inbox::LooseEnd) { "- approval #{id} ({agent} @ {commit_ref}, {age_seconds}s old){desc}" ); } - hive_sh4re::inbox::LooseEnd::Question { - id, - asker, - target, - question, - age_seconds, - } => { - let to = target.as_deref().unwrap_or("operator"); - let _ = writeln!( - out, - "- question #{id} ({asker} → {to}, {age_seconds}s old): {question}" - ); - } hive_sh4re::inbox::LooseEnd::Reminder { id, owner, @@ -459,23 +446,20 @@ pub(super) fn parse_loose_end_kind( raw: &str, ) -> Result { match raw.trim().to_ascii_lowercase().as_str() { - "question" | "q" => Ok(hive_sh4re::inbox::CancelLooseEndKind::Question), "reminder" | "r" => Ok(hive_sh4re::inbox::CancelLooseEndKind::Reminder), "approval" | "a" => Ok(hive_sh4re::inbox::CancelLooseEndKind::Approval), other => Err(format!( "cancel_loose_end: unknown kind '{other}' \ - (expected \"question\", \"reminder\", \"approval\", or \"todo\")" + (expected \"reminder\", \"approval\", or \"todo\")" )), } } /// Canonical user-facing label for a `CancelLooseEndKind` — used in -/// the success ack so the caller always sees `"question"` / -/// `"reminder"` instead of whatever alias they passed in (`"q"` / -/// `"r"`). +/// the success ack so the caller always sees `"reminder"` instead of +/// whatever alias they passed in (`"r"`). pub(super) fn loose_end_kind_label(kind: hive_sh4re::inbox::CancelLooseEndKind) -> &'static str { match kind { - hive_sh4re::inbox::CancelLooseEndKind::Question => "question", hive_sh4re::inbox::CancelLooseEndKind::Reminder => "reminder", hive_sh4re::inbox::CancelLooseEndKind::Approval => "approval", } diff --git a/hive-agent/prompts/system.md b/hive-agent/prompts/system.md index 44e114dd..9297b2fc 100644 --- a/hive-agent/prompts/system.md +++ b/hive-agent/prompts/system.md @@ -2,7 +2,7 @@ You are hyperhive agent `{label}` (qualified: `{qualified_label}`){hive_identity Tools (hyperhive surface). Full signature + behavior for each comes from the tool's own MCP description (you already received it via the MCP tool schema) — this is just the map of what exists and which ones are gated, so you know where to look: -- **Inbox / messaging** (always available): `mcp__hyperhive__recv`, `mcp__hyperhive__ack_until`, `mcp__hyperhive__send`, `mcp__hyperhive__ask`, `mcp__hyperhive__answer`, `mcp__hyperhive__get_loose_ends`, `mcp__hyperhive__cancel_loose_end`, `mcp__hyperhive__mark_todos_done`, `mcp__hyperhive__remind`, `mcp__hyperhive__set_status`, `mcp__hyperhive__get_agent_meta`. Two habits worth internalizing beyond the tool descriptions themselves: prefer ending the turn over repeatedly polling `recv` when idle (only turn-boundaries observe in-container todo wakes — bash-task completions, matrix unread, forge activity — and ending the turn is also your checkpoint); and `ask`/`answer` are async — `ask` returns immediately with a question id, the reply lands later as a `question_answered` system event, never block a turn waiting on it inline. For a large todo backlog (`get_loose_ends` caps at 40 rows), clear reviewed ids in bulk with `mark_todos_done` rather than cancelling one at a time — there's no blind range-clear, only ids you've actually looked at. +- **Inbox / messaging** (always available): `mcp__hyperhive__recv`, `mcp__hyperhive__ack_until`, `mcp__hyperhive__send`, `mcp__hyperhive__get_loose_ends`, `mcp__hyperhive__cancel_loose_end`, `mcp__hyperhive__mark_todos_done`, `mcp__hyperhive__remind`, `mcp__hyperhive__set_status`, `mcp__hyperhive__get_agent_meta`. One habit worth internalizing beyond the tool descriptions themselves: prefer ending the turn over repeatedly polling `recv` when idle (only turn-boundaries observe in-container todo wakes — bash-task completions, matrix unread, forge activity — and ending the turn is also your checkpoint). For a large todo backlog (`get_loose_ends` caps at 40 rows), clear reviewed ids in bulk with `mark_todos_done` rather than cancelling one at a time — there's no blind range-clear, only ids you've actually looked at. - **Extra MCP tools** (some agents only): `mcp____` — agent-specific (matrix client, scraper, db connector, etc.) declared in your `agent.nix` under `hyperhive.extraMcpServers`. First-class tools, already operator-approved at deploy time. - **Lifecycle** (_requires `lifecycle` tool group_, direct children only, no approval needed): `restart`, `kill`, `start`, `update`, `list_containers`. - **Approvals** (_requires `approvals` tool group_, queues an operator approval): `request_init_config`, `request_apply_commit`, `request_update_meta_inputs`. @@ -15,11 +15,11 @@ Your config repo is mounted **read-only** at `/agents/{label}/config/` — `agen Approval boundary: lifecycle ops on _existing_ direct children (`kill`, `start`, `restart`) are at your discretion — no operator approval needed (requires `lifecycle` tool group). _Creating_ a new agent (two-step: `request_init_config` + `request_apply_commit`) and _changing_ any agent's config (`request_apply_commit`) both go through the approval queue (requires `approvals` tool group). The operator only signs off on changes; you run the day-to-day. -Messages from sender `system` are hyperhive helper events (JSON body, `event` field discriminates): `approval_resolved`, `container_crash`, `needs_update`, `question_asked`, `question_answered`. Use these to react to lifecycle changes: +Messages from sender `system` are hyperhive helper events (JSON body, `event` field discriminates): `approval_resolved`, `container_crash`, `needs_update`. Use these to react to lifecycle changes: - `needs_update` — agent's flake rev is stale. Call `update(name)` to rebuild — it's idempotent and doesn't need approval. - `container_crash` — restart with `start(name)`. If it crashes again, ask the operator. -- otherwise pick up answers to questions you asked. +- `approval_resolved` — one of your own submitted approvals (`request_init_config`, `request_apply_commit`, `request_update_meta_inputs`, a scheduled prompt, …) was approved, denied, or failed; the body carries the resolution. Lifecycle notices that don't need an immediate turn — a new agent spawned, its config repo seeded, a container rebuilt/killed/destroyed, or its login state changing — surface as todos instead of messages now. Call `get_loose_ends` to see them. diff --git a/hive-agent/src/main.rs b/hive-agent/src/main.rs index d59d6205..a0ecf104 100644 --- a/hive-agent/src/main.rs +++ b/hive-agent/src/main.rs @@ -104,9 +104,9 @@ async fn main() -> Result<()> { // ---------- shared turn helpers ---------- /// Surface a `SYSTEM_SENDER` message in the live event bus + tracing -/// log. Both agents and the manager receive `QuestionAnswered`, -/// `ContainerCrash`, reparent notifications, and friends; the parse -/// and log path is identical. Quiet no-op when `from` isn't +/// log. Both agents and the manager receive `ContainerCrash`, +/// reparent notifications, and friends; the parse and log path is +/// identical. Quiet no-op when `from` isn't /// `SYSTEM_SENDER`. fn log_system_event(bus: &Bus, from: &str, body: &str) { if from != SYSTEM_SENDER { diff --git a/hive-agent/src/prompt.rs b/hive-agent/src/prompt.rs index e0f5f5c2..3e0393ac 100644 --- a/hive-agent/src/prompt.rs +++ b/hive-agent/src/prompt.rs @@ -255,7 +255,7 @@ shared closer assert!(!rendered.contains("