fix(context left after review): review footer context after /review (#5610)

## Summary
- show live review token usage while `/review` runs and restore the main
session indicator afterward
  - add regression coverage for the footer behavior

## Testing
  - just fmt
  - cargo test -p codex-tui

Fixes #5604

---------

Signed-off-by: Fahad <fahad@2doapp.com>
This commit is contained in:
Beehive Innovations
2025-11-20 02:50:07 +04:00
committed by GitHub
parent 2fde03b4a0
commit 692989c277
3 changed files with 138 additions and 8 deletions

View File

@@ -38,6 +38,9 @@ use codex_core::protocol::ReviewRequest;
use codex_core::protocol::StreamErrorEvent;
use codex_core::protocol::TaskCompleteEvent;
use codex_core::protocol::TaskStartedEvent;
use codex_core::protocol::TokenCountEvent;
use codex_core::protocol::TokenUsage;
use codex_core::protocol::TokenUsageInfo;
use codex_core::protocol::UndoCompletedEvent;
use codex_core::protocol::UndoStartedEvent;
use codex_core::protocol::ViewImageToolCallEvent;
@@ -215,6 +218,81 @@ fn exited_review_mode_emits_results_and_finishes() {
assert!(!chat.is_review_mode);
}
/// Exiting review restores the pre-review context window indicator.
#[test]
fn review_restores_context_window_indicator() {
let (mut chat, mut rx, _ops) = make_chatwidget_manual();
let context_window = 13_000;
let pre_review_tokens = 12_700; // ~30% remaining after subtracting baseline.
let review_tokens = 12_030; // ~97% remaining after subtracting baseline.
chat.handle_codex_event(Event {
id: "token-before".into(),
msg: EventMsg::TokenCount(TokenCountEvent {
info: Some(make_token_info(pre_review_tokens, context_window)),
rate_limits: None,
}),
});
assert_eq!(chat.bottom_pane.context_window_percent(), Some(30));
chat.handle_codex_event(Event {
id: "review-start".into(),
msg: EventMsg::EnteredReviewMode(ReviewRequest {
prompt: "Review the latest changes".to_string(),
user_facing_hint: "feature branch".to_string(),
append_to_original_thread: true,
}),
});
chat.handle_codex_event(Event {
id: "token-review".into(),
msg: EventMsg::TokenCount(TokenCountEvent {
info: Some(make_token_info(review_tokens, context_window)),
rate_limits: None,
}),
});
assert_eq!(chat.bottom_pane.context_window_percent(), Some(97));
chat.handle_codex_event(Event {
id: "review-end".into(),
msg: EventMsg::ExitedReviewMode(ExitedReviewModeEvent {
review_output: None,
}),
});
let _ = drain_insert_history(&mut rx);
assert_eq!(chat.bottom_pane.context_window_percent(), Some(30));
assert!(!chat.is_review_mode);
}
/// Receiving a TokenCount event without usage clears the context indicator.
#[test]
fn token_count_none_resets_context_indicator() {
let (mut chat, _rx, _ops) = make_chatwidget_manual();
let context_window = 13_000;
let pre_compact_tokens = 12_700;
chat.handle_codex_event(Event {
id: "token-before".into(),
msg: EventMsg::TokenCount(TokenCountEvent {
info: Some(make_token_info(pre_compact_tokens, context_window)),
rate_limits: None,
}),
});
assert_eq!(chat.bottom_pane.context_window_percent(), Some(30));
chat.handle_codex_event(Event {
id: "token-cleared".into(),
msg: EventMsg::TokenCount(TokenCountEvent {
info: None,
rate_limits: None,
}),
});
assert_eq!(chat.bottom_pane.context_window_percent(), None);
}
#[cfg_attr(
target_os = "macos",
ignore = "system configuration APIs are blocked under macOS seatbelt"
@@ -292,6 +370,7 @@ fn make_chatwidget_manual() -> (
suppress_session_configured_redraw: false,
pending_notification: None,
is_review_mode: false,
pre_review_token_info: None,
needs_final_message_separator: false,
last_rendered_width: std::cell::Cell::new(None),
feedback: codex_feedback::CodexFeedback::new(),
@@ -338,6 +417,21 @@ fn lines_to_single_string(lines: &[ratatui::text::Line<'static>]) -> String {
s
}
fn make_token_info(total_tokens: i64, context_window: i64) -> TokenUsageInfo {
fn usage(total_tokens: i64) -> TokenUsage {
TokenUsage {
total_tokens,
..TokenUsage::default()
}
}
TokenUsageInfo {
total_token_usage: usage(total_tokens),
last_token_usage: usage(total_tokens),
model_context_window: Some(context_window),
}
}
#[test]
fn rate_limit_warnings_emit_thresholds() {
let mut state = RateLimitWarningState::default();