From 78a1d49fac0fbc150e5e862fd3f24c8af50e393c Mon Sep 17 00:00:00 2001
From: Jeremy Rose <172423086+nornagon-openai@users.noreply.github.com>
Date: Sun, 3 Aug 2025 11:33:44 -0700
Subject: [PATCH] fix command duration display (#1806)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
we were always displaying "0ms" before.
---
codex-rs/core/src/codex.rs | 50 +++++++++----------
codex-rs/core/src/protocol.rs | 2 +
codex-rs/core/tests/live_agent.rs | 6 +--
.../src/event_processor_with_human_output.rs | 10 ++--
codex-rs/tui/src/chatwidget.rs | 4 +-
5 files changed, 32 insertions(+), 40 deletions(-)
diff --git a/codex-rs/core/src/codex.rs b/codex-rs/core/src/codex.rs
index e759acb4..7004dcfc 100644
--- a/codex-rs/core/src/codex.rs
+++ b/codex-rs/core/src/codex.rs
@@ -396,11 +396,15 @@ impl Session {
&self,
sub_id: &str,
call_id: &str,
- stdout: &str,
- stderr: &str,
- exit_code: i32,
+ output: &ExecToolCallOutput,
is_apply_patch: bool,
) {
+ let ExecToolCallOutput {
+ stdout,
+ stderr,
+ duration,
+ exit_code,
+ } = output;
// Because stdout and stderr could each be up to 100 KiB, we send
// truncated versions.
const MAX_STREAM_OUTPUT: usize = 5 * 1024; // 5KiB
@@ -412,14 +416,15 @@ impl Session {
call_id: call_id.to_string(),
stdout,
stderr,
- success: exit_code == 0,
+ success: *exit_code == 0,
})
} else {
EventMsg::ExecCommandEnd(ExecCommandEndEvent {
call_id: call_id.to_string(),
stdout,
stderr,
- exit_code,
+ duration: *duration,
+ exit_code: *exit_code,
})
};
@@ -1775,23 +1780,21 @@ async fn handle_container_exec_with_params(
stdout,
stderr,
duration,
- } = output;
+ } = &output;
sess.notify_exec_command_end(
&sub_id,
&call_id,
- &stdout,
- &stderr,
- exit_code,
+ &output,
exec_command_context.apply_patch.is_some(),
)
.await;
- let is_success = exit_code == 0;
+ let is_success = *exit_code == 0;
let content = format_exec_output(
- if is_success { &stdout } else { &stderr },
- exit_code,
- duration,
+ if is_success { stdout } else { stderr },
+ *exit_code,
+ *duration,
);
ResponseInputItem::FunctionCallOutput {
@@ -1900,23 +1903,16 @@ async fn handle_sandbox_error(
stdout,
stderr,
duration,
- } = retry_output;
+ } = &retry_output;
- sess.notify_exec_command_end(
- &sub_id,
- &call_id,
- &stdout,
- &stderr,
- exit_code,
- is_apply_patch,
- )
- .await;
+ sess.notify_exec_command_end(&sub_id, &call_id, &retry_output, is_apply_patch)
+ .await;
- let is_success = exit_code == 0;
+ let is_success = *exit_code == 0;
let content = format_exec_output(
- if is_success { &stdout } else { &stderr },
- exit_code,
- duration,
+ if is_success { stdout } else { stderr },
+ *exit_code,
+ *duration,
);
ResponseInputItem::FunctionCallOutput {
diff --git a/codex-rs/core/src/protocol.rs b/codex-rs/core/src/protocol.rs
index 1bfeee56..cbb211d9 100644
--- a/codex-rs/core/src/protocol.rs
+++ b/codex-rs/core/src/protocol.rs
@@ -523,6 +523,8 @@ pub struct ExecCommandEndEvent {
pub stderr: String,
/// The command's exit code.
pub exit_code: i32,
+ /// The duration of the command execution.
+ pub duration: Duration,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
diff --git a/codex-rs/core/tests/live_agent.rs b/codex-rs/core/tests/live_agent.rs
index 95408e20..81b3bb2a 100644
--- a/codex-rs/core/tests/live_agent.rs
+++ b/codex-rs/core/tests/live_agent.rs
@@ -177,8 +177,7 @@ async fn live_shell_function_call() {
match ev.msg {
EventMsg::ExecCommandBegin(codex_core::protocol::ExecCommandBeginEvent {
command,
- call_id: _,
- cwd: _,
+ ..
}) => {
assert_eq!(command, vec!["echo", MARKER]);
saw_begin = true;
@@ -186,8 +185,7 @@ async fn live_shell_function_call() {
EventMsg::ExecCommandEnd(codex_core::protocol::ExecCommandEndEvent {
stdout,
exit_code,
- call_id: _,
- stderr: _,
+ ..
}) => {
assert_eq!(exit_code, 0, "echo returned non‑zero exit code");
assert!(stdout.contains(MARKER));
diff --git a/codex-rs/exec/src/event_processor_with_human_output.rs b/codex-rs/exec/src/event_processor_with_human_output.rs
index 7393ab72..72e2f929 100644
--- a/codex-rs/exec/src/event_processor_with_human_output.rs
+++ b/codex-rs/exec/src/event_processor_with_human_output.rs
@@ -106,7 +106,6 @@ impl EventProcessorWithHumanOutput {
struct ExecCommandBegin {
command: Vec,
- start_time: Instant,
}
struct PatchApplyBegin {
@@ -228,7 +227,6 @@ impl EventProcessor for EventProcessorWithHumanOutput {
call_id.clone(),
ExecCommandBegin {
command: command.clone(),
- start_time: Instant::now(),
},
);
ts_println!(
@@ -244,16 +242,14 @@ impl EventProcessor for EventProcessorWithHumanOutput {
call_id,
stdout,
stderr,
+ duration,
exit_code,
}) => {
let exec_command = self.call_id_to_command.remove(&call_id);
- let (duration, call) = if let Some(ExecCommandBegin {
- command,
- start_time,
- }) = exec_command
+ let (duration, call) = if let Some(ExecCommandBegin { command, .. }) = exec_command
{
(
- format!(" in {}", format_elapsed(start_time)),
+ format!(" in {}", format_duration(duration)),
format!("{}", escape_command(&command).style(self.bold)),
)
} else {
diff --git a/codex-rs/tui/src/chatwidget.rs b/codex-rs/tui/src/chatwidget.rs
index 31151d56..e5ebf58a 100644
--- a/codex-rs/tui/src/chatwidget.rs
+++ b/codex-rs/tui/src/chatwidget.rs
@@ -1,7 +1,6 @@
use std::collections::HashMap;
use std::path::PathBuf;
use std::sync::Arc;
-use std::time::Duration;
use codex_core::codex_wrapper::CodexConversation;
use codex_core::codex_wrapper::init_codex;
@@ -390,6 +389,7 @@ impl ChatWidget<'_> {
EventMsg::ExecCommandEnd(ExecCommandEndEvent {
call_id,
exit_code,
+ duration,
stdout,
stderr,
}) => {
@@ -400,7 +400,7 @@ impl ChatWidget<'_> {
exit_code,
stdout,
stderr,
- duration: Duration::from_secs(0),
+ duration,
},
));
}