mirror of
https://github.com/codewhale-hq/Codewhale.git
synced 2026-11-08 08:04:25 -10:00
Merge pull request #6452 from Hmbown/fix/turn-stop-reason-in-transcript
fix(tui): keep an Engine-stopped turn's reason in the transcript
This commit is contained in:
2 files changed
+67
-17
No files matched your search
@@ -1307,6 +1307,31 @@ async fn dispatch_launch_composer_submit(
|
||||
/// from slash-menu selection onward is the shared `submit_decided_composer_input`
|
||||
/// tail the keyboard Enter arm also uses, so the two surfaces cannot drift.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
/// Show why a turn ended without success. The composer status line always
|
||||
/// names it; a turn the Engine stopped itself (wall-clock or step budget, no
|
||||
/// progress, an incomplete response) posts no error event, so its reason also
|
||||
/// goes into the transcript — a footer line alone is replaced by the next
|
||||
/// notice, and the session then reads as hung. When an error event already
|
||||
/// put the message in the transcript, nothing is repeated.
|
||||
pub(super) fn present_turn_failure(
|
||||
app: &mut App,
|
||||
status: crate::core::events::TurnOutcomeStatus,
|
||||
error: Option<&str>,
|
||||
) {
|
||||
let Some(error) = error else { return };
|
||||
if app.turn_error_posted {
|
||||
return;
|
||||
}
|
||||
let notice = format!("{}: {error}", app.tr(MessageId::NotificationTurnFailed));
|
||||
if matches!(status, crate::core::events::TurnOutcomeStatus::Failed) {
|
||||
app.add_message(HistoryCell::Error {
|
||||
message: notice.clone(),
|
||||
severity: crate::error_taxonomy::ErrorSeverity::Warning,
|
||||
});
|
||||
}
|
||||
app.set_sticky_status(notice, StatusToastLevel::Error, None);
|
||||
}
|
||||
|
||||
async fn dispatch_session_composer_submit(
|
||||
terminal: &mut AppTerminal,
|
||||
app: &mut App,
|
||||
@@ -2754,23 +2779,7 @@ pub(crate) async fn run_event_loop(
|
||||
recorded_at: Instant::now(),
|
||||
});
|
||||
app.retire_action_notices(None);
|
||||
if let Some(error) = error.as_deref() {
|
||||
// Only show "Turn failed:" in the composer status
|
||||
// area when an EngineEvent::Error has NOT already
|
||||
// posted the same message into the transcript.
|
||||
// Otherwise the error appears twice: once in a
|
||||
// HistoryCell and again as a redundant status line.
|
||||
if !app.turn_error_posted {
|
||||
app.set_sticky_status(
|
||||
format!(
|
||||
"{}: {error}",
|
||||
app.tr(MessageId::NotificationTurnFailed)
|
||||
),
|
||||
StatusToastLevel::Error,
|
||||
None,
|
||||
);
|
||||
}
|
||||
}
|
||||
present_turn_failure(app, status, error.as_deref());
|
||||
|
||||
// Update session cost, and record what the total does
|
||||
// *not* cover so `/cost` can stay honest about it.
|
||||
|
||||
@@ -16902,6 +16902,47 @@ fn local_cancel_marks_late_stream_events_for_suppression() {
|
||||
));
|
||||
}
|
||||
|
||||
/// A turn the Engine stopped itself (here its per-turn wall-clock budget)
|
||||
/// posts no error event. Its reason must stay in the transcript, not only the
|
||||
/// footer, or a later notice replaces it and the session looks hung.
|
||||
#[test]
|
||||
fn an_engine_stopped_turn_keeps_its_reason_in_the_transcript() {
|
||||
let mut app = create_test_app();
|
||||
let reason = "Per-turn wall-clock budget exhausted after 3600s (limit: 3600s). Send another message to continue.";
|
||||
super::event_loop::present_turn_failure(
|
||||
&mut app,
|
||||
crate::core::events::TurnOutcomeStatus::Failed,
|
||||
Some(reason),
|
||||
);
|
||||
assert!(
|
||||
app.history.iter().any(|cell| matches!(
|
||||
cell,
|
||||
HistoryCell::Error { message, .. } if message.contains(reason)
|
||||
)),
|
||||
"the stop reason is in the transcript"
|
||||
);
|
||||
assert!(
|
||||
app.sticky_status
|
||||
.as_ref()
|
||||
.is_some_and(|toast| toast.text.contains(reason))
|
||||
);
|
||||
|
||||
// An error event already posted it: never repeat it in the transcript.
|
||||
let mut posted = create_test_app();
|
||||
posted.turn_error_posted = true;
|
||||
super::event_loop::present_turn_failure(
|
||||
&mut posted,
|
||||
crate::core::events::TurnOutcomeStatus::Failed,
|
||||
Some(reason),
|
||||
);
|
||||
assert!(
|
||||
!posted
|
||||
.history
|
||||
.iter()
|
||||
.any(|cell| matches!(cell, HistoryCell::Error { .. }))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn turn_started_route_is_captured_before_cancel_suppression() {
|
||||
let mut app = create_test_app();
|
||||
|
||||
Reference in new issue
Block a user