Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 46 additions & 31 deletions codex-rs/exec/src/event_processor.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use chrono::Utc;
use codex_common::elapsed::format_elapsed;
use codex_core::config::Config;
use codex_core::protocol::AgentMessageEvent;
Expand Down Expand Up @@ -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 {
Expand All @@ -55,21 +56,25 @@ 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 {
Self {
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,
}
}
Expand All @@ -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 {
Expand All @@ -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,
Expand Down Expand Up @@ -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 {
Expand Down
8 changes: 4 additions & 4 deletions codex-rs/exec/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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)?;
// 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.");
Expand Down Expand Up @@ -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}");

// 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 }) => {
Expand Down