mirror of
https://github.com/hanzoai/dev.git
synced 2026-10-25 03:32:32 +00:00
merge code-branch-20250929-114830 via /merge
This commit is contained in:
@@ -38,8 +38,8 @@ pub(crate) struct AutoCoordinatorViewModel {
|
||||
pub status_lines: Vec<String>,
|
||||
pub prompt: Option<String>,
|
||||
pub awaiting_submission: bool,
|
||||
#[allow(dead_code)]
|
||||
pub waiting_for_response: bool,
|
||||
pub coordinator_waiting: bool,
|
||||
pub countdown: Option<CountdownState>,
|
||||
pub button: Option<AutoCoordinatorButton>,
|
||||
pub manual_hint: Option<String>,
|
||||
@@ -173,17 +173,33 @@ impl AutoCoordinatorView {
|
||||
}
|
||||
|
||||
fn status_lines(&self) -> Vec<Line<'static>> {
|
||||
let frame_idx = DRIVE_SPINNER_TICK.fetch_add(1, Ordering::Relaxed);
|
||||
let spinner_symbol = DRIVE_SPINNER_FRAMES[frame_idx % DRIVE_SPINNER_FRAMES.len()].to_string();
|
||||
let spinner_symbol = if self.model.coordinator_waiting {
|
||||
let frame_idx = DRIVE_SPINNER_TICK.fetch_add(1, Ordering::Relaxed);
|
||||
Some(DRIVE_SPINNER_FRAMES[frame_idx % DRIVE_SPINNER_FRAMES.len()].to_string())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let mut lines: Vec<Line<'static>> = Vec::new();
|
||||
for (index, (text, style)) in self.derived_status_entries().into_iter().enumerate() {
|
||||
if index == 0 {
|
||||
lines.push(Line::from(vec![
|
||||
Span::styled(spinner_symbol.clone(), Style::default().fg(colors::spinner()).add_modifier(Modifier::BOLD)),
|
||||
Span::raw(" "),
|
||||
Span::styled(text, style),
|
||||
]));
|
||||
if let Some(symbol) = spinner_symbol.as_ref() {
|
||||
lines.push(Line::from(vec![
|
||||
Span::styled(
|
||||
symbol.clone(),
|
||||
Style::default()
|
||||
.fg(colors::spinner())
|
||||
.add_modifier(Modifier::BOLD),
|
||||
),
|
||||
Span::raw(" "),
|
||||
Span::styled(text, style),
|
||||
]));
|
||||
} else {
|
||||
lines.push(Line::from(vec![
|
||||
Span::raw(" "),
|
||||
Span::styled(text, style),
|
||||
]));
|
||||
}
|
||||
} else {
|
||||
lines.push(Line::from(Span::styled(text, style)));
|
||||
}
|
||||
@@ -279,7 +295,7 @@ impl AutoCoordinatorView {
|
||||
};
|
||||
|
||||
let awaiting = self.model.awaiting_submission;
|
||||
let spinner_active = !awaiting && self.model.waiting_for_response;
|
||||
let spinner_active = !awaiting && self.model.coordinator_waiting;
|
||||
|
||||
let mut total = 0;
|
||||
|
||||
@@ -358,7 +374,7 @@ impl AutoCoordinatorView {
|
||||
return;
|
||||
}
|
||||
|
||||
let spinner_active = !self.model.awaiting_submission && self.model.waiting_for_response;
|
||||
let spinner_active = !self.model.awaiting_submission && self.model.coordinator_waiting;
|
||||
|
||||
let mut lines: Vec<Line<'static>> = Vec::new();
|
||||
|
||||
|
||||
@@ -473,6 +473,7 @@ struct AutoCoordinatorUiState {
|
||||
last_decision_display: Option<String>,
|
||||
observer_telemetry: Option<AutoObserverTelemetry>,
|
||||
observer_status: AutoObserverStatus,
|
||||
coordinator_waiting: bool,
|
||||
}
|
||||
|
||||
impl AutoCoordinatorUiState {
|
||||
@@ -9771,6 +9772,7 @@ impl ChatWidget<'_> {
|
||||
self.auto_state.last_broadcast_thought = None;
|
||||
self.auto_state.seconds_remaining = AUTO_COUNTDOWN_SECONDS;
|
||||
self.auto_state.waiting_for_response = true;
|
||||
self.auto_state.coordinator_waiting = true;
|
||||
self.update_header_border_activation();
|
||||
self.auto_rebuild_live_ring();
|
||||
self.push_background_tail(format!("Auto Drive started: {goal_text}"));
|
||||
@@ -9797,11 +9799,10 @@ impl ChatWidget<'_> {
|
||||
self.auto_stop(Some("Coordinator stopped unexpectedly.".to_string()));
|
||||
} else {
|
||||
self.auto_state.waiting_for_response = true;
|
||||
self.auto_state.coordinator_waiting = true;
|
||||
self.auto_state.current_thoughts = None;
|
||||
self.auto_state.last_broadcast_thought = None;
|
||||
self.auto_state.current_display_line = None;
|
||||
self.auto_state.current_summary_index = None;
|
||||
self.auto_state.last_decision_display = None;
|
||||
self.auto_state.placeholder_phrase =
|
||||
Some(auto_drive_strings::next_auto_drive_phrase().to_string());
|
||||
self.auto_state.thinking_prefix_stripped = false;
|
||||
@@ -9821,6 +9822,7 @@ impl ChatWidget<'_> {
|
||||
return;
|
||||
}
|
||||
|
||||
self.auto_state.coordinator_waiting = false;
|
||||
self.auto_on_reasoning_final(&thoughts);
|
||||
self.auto_state.last_decision_display = self.auto_state.current_display_line.clone();
|
||||
self.auto_state.paused_for_manual_edit = false;
|
||||
@@ -9983,6 +9985,7 @@ impl ChatWidget<'_> {
|
||||
|
||||
self.auto_state.awaiting_submission = false;
|
||||
self.auto_state.waiting_for_response = true;
|
||||
self.auto_state.coordinator_waiting = false;
|
||||
self.auto_state.paused_for_manual_edit = false;
|
||||
self.auto_state.resume_after_manual_submit = false;
|
||||
self.auto_state.seconds_remaining = 0;
|
||||
@@ -10064,16 +10067,15 @@ impl ChatWidget<'_> {
|
||||
return;
|
||||
}
|
||||
self.auto_state.waiting_for_response = false;
|
||||
self.auto_state.coordinator_waiting = false;
|
||||
self.auto_state.awaiting_submission = false;
|
||||
self.auto_state.paused_for_manual_edit = false;
|
||||
self.auto_state.resume_after_manual_submit = false;
|
||||
self.auto_state.seconds_remaining = AUTO_COUNTDOWN_SECONDS;
|
||||
self.auto_state.current_thoughts = Some(String::new());
|
||||
self.auto_state.current_display_line = None;
|
||||
self.auto_state.current_summary_index = None;
|
||||
self.auto_state.placeholder_phrase = None;
|
||||
self.auto_state.thinking_prefix_stripped = false;
|
||||
self.auto_state.last_decision_display = None;
|
||||
self.update_header_border_activation();
|
||||
self.auto_rebuild_live_ring();
|
||||
self.request_redraw();
|
||||
@@ -10183,6 +10185,7 @@ impl ChatWidget<'_> {
|
||||
prompt,
|
||||
awaiting_submission: self.auto_state.awaiting_submission,
|
||||
waiting_for_response: self.auto_state.waiting_for_response,
|
||||
coordinator_waiting: self.auto_state.coordinator_waiting,
|
||||
countdown,
|
||||
button,
|
||||
manual_hint,
|
||||
@@ -10267,10 +10270,7 @@ impl ChatWidget<'_> {
|
||||
if self.auto_state.current_summary_index != Some(idx) {
|
||||
self.auto_state.current_summary_index = Some(idx);
|
||||
self.auto_state.current_thoughts = Some(String::new());
|
||||
self.auto_state.current_display_line = None;
|
||||
self.auto_state.last_broadcast_thought = None;
|
||||
self.auto_state.thinking_prefix_stripped = false;
|
||||
self.auto_state.last_decision_display = None;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10288,7 +10288,7 @@ impl ChatWidget<'_> {
|
||||
self.auto_state.thinking_prefix_stripped = true;
|
||||
}
|
||||
|
||||
let payload = {
|
||||
{
|
||||
let entry = self
|
||||
.auto_state
|
||||
.current_thoughts
|
||||
@@ -10299,15 +10299,6 @@ impl ChatWidget<'_> {
|
||||
}
|
||||
|
||||
entry.push_str(&cleaned_delta);
|
||||
entry.clone()
|
||||
};
|
||||
|
||||
self.auto_update_display_title();
|
||||
self.auto_broadcast_thoughts(&payload);
|
||||
|
||||
if self.auto_state.waiting_for_response {
|
||||
self.auto_rebuild_live_ring();
|
||||
self.request_redraw();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -64,6 +64,14 @@ impl AutoCoordinatorHandle {
|
||||
pub fn cancel(&self) {
|
||||
self.cancel_token.cancel();
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(super) fn for_tests(tx: Sender<AutoCoordinatorCommand>) -> Self {
|
||||
Self {
|
||||
tx,
|
||||
cancel_token: CancellationToken::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
use super::*;
|
||||
use crate::app_event::{
|
||||
AppEvent,
|
||||
AutoCoordinatorStatus,
|
||||
AutoObserverStatus,
|
||||
AutoObserverTelemetry,
|
||||
BackgroundPlacement,
|
||||
@@ -10,6 +11,7 @@ use crate::app_event::{
|
||||
use crate::app_event_sender::AppEventSender;
|
||||
use crate::history::state::{ExecStatus, ExploreEntryStatus, HistoryId, HistoryRecord, HistoryState};
|
||||
use crate::slash_command::SlashCommand;
|
||||
use super::auto_coordinator::AutoCoordinatorHandle;
|
||||
use codex_core::config::Config;
|
||||
use codex_core::config::ConfigOverrides;
|
||||
use codex_core::config::ConfigToml;
|
||||
@@ -1000,6 +1002,61 @@ fn agents_terminal_esc_closes_from_sidebar() {
|
||||
assert!(!chat.agents_terminal.active, "Esc should close from sidebar focus");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn auto_thinking_keeps_previous_display_until_decision() {
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual();
|
||||
chat.auto_state.active = true;
|
||||
chat.auto_state.waiting_for_response = true;
|
||||
chat.auto_state.coordinator_waiting = true;
|
||||
chat.auto_state.current_display_line = Some("Prev thought".to_string());
|
||||
chat.auto_state.current_thoughts = Some("Prev thought".to_string());
|
||||
|
||||
chat.auto_handle_thinking("**Next:** run lint".to_string(), Some(3));
|
||||
assert_eq!(chat.auto_state.current_summary_index, Some(3));
|
||||
assert_eq!(
|
||||
chat.auto_state.current_display_line.as_deref(),
|
||||
Some("Prev thought")
|
||||
);
|
||||
|
||||
chat.auto_handle_thinking("extra detail".to_string(), None);
|
||||
assert_eq!(
|
||||
chat.auto_state.current_display_line.as_deref(),
|
||||
Some("Prev thought")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn auto_decision_persists_thought_through_cli_cycle() {
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual();
|
||||
chat.auto_state.active = true;
|
||||
chat.auto_state.waiting_for_response = true;
|
||||
chat.auto_state.coordinator_waiting = true;
|
||||
|
||||
chat.auto_handle_decision(
|
||||
AutoCoordinatorStatus::Continue,
|
||||
"**Plan:** run tests".to_string(),
|
||||
Some("run tests".to_string()),
|
||||
);
|
||||
|
||||
assert_eq!(chat.auto_state.current_display_line.as_deref(), Some("Plan:"));
|
||||
assert!(!chat.auto_state.coordinator_waiting, "spinner should stop");
|
||||
assert!(!chat.auto_state.waiting_for_response, "coordinator finished");
|
||||
|
||||
chat.auto_submit_prompt();
|
||||
assert!(chat.auto_state.waiting_for_response, "waiting on CLI");
|
||||
assert!(!chat.auto_state.coordinator_waiting, "spinner stays off during CLI");
|
||||
assert_eq!(chat.auto_state.current_display_line.as_deref(), Some("Plan:"));
|
||||
|
||||
chat.auto_state.placeholder_phrase = None;
|
||||
let (tx, _rx_handle) = channel();
|
||||
chat.auto_handle = Some(AutoCoordinatorHandle::for_tests(tx));
|
||||
chat.auto_on_assistant_final();
|
||||
|
||||
assert!(chat.auto_state.waiting_for_response, "next JSON in flight");
|
||||
assert!(chat.auto_state.coordinator_waiting, "spinner resumes for JSON");
|
||||
assert_eq!(chat.auto_state.current_display_line.as_deref(), Some("Plan:"));
|
||||
}
|
||||
|
||||
fn pump_app_events(
|
||||
chat: &mut ChatWidget<'_>,
|
||||
rx: &std::sync::mpsc::Receiver<AppEvent>,
|
||||
|
||||
Reference in New Issue
Block a user