diff --git a/codex-rs/core/src/codex.rs b/codex-rs/core/src/codex.rs index 26e1f665..440451a9 100644 --- a/codex-rs/core/src/codex.rs +++ b/codex-rs/core/src/codex.rs @@ -55,12 +55,22 @@ use crate::models::ResponseInputItem; use crate::models::ResponseItem; use crate::models::ShellToolCallParams; use crate::project_doc::create_full_instructions; +use crate::protocol::AgentMessageEvent; +use crate::protocol::AgentReasoningEvent; +use crate::protocol::ApplyPatchApprovalRequestEvent; use crate::protocol::AskForApproval; +use crate::protocol::BackgroundEventEvent; +use crate::protocol::ErrorEvent; use crate::protocol::Event; use crate::protocol::EventMsg; +use crate::protocol::ExecApprovalRequestEvent; +use crate::protocol::ExecCommandBeginEvent; +use crate::protocol::ExecCommandEndEvent; use crate::protocol::FileChange; use crate::protocol::InputItem; use crate::protocol::Op; +use crate::protocol::PatchApplyBeginEvent; +use crate::protocol::PatchApplyEndEvent; use crate::protocol::ReviewDecision; use crate::protocol::SandboxPolicy; use crate::protocol::SessionConfiguredEvent; @@ -227,11 +237,11 @@ impl Session { let (tx_approve, rx_approve) = oneshot::channel(); let event = Event { id: sub_id.clone(), - msg: EventMsg::ExecApprovalRequest { + msg: EventMsg::ExecApprovalRequest(ExecApprovalRequestEvent { command, cwd, reason, - }, + }), }; let _ = self.tx_event.send(event).await; { @@ -251,11 +261,11 @@ impl Session { let (tx_approve, rx_approve) = oneshot::channel(); let event = Event { id: sub_id.clone(), - msg: EventMsg::ApplyPatchApprovalRequest { + msg: EventMsg::ApplyPatchApprovalRequest(ApplyPatchApprovalRequestEvent { changes: convert_apply_patch_to_protocol(action), reason, grant_root, - }, + }), }; let _ = self.tx_event.send(event).await; { @@ -297,11 +307,11 @@ impl Session { async fn notify_exec_command_begin(&self, sub_id: &str, call_id: &str, params: &ExecParams) { let event = Event { id: sub_id.to_string(), - msg: EventMsg::ExecCommandBegin { + msg: EventMsg::ExecCommandBegin(ExecCommandBeginEvent { call_id: call_id.to_string(), command: params.command.clone(), cwd: params.cwd.clone(), - }, + }), }; let _ = self.tx_event.send(event).await; } @@ -319,12 +329,12 @@ impl Session { id: sub_id.to_string(), // Because stdout and stderr could each be up to 100 KiB, we send // truncated versions. - msg: EventMsg::ExecCommandEnd { + msg: EventMsg::ExecCommandEnd(ExecCommandEndEvent { call_id: call_id.to_string(), stdout: stdout.chars().take(MAX_STREAM_OUTPUT).collect(), stderr: stderr.chars().take(MAX_STREAM_OUTPUT).collect(), exit_code, - }, + }), }; let _ = self.tx_event.send(event).await; } @@ -335,9 +345,9 @@ impl Session { async fn notify_background_event(&self, sub_id: &str, message: impl Into) { let event = Event { id: sub_id.to_string(), - msg: EventMsg::BackgroundEvent { + msg: EventMsg::BackgroundEvent(BackgroundEventEvent { message: message.into(), - }, + }), }; let _ = self.tx_event.send(event).await; } @@ -460,9 +470,9 @@ impl AgentTask { self.handle.abort(); let event = Event { id: self.sub_id, - msg: EventMsg::Error { + msg: EventMsg::Error(ErrorEvent { message: "Turn interrupted".to_string(), - }, + }), }; let tx_event = self.sess.tx_event.clone(); tokio::spawn(async move { @@ -483,10 +493,10 @@ async fn submission_loop( let send_no_session_event = |sub_id: String| async { let event = Event { id: sub_id, - msg: EventMsg::Error { + msg: EventMsg::Error(ErrorEvent { message: "No session initialized, expected 'ConfigureSession' as first Op" .to_string(), - }, + }), }; tx_event.send(event).await.ok(); }; @@ -534,7 +544,7 @@ async fn submission_loop( error!(message); let event = Event { id: sub.id, - msg: EventMsg::Error { message }, + msg: EventMsg::Error(ErrorEvent { message }), }; if let Err(e) = tx_event.send(event).await { error!("failed to send error message: {e:?}"); @@ -577,7 +587,7 @@ async fn submission_loop( error!("{message}"); mcp_connection_errors.push(Event { id: sub.id.clone(), - msg: EventMsg::Error { message }, + msg: EventMsg::Error(ErrorEvent { message }), }); (McpConnectionManager::default(), Default::default()) } @@ -591,7 +601,7 @@ async fn submission_loop( error!("{message}"); mcp_connection_errors.push(Event { id: sub.id.clone(), - msg: EventMsg::Error { message }, + msg: EventMsg::Error(ErrorEvent { message }), }); } } @@ -792,9 +802,9 @@ async fn run_task(sess: Arc, sub_id: String, input: Vec) { info!("Turn error: {e:#}"); let event = Event { id: sub_id.clone(), - msg: EventMsg::Error { + msg: EventMsg::Error(ErrorEvent { message: e.to_string(), - }, + }), }; sess.tx_event.send(event).await.ok(); return; @@ -933,7 +943,7 @@ async fn handle_response_item( if let ContentItem::OutputText { text } = item { let event = Event { id: sub_id.to_string(), - msg: EventMsg::AgentMessage { message: text }, + msg: EventMsg::AgentMessage(AgentMessageEvent { message: text }), }; sess.tx_event.send(event).await.ok(); } @@ -946,7 +956,7 @@ async fn handle_response_item( }; let event = Event { id: sub_id.to_string(), - msg: EventMsg::AgentReasoning { text }, + msg: EventMsg::AgentReasoning(AgentReasoningEvent { text }), }; sess.tx_event.send(event).await.ok(); } @@ -1346,11 +1356,11 @@ async fn apply_patch( .tx_event .send(Event { id: sub_id.clone(), - msg: EventMsg::PatchApplyBegin { + msg: EventMsg::PatchApplyBegin(PatchApplyBeginEvent { call_id: call_id.clone(), auto_approved, changes: convert_apply_patch_to_protocol(&action), - }, + }), }) .await; @@ -1435,12 +1445,12 @@ async fn apply_patch( .tx_event .send(Event { id: sub_id.clone(), - msg: EventMsg::PatchApplyEnd { + msg: EventMsg::PatchApplyEnd(PatchApplyEndEvent { call_id: call_id.clone(), stdout: String::from_utf8_lossy(&stdout).to_string(), stderr: String::from_utf8_lossy(&stderr).to_string(), success: success_flag, - }, + }), }) .await; diff --git a/codex-rs/core/src/codex_wrapper.rs b/codex-rs/core/src/codex_wrapper.rs index 431b580c..f2ece22d 100644 --- a/codex-rs/core/src/codex_wrapper.rs +++ b/codex-rs/core/src/codex_wrapper.rs @@ -24,7 +24,7 @@ pub async fn init_codex(config: Config) -> anyhow::Result<(Codex, Event, Arc ( - EventMsg::McpToolCallEnd { + EventMsg::McpToolCallEnd(McpToolCallEndEvent { call_id, success: !result.is_error.unwrap_or(false), result: Some(result), - }, + }), None, ), Err(e) => ( - EventMsg::McpToolCallEnd { + EventMsg::McpToolCallEnd(McpToolCallEndEvent { call_id, success: false, result: None, - }, + }), Some(e), ), }; notify_mcp_tool_call_event(sess, sub_id, tool_call_end_event.clone()).await; - let EventMsg::McpToolCallEnd { + let EventMsg::McpToolCallEnd(McpToolCallEndEvent { call_id, success, result, - } = tool_call_end_event + }) = tool_call_end_event else { unimplemented!("unexpected event type"); }; diff --git a/codex-rs/core/src/protocol.rs b/codex-rs/core/src/protocol.rs index e4b83826..80087430 100644 --- a/codex-rs/core/src/protocol.rs +++ b/codex-rs/core/src/protocol.rs @@ -303,9 +303,7 @@ pub struct Event { #[serde(tag = "type", rename_all = "snake_case")] pub enum EventMsg { /// Error while executing a submission - Error { - message: String, - }, + Error(ErrorEvent), /// Agent has started a task TaskStarted, @@ -314,117 +312,145 @@ pub enum EventMsg { TaskComplete, /// Agent text output message - AgentMessage { - message: String, - }, + AgentMessage(AgentMessageEvent), /// Reasoning event from agent. - AgentReasoning { - text: String, - }, + AgentReasoning(AgentReasoningEvent), /// Ack the client's configure message. SessionConfigured(SessionConfiguredEvent), - McpToolCallBegin { - /// Identifier so this can be paired with the McpToolCallEnd event. - call_id: String, + McpToolCallBegin(McpToolCallBeginEvent), - /// Name of the MCP server as defined in the config. - server: String, - - /// Name of the tool as given by the MCP server. - tool: String, - - /// Arguments to the tool call. - arguments: Option, - }, - - McpToolCallEnd { - /// Identifier for the McpToolCallBegin that finished. - call_id: String, - - /// Whether the tool call was successful. If `false`, `result` might - /// not be present. - success: bool, - - /// Result of the tool call. Note this could be an error. - result: Option, - }, + McpToolCallEnd(McpToolCallEndEvent), /// Notification that the server is about to execute a command. - ExecCommandBegin { - /// Identifier so this can be paired with the ExecCommandEnd event. - call_id: String, - /// The command to be executed. - command: Vec, - /// The command's working directory if not the default cwd for the - /// agent. - cwd: PathBuf, - }, + ExecCommandBegin(ExecCommandBeginEvent), - ExecCommandEnd { - /// Identifier for the ExecCommandBegin that finished. - call_id: String, - /// Captured stdout - stdout: String, - /// Captured stderr - stderr: String, - /// The command's exit code. - exit_code: i32, - }, + ExecCommandEnd(ExecCommandEndEvent), - ExecApprovalRequest { - /// The command to be executed. - command: Vec, - /// The command's working directory. - cwd: PathBuf, - /// Optional human‑readable reason for the approval (e.g. retry without - /// sandbox). - #[serde(skip_serializing_if = "Option::is_none")] - reason: Option, - }, + ExecApprovalRequest(ExecApprovalRequestEvent), - ApplyPatchApprovalRequest { - changes: HashMap, - /// Optional explanatory reason (e.g. request for extra write access). - #[serde(skip_serializing_if = "Option::is_none")] - reason: Option, + ApplyPatchApprovalRequest(ApplyPatchApprovalRequestEvent), - /// When set, the agent is asking the user to allow writes under this - /// root for the remainder of the session. - #[serde(skip_serializing_if = "Option::is_none")] - grant_root: Option, - }, - - BackgroundEvent { - message: String, - }, + BackgroundEvent(BackgroundEventEvent), /// Notification that the agent is about to apply a code patch. Mirrors /// `ExecCommandBegin` so front‑ends can show progress indicators. - PatchApplyBegin { - /// Identifier so this can be paired with the PatchApplyEnd event. - call_id: String, - - /// If true, there was no ApplyPatchApprovalRequest for this patch. - auto_approved: bool, - - /// The changes to be applied. - changes: HashMap, - }, + PatchApplyBegin(PatchApplyBeginEvent), /// Notification that a patch application has finished. - PatchApplyEnd { - /// Identifier for the PatchApplyBegin that finished. - call_id: String, - /// Captured stdout (summary printed by apply_patch). - stdout: String, - /// Captured stderr (parser errors, IO failures, etc.). - stderr: String, - /// Whether the patch was applied successfully. - success: bool, - }, + PatchApplyEnd(PatchApplyEndEvent), +} + +// Individual event payload types matching each `EventMsg` variant. + +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct ErrorEvent { + pub message: String, +} + +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct AgentMessageEvent { + pub message: String, +} + +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct AgentReasoningEvent { + pub text: String, +} + +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct McpToolCallBeginEvent { + /// Identifier so this can be paired with the McpToolCallEnd event. + pub call_id: String, + /// Name of the MCP server as defined in the config. + pub server: String, + /// Name of the tool as given by the MCP server. + pub tool: String, + /// Arguments to the tool call. + pub arguments: Option, +} + +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct McpToolCallEndEvent { + /// Identifier for the corresponding McpToolCallBegin that finished. + pub call_id: String, + /// Whether the tool call was successful. If `false`, `result` might not be present. + pub success: bool, + /// Result of the tool call. Note this could be an error. + pub result: Option, +} + +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct ExecCommandBeginEvent { + /// Identifier so this can be paired with the ExecCommandEnd event. + pub call_id: String, + /// The command to be executed. + pub command: Vec, + /// The command's working directory if not the default cwd for the agent. + pub cwd: PathBuf, +} + +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct ExecCommandEndEvent { + /// Identifier for the ExecCommandBegin that finished. + pub call_id: String, + /// Captured stdout + pub stdout: String, + /// Captured stderr + pub stderr: String, + /// The command's exit code. + pub exit_code: i32, +} + +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct ExecApprovalRequestEvent { + /// The command to be executed. + pub command: Vec, + /// The command's working directory. + pub cwd: PathBuf, + /// Optional human-readable reason for the approval (e.g. retry without sandbox). + #[serde(skip_serializing_if = "Option::is_none")] + pub reason: Option, +} + +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct ApplyPatchApprovalRequestEvent { + pub changes: HashMap, + /// Optional explanatory reason (e.g. request for extra write access). + #[serde(skip_serializing_if = "Option::is_none")] + pub reason: Option, + /// When set, the agent is asking the user to allow writes under this root for the remainder of the session. + #[serde(skip_serializing_if = "Option::is_none")] + pub grant_root: Option, +} + +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct BackgroundEventEvent { + pub message: String, +} + +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct PatchApplyBeginEvent { + /// Identifier so this can be paired with the PatchApplyEnd event. + pub call_id: String, + /// If true, there was no ApplyPatchApprovalRequest for this patch. + pub auto_approved: bool, + /// The changes to be applied. + pub changes: HashMap, +} + +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct PatchApplyEndEvent { + /// Identifier for the PatchApplyBegin that finished. + pub call_id: String, + /// Captured stdout (summary printed by apply_patch). + pub stdout: String, + /// Captured stderr (parser errors, IO failures, etc.). + pub stderr: String, + /// Whether the patch was applied successfully. + pub success: bool, } #[derive(Debug, Default, Clone, Deserialize, Serialize)] @@ -478,3 +504,28 @@ pub struct Chunk { pub deleted_lines: Vec, pub inserted_lines: Vec, } + +#[cfg(test)] +mod tests { + #![allow(clippy::unwrap_used)] + use super::*; + + /// Serialize Event to verify that its JSON representation has the expected + /// amount of nesting. + #[test] + fn serialize_event() { + let session_id: Uuid = uuid::uuid!("67e55044-10b1-426f-9247-bb680e5fe0c8"); + let event = Event { + id: "1234".to_string(), + msg: EventMsg::SessionConfigured(SessionConfiguredEvent { + session_id, + model: "o4-mini".to_string(), + }), + }; + let serialized = serde_json::to_string(&event).unwrap(); + assert_eq!( + serialized, + r#"{"id":"1234","msg":{"type":"session_configured","session_id":"67e55044-10b1-426f-9247-bb680e5fe0c8","model":"o4-mini"}}"# + ); + } +} diff --git a/codex-rs/core/tests/live_agent.rs b/codex-rs/core/tests/live_agent.rs index c43c5c19..d6afb895 100644 --- a/codex-rs/core/tests/live_agent.rs +++ b/codex-rs/core/tests/live_agent.rs @@ -22,6 +22,8 @@ use std::time::Duration; use codex_core::Codex; use codex_core::config::Config; use codex_core::error::CodexErr; +use codex_core::protocol::AgentMessageEvent; +use codex_core::protocol::ErrorEvent; use codex_core::protocol::EventMsg; use codex_core::protocol::InputItem; use codex_core::protocol::Op; @@ -92,9 +94,11 @@ async fn live_streaming_and_prev_id_reset() { .expect("agent closed"); match ev.msg { - EventMsg::AgentMessage { .. } => saw_message_before_complete = true, + EventMsg::AgentMessage(_) => saw_message_before_complete = true, EventMsg::TaskComplete => break, - EventMsg::Error { message } => panic!("agent reported error in task1: {message}"), + EventMsg::Error(ErrorEvent { message }) => { + panic!("agent reported error in task1: {message}") + } _ => (), } } @@ -122,11 +126,15 @@ async fn live_streaming_and_prev_id_reset() { .expect("agent closed"); match &ev.msg { - EventMsg::AgentMessage { message } if message.contains("second turn succeeded") => { + EventMsg::AgentMessage(AgentMessageEvent { message }) + if message.contains("second turn succeeded") => + { got_expected = true; } EventMsg::TaskComplete => break, - EventMsg::Error { message } => panic!("agent reported error in task2: {message}"), + EventMsg::Error(ErrorEvent { message }) => { + panic!("agent reported error in task2: {message}") + } _ => (), } } @@ -171,19 +179,28 @@ async fn live_shell_function_call() { .expect("agent closed"); match ev.msg { - EventMsg::ExecCommandBegin { command, .. } => { + EventMsg::ExecCommandBegin(codex_core::protocol::ExecCommandBeginEvent { + command, + call_id: _, + cwd: _, + }) => { assert_eq!(command, vec!["echo", MARKER]); saw_begin = true; } - EventMsg::ExecCommandEnd { - stdout, exit_code, .. - } => { + 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)); saw_end_with_output = true; } EventMsg::TaskComplete => break, - EventMsg::Error { message } => panic!("agent error during shell test: {message}"), + EventMsg::Error(codex_core::protocol::ErrorEvent { message }) => { + panic!("agent error during shell test: {message}") + } _ => (), } } diff --git a/codex-rs/core/tests/previous_response_id.rs b/codex-rs/core/tests/previous_response_id.rs index 2c899df0..166e2be3 100644 --- a/codex-rs/core/tests/previous_response_id.rs +++ b/codex-rs/core/tests/previous_response_id.rs @@ -4,6 +4,8 @@ use codex_core::Codex; use codex_core::ModelProviderInfo; use codex_core::config::Config; use codex_core::exec::CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR; +use codex_core::protocol::ErrorEvent; +use codex_core::protocol::EventMsg; use codex_core::protocol::InputItem; use codex_core::protocol::Op; use serde_json::Value; @@ -127,7 +129,7 @@ async fn keeps_previous_response_id_between_tasks() { .await .unwrap() .unwrap(); - if matches!(ev.msg, codex_core::protocol::EventMsg::TaskComplete) { + if matches!(ev.msg, EventMsg::TaskComplete) { break; } } @@ -149,8 +151,8 @@ async fn keeps_previous_response_id_between_tasks() { .unwrap() .unwrap(); match ev.msg { - codex_core::protocol::EventMsg::TaskComplete => break, - codex_core::protocol::EventMsg::Error { message } => { + EventMsg::TaskComplete => break, + EventMsg::Error(ErrorEvent { message }) => { panic!("unexpected error: {message}") } _ => (), diff --git a/codex-rs/exec/src/event_processor.rs b/codex-rs/exec/src/event_processor.rs index d43f9d59..191d616b 100644 --- a/codex-rs/exec/src/event_processor.rs +++ b/codex-rs/exec/src/event_processor.rs @@ -1,8 +1,17 @@ use chrono::Utc; use codex_common::elapsed::format_elapsed; +use codex_core::protocol::AgentMessageEvent; +use codex_core::protocol::BackgroundEventEvent; +use codex_core::protocol::ErrorEvent; use codex_core::protocol::Event; use codex_core::protocol::EventMsg; +use codex_core::protocol::ExecCommandBeginEvent; +use codex_core::protocol::ExecCommandEndEvent; use codex_core::protocol::FileChange; +use codex_core::protocol::McpToolCallBeginEvent; +use codex_core::protocol::McpToolCallEndEvent; +use codex_core::protocol::PatchApplyBeginEvent; +use codex_core::protocol::PatchApplyEndEvent; use owo_colors::OwoColorize; use owo_colors::Style; use shlex::try_join; @@ -95,11 +104,11 @@ impl EventProcessor { pub(crate) fn process_event(&mut self, event: Event) { let Event { id, msg } = event; match msg { - EventMsg::Error { message } => { + EventMsg::Error(ErrorEvent { message }) => { let prefix = "ERROR:".style(self.red); ts_println!("{prefix} {message}"); } - EventMsg::BackgroundEvent { message } => { + EventMsg::BackgroundEvent(BackgroundEventEvent { message }) => { ts_println!("{}", message.style(self.dimmed)); } EventMsg::TaskStarted => { @@ -110,15 +119,15 @@ impl EventProcessor { let msg = format!("Task complete: {id}"); ts_println!("{}", msg.style(self.bold)); } - EventMsg::AgentMessage { message } => { + EventMsg::AgentMessage(AgentMessageEvent { message }) => { let prefix = "Agent message:".style(self.bold); ts_println!("{prefix} {message}"); } - EventMsg::ExecCommandBegin { + EventMsg::ExecCommandBegin(ExecCommandBeginEvent { call_id, command, cwd, - } => { + }) => { self.call_id_to_command.insert( call_id.clone(), ExecCommandBegin { @@ -133,12 +142,12 @@ impl EventProcessor { cwd.to_string_lossy(), ); } - EventMsg::ExecCommandEnd { + EventMsg::ExecCommandEnd(ExecCommandEndEvent { call_id, stdout, stderr, exit_code, - } => { + }) => { let exec_command = self.call_id_to_command.remove(&call_id); let (duration, call) = if let Some(ExecCommandBegin { command, @@ -173,19 +182,21 @@ impl EventProcessor { } // Handle MCP tool calls (e.g. calling external functions via MCP). - EventMsg::McpToolCallBegin { + EventMsg::McpToolCallBegin(McpToolCallBeginEvent { call_id, server, tool, arguments, - } => { + }) => { // Build fully-qualified tool name: server.tool let fq_tool_name = format!("{server}.{tool}"); // Format arguments as compact JSON so they fit on one line. let args_str = arguments .as_ref() - .map(|v| serde_json::to_string(v).unwrap_or_else(|_| v.to_string())) + .map(|v: &serde_json::Value| { + serde_json::to_string(v).unwrap_or_else(|_| v.to_string()) + }) .unwrap_or_default(); let invocation = if args_str.is_empty() { @@ -208,11 +219,11 @@ impl EventProcessor { invocation.style(self.bold), ); } - EventMsg::McpToolCallEnd { + EventMsg::McpToolCallEnd(McpToolCallEndEvent { call_id, success, result, - } => { + }) => { // Retrieve start time and invocation for duration calculation and labeling. let info = self.call_id_to_tool_call.remove(&call_id); @@ -243,11 +254,11 @@ impl EventProcessor { } } } - EventMsg::PatchApplyBegin { + EventMsg::PatchApplyBegin(PatchApplyBeginEvent { call_id, auto_approved, changes, - } => { + }) => { // Store metadata so we can calculate duration later when we // receive the corresponding PatchApplyEnd event. self.call_id_to_patch.insert( @@ -321,12 +332,12 @@ impl EventProcessor { } } } - EventMsg::PatchApplyEnd { + EventMsg::PatchApplyEnd(PatchApplyEndEvent { call_id, stdout, stderr, success, - } => { + }) => { let patch_begin = self.call_id_to_patch.remove(&call_id); // Compute duration and summary label similar to exec commands. @@ -355,10 +366,10 @@ impl EventProcessor { println!("{}", line.style(self.dimmed)); } } - EventMsg::ExecApprovalRequest { .. } => { + EventMsg::ExecApprovalRequest(_) => { // Should we exit? } - EventMsg::ApplyPatchApprovalRequest { .. } => { + EventMsg::ApplyPatchApprovalRequest(_) => { // Should we exit? } _ => { diff --git a/codex-rs/mcp-server/src/codex_tool_runner.rs b/codex-rs/mcp-server/src/codex_tool_runner.rs index 2f8a1a34..34534809 100644 --- a/codex-rs/mcp-server/src/codex_tool_runner.rs +++ b/codex-rs/mcp-server/src/codex_tool_runner.rs @@ -4,6 +4,7 @@ use codex_core::codex_wrapper::init_codex; use codex_core::config::Config as CodexConfig; +use codex_core::protocol::AgentMessageEvent; use codex_core::protocol::Event; use codex_core::protocol::EventMsg; use codex_core::protocol::InputItem; @@ -85,10 +86,10 @@ pub async fn run_codex_tool_session( let _ = outgoing.send(codex_event_to_notification(&event)).await; match &event.msg { - EventMsg::AgentMessage { message } => { + EventMsg::AgentMessage(AgentMessageEvent { message }) => { last_agent_message = Some(message.clone()); } - EventMsg::ExecApprovalRequest { .. } => { + EventMsg::ExecApprovalRequest(_) => { let result = CallToolResult { content: vec![CallToolResultContent::TextContent(TextContent { r#type: "text".to_string(), @@ -106,7 +107,7 @@ pub async fn run_codex_tool_session( .await; break; } - EventMsg::ApplyPatchApprovalRequest { .. } => { + EventMsg::ApplyPatchApprovalRequest(_) => { let result = CallToolResult { content: vec![CallToolResultContent::TextContent(TextContent { r#type: "text".to_string(), @@ -153,7 +154,7 @@ pub async fn run_codex_tool_session( .await; break; } - EventMsg::SessionConfigured { .. } => { + EventMsg::SessionConfigured(_) => { tracing::error!("unexpected SessionConfigured event"); } _ => {} diff --git a/codex-rs/tui/src/chatwidget.rs b/codex-rs/tui/src/chatwidget.rs index accb7305..a7ba51eb 100644 --- a/codex-rs/tui/src/chatwidget.rs +++ b/codex-rs/tui/src/chatwidget.rs @@ -5,10 +5,20 @@ use std::sync::mpsc::Sender; use codex_core::codex_wrapper::init_codex; use codex_core::config::Config; +use codex_core::protocol::AgentMessageEvent; +use codex_core::protocol::AgentReasoningEvent; +use codex_core::protocol::ApplyPatchApprovalRequestEvent; +use codex_core::protocol::ErrorEvent; use codex_core::protocol::Event; use codex_core::protocol::EventMsg; +use codex_core::protocol::ExecApprovalRequestEvent; +use codex_core::protocol::ExecCommandBeginEvent; +use codex_core::protocol::ExecCommandEndEvent; use codex_core::protocol::InputItem; +use codex_core::protocol::McpToolCallBeginEvent; +use codex_core::protocol::McpToolCallEndEvent; use codex_core::protocol::Op; +use codex_core::protocol::PatchApplyBeginEvent; use crossterm::event::KeyEvent; use ratatui::buffer::Buffer; use ratatui::layout::Constraint; @@ -213,11 +223,11 @@ impl ChatWidget<'_> { .add_session_info(&self.config, event); self.request_redraw()?; } - EventMsg::AgentMessage { message } => { + EventMsg::AgentMessage(AgentMessageEvent { message }) => { self.conversation_history.add_agent_message(message); self.request_redraw()?; } - EventMsg::AgentReasoning { text } => { + EventMsg::AgentReasoning(AgentReasoningEvent { text }) => { self.conversation_history.add_agent_reasoning(text); self.request_redraw()?; } @@ -229,15 +239,15 @@ impl ChatWidget<'_> { self.bottom_pane.set_task_running(false)?; self.request_redraw()?; } - EventMsg::Error { message } => { + EventMsg::Error(ErrorEvent { message }) => { self.conversation_history.add_error(message); self.bottom_pane.set_task_running(false)?; } - EventMsg::ExecApprovalRequest { + EventMsg::ExecApprovalRequest(ExecApprovalRequestEvent { command, cwd, reason, - } => { + }) => { let request = ApprovalRequest::Exec { id, command, @@ -246,11 +256,11 @@ impl ChatWidget<'_> { }; self.bottom_pane.push_approval_request(request)?; } - EventMsg::ApplyPatchApprovalRequest { + EventMsg::ApplyPatchApprovalRequest(ApplyPatchApprovalRequestEvent { changes, reason, grant_root, - } => { + }) => { // ------------------------------------------------------------------ // Before we even prompt the user for approval we surface the patch // summary in the main conversation so that the dialog appears in a @@ -276,18 +286,20 @@ impl ChatWidget<'_> { self.bottom_pane.push_approval_request(request)?; self.request_redraw()?; } - EventMsg::ExecCommandBegin { - call_id, command, .. - } => { + EventMsg::ExecCommandBegin(ExecCommandBeginEvent { + call_id, + command, + cwd: _, + }) => { self.conversation_history .add_active_exec_command(call_id, command); self.request_redraw()?; } - EventMsg::PatchApplyBegin { + EventMsg::PatchApplyBegin(PatchApplyBeginEvent { call_id: _, auto_approved, changes, - } => { + }) => { // Even when a patch is auto‑approved we still display the // summary so the user can follow along. self.conversation_history @@ -297,32 +309,31 @@ impl ChatWidget<'_> { } self.request_redraw()?; } - EventMsg::ExecCommandEnd { + EventMsg::ExecCommandEnd(ExecCommandEndEvent { call_id, exit_code, stdout, stderr, - .. - } => { + }) => { self.conversation_history .record_completed_exec_command(call_id, stdout, stderr, exit_code); self.request_redraw()?; } - EventMsg::McpToolCallBegin { + EventMsg::McpToolCallBegin(McpToolCallBeginEvent { call_id, server, tool, arguments, - } => { + }) => { self.conversation_history .add_active_mcp_tool_call(call_id, server, tool, arguments); self.request_redraw()?; } - EventMsg::McpToolCallEnd { + EventMsg::McpToolCallEnd(McpToolCallEndEvent { call_id, success, result, - } => { + }) => { self.conversation_history .record_completed_mcp_tool_call(call_id, success, result); self.request_redraw()?;