feat: grab-bag of improvements to exec output (#1179)
Fixes: * Instantiate `EventProcessor` earlier in `lib.rs` so `print_config_summary()` can be an instance method of it and leverage its various `Style` fields to ensure it honors `with_ansi` properly. * After printing the config summary, print out user's prompt with the heading `User instructions:`. As noted in the comment, now that we can read the instructions via stdin as of #1178, it is helpful to the user to ensure they know what instructions were given to Codex. * Use same colors/bold/italic settings for headers as the TUI, making the output a bit easier to read.
This commit is contained in:
@@ -1,4 +1,3 @@
|
|||||||
use chrono::Utc;
|
|
||||||
use codex_common::elapsed::format_elapsed;
|
use codex_common::elapsed::format_elapsed;
|
||||||
use codex_core::config::Config;
|
use codex_core::config::Config;
|
||||||
use codex_core::protocol::AgentMessageEvent;
|
use codex_core::protocol::AgentMessageEvent;
|
||||||
@@ -37,11 +36,13 @@ pub(crate) struct EventProcessor {
|
|||||||
// using .style() with one of these fields. If you need a new style, add a
|
// using .style() with one of these fields. If you need a new style, add a
|
||||||
// new field here.
|
// new field here.
|
||||||
bold: Style,
|
bold: Style,
|
||||||
|
italic: Style,
|
||||||
dimmed: Style,
|
dimmed: Style,
|
||||||
|
|
||||||
magenta: Style,
|
magenta: Style,
|
||||||
red: Style,
|
red: Style,
|
||||||
green: Style,
|
green: Style,
|
||||||
|
cyan: Style,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EventProcessor {
|
impl EventProcessor {
|
||||||
@@ -55,10 +56,12 @@ impl EventProcessor {
|
|||||||
call_id_to_command,
|
call_id_to_command,
|
||||||
call_id_to_patch,
|
call_id_to_patch,
|
||||||
bold: Style::new().bold(),
|
bold: Style::new().bold(),
|
||||||
|
italic: Style::new().italic(),
|
||||||
dimmed: Style::new().dimmed(),
|
dimmed: Style::new().dimmed(),
|
||||||
magenta: Style::new().magenta(),
|
magenta: Style::new().magenta(),
|
||||||
red: Style::new().red(),
|
red: Style::new().red(),
|
||||||
green: Style::new().green(),
|
green: Style::new().green(),
|
||||||
|
cyan: Style::new().cyan(),
|
||||||
call_id_to_tool_call,
|
call_id_to_tool_call,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -66,10 +69,12 @@ impl EventProcessor {
|
|||||||
call_id_to_command,
|
call_id_to_command,
|
||||||
call_id_to_patch,
|
call_id_to_patch,
|
||||||
bold: Style::new(),
|
bold: Style::new(),
|
||||||
|
italic: Style::new(),
|
||||||
dimmed: Style::new(),
|
dimmed: Style::new(),
|
||||||
magenta: Style::new(),
|
magenta: Style::new(),
|
||||||
red: Style::new(),
|
red: Style::new(),
|
||||||
green: Style::new(),
|
green: Style::new(),
|
||||||
|
cyan: Style::new(),
|
||||||
call_id_to_tool_call,
|
call_id_to_tool_call,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -94,43 +99,47 @@ struct PatchApplyBegin {
|
|||||||
auto_approved: bool,
|
auto_approved: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
macro_rules! ts_println {
|
macro_rules! ts_println {
|
||||||
($($arg:tt)*) => {{
|
($($arg:tt)*) => {{
|
||||||
let now = Utc::now();
|
let now = chrono::Utc::now();
|
||||||
let formatted = now.format("%Y-%m-%dT%H:%M:%S").to_string();
|
let formatted = now.format("%Y-%m-%dT%H:%M:%S").to_string();
|
||||||
print!("[{}] ", formatted);
|
print!("[{}] ", formatted);
|
||||||
println!($($arg)*);
|
println!($($arg)*);
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Print a concise summary of the effective configuration that will be used
|
impl EventProcessor {
|
||||||
/// for the session. This mirrors the information shown in the TUI welcome
|
/// Print a concise summary of the effective configuration that will be used
|
||||||
/// screen.
|
/// for the session. This mirrors the information shown in the TUI welcome
|
||||||
pub(crate) fn print_config_summary(config: &Config, with_ansi: bool) {
|
/// screen.
|
||||||
let bold = if with_ansi {
|
pub(crate) fn print_config_summary(&mut self, config: &Config, prompt: &str) {
|
||||||
Style::new().bold()
|
ts_println!("OpenAI Codex (research preview)\n--------");
|
||||||
} else {
|
|
||||||
Style::new()
|
|
||||||
};
|
|
||||||
|
|
||||||
ts_println!("OpenAI Codex (research preview)\n--------");
|
let entries = vec![
|
||||||
|
("workdir", config.cwd.display().to_string()),
|
||||||
|
("model", config.model.clone()),
|
||||||
|
("provider", config.model_provider_id.clone()),
|
||||||
|
("approval", format!("{:?}", config.approval_policy)),
|
||||||
|
("sandbox", format!("{:?}", config.sandbox_policy)),
|
||||||
|
];
|
||||||
|
|
||||||
let entries = vec![
|
for (key, value) in entries {
|
||||||
("workdir", config.cwd.display().to_string()),
|
println!("{} {}", format!("{key}: ").style(self.bold), value);
|
||||||
("model", config.model.clone()),
|
}
|
||||||
("provider", config.model_provider_id.clone()),
|
|
||||||
("approval", format!("{:?}", config.approval_policy)),
|
|
||||||
("sandbox", format!("{:?}", config.sandbox_policy)),
|
|
||||||
];
|
|
||||||
|
|
||||||
for (key, value) in entries {
|
println!("--------");
|
||||||
println!("{} {}", format!("{key}: ").style(bold), value);
|
|
||||||
|
// Echo the prompt that will be sent to the agent so it is visible in the
|
||||||
|
// transcript/logs before any events come in. Note the prompt may have been
|
||||||
|
// read from stdin, so it may not be visible in the terminal otherwise.
|
||||||
|
ts_println!(
|
||||||
|
"{}\n{}",
|
||||||
|
"User instructions:".style(self.bold).style(self.cyan),
|
||||||
|
prompt
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
println!("--------\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
impl EventProcessor {
|
|
||||||
pub(crate) fn process_event(&mut self, event: Event) {
|
pub(crate) fn process_event(&mut self, event: Event) {
|
||||||
let Event { id: _, msg } = event;
|
let Event { id: _, msg } = event;
|
||||||
match msg {
|
match msg {
|
||||||
@@ -145,8 +154,10 @@ impl EventProcessor {
|
|||||||
// Ignore.
|
// Ignore.
|
||||||
}
|
}
|
||||||
EventMsg::AgentMessage(AgentMessageEvent { message }) => {
|
EventMsg::AgentMessage(AgentMessageEvent { message }) => {
|
||||||
let prefix = "Agent message:".style(self.bold);
|
ts_println!(
|
||||||
ts_println!("{prefix} {message}");
|
"{}\n{message}",
|
||||||
|
"codex".style(self.bold).style(self.magenta)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
EventMsg::ExecCommandBegin(ExecCommandBeginEvent {
|
EventMsg::ExecCommandBegin(ExecCommandBeginEvent {
|
||||||
call_id,
|
call_id,
|
||||||
@@ -394,7 +405,11 @@ impl EventProcessor {
|
|||||||
// Should we exit?
|
// Should we exit?
|
||||||
}
|
}
|
||||||
EventMsg::AgentReasoning(agent_reasoning_event) => {
|
EventMsg::AgentReasoning(agent_reasoning_event) => {
|
||||||
println!("thinking: {}", agent_reasoning_event.text);
|
ts_println!(
|
||||||
|
"{}\n{}",
|
||||||
|
"thinking".style(self.italic).style(self.magenta),
|
||||||
|
agent_reasoning_event.text
|
||||||
|
);
|
||||||
}
|
}
|
||||||
EventMsg::SessionConfigured(session_configured_event) => {
|
EventMsg::SessionConfigured(session_configured_event) => {
|
||||||
let SessionConfiguredEvent {
|
let SessionConfiguredEvent {
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ use codex_core::protocol::SandboxPolicy;
|
|||||||
use codex_core::protocol::TaskCompleteEvent;
|
use codex_core::protocol::TaskCompleteEvent;
|
||||||
use codex_core::util::is_inside_git_repo;
|
use codex_core::util::is_inside_git_repo;
|
||||||
use event_processor::EventProcessor;
|
use event_processor::EventProcessor;
|
||||||
use event_processor::print_config_summary;
|
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
use tracing::error;
|
use tracing::error;
|
||||||
use tracing::info;
|
use tracing::info;
|
||||||
@@ -113,8 +112,10 @@ pub async fn run_main(cli: Cli, codex_linux_sandbox_exe: Option<PathBuf>) -> any
|
|||||||
};
|
};
|
||||||
|
|
||||||
let config = Config::load_with_cli_overrides(cli_kv_overrides, overrides)?;
|
let config = Config::load_with_cli_overrides(cli_kv_overrides, overrides)?;
|
||||||
// Print the effective configuration so users can see what Codex is using.
|
let mut event_processor = EventProcessor::create_with_ansi(stdout_with_ansi);
|
||||||
print_config_summary(&config, stdout_with_ansi);
|
// Print the effective configuration and prompt so users can see what Codex
|
||||||
|
// is using.
|
||||||
|
event_processor.print_config_summary(&config, &prompt);
|
||||||
|
|
||||||
if !skip_git_repo_check && !is_inside_git_repo(&config) {
|
if !skip_git_repo_check && !is_inside_git_repo(&config) {
|
||||||
eprintln!("Not inside a Git repo and --skip-git-repo-check was not specified.");
|
eprintln!("Not inside a Git repo and --skip-git-repo-check was not specified.");
|
||||||
@@ -204,7 +205,6 @@ pub async fn run_main(cli: Cli, codex_linux_sandbox_exe: Option<PathBuf>) -> any
|
|||||||
info!("Sent prompt with event ID: {initial_prompt_task_id}");
|
info!("Sent prompt with event ID: {initial_prompt_task_id}");
|
||||||
|
|
||||||
// Run the loop until the task is complete.
|
// Run the loop until the task is complete.
|
||||||
let mut event_processor = EventProcessor::create_with_ansi(stdout_with_ansi);
|
|
||||||
while let Some(event) = rx.recv().await {
|
while let Some(event) = rx.recv().await {
|
||||||
let (is_last_event, last_assistant_message) = match &event.msg {
|
let (is_last_event, last_assistant_message) = match &event.msg {
|
||||||
EventMsg::TaskComplete(TaskCompleteEvent { last_agent_message }) => {
|
EventMsg::TaskComplete(TaskCompleteEvent { last_agent_message }) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user