feat(#2635): wire harness-local questions mirror (inc2 pt2)
This commit is contained in:
parent
9471201698
commit
e5ef5a72be
8 changed files with 465 additions and 191 deletions
|
|
@ -35,9 +35,9 @@ pub use args::{
|
|||
pub use render::{annotate_retries, format_ack, format_agent_meta, format_recv};
|
||||
|
||||
use render::{
|
||||
dial_agent_socket, format_matrix_summary, local_reminders, local_todos, loose_end_kind_label,
|
||||
mark_local_todo_done, matrix_unread_summary, parse_loose_end_kind, render_loose_ends,
|
||||
reply_err,
|
||||
dial_agent_socket, format_matrix_summary, local_questions, local_reminders, local_todos,
|
||||
loose_end_kind_label, mark_local_todo_done, matrix_unread_summary, parse_loose_end_kind,
|
||||
render_loose_ends, reply_err,
|
||||
};
|
||||
|
||||
/// Write (or remove) the status file in the agent's own `state/` directory.
|
||||
|
|
@ -186,9 +186,13 @@ impl AgentServer {
|
|||
Ok(to) => to,
|
||||
Err(reason) => return format!("invalid `to` agent name: {reason}"),
|
||||
};
|
||||
let question = args.question;
|
||||
let target = to
|
||||
.as_ref()
|
||||
.map_or_else(|| "operator".to_owned(), std::string::ToString::to_string);
|
||||
let (resp, retries) = self
|
||||
.dispatch(hive_core_agent_sock::Request::Ask {
|
||||
question: args.question,
|
||||
question: question.clone(),
|
||||
options: args.options,
|
||||
multi: args.multi,
|
||||
ttl_seconds: args.ttl_seconds,
|
||||
|
|
@ -196,10 +200,22 @@ impl AgentServer {
|
|||
})
|
||||
.await;
|
||||
let s = match resp {
|
||||
Ok(hive_core_agent_sock::Response::QuestionQueued { id }) => format!(
|
||||
"question queued (id={id}); answer will arrive as a system \
|
||||
`question_answered` event in your inbox"
|
||||
),
|
||||
Ok(hive_core_agent_sock::Response::QuestionQueued { id }) => {
|
||||
// Best-effort local questions-mirror record — a dial
|
||||
// failure just means `get_loose_ends` won't show this
|
||||
// row locally; the actual question is already queued
|
||||
// in c0re regardless.
|
||||
let _ = dial_agent_socket(&hive_agent_sock::Request::RecordAskedQuestion {
|
||||
id,
|
||||
target,
|
||||
question,
|
||||
})
|
||||
.await;
|
||||
format!(
|
||||
"question queued (id={id}); answer will arrive as a system \
|
||||
`question_answered` event in your inbox"
|
||||
)
|
||||
}
|
||||
other => reply_err(other, "ask"),
|
||||
};
|
||||
annotate_retries(s, retries)
|
||||
|
|
@ -226,6 +242,11 @@ impl AgentServer {
|
|||
answer: args.answer,
|
||||
})
|
||||
.await;
|
||||
if resp.is_ok() {
|
||||
// Best-effort — this agent is done owing a reply for `id`;
|
||||
// drop its `answering` mirror row.
|
||||
let _ = dial_agent_socket(&hive_agent_sock::Request::ClearQuestion { id }).await;
|
||||
}
|
||||
annotate_retries(
|
||||
format_ack(resp, "answer", format!("answered question {id}")),
|
||||
retries,
|
||||
|
|
@ -350,6 +371,16 @@ impl AgentServer {
|
|||
if is_self_query && let Some(reminders) = local_reminders().await {
|
||||
loose_ends.extend(reminders);
|
||||
}
|
||||
// Merge local mirrored questions — same self-query-only
|
||||
// restriction as todos/reminders above. c0re no longer sources
|
||||
// `Question` rows for `for_agent`/`hive_wide` (see
|
||||
// `hive-c0re::loose_ends` doc), so this is the only place a
|
||||
// self-query sees its own questions now; a manager query for a
|
||||
// child still sees that child's approvals (unaffected) but no
|
||||
// longer its questions, matching the reminders precedent.
|
||||
if is_self_query && let Some(questions) = local_questions().await {
|
||||
loose_ends.extend(questions);
|
||||
}
|
||||
annotate_retries(render_loose_ends(&loose_ends), retries)
|
||||
})
|
||||
.await
|
||||
|
|
@ -476,6 +507,15 @@ impl AgentServer {
|
|||
let (resp, retries) = self
|
||||
.dispatch(hive_core_agent_sock::Request::CancelLooseEnd { kind, id })
|
||||
.await;
|
||||
if resp.is_ok() && kind == hive_sh4re::CancelLooseEndKind::Question {
|
||||
// Best-effort — cancel is ownership-gated to the asker on
|
||||
// the c0re side, so a successful cancel here always means
|
||||
// *this* agent's own `asked` mirror row for `id`. Known gap
|
||||
// (documented on `Questions`): the target isn't notified,
|
||||
// so their `answering` row lingers until they call
|
||||
// `answer()` or it goes stale.
|
||||
let _ = dial_agent_socket(&hive_agent_sock::Request::ClearQuestion { id }).await;
|
||||
}
|
||||
annotate_retries(
|
||||
format_ack(
|
||||
resp,
|
||||
|
|
|
|||
|
|
@ -351,6 +351,17 @@ pub(super) async fn local_reminders() -> Option<Vec<hive_sh4re::LooseEnd>> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Query the harness's in-agent socket for this agent's mirrored questions
|
||||
/// (both roles — asked and answering). Same best-effort
|
||||
/// contract as [`local_reminders`]; c0re stays the actual `Ask`/`Answer`
|
||||
/// routing, this only mirrors the durable "still owed a reply" view.
|
||||
pub(super) async fn local_questions() -> Option<Vec<hive_sh4re::LooseEnd>> {
|
||||
match dial_agent_socket(&hive_agent_sock::Request::ListQuestions).await? {
|
||||
hive_agent_sock::Response::LooseEnds { loose_ends } => Some(loose_ends),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Mark one of this agent's local todos (loose-ends v2) done by id, via
|
||||
/// the harness's in-agent socket — reachable through `cancel_loose_end`
|
||||
/// kind `"todo"` so clearing a todo never has to shell out through a
|
||||
|
|
|
|||
Loading…
Reference in a new issue