agent ui: event-driven status / model / token_usage / turn_state
new LiveEvent variants on the per-agent bus —
status_changed / model_changed / token_usage_changed /
turn_state_changed — replace the per-agent web UI's
/api/state polling for the badge row.
emit sites:
- Bus::set_model → model_changed
- Bus::record_usage → token_usage_changed
- Bus::set_state → turn_state_changed
- turn::wait_for_login → status_changed("online") on creds detect
- post_login_start / post_login_cancel → status_changed("needs_login_*")
per-agent endpoints (post_set_model / post_compact / post_new_session
/ post_cancel_turn / post_login_*) now all return 200; client
drops the post-submit refetch except on login transitions, which
still need /api/state to render the OAuth form + session stream.
client adds dispatch on the four new event kinds, threads
`currentLabel` through so the composer re-enables on a live
status flip, and no longer fires refreshState() from turn_end or
postModel — the events carry the same signal faster.
closes the per-agent half of the dashboard event-channel
refactor; TODO entry dropped.
This commit is contained in:
parent
b444dac6e8
commit
39d8359c10
7 changed files with 120 additions and 22 deletions
|
|
@ -16,7 +16,7 @@ use axum::{
|
|||
extract::State,
|
||||
http::StatusCode,
|
||||
response::{
|
||||
IntoResponse, Redirect, Response,
|
||||
IntoResponse, Response,
|
||||
sse::{Event, KeepAlive, Sse},
|
||||
},
|
||||
routing::{get, post},
|
||||
|
|
@ -388,13 +388,19 @@ async fn post_login_start(State(state): State<AppState>) -> Response {
|
|||
{
|
||||
let guard = state.session.lock().unwrap();
|
||||
if guard.is_some() {
|
||||
return Redirect::to("/").into_response();
|
||||
return (axum::http::StatusCode::OK, "ok").into_response();
|
||||
}
|
||||
}
|
||||
match LoginSession::start() {
|
||||
Ok(session) => {
|
||||
*state.session.lock().unwrap() = Some(Arc::new(session));
|
||||
Redirect::to("/").into_response()
|
||||
// Flip status from needs_login_idle → needs_login_in_progress
|
||||
// so the web UI's badge + polling kick in (polling is still
|
||||
// the right tool for the streaming session output during
|
||||
// the login flow itself; events drop the poll for
|
||||
// *everything else*).
|
||||
state.bus.emit_status("needs_login_in_progress");
|
||||
(axum::http::StatusCode::OK, "ok").into_response()
|
||||
}
|
||||
Err(e) => error_response(&format!("login start failed: {e:#}")),
|
||||
}
|
||||
|
|
@ -413,7 +419,7 @@ async fn post_login_code(State(state): State<AppState>, Form(form): Form<CodeFor
|
|||
if let Err(e) = session.submit_code(&form.code).await {
|
||||
return error_response(&format!("submit code failed: {e:#}"));
|
||||
}
|
||||
Redirect::to("/").into_response()
|
||||
(axum::http::StatusCode::OK, "ok").into_response()
|
||||
}
|
||||
|
||||
async fn post_login_cancel(State(state): State<AppState>) -> Response {
|
||||
|
|
@ -422,7 +428,9 @@ async fn post_login_cancel(State(state): State<AppState>) -> Response {
|
|||
session.close_stdin().await;
|
||||
session.kill();
|
||||
}
|
||||
Redirect::to("/").into_response()
|
||||
// Back to needs_login_idle (LoginState unchanged, session gone).
|
||||
state.bus.emit_status("needs_login_idle");
|
||||
(axum::http::StatusCode::OK, "ok").into_response()
|
||||
}
|
||||
|
||||
/// Operator-initiated session compaction. Spawns `turn::compact_session`
|
||||
|
|
@ -452,7 +460,7 @@ async fn post_set_model(State(state): State<AppState>, Form(form): Form<ModelFor
|
|||
text: format!("operator: /model — claude model set to '{name}' for future turns"),
|
||||
});
|
||||
tracing::info!(%name, "operator set model");
|
||||
Redirect::to("/").into_response()
|
||||
(axum::http::StatusCode::OK, "ok").into_response()
|
||||
}
|
||||
|
||||
async fn post_compact(State(state): State<AppState>) -> Response {
|
||||
|
|
@ -483,7 +491,7 @@ async fn post_compact(State(state): State<AppState>) -> Response {
|
|||
});
|
||||
}
|
||||
});
|
||||
Redirect::to("/").into_response()
|
||||
(axum::http::StatusCode::OK, "ok").into_response()
|
||||
}
|
||||
|
||||
/// Cancel the in-flight claude turn. Coarse-grained: shells out
|
||||
|
|
@ -504,7 +512,7 @@ async fn post_new_session(State(state): State<AppState>) -> Response {
|
|||
state.bus.emit(crate::events::LiveEvent::Note {
|
||||
text: "operator: new session armed — next turn runs without --continue".into(),
|
||||
});
|
||||
Redirect::to("/").into_response()
|
||||
(axum::http::StatusCode::OK, "ok").into_response()
|
||||
}
|
||||
|
||||
async fn post_cancel_turn(State(state): State<AppState>) -> Response {
|
||||
|
|
@ -525,7 +533,7 @@ async fn post_cancel_turn(State(state): State<AppState>) -> Response {
|
|||
Err(e) => format!("operator: /cancel — pkill failed: {e}"),
|
||||
};
|
||||
state.bus.emit(crate::events::LiveEvent::Note { text: note });
|
||||
Redirect::to("/").into_response()
|
||||
(axum::http::StatusCode::OK, "ok").into_response()
|
||||
}
|
||||
|
||||
fn error_response(message: &str) -> Response {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue