agent ui: open-threads section (questions + approvals pending)
new /api/open-threads endpoint on hive-ag3nt proxies the agent's own GetOpenThreads RPC (manager flavour proxies the hive-wide ManagerRequest::GetOpenThreads). same data the mcp__hyperhive__get_open_threads tool sees from inside claude. frontend renders a collapsible <details> section above the terminal, listing each pending row (approval / question) with asker → target, age, and free-form body. auto-expands on the first appearance of any open thread; sticky after that. refreshed on cold load + after every turn_end (turns are when threads land or resolve).
This commit is contained in:
parent
087a5366fb
commit
378e8bf9df
3 changed files with 124 additions and 0 deletions
|
|
@ -105,6 +105,7 @@ pub async fn serve(
|
|||
.route("/api/compact", post(post_compact))
|
||||
.route("/api/model", post(post_set_model))
|
||||
.route("/api/new-session", post(post_new_session))
|
||||
.route("/api/open-threads", get(api_open_threads))
|
||||
.with_state(state);
|
||||
let addr = SocketAddr::from(([0, 0, 0, 0], port));
|
||||
let listener = bind_with_retry(addr, "web UI").await?;
|
||||
|
|
@ -231,6 +232,49 @@ struct SessionView {
|
|||
exit_note: Option<String>,
|
||||
}
|
||||
|
||||
/// Proxy this agent's open-threads via the per-agent socket. The
|
||||
/// web UI surfaces the result as a collapsible section in the page
|
||||
/// so the operator can see at a glance what's pending against the
|
||||
/// agent (questions asked by it, peer questions targeting it,
|
||||
/// approvals for the manager). Same data the
|
||||
/// `mcp__hyperhive__get_open_threads` tool sees from inside the
|
||||
/// container.
|
||||
async fn api_open_threads(State(state): State<AppState>) -> Response {
|
||||
let threads: Vec<hive_sh4re::OpenThread> = match state.flavor() {
|
||||
Flavor::Agent => {
|
||||
match client::request::<_, hive_sh4re::AgentResponse>(
|
||||
&state.socket,
|
||||
&hive_sh4re::AgentRequest::GetOpenThreads,
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(hive_sh4re::AgentResponse::OpenThreads { threads }) => threads,
|
||||
Ok(hive_sh4re::AgentResponse::Err { message }) => {
|
||||
return error_response(&format!("get_open_threads: {message}"));
|
||||
}
|
||||
Ok(other) => return error_response(&format!("unexpected response: {other:?}")),
|
||||
Err(e) => return error_response(&format!("transport: {e:#}")),
|
||||
}
|
||||
}
|
||||
Flavor::Manager => {
|
||||
match client::request::<_, hive_sh4re::ManagerResponse>(
|
||||
&state.socket,
|
||||
&hive_sh4re::ManagerRequest::GetOpenThreads,
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(hive_sh4re::ManagerResponse::OpenThreads { threads }) => threads,
|
||||
Ok(hive_sh4re::ManagerResponse::Err { message }) => {
|
||||
return error_response(&format!("get_open_threads: {message}"));
|
||||
}
|
||||
Ok(other) => return error_response(&format!("unexpected response: {other:?}")),
|
||||
Err(e) => return error_response(&format!("transport: {e:#}")),
|
||||
}
|
||||
}
|
||||
};
|
||||
axum::Json(serde_json::json!({ "threads": threads })).into_response()
|
||||
}
|
||||
|
||||
async fn api_state(State(state): State<AppState>) -> axum::Json<StateSnapshot> {
|
||||
// Capture seq *before* any reads so the dedupe contract is
|
||||
// "events with seq > snapshot.seq are post-snapshot, never missed."
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue