hive-agent: remove the in-container questions mirror
This commit is contained in:
parent
1c4d662cf1
commit
c865114743
7 changed files with 59 additions and 528 deletions
|
|
@ -22,7 +22,6 @@ mod otel_turn_metrics;
|
|||
mod paths;
|
||||
mod plugins;
|
||||
mod prompt;
|
||||
mod questions;
|
||||
mod reminder_timer;
|
||||
mod reminders;
|
||||
mod serve_common;
|
||||
|
|
@ -109,17 +108,7 @@ async fn main() -> Result<()> {
|
|||
/// `ContainerCrash`, reparent notifications, and friends; the parse
|
||||
/// and log path is identical. Quiet no-op when `from` isn't
|
||||
/// `SYSTEM_SENDER`.
|
||||
///
|
||||
/// Also keeps the harness-local questions mirror in sync on the two
|
||||
/// question-shaped events: `QuestionAsked` (a peer/manager asked *this*
|
||||
/// agent — mirror an `Answering` row) and
|
||||
/// `QuestionAnswered` (a question *this* agent asked got a reply —
|
||||
/// clear the mirrored `Asked` row). Best-effort loopback dial of the
|
||||
/// in-agent socket (see `todo_server::dial`'s docs on why a dial beats
|
||||
/// threading an `Arc<Questions>` through this whole call chain) — a
|
||||
/// dial failure is a missed mirror update, not a turn failure, so it's
|
||||
/// logged and swallowed rather than propagated.
|
||||
async fn log_system_event(bus: &Bus, from: &str, body: &str) {
|
||||
fn log_system_event(bus: &Bus, from: &str, body: &str) {
|
||||
if from != SYSTEM_SENDER {
|
||||
return;
|
||||
}
|
||||
|
|
@ -129,62 +118,11 @@ async fn log_system_event(bus: &Bus, from: &str, body: &str) {
|
|||
} else {
|
||||
tracing::info!(%from, %body, "system message");
|
||||
}
|
||||
match parsed {
|
||||
Some(HelperEvent::QuestionAsked {
|
||||
id,
|
||||
asker,
|
||||
question,
|
||||
..
|
||||
}) => {
|
||||
mirror_question(hive_agent_sock::Request::RecordAnsweringQuestion {
|
||||
id,
|
||||
asker,
|
||||
question,
|
||||
})
|
||||
.await;
|
||||
}
|
||||
Some(HelperEvent::QuestionAnswered { id, .. }) => {
|
||||
mirror_question(hive_agent_sock::Request::ClearQuestion { id }).await;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
bus.emit(LiveEvent::Note {
|
||||
text: format!("[system] {body}"),
|
||||
});
|
||||
}
|
||||
|
||||
/// Open the harness-local questions mirror against the consolidated state
|
||||
/// db — same open-alongside shape as the reminders store
|
||||
/// (`reminders::Reminders::open`) opened just above this call site in
|
||||
/// `serve_main`, sharing the same file (distinct table, see
|
||||
/// `questions::SCHEMA`). `None` on open failure disables question ops the
|
||||
/// same way a failed reminders open disables reminder ops
|
||||
/// (`no_questions_store` in `todo_server`). Pulled out to its own fn to
|
||||
/// keep `serve_main` under the pedantic line-count lint.
|
||||
fn open_question_store() -> Option<Arc<questions::Questions>> {
|
||||
match questions::Questions::open(&paths::state_db()) {
|
||||
Ok(store) => Some(Arc::new(store)),
|
||||
Err(e) => {
|
||||
tracing::error!(error = ?e, "open questions db failed — question mirror disabled");
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Fire-and-forget helper for `log_system_event`'s questions-mirror dial:
|
||||
/// logs a warning on dial failure / an `Err` response, otherwise silent.
|
||||
async fn mirror_question(req: hive_agent_sock::Request) {
|
||||
match todo_server::dial(&req).await {
|
||||
Some(hive_agent_sock::Response::Err { message }) => {
|
||||
tracing::warn!(%message, ?req, "questions mirror dial returned an error");
|
||||
}
|
||||
None => {
|
||||
tracing::warn!(?req, "questions mirror dial failed (socket unavailable?)");
|
||||
}
|
||||
Some(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
/// Body string for the turn-failure notification we route to
|
||||
/// `<parent>` on `TurnError::Failed`. Reads the hive-qualified
|
||||
/// identity so the receiver sees `agent@hive` rather than relying on
|
||||
|
|
@ -482,23 +420,22 @@ impl Surface for AgentSurface {
|
|||
// ---------- generic turn loop ----------
|
||||
|
||||
/// Opens the todos store and spawns the in-agent todo socket (loose-ends
|
||||
/// v2 + harness-local reminders + questions): the harness owns the todo +
|
||||
/// reminder + question stores locally and serves the in-container
|
||||
/// producers on `HIVE_AGENT_SOCKET`. A new/changed todo upsert fires the
|
||||
/// returned `Notify` so the serve loop drives a turn directly — no broker
|
||||
/// v2 + harness-local reminders): the harness owns the todo + reminder
|
||||
/// stores locally and serves the in-container producers on
|
||||
/// `HIVE_AGENT_SOCKET`. A new/changed todo upsert fires the returned
|
||||
/// `Notify` so the serve loop drives a turn directly — no broker
|
||||
/// round-trip, no marker files. Best-effort: if the todos store can't
|
||||
/// open, the whole socket isn't served (reminder + question ops ride
|
||||
/// along on the same listener, so they're gated on the same store —
|
||||
/// acceptable since a from-scratch harness boot either has a writable
|
||||
/// harness dir or doesn't). Split out of `serve_main` to keep it under
|
||||
/// clippy's `too_many_lines` limit; kept alongside the returned `Notify`
|
||||
/// so the serve loop's `LocalTodo` arm can gate a wake on `has_any()`
|
||||
/// before spawning a turn — see its doc comment (the phantom-todo-wake
|
||||
/// issue: a burst of same-turn upserts can arm a second `Notify` permit
|
||||
/// that outlives the turn that already drained its payload).
|
||||
/// open, the whole socket isn't served (reminder ops ride along on the
|
||||
/// same listener, so they're gated on the same store — acceptable since a
|
||||
/// from-scratch harness boot either has a writable harness dir or
|
||||
/// doesn't). Split out of `serve_main` to keep it under clippy's
|
||||
/// `too_many_lines` limit; kept alongside the returned `Notify` so the
|
||||
/// serve loop's `LocalTodo` arm can gate a wake on `has_any()` before
|
||||
/// spawning a turn — see its doc comment (the phantom-todo-wake issue: a
|
||||
/// burst of same-turn upserts can arm a second `Notify` permit that
|
||||
/// outlives the turn that already drained its payload).
|
||||
fn spawn_todo_socket(
|
||||
reminder_store: Option<Arc<reminders::Reminders>>,
|
||||
question_store: Option<Arc<questions::Questions>>,
|
||||
bus: &Bus,
|
||||
) -> (Arc<tokio::sync::Notify>, Option<Arc<todos::Todos>>) {
|
||||
let todo_wake = Arc::new(tokio::sync::Notify::new());
|
||||
|
|
@ -509,14 +446,8 @@ fn spawn_todo_socket(
|
|||
let bus_for_socket = bus.clone();
|
||||
let store_for_socket = store.clone();
|
||||
tokio::spawn(async move {
|
||||
if let Err(e) = todo_server::run(
|
||||
store_for_socket,
|
||||
wake,
|
||||
reminder_store,
|
||||
question_store,
|
||||
bus_for_socket,
|
||||
)
|
||||
.await
|
||||
if let Err(e) =
|
||||
todo_server::run(store_for_socket, wake, reminder_store, bus_for_socket).await
|
||||
{
|
||||
tracing::error!(error = %e, "in-agent todo socket exited with error");
|
||||
}
|
||||
|
|
@ -646,9 +577,7 @@ async fn serve_main<S: Surface>(socket: &Path, poll_ms: u64) -> Result<()> {
|
|||
}
|
||||
};
|
||||
tokio::spawn(reminder_timer::run(reminder_store.clone(), reminder_tx));
|
||||
let question_store = open_question_store();
|
||||
let (todo_wake, todos_store) =
|
||||
spawn_todo_socket(reminder_store.clone(), question_store.clone(), &bus);
|
||||
let (todo_wake, todos_store) = spawn_todo_socket(reminder_store.clone(), &bus);
|
||||
if matches!(initial, LoginState::NeedsLogin) {
|
||||
login::wait_for_login(
|
||||
&claude_dir,
|
||||
|
|
@ -973,7 +902,7 @@ async fn handle_turn<S: Surface>(
|
|||
let body = first.body;
|
||||
let redelivered = first.redelivered;
|
||||
let msg_id = first.id;
|
||||
log_system_event(bus, &from, &body).await;
|
||||
log_system_event(bus, &from, &body);
|
||||
tracing::info!(%from, %body, %redelivered, "inbox");
|
||||
let unread = S::inbox_unread(socket).await;
|
||||
bus.emit(LiveEvent::TurnStart {
|
||||
|
|
|
|||
Loading…
Reference in a new issue