show thinking in transcript (#2538)
record the full reasoning trace and show it in transcript mode
This commit is contained in:
@@ -140,10 +140,17 @@ impl App {
|
|||||||
|
|
||||||
fn handle_event(&mut self, tui: &mut tui::Tui, event: AppEvent) -> Result<bool> {
|
fn handle_event(&mut self, tui: &mut tui::Tui, event: AppEvent) -> Result<bool> {
|
||||||
match event {
|
match event {
|
||||||
AppEvent::InsertHistory(lines) => {
|
AppEvent::InsertHistoryLines(lines) => {
|
||||||
self.transcript_lines.extend(lines.clone());
|
self.transcript_lines.extend(lines.clone());
|
||||||
tui.insert_history_lines(lines);
|
tui.insert_history_lines(lines);
|
||||||
}
|
}
|
||||||
|
AppEvent::InsertHistoryCell(cell) => {
|
||||||
|
self.transcript_lines.extend(cell.transcript_lines());
|
||||||
|
let display = cell.display_lines();
|
||||||
|
if !display.is_empty() {
|
||||||
|
tui.insert_history_lines(display);
|
||||||
|
}
|
||||||
|
}
|
||||||
AppEvent::StartCommitAnimation => {
|
AppEvent::StartCommitAnimation => {
|
||||||
if self
|
if self
|
||||||
.commit_anim_running
|
.commit_anim_running
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ use codex_core::protocol::Event;
|
|||||||
use codex_file_search::FileMatch;
|
use codex_file_search::FileMatch;
|
||||||
use ratatui::text::Line;
|
use ratatui::text::Line;
|
||||||
|
|
||||||
|
use crate::history_cell::HistoryCell;
|
||||||
|
|
||||||
use crate::slash_command::SlashCommand;
|
use crate::slash_command::SlashCommand;
|
||||||
use codex_core::protocol::AskForApproval;
|
use codex_core::protocol::AskForApproval;
|
||||||
use codex_core::protocol::SandboxPolicy;
|
use codex_core::protocol::SandboxPolicy;
|
||||||
@@ -39,7 +41,8 @@ pub(crate) enum AppEvent {
|
|||||||
/// Result of computing a `/diff` command.
|
/// Result of computing a `/diff` command.
|
||||||
DiffResult(String),
|
DiffResult(String),
|
||||||
|
|
||||||
InsertHistory(Vec<Line<'static>>),
|
InsertHistoryLines(Vec<Line<'static>>),
|
||||||
|
InsertHistoryCell(Box<dyn HistoryCell>),
|
||||||
|
|
||||||
StartCommitAnimation,
|
StartCommitAnimation,
|
||||||
StopCommitAnimation,
|
StopCommitAnimation,
|
||||||
|
|||||||
@@ -98,6 +98,8 @@ pub(crate) struct ChatWidget {
|
|||||||
needs_redraw: bool,
|
needs_redraw: bool,
|
||||||
// Accumulates the current reasoning block text to extract a header
|
// Accumulates the current reasoning block text to extract a header
|
||||||
reasoning_buffer: String,
|
reasoning_buffer: String,
|
||||||
|
// Accumulates full reasoning content for transcript-only recording
|
||||||
|
full_reasoning_buffer: String,
|
||||||
session_id: Option<Uuid>,
|
session_id: Option<Uuid>,
|
||||||
frame_requester: FrameRequester,
|
frame_requester: FrameRequester,
|
||||||
}
|
}
|
||||||
@@ -138,7 +140,7 @@ impl ChatWidget {
|
|||||||
self.bottom_pane
|
self.bottom_pane
|
||||||
.set_history_metadata(event.history_log_id, event.history_entry_count);
|
.set_history_metadata(event.history_log_id, event.history_entry_count);
|
||||||
self.session_id = Some(event.session_id);
|
self.session_id = Some(event.session_id);
|
||||||
self.add_to_history(&history_cell::new_session_info(&self.config, event, true));
|
self.add_to_history(history_cell::new_session_info(&self.config, event, true));
|
||||||
if let Some(user_message) = self.initial_user_message.take() {
|
if let Some(user_message) = self.initial_user_message.take() {
|
||||||
self.submit_user_message(user_message);
|
self.submit_user_message(user_message);
|
||||||
}
|
}
|
||||||
@@ -172,13 +174,23 @@ impl ChatWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn on_agent_reasoning_final(&mut self) {
|
fn on_agent_reasoning_final(&mut self) {
|
||||||
// Clear the reasoning buffer at the end of a reasoning block.
|
// At the end of a reasoning block, record transcript-only content.
|
||||||
|
self.full_reasoning_buffer.push_str(&self.reasoning_buffer);
|
||||||
|
if !self.full_reasoning_buffer.is_empty() {
|
||||||
|
self.add_to_history(history_cell::new_reasoning_block(
|
||||||
|
self.full_reasoning_buffer.clone(),
|
||||||
|
&self.config,
|
||||||
|
));
|
||||||
|
}
|
||||||
self.reasoning_buffer.clear();
|
self.reasoning_buffer.clear();
|
||||||
|
self.full_reasoning_buffer.clear();
|
||||||
self.mark_needs_redraw();
|
self.mark_needs_redraw();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_reasoning_section_break(&mut self) {
|
fn on_reasoning_section_break(&mut self) {
|
||||||
// Start a new reasoning block for header extraction.
|
// Start a new reasoning block for header extraction and accumulate transcript.
|
||||||
|
self.full_reasoning_buffer.push_str(&self.reasoning_buffer);
|
||||||
|
self.full_reasoning_buffer.push_str("\n\n");
|
||||||
self.reasoning_buffer.clear();
|
self.reasoning_buffer.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -188,6 +200,7 @@ impl ChatWidget {
|
|||||||
self.bottom_pane.clear_ctrl_c_quit_hint();
|
self.bottom_pane.clear_ctrl_c_quit_hint();
|
||||||
self.bottom_pane.set_task_running(true);
|
self.bottom_pane.set_task_running(true);
|
||||||
self.stream.reset_headers_for_new_turn();
|
self.stream.reset_headers_for_new_turn();
|
||||||
|
self.full_reasoning_buffer.clear();
|
||||||
self.reasoning_buffer.clear();
|
self.reasoning_buffer.clear();
|
||||||
self.mark_needs_redraw();
|
self.mark_needs_redraw();
|
||||||
}
|
}
|
||||||
@@ -216,7 +229,7 @@ impl ChatWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn on_error(&mut self, message: String) {
|
fn on_error(&mut self, message: String) {
|
||||||
self.add_to_history(&history_cell::new_error_event(message));
|
self.add_to_history(history_cell::new_error_event(message));
|
||||||
self.bottom_pane.set_task_running(false);
|
self.bottom_pane.set_task_running(false);
|
||||||
self.running_commands.clear();
|
self.running_commands.clear();
|
||||||
self.stream.clear_all();
|
self.stream.clear_all();
|
||||||
@@ -224,7 +237,7 @@ impl ChatWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn on_plan_update(&mut self, update: codex_core::plan_tool::UpdatePlanArgs) {
|
fn on_plan_update(&mut self, update: codex_core::plan_tool::UpdatePlanArgs) {
|
||||||
self.add_to_history(&history_cell::new_plan_update(update));
|
self.add_to_history(history_cell::new_plan_update(update));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_exec_approval_request(&mut self, id: String, ev: ExecApprovalRequestEvent) {
|
fn on_exec_approval_request(&mut self, id: String, ev: ExecApprovalRequestEvent) {
|
||||||
@@ -259,7 +272,7 @@ impl ChatWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn on_patch_apply_begin(&mut self, event: PatchApplyBeginEvent) {
|
fn on_patch_apply_begin(&mut self, event: PatchApplyBeginEvent) {
|
||||||
self.add_to_history(&history_cell::new_patch_event(
|
self.add_to_history(history_cell::new_patch_event(
|
||||||
PatchEventType::ApplyBegin {
|
PatchEventType::ApplyBegin {
|
||||||
auto_approved: event.auto_approved,
|
auto_approved: event.auto_approved,
|
||||||
},
|
},
|
||||||
@@ -386,7 +399,7 @@ impl ChatWidget {
|
|||||||
self.active_exec_cell = None;
|
self.active_exec_cell = None;
|
||||||
let pending = std::mem::take(&mut self.pending_exec_completions);
|
let pending = std::mem::take(&mut self.pending_exec_completions);
|
||||||
for (command, parsed, output) in pending {
|
for (command, parsed, output) in pending {
|
||||||
self.add_to_history(&history_cell::new_completed_exec_command(
|
self.add_to_history(history_cell::new_completed_exec_command(
|
||||||
command, parsed, output,
|
command, parsed, output,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@@ -398,9 +411,9 @@ impl ChatWidget {
|
|||||||
event: codex_core::protocol::PatchApplyEndEvent,
|
event: codex_core::protocol::PatchApplyEndEvent,
|
||||||
) {
|
) {
|
||||||
if event.success {
|
if event.success {
|
||||||
self.add_to_history(&history_cell::new_patch_apply_success(event.stdout));
|
self.add_to_history(history_cell::new_patch_apply_success(event.stdout));
|
||||||
} else {
|
} else {
|
||||||
self.add_to_history(&history_cell::new_patch_apply_failure(event.stderr));
|
self.add_to_history(history_cell::new_patch_apply_failure(event.stderr));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -422,7 +435,7 @@ impl ChatWidget {
|
|||||||
ev: ApplyPatchApprovalRequestEvent,
|
ev: ApplyPatchApprovalRequestEvent,
|
||||||
) {
|
) {
|
||||||
self.flush_answer_stream_with_separator();
|
self.flush_answer_stream_with_separator();
|
||||||
self.add_to_history(&history_cell::new_patch_event(
|
self.add_to_history(history_cell::new_patch_event(
|
||||||
PatchEventType::ApprovalRequest,
|
PatchEventType::ApprovalRequest,
|
||||||
ev.changes.clone(),
|
ev.changes.clone(),
|
||||||
));
|
));
|
||||||
@@ -464,11 +477,11 @@ impl ChatWidget {
|
|||||||
|
|
||||||
pub(crate) fn handle_mcp_begin_now(&mut self, ev: McpToolCallBeginEvent) {
|
pub(crate) fn handle_mcp_begin_now(&mut self, ev: McpToolCallBeginEvent) {
|
||||||
self.flush_answer_stream_with_separator();
|
self.flush_answer_stream_with_separator();
|
||||||
self.add_to_history(&history_cell::new_active_mcp_tool_call(ev.invocation));
|
self.add_to_history(history_cell::new_active_mcp_tool_call(ev.invocation));
|
||||||
}
|
}
|
||||||
pub(crate) fn handle_mcp_end_now(&mut self, ev: McpToolCallEndEvent) {
|
pub(crate) fn handle_mcp_end_now(&mut self, ev: McpToolCallEndEvent) {
|
||||||
self.flush_answer_stream_with_separator();
|
self.flush_answer_stream_with_separator();
|
||||||
self.add_to_history(&*history_cell::new_completed_mcp_tool_call(
|
self.add_boxed_history(history_cell::new_completed_mcp_tool_call(
|
||||||
80,
|
80,
|
||||||
ev.invocation,
|
ev.invocation,
|
||||||
ev.duration,
|
ev.duration,
|
||||||
@@ -541,6 +554,7 @@ impl ChatWidget {
|
|||||||
interrupts: InterruptManager::new(),
|
interrupts: InterruptManager::new(),
|
||||||
needs_redraw: false,
|
needs_redraw: false,
|
||||||
reasoning_buffer: String::new(),
|
reasoning_buffer: String::new(),
|
||||||
|
full_reasoning_buffer: String::new(),
|
||||||
session_id: None,
|
session_id: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -573,14 +587,19 @@ impl ChatWidget {
|
|||||||
fn flush_active_exec_cell(&mut self) {
|
fn flush_active_exec_cell(&mut self) {
|
||||||
if let Some(active) = self.active_exec_cell.take() {
|
if let Some(active) = self.active_exec_cell.take() {
|
||||||
self.app_event_tx
|
self.app_event_tx
|
||||||
.send(AppEvent::InsertHistory(active.display_lines()));
|
.send(AppEvent::InsertHistoryCell(Box::new(active)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_to_history(&mut self, cell: &dyn HistoryCell) {
|
fn add_to_history(&mut self, cell: impl HistoryCell + 'static) {
|
||||||
self.flush_active_exec_cell();
|
self.flush_active_exec_cell();
|
||||||
self.app_event_tx
|
self.app_event_tx
|
||||||
.send(AppEvent::InsertHistory(cell.display_lines()));
|
.send(AppEvent::InsertHistoryCell(Box::new(cell)));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_boxed_history(&mut self, cell: Box<dyn HistoryCell>) {
|
||||||
|
self.flush_active_exec_cell();
|
||||||
|
self.app_event_tx.send(AppEvent::InsertHistoryCell(cell));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn submit_user_message(&mut self, user_message: UserMessage) {
|
fn submit_user_message(&mut self, user_message: UserMessage) {
|
||||||
@@ -616,7 +635,7 @@ impl ChatWidget {
|
|||||||
|
|
||||||
// Only show the text portion in conversation history.
|
// Only show the text portion in conversation history.
|
||||||
if !text.is_empty() {
|
if !text.is_empty() {
|
||||||
self.add_to_history(&history_cell::new_user_prompt(text.clone()));
|
self.add_to_history(history_cell::new_user_prompt(text.clone()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -692,12 +711,12 @@ impl ChatWidget {
|
|||||||
|
|
||||||
pub(crate) fn add_diff_output(&mut self, diff_output: String) {
|
pub(crate) fn add_diff_output(&mut self, diff_output: String) {
|
||||||
self.bottom_pane.set_task_running(false);
|
self.bottom_pane.set_task_running(false);
|
||||||
self.add_to_history(&history_cell::new_diff_output(diff_output));
|
self.add_to_history(history_cell::new_diff_output(diff_output));
|
||||||
self.mark_needs_redraw();
|
self.mark_needs_redraw();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn add_status_output(&mut self) {
|
pub(crate) fn add_status_output(&mut self) {
|
||||||
self.add_to_history(&history_cell::new_status_output(
|
self.add_to_history(history_cell::new_status_output(
|
||||||
&self.config,
|
&self.config,
|
||||||
&self.total_token_usage,
|
&self.total_token_usage,
|
||||||
&self.session_id,
|
&self.session_id,
|
||||||
@@ -808,7 +827,7 @@ impl ChatWidget {
|
|||||||
|
|
||||||
pub(crate) fn add_mcp_output(&mut self) {
|
pub(crate) fn add_mcp_output(&mut self) {
|
||||||
if self.config.mcp_servers.is_empty() {
|
if self.config.mcp_servers.is_empty() {
|
||||||
self.add_to_history(&history_cell::empty_mcp_output());
|
self.add_to_history(history_cell::empty_mcp_output());
|
||||||
} else {
|
} else {
|
||||||
self.submit_op(Op::ListMcpTools);
|
self.submit_op(Op::ListMcpTools);
|
||||||
}
|
}
|
||||||
@@ -856,7 +875,7 @@ impl ChatWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn on_list_mcp_tools(&mut self, ev: McpListToolsResponseEvent) {
|
fn on_list_mcp_tools(&mut self, ev: McpListToolsResponseEvent) {
|
||||||
self.add_to_history(&history_cell::new_mcp_tools_output(&self.config, ev.tools));
|
self.add_to_history(history_cell::new_mcp_tools_output(&self.config, ev.tools));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Programmatically submit a user text message as if typed in the
|
/// Programmatically submit a user text message as if typed in the
|
||||||
|
|||||||
@@ -150,6 +150,7 @@ fn make_chatwidget_manual() -> (
|
|||||||
interrupts: InterruptManager::new(),
|
interrupts: InterruptManager::new(),
|
||||||
needs_redraw: false,
|
needs_redraw: false,
|
||||||
reasoning_buffer: String::new(),
|
reasoning_buffer: String::new(),
|
||||||
|
full_reasoning_buffer: String::new(),
|
||||||
session_id: None,
|
session_id: None,
|
||||||
frame_requester: crate::tui::FrameRequester::test_dummy(),
|
frame_requester: crate::tui::FrameRequester::test_dummy(),
|
||||||
};
|
};
|
||||||
@@ -161,8 +162,10 @@ fn drain_insert_history(
|
|||||||
) -> Vec<Vec<ratatui::text::Line<'static>>> {
|
) -> Vec<Vec<ratatui::text::Line<'static>>> {
|
||||||
let mut out = Vec::new();
|
let mut out = Vec::new();
|
||||||
while let Ok(ev) = rx.try_recv() {
|
while let Ok(ev) = rx.try_recv() {
|
||||||
if let AppEvent::InsertHistory(lines) = ev {
|
match ev {
|
||||||
out.push(lines);
|
AppEvent::InsertHistoryLines(lines) => out.push(lines),
|
||||||
|
AppEvent::InsertHistoryCell(cell) => out.push(cell.display_lines()),
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
out
|
out
|
||||||
@@ -336,13 +339,25 @@ async fn binary_size_transcript_matches_ideal_fixture() {
|
|||||||
let ev: Event = serde_json::from_value(payload.clone()).expect("parse");
|
let ev: Event = serde_json::from_value(payload.clone()).expect("parse");
|
||||||
chat.handle_codex_event(ev);
|
chat.handle_codex_event(ev);
|
||||||
while let Ok(app_ev) = rx.try_recv() {
|
while let Ok(app_ev) = rx.try_recv() {
|
||||||
if let AppEvent::InsertHistory(lines) = app_ev {
|
match app_ev {
|
||||||
transcript.push_str(&lines_to_single_string(&lines));
|
AppEvent::InsertHistoryLines(lines) => {
|
||||||
crate::insert_history::insert_history_lines_to_writer(
|
transcript.push_str(&lines_to_single_string(&lines));
|
||||||
&mut terminal,
|
crate::insert_history::insert_history_lines_to_writer(
|
||||||
&mut ansi,
|
&mut terminal,
|
||||||
lines,
|
&mut ansi,
|
||||||
);
|
lines,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
AppEvent::InsertHistoryCell(cell) => {
|
||||||
|
let lines = cell.display_lines();
|
||||||
|
transcript.push_str(&lines_to_single_string(&lines));
|
||||||
|
crate::insert_history::insert_history_lines_to_writer(
|
||||||
|
&mut terminal,
|
||||||
|
&mut ansi,
|
||||||
|
lines,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -353,13 +368,25 @@ async fn binary_size_transcript_matches_ideal_fixture() {
|
|||||||
{
|
{
|
||||||
chat.on_commit_tick();
|
chat.on_commit_tick();
|
||||||
while let Ok(app_ev) = rx.try_recv() {
|
while let Ok(app_ev) = rx.try_recv() {
|
||||||
if let AppEvent::InsertHistory(lines) = app_ev {
|
match app_ev {
|
||||||
transcript.push_str(&lines_to_single_string(&lines));
|
AppEvent::InsertHistoryLines(lines) => {
|
||||||
crate::insert_history::insert_history_lines_to_writer(
|
transcript.push_str(&lines_to_single_string(&lines));
|
||||||
&mut terminal,
|
crate::insert_history::insert_history_lines_to_writer(
|
||||||
&mut ansi,
|
&mut terminal,
|
||||||
lines,
|
&mut ansi,
|
||||||
);
|
lines,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
AppEvent::InsertHistoryCell(cell) => {
|
||||||
|
let lines = cell.display_lines();
|
||||||
|
transcript.push_str(&lines_to_single_string(&lines));
|
||||||
|
crate::insert_history::insert_history_lines_to_writer(
|
||||||
|
&mut terminal,
|
||||||
|
&mut ansi,
|
||||||
|
lines,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -809,7 +836,7 @@ fn headers_emitted_on_stream_begin_for_answer_and_not_for_reasoning() {
|
|||||||
});
|
});
|
||||||
let mut saw_codex_pre = false;
|
let mut saw_codex_pre = false;
|
||||||
while let Ok(ev) = rx.try_recv() {
|
while let Ok(ev) = rx.try_recv() {
|
||||||
if let AppEvent::InsertHistory(lines) = ev {
|
if let AppEvent::InsertHistoryLines(lines) = ev {
|
||||||
let s = lines
|
let s = lines
|
||||||
.iter()
|
.iter()
|
||||||
.flat_map(|l| l.spans.iter())
|
.flat_map(|l| l.spans.iter())
|
||||||
@@ -837,7 +864,7 @@ fn headers_emitted_on_stream_begin_for_answer_and_not_for_reasoning() {
|
|||||||
chat.on_commit_tick();
|
chat.on_commit_tick();
|
||||||
let mut saw_codex_post = false;
|
let mut saw_codex_post = false;
|
||||||
while let Ok(ev) = rx.try_recv() {
|
while let Ok(ev) = rx.try_recv() {
|
||||||
if let AppEvent::InsertHistory(lines) = ev {
|
if let AppEvent::InsertHistoryLines(lines) = ev {
|
||||||
let s = lines
|
let s = lines
|
||||||
.iter()
|
.iter()
|
||||||
.flat_map(|l| l.spans.iter())
|
.flat_map(|l| l.spans.iter())
|
||||||
@@ -865,7 +892,7 @@ fn headers_emitted_on_stream_begin_for_answer_and_not_for_reasoning() {
|
|||||||
});
|
});
|
||||||
let mut saw_thinking = false;
|
let mut saw_thinking = false;
|
||||||
while let Ok(ev) = rx2.try_recv() {
|
while let Ok(ev) = rx2.try_recv() {
|
||||||
if let AppEvent::InsertHistory(lines) = ev {
|
if let AppEvent::InsertHistoryLines(lines) = ev {
|
||||||
let s = lines
|
let s = lines
|
||||||
.iter()
|
.iter()
|
||||||
.flat_map(|l| l.spans.iter())
|
.flat_map(|l| l.spans.iter())
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
use crate::diff_render::create_diff_summary;
|
use crate::diff_render::create_diff_summary;
|
||||||
use crate::exec_command::relativize_to_home;
|
use crate::exec_command::relativize_to_home;
|
||||||
use crate::exec_command::strip_bash_lc_and_escape;
|
use crate::exec_command::strip_bash_lc_and_escape;
|
||||||
|
use crate::markdown::append_markdown;
|
||||||
use crate::slash_command::SlashCommand;
|
use crate::slash_command::SlashCommand;
|
||||||
use crate::text_formatting::format_and_truncate_tool_result;
|
use crate::text_formatting::format_and_truncate_tool_result;
|
||||||
use base64::Engine;
|
use base64::Engine;
|
||||||
@@ -39,7 +40,7 @@ use std::time::Instant;
|
|||||||
use tracing::error;
|
use tracing::error;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Debug)]
|
||||||
pub(crate) struct CommandOutput {
|
pub(crate) struct CommandOutput {
|
||||||
pub(crate) exit_code: i32,
|
pub(crate) exit_code: i32,
|
||||||
pub(crate) stdout: String,
|
pub(crate) stdout: String,
|
||||||
@@ -54,9 +55,13 @@ pub(crate) enum PatchEventType {
|
|||||||
/// Represents an event to display in the conversation history. Returns its
|
/// Represents an event to display in the conversation history. Returns its
|
||||||
/// `Vec<Line<'static>>` representation to make it easier to display in a
|
/// `Vec<Line<'static>>` representation to make it easier to display in a
|
||||||
/// scrollable list.
|
/// scrollable list.
|
||||||
pub(crate) trait HistoryCell {
|
pub(crate) trait HistoryCell: std::fmt::Debug + Send + Sync {
|
||||||
fn display_lines(&self) -> Vec<Line<'static>>;
|
fn display_lines(&self) -> Vec<Line<'static>>;
|
||||||
|
|
||||||
|
fn transcript_lines(&self) -> Vec<Line<'static>> {
|
||||||
|
self.display_lines()
|
||||||
|
}
|
||||||
|
|
||||||
fn desired_height(&self, width: u16) -> u16 {
|
fn desired_height(&self, width: u16) -> u16 {
|
||||||
Paragraph::new(Text::from(self.display_lines()))
|
Paragraph::new(Text::from(self.display_lines()))
|
||||||
.wrap(Wrap { trim: false })
|
.wrap(Wrap { trim: false })
|
||||||
@@ -66,6 +71,7 @@ pub(crate) trait HistoryCell {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub(crate) struct PlainHistoryCell {
|
pub(crate) struct PlainHistoryCell {
|
||||||
lines: Vec<Line<'static>>,
|
lines: Vec<Line<'static>>,
|
||||||
}
|
}
|
||||||
@@ -76,6 +82,22 @@ impl HistoryCell for PlainHistoryCell {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(crate) struct TranscriptOnlyHistoryCell {
|
||||||
|
lines: Vec<Line<'static>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HistoryCell for TranscriptOnlyHistoryCell {
|
||||||
|
fn display_lines(&self) -> Vec<Line<'static>> {
|
||||||
|
Vec::new()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn transcript_lines(&self) -> Vec<Line<'static>> {
|
||||||
|
self.lines.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub(crate) struct ExecCell {
|
pub(crate) struct ExecCell {
|
||||||
pub(crate) command: Vec<String>,
|
pub(crate) command: Vec<String>,
|
||||||
pub(crate) parsed: Vec<ParsedCommand>,
|
pub(crate) parsed: Vec<ParsedCommand>,
|
||||||
@@ -101,6 +123,7 @@ impl WidgetRef for &ExecCell {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
struct CompletedMcpToolCallWithImageOutput {
|
struct CompletedMcpToolCallWithImageOutput {
|
||||||
_image: DynamicImage,
|
_image: DynamicImage,
|
||||||
}
|
}
|
||||||
@@ -930,6 +953,17 @@ pub(crate) fn new_patch_apply_success(stdout: String) -> PlainHistoryCell {
|
|||||||
PlainHistoryCell { lines }
|
PlainHistoryCell { lines }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn new_reasoning_block(
|
||||||
|
full_reasoning_buffer: String,
|
||||||
|
config: &Config,
|
||||||
|
) -> TranscriptOnlyHistoryCell {
|
||||||
|
let mut lines: Vec<Line<'static>> = Vec::new();
|
||||||
|
lines.push(Line::from("thinking".magenta().italic()));
|
||||||
|
append_markdown(&full_reasoning_buffer, &mut lines, config);
|
||||||
|
lines.push(Line::from(""));
|
||||||
|
TranscriptOnlyHistoryCell { lines }
|
||||||
|
}
|
||||||
|
|
||||||
fn output_lines(
|
fn output_lines(
|
||||||
output: Option<&CommandOutput>,
|
output: Option<&CommandOutput>,
|
||||||
only_err: bool,
|
only_err: bool,
|
||||||
|
|||||||
@@ -143,7 +143,7 @@ pub(crate) fn log_inbound_app_event(event: &AppEvent) {
|
|||||||
LOGGER.write_json_line(value);
|
LOGGER.write_json_line(value);
|
||||||
}
|
}
|
||||||
// Internal UI events; still log for fidelity, but avoid heavy payloads.
|
// Internal UI events; still log for fidelity, but avoid heavy payloads.
|
||||||
AppEvent::InsertHistory(lines) => {
|
AppEvent::InsertHistoryLines(lines) => {
|
||||||
let value = json!({
|
let value = json!({
|
||||||
"ts": now_ts(),
|
"ts": now_ts(),
|
||||||
"dir": "to_tui",
|
"dir": "to_tui",
|
||||||
@@ -152,6 +152,15 @@ pub(crate) fn log_inbound_app_event(event: &AppEvent) {
|
|||||||
});
|
});
|
||||||
LOGGER.write_json_line(value);
|
LOGGER.write_json_line(value);
|
||||||
}
|
}
|
||||||
|
AppEvent::InsertHistoryCell(cell) => {
|
||||||
|
let value = json!({
|
||||||
|
"ts": now_ts(),
|
||||||
|
"dir": "to_tui",
|
||||||
|
"kind": "insert_history_cell",
|
||||||
|
"lines": cell.transcript_lines().len(),
|
||||||
|
});
|
||||||
|
LOGGER.write_json_line(value);
|
||||||
|
}
|
||||||
AppEvent::StartFileSearch(query) => {
|
AppEvent::StartFileSearch(query) => {
|
||||||
let value = json!({
|
let value = json!({
|
||||||
"ts": now_ts(),
|
"ts": now_ts(),
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ pub(crate) struct AppEventHistorySink(pub(crate) crate::app_event_sender::AppEve
|
|||||||
impl HistorySink for AppEventHistorySink {
|
impl HistorySink for AppEventHistorySink {
|
||||||
fn insert_history(&self, lines: Vec<Line<'static>>) {
|
fn insert_history(&self, lines: Vec<Line<'static>>) {
|
||||||
self.0
|
self.0
|
||||||
.send(crate::app_event::AppEvent::InsertHistory(lines))
|
.send(crate::app_event::AppEvent::InsertHistoryLines(lines))
|
||||||
}
|
}
|
||||||
fn start_commit_animation(&self) {
|
fn start_commit_animation(&self) {
|
||||||
self.0
|
self.0
|
||||||
|
|||||||
@@ -328,7 +328,7 @@ impl UserApprovalWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
lines.push(Line::from(""));
|
lines.push(Line::from(""));
|
||||||
self.app_event_tx.send(AppEvent::InsertHistory(lines));
|
self.app_event_tx.send(AppEvent::InsertHistoryLines(lines));
|
||||||
|
|
||||||
let op = match &self.approval_request {
|
let op = match &self.approval_request {
|
||||||
ApprovalRequest::Exec { id, .. } => Op::ExecApproval {
|
ApprovalRequest::Exec { id, .. } => Op::ExecApproval {
|
||||||
|
|||||||
Reference in New Issue
Block a user