turn: propagate auth_failed out of compact_session

This commit is contained in:
damocles 2026-05-25 21:59:11 +02:00
commit c8120f9edc

View file

@ -306,9 +306,13 @@ pub async fn drive_turn(prompt: &str, files: &TurnFiles, bus: &Bus) -> TurnOutco
maybe_auto_reset(bus); maybe_auto_reset(bus);
let outcome = match run_turn(prompt, files, bus).await { let outcome = match run_turn(prompt, files, bus).await {
TurnOutcome::PromptTooLong => { TurnOutcome::PromptTooLong => {
if let Err(e) = compact_session(files, bus).await { match compact_session(files, bus).await {
tracing::warn!(error = %format!("{e:#}"), "compact failed"); Ok(true) => return TurnOutcome::AuthFailed,
return TurnOutcome::Failed(e); Ok(false) => {}
Err(e) => {
tracing::warn!(error = %format!("{e:#}"), "compact failed");
return TurnOutcome::Failed(e);
}
} }
run_turn(prompt, files, bus).await run_turn(prompt, files, bus).await
} }
@ -370,11 +374,17 @@ async fn maybe_checkpoint_and_compact(files: &TurnFiles, bus: &Bus) -> bool {
text: format!("checkpoint turn failed ({e:#}) — compacting anyway"), text: format!("checkpoint turn failed ({e:#}) — compacting anyway"),
}), }),
} }
if let Err(e) = compact_session(files, bus).await { match compact_session(files, bus).await {
tracing::warn!(error = %format!("{e:#}"), "post-checkpoint compact failed"); Ok(true) => bus.emit(LiveEvent::Note {
bus.emit(LiveEvent::Note { text: "/compact hit 401 — next turn will trigger the re-login flow".into(),
text: format!("/compact after checkpoint failed: {e:#}"), }),
}); Ok(false) => {}
Err(e) => {
tracing::warn!(error = %format!("{e:#}"), "post-checkpoint compact failed");
bus.emit(LiveEvent::Note {
text: format!("/compact after checkpoint failed: {e:#}"),
});
}
} }
true true
} }
@ -506,19 +516,31 @@ pub async fn run_turn(prompt: &str, files: &TurnFiles, bus: &Bus) -> TurnOutcome
/// compact state matches a normal turn's. Only the prompt over stdin /// compact state matches a normal turn's. Only the prompt over stdin
/// differs (`/compact` vs the wake-up payload). /// differs (`/compact` vs the wake-up payload).
/// ///
/// Returns `true` if claude reported a 401 mid-compact — the caller
/// should bail into the re-login flow instead of treating compaction
/// as succeeded. (`prompt_too_long` / `rate_limited` are unlikely on
/// a slash command and are folded into the boolean by being ignored
/// — the next real turn surfaces them through the normal path.)
///
/// # Errors /// # Errors
/// ///
/// Returns an error if the `claude --print /compact` invocation fails /// Returns an error if the `claude --print /compact` invocation fails
/// (non-zero exit or I/O error). /// (non-zero exit or I/O error).
pub async fn compact_session(files: &TurnFiles, bus: &Bus) -> Result<()> { pub async fn compact_session(files: &TurnFiles, bus: &Bus) -> Result<bool> {
bus.emit(LiveEvent::Note { bus.emit(LiveEvent::Note {
text: "context overflow — running /compact on the persistent session".into(), text: "context overflow — running /compact on the persistent session".into(),
}); });
let (_, _, _) = run_claude("/compact", files, bus).await?; let (_, _, auth_failed) = run_claude("/compact", files, bus).await?;
bus.emit(LiveEvent::Note { if auth_failed {
text: "/compact done".into(), bus.emit(LiveEvent::Note {
}); text: "/compact hit 401 — bubbling up for re-login flow".into(),
Ok(()) });
} else {
bus.emit(LiveEvent::Note {
text: "/compact done".into(),
});
}
Ok(auth_failed)
} }
#[allow(clippy::too_many_lines)] #[allow(clippy::too_many_lines)]