diff --git a/codex-rs/exec/src/event_processor.rs b/codex-rs/exec/src/event_processor.rs index 352275bf43..8005980d8e 100644 --- a/codex-rs/exec/src/event_processor.rs +++ b/codex-rs/exec/src/event_processor.rs @@ -1,4 +1,3 @@ -use chrono::Utc; use codex_common::elapsed::format_elapsed; use codex_core::config::Config; 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 // new field here. bold: Style, + italic: Style, dimmed: Style, magenta: Style, red: Style, green: Style, + cyan: Style, } impl EventProcessor { @@ -55,10 +56,12 @@ impl EventProcessor { call_id_to_command, call_id_to_patch, bold: Style::new().bold(), + italic: Style::new().italic(), dimmed: Style::new().dimmed(), magenta: Style::new().magenta(), red: Style::new().red(), green: Style::new().green(), + cyan: Style::new().cyan(), call_id_to_tool_call, } } else { @@ -66,10 +69,12 @@ impl EventProcessor { call_id_to_command, call_id_to_patch, bold: Style::new(), + italic: Style::new(), dimmed: Style::new(), magenta: Style::new(), red: Style::new(), green: Style::new(), + cyan: Style::new(), call_id_to_tool_call, } } @@ -94,43 +99,47 @@ struct PatchApplyBegin { auto_approved: bool, } +#[macro_export] macro_rules! ts_println { ($($arg:tt)*) => {{ - let now = Utc::now(); + let now = chrono::Utc::now(); let formatted = now.format("%Y-%m-%dT%H:%M:%S").to_string(); print!("[{}] ", formatted); println!($($arg)*); }}; } -/// Print a concise summary of the effective configuration that will be used -/// for the session. This mirrors the information shown in the TUI welcome -/// screen. -pub(crate) fn print_config_summary(config: &Config, with_ansi: bool) { - let bold = if with_ansi { - Style::new().bold() - } 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)), - ]; - - for (key, value) in entries { - println!("{} {}", format!("{key}: ").style(bold), value); - } +impl EventProcessor { + /// Print a concise summary of the effective configuration that will be used + /// for the session. This mirrors the information shown in the TUI welcome + /// screen. + pub(crate) fn print_config_summary(&mut self, config: &Config, prompt: &str) { + 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)), + ]; + + for (key, value) in entries { + println!("{} {}", format!("{key}: ").style(self.bold), value); + } - println!("--------\n"); -} + println!("--------"); + + // 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 + ); + } -impl EventProcessor { pub(crate) fn process_event(&mut self, event: Event) { let Event { id: _, msg } = event; match msg { @@ -145,8 +154,10 @@ impl EventProcessor { // Ignore. } EventMsg::AgentMessage(AgentMessageEvent { message }) => { - let prefix = "Agent message:".style(self.bold); - ts_println!("{prefix} {message}"); + ts_println!( + "{}\n{message}", + "codex".style(self.bold).style(self.magenta) + ); } EventMsg::ExecCommandBegin(ExecCommandBeginEvent { call_id, @@ -394,7 +405,11 @@ impl EventProcessor { // Should we exit? } 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) => { let SessionConfiguredEvent { diff --git a/codex-rs/exec/src/lib.rs b/codex-rs/exec/src/lib.rs index 6602213b14..e203c2f161 100644 --- a/codex-rs/exec/src/lib.rs +++ b/codex-rs/exec/src/lib.rs @@ -20,7 +20,6 @@ use codex_core::protocol::SandboxPolicy; use codex_core::protocol::TaskCompleteEvent; use codex_core::util::is_inside_git_repo; use event_processor::EventProcessor; -use event_processor::print_config_summary; use tracing::debug; use tracing::error; use tracing::info; @@ -113,8 +112,10 @@ pub async fn run_main(cli: Cli, codex_linux_sandbox_exe: Option) -> any }; let config = Config::load_with_cli_overrides(cli_kv_overrides, overrides)?; - // Print the effective configuration so users can see what Codex is using. - print_config_summary(&config, stdout_with_ansi); + let mut event_processor = EventProcessor::create_with_ansi(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) { 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) -> any info!("Sent prompt with event ID: {initial_prompt_task_id}"); // 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 { let (is_last_event, last_assistant_message) = match &event.msg { EventMsg::TaskComplete(TaskCompleteEvent { last_agent_message }) => {