fix(agent): recover PromptTooLong via archive+requeue; model TurnOutcome as Result

This commit is contained in:
müde 2026-07-05 21:42:13 +02:00
commit 2b5edef4d1
3 changed files with 76 additions and 43 deletions

View file

@ -108,7 +108,7 @@ fn log_system_event(bus: &Bus, from: &str, body: &str) {
}
/// Body string for the turn-failure notification we route to
/// `<parent>` on `TurnOutcome::Failed`. Reads the hive-qualified
/// `<parent>` on `TurnError::Failed`. Reads the hive-qualified
/// identity so the receiver sees `agent@hive` rather than relying on
/// the caller threading a `label` through every turn-handling layer.
/// Falls back to `<unknown>` when `HIVE_LABEL` is missing so a
@ -601,13 +601,10 @@ async fn handle_turn<S: Surface>(
let outcome = turn::drive_turn(&prompt, files, bus, session).await;
turn::emit_turn_end(bus, &outcome);
bus.set_state(TurnState::Idle);
if matches!(
outcome,
turn::TurnOutcome::Ok | turn::TurnOutcome::Compacted
) {
if outcome.is_ok() {
S::ack_turn(socket).await;
}
if matches!(outcome, turn::TurnOutcome::RateLimited) {
if matches!(outcome, Err(turn::TurnError::RateLimited)) {
let secs = turn::rate_limit_sleep_secs();
bus.emit_status("rate_limited");
bus.emit(LiveEvent::Note {
@ -618,7 +615,7 @@ async fn handle_turn<S: Surface>(
S::requeue_inflight(socket).await;
bus.emit_status("online");
}
if matches!(outcome, turn::TurnOutcome::AuthFailed) {
if matches!(outcome, Err(turn::TurnError::AuthFailed)) {
bus.emit_status("needs_login_idle");
bus.emit(LiveEvent::Note {
text: "API 401 — waiting for re-login via web UI".into(),
@ -626,7 +623,15 @@ async fn handle_turn<S: Surface>(
tracing::warn!("auth-failed; parking until re-login");
S::requeue_inflight(socket).await;
}
if let turn::TurnOutcome::Failed(e) = &outcome {
if matches!(outcome, Err(turn::TurnError::PromptTooLong)) {
// `drive_turn` already archived the session; requeue the message so it
// redelivers into the fresh session (which fits — the wake prompt is
// tiny, the overflow was the now-cleared context). No status park: the
// agent is healthy, it just needs one more delivery.
tracing::warn!("prompt-too-long; session archived, requeueing message for a fresh turn");
S::requeue_inflight(socket).await;
}
if let Err(turn::TurnError::Failed(e)) = &outcome {
S::send_to_parent(socket, format_turn_failure(e)).await;
}
if let Some(stats) = stats {
@ -659,7 +664,7 @@ async fn handle_turn<S: Surface>(
tracing::info!(%pending, "pending messages after turn; fetching next");
}
TurnControl {
auth_failed: matches!(outcome, turn::TurnOutcome::AuthFailed),
auth_failed: matches!(outcome, Err(turn::TurnError::AuthFailed)),
continue_requested: consume_continue_sentinel(),
pending,
}