feat(tui/auto_drive): export rendered history for coordinator

This commit is contained in:
James Peter
2025-10-02 23:27:53 +10:00
parent 76cf0397da
commit ec0bbc4bf1
2 changed files with 94 additions and 35 deletions
+65 -35
View File
@@ -1264,6 +1264,12 @@ enum SystemPlacement {
PrePromptInCurrent, PrePromptInCurrent,
} }
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum AutoDriveRole {
User,
Assistant,
}
impl ChatWidget<'_> { impl ChatWidget<'_> {
const MAX_UNDO_CONVERSATION_MESSAGES: usize = 8; const MAX_UNDO_CONVERSATION_MESSAGES: usize = 8;
const MAX_UNDO_PREVIEW_CHARS: usize = 160; const MAX_UNDO_PREVIEW_CHARS: usize = 160;
@@ -3543,6 +3549,26 @@ impl ChatWidget<'_> {
rows.join("\n") rows.join("\n")
} }
fn auto_drive_role_for_kind(kind: HistoryCellType) -> Option<AutoDriveRole> {
use AutoDriveRole::{Assistant, User};
match kind {
HistoryCellType::User => Some(Assistant),
HistoryCellType::Assistant
| HistoryCellType::Reasoning
| HistoryCellType::Error
| HistoryCellType::Exec { .. }
| HistoryCellType::Tool { .. }
| HistoryCellType::Patch { .. }
| HistoryCellType::PlanUpdate
| HistoryCellType::BackgroundEvent
| HistoryCellType::Notice
| HistoryCellType::Diff
| HistoryCellType::Plain
| HistoryCellType::Image => Some(User),
HistoryCellType::AnimatedWelcome | HistoryCellType::Loading => None,
}
}
fn auto_drive_cell_text(cell: &dyn HistoryCell) -> Option<String> { fn auto_drive_cell_text(cell: &dyn HistoryCell) -> Option<String> {
let text = Self::auto_drive_lines_to_string(cell.display_lines()); let text = Self::auto_drive_lines_to_string(cell.display_lines());
if text.trim().is_empty() { if text.trim().is_empty() {
@@ -3658,31 +3684,12 @@ impl ChatWidget<'_> {
pub(crate) fn export_auto_drive_items(&self) -> Vec<codex_protocol::models::ResponseItem> { pub(crate) fn export_auto_drive_items(&self) -> Vec<codex_protocol::models::ResponseItem> {
let mut items = Vec::new(); let mut items = Vec::new();
for cell in &self.history_cells { for cell in &self.history_cells {
match cell.kind() { let Some(role) = Self::auto_drive_role_for_kind(cell.kind()) else {
HistoryCellType::User => { continue;
if let Some(text) = Self::auto_drive_cell_text(cell.as_ref()) { };
if let Some(item) = Self::auto_drive_make_assistant_message(text) {
items.push(item); let text = match cell.kind() {
} HistoryCellType::Reasoning => Self::auto_drive_cell_text(cell.as_ref()).map(|text| (text, true)),
}
}
HistoryCellType::Assistant => {
if let Some(text) = Self::auto_drive_cell_text(cell.as_ref()) {
if let Some(item) = Self::auto_drive_make_user_message(text) {
items.push(item);
}
}
}
HistoryCellType::Reasoning => {
if let Some(text) = Self::auto_drive_cell_text(cell.as_ref()) {
use codex_protocol::models::{ContentItem, ResponseItem};
items.push(ResponseItem::Message {
id: Some("auto-drive-reasoning".to_string()),
role: "user".to_string(),
content: vec![ContentItem::InputText { text }],
});
}
}
HistoryCellType::PlanUpdate => { HistoryCellType::PlanUpdate => {
if let Some(plan) = cell.as_any().downcast_ref::<PlanUpdateCell>() { if let Some(plan) = cell.as_any().downcast_ref::<PlanUpdateCell>() {
let state = plan.state(); let state = plan.state();
@@ -3711,22 +3718,45 @@ impl ChatWidget<'_> {
} }
} }
let text = lines.join("\n"); let text = lines.join("\n");
if let Some(item) = Self::auto_drive_make_user_message(text) { Some((text, false))
items.push(item); } else {
} Self::auto_drive_cell_text(cell.as_ref()).map(|text| (text, false))
} }
} }
HistoryCellType::Diff => { HistoryCellType::Diff => {
if let Some(diff_cell) = cell.as_any().downcast_ref::<DiffCell>() { if let Some(diff_cell) = cell.as_any().downcast_ref::<DiffCell>() {
if let Some(summary) = Self::auto_drive_diff_summary(diff_cell.record()) { Self::auto_drive_diff_summary(diff_cell.record()).map(|text| (text, false))
if let Some(item) = Self::auto_drive_make_user_message(summary) { } else {
items.push(item); Self::auto_drive_cell_text(cell.as_ref()).map(|text| (text, false))
}
}
} }
} }
_ => {} _ => Self::auto_drive_cell_text(cell.as_ref()).map(|text| (text, false)),
} };
let Some((text, is_reasoning)) = text else {
continue;
};
let item = if is_reasoning {
codex_protocol::models::ResponseItem::Message {
id: Some("auto-drive-reasoning".to_string()),
role: "user".to_string(),
content: vec![codex_protocol::models::ContentItem::InputText { text }],
}
} else {
match role {
AutoDriveRole::Assistant => match Self::auto_drive_make_assistant_message(text) {
Some(item) => item,
None => continue,
},
AutoDriveRole::User => match Self::auto_drive_make_user_message(text) {
Some(item) => item,
None => continue,
},
}
};
items.push(item);
} }
items items
} }
+29
View File
@@ -11,12 +11,17 @@ use crate::app_event::{
}; };
use crate::app_event_sender::AppEventSender; use crate::app_event_sender::AppEventSender;
use crate::history::state::{ use crate::history::state::{
ArgumentValue,
ExecStatus, ExecStatus,
ExploreEntryStatus, ExploreEntryStatus,
HistoryDomainRecord, HistoryDomainRecord,
HistoryId, HistoryId,
HistoryRecord, HistoryRecord,
HistoryState, HistoryState,
ToolArgument,
ToolCallState,
ToolResultPreview,
ToolStatus,
}; };
use crate::slash_command::SlashCommand; use crate::slash_command::SlashCommand;
use super::auto_coordinator::AutoCoordinatorHandle; use super::auto_coordinator::AutoCoordinatorHandle;
@@ -3663,6 +3668,24 @@ fn export_auto_drive_items_includes_cli_outputs() {
let diff = "diff --git a/foo.rs b/foo.rs\n--- a/foo.rs\n+++ b/foo.rs\n@@ -1 +1,2 @@\n-println!(\"old\");\n+println!(\"new\");\n+println!(\"extra\");\n"; let diff = "diff --git a/foo.rs b/foo.rs\n--- a/foo.rs\n+++ b/foo.rs\n@@ -1 +1,2 @@\n-println!(\"old\");\n+println!(\"new\");\n+println!(\"extra\");\n";
chat.add_diff_output(diff.to_string()); chat.add_diff_output(diff.to_string());
let tool_state = ToolCallState {
id: HistoryId::ZERO,
call_id: Some("tool-1".to_string()),
status: ToolStatus::Success,
title: "Run formatter".to_string(),
duration: None,
arguments: vec![ToolArgument {
name: "command".to_string(),
value: ArgumentValue::Text("just fmt".to_string()),
}],
result_preview: Some(ToolResultPreview {
lines: vec!["Formatted 12 files".to_string()],
truncated: false,
}),
error_message: None,
};
chat.history_push(history_cell::ToolCallCell::new(tool_state));
let items = chat.export_auto_drive_items(); let items = chat.export_auto_drive_items();
let coordinator = items let coordinator = items
@@ -3707,6 +3730,12 @@ fn export_auto_drive_items_includes_cli_outputs() {
.map(|text| text.contains("Files changed") && text.contains("foo.rs")) .map(|text| text.contains("Files changed") && text.contains("foo.rs"))
.unwrap_or(false) .unwrap_or(false)
}), "diff summary present"); }), "diff summary present");
assert!(items.iter().any(|item| {
message_text(item)
.map(|text| text.contains("Run formatter") && text.contains("Formatted 12 files"))
.unwrap_or(false)
}), "tool call output present");
} }
#[test] #[test]