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,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum AutoDriveRole {
User,
Assistant,
}
impl ChatWidget<'_> {
const MAX_UNDO_CONVERSATION_MESSAGES: usize = 8;
const MAX_UNDO_PREVIEW_CHARS: usize = 160;
@@ -3543,6 +3549,26 @@ impl ChatWidget<'_> {
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> {
let text = Self::auto_drive_lines_to_string(cell.display_lines());
if text.trim().is_empty() {
@@ -3658,31 +3684,12 @@ impl ChatWidget<'_> {
pub(crate) fn export_auto_drive_items(&self) -> Vec<codex_protocol::models::ResponseItem> {
let mut items = Vec::new();
for cell in &self.history_cells {
match cell.kind() {
HistoryCellType::User => {
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);
}
}
}
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 }],
});
}
}
let Some(role) = Self::auto_drive_role_for_kind(cell.kind()) else {
continue;
};
let text = match cell.kind() {
HistoryCellType::Reasoning => Self::auto_drive_cell_text(cell.as_ref()).map(|text| (text, true)),
HistoryCellType::PlanUpdate => {
if let Some(plan) = cell.as_any().downcast_ref::<PlanUpdateCell>() {
let state = plan.state();
@@ -3711,22 +3718,45 @@ impl ChatWidget<'_> {
}
}
let text = lines.join("\n");
if let Some(item) = Self::auto_drive_make_user_message(text) {
items.push(item);
}
Some((text, false))
} else {
Self::auto_drive_cell_text(cell.as_ref()).map(|text| (text, false))
}
}
HistoryCellType::Diff => {
if let Some(diff_cell) = cell.as_any().downcast_ref::<DiffCell>() {
if let Some(summary) = Self::auto_drive_diff_summary(diff_cell.record()) {
if let Some(item) = Self::auto_drive_make_user_message(summary) {
items.push(item);
}
}
Self::auto_drive_diff_summary(diff_cell.record()).map(|text| (text, false))
} else {
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
}
+29
View File
@@ -11,12 +11,17 @@ use crate::app_event::{
};
use crate::app_event_sender::AppEventSender;
use crate::history::state::{
ArgumentValue,
ExecStatus,
ExploreEntryStatus,
HistoryDomainRecord,
HistoryId,
HistoryRecord,
HistoryState,
ToolArgument,
ToolCallState,
ToolResultPreview,
ToolStatus,
};
use crate::slash_command::SlashCommand;
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";
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 coordinator = items
@@ -3707,6 +3730,12 @@ fn export_auto_drive_items_includes_cli_outputs() {
.map(|text| text.contains("Files changed") && text.contains("foo.rs"))
.unwrap_or(false)
}), "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]