From a534356fe1567ae1f452a980836fb3c4e2e04535 Mon Sep 17 00:00:00 2001 From: pakrym-oai Date: Tue, 30 Sep 2025 12:01:17 -0700 Subject: [PATCH 1/3] Wire up web search item (#4511) Add handling for web search events. --- ...mental_event_processor_with_json_output.rs | 15 +++++++++++ .../tests/event_processor_with_json_output.rs | 25 +++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/codex-rs/exec/src/experimental_event_processor_with_json_output.rs b/codex-rs/exec/src/experimental_event_processor_with_json_output.rs index 164ebecefa..01c439edc7 100644 --- a/codex-rs/exec/src/experimental_event_processor_with_json_output.rs +++ b/codex-rs/exec/src/experimental_event_processor_with_json_output.rs @@ -29,6 +29,7 @@ use crate::exec_events::TurnCompletedEvent; use crate::exec_events::TurnFailedEvent; use crate::exec_events::TurnStartedEvent; use crate::exec_events::Usage; +use crate::exec_events::WebSearchItem; use codex_core::config::Config; use codex_core::plan_tool::StepStatus; use codex_core::plan_tool::UpdatePlanArgs; @@ -46,6 +47,7 @@ use codex_core::protocol::PatchApplyEndEvent; use codex_core::protocol::SessionConfiguredEvent; use codex_core::protocol::TaskCompleteEvent; use codex_core::protocol::TaskStartedEvent; +use codex_core::protocol::WebSearchEndEvent; use tracing::error; use tracing::warn; @@ -106,6 +108,8 @@ impl ExperimentalEventProcessorWithJsonOutput { EventMsg::McpToolCallEnd(ev) => self.handle_mcp_tool_call_end(ev), EventMsg::PatchApplyBegin(ev) => self.handle_patch_apply_begin(ev), EventMsg::PatchApplyEnd(ev) => self.handle_patch_apply_end(ev), + EventMsg::WebSearchBegin(_) => Vec::new(), + EventMsg::WebSearchEnd(ev) => self.handle_web_search_end(ev), EventMsg::TokenCount(ev) => { if let Some(info) = &ev.info { self.last_total_token_usage = Some(info.total_token_usage.clone()); @@ -143,6 +147,17 @@ impl ExperimentalEventProcessorWithJsonOutput { })] } + fn handle_web_search_end(&self, ev: &WebSearchEndEvent) -> Vec { + let item = ThreadItem { + id: self.get_next_item_id(), + details: ThreadItemDetails::WebSearch(WebSearchItem { + query: ev.query.clone(), + }), + }; + + vec![ThreadEvent::ItemCompleted(ItemCompletedEvent { item })] + } + fn handle_agent_message(&self, payload: &AgentMessageEvent) -> Vec { let item = ThreadItem { id: self.get_next_item_id(), diff --git a/codex-rs/exec/tests/event_processor_with_json_output.rs b/codex-rs/exec/tests/event_processor_with_json_output.rs index c462879767..8ab5f3b263 100644 --- a/codex-rs/exec/tests/event_processor_with_json_output.rs +++ b/codex-rs/exec/tests/event_processor_with_json_output.rs @@ -12,6 +12,7 @@ use codex_core::protocol::McpToolCallEndEvent; use codex_core::protocol::PatchApplyBeginEvent; use codex_core::protocol::PatchApplyEndEvent; use codex_core::protocol::SessionConfiguredEvent; +use codex_core::protocol::WebSearchEndEvent; use codex_exec::exec_events::AssistantMessageItem; use codex_exec::exec_events::CommandExecutionItem; use codex_exec::exec_events::CommandExecutionStatus; @@ -34,6 +35,7 @@ use codex_exec::exec_events::TurnCompletedEvent; use codex_exec::exec_events::TurnFailedEvent; use codex_exec::exec_events::TurnStartedEvent; use codex_exec::exec_events::Usage; +use codex_exec::exec_events::WebSearchItem; use codex_exec::experimental_event_processor_with_json_output::ExperimentalEventProcessorWithJsonOutput; use mcp_types::CallToolResult; use pretty_assertions::assert_eq; @@ -89,6 +91,29 @@ fn task_started_produces_turn_started_event() { assert_eq!(out, vec![ThreadEvent::TurnStarted(TurnStartedEvent {})]); } +#[test] +fn web_search_end_emits_item_completed() { + let mut ep = ExperimentalEventProcessorWithJsonOutput::new(None); + let query = "rust async await".to_string(); + let out = ep.collect_thread_events(&event( + "w1", + EventMsg::WebSearchEnd(WebSearchEndEvent { + call_id: "call-123".to_string(), + query: query.clone(), + }), + )); + + assert_eq!( + out, + vec![ThreadEvent::ItemCompleted(ItemCompletedEvent { + item: ThreadItem { + id: "item_0".to_string(), + details: ThreadItemDetails::WebSearch(WebSearchItem { query }), + }, + })] + ); +} + #[test] fn plan_update_emits_todo_list_started_updated_and_completed() { use codex_core::plan_tool::PlanItemArg; From 6910be32245988c5d531e791157c469e546a7701 Mon Sep 17 00:00:00 2001 From: Michael Bolin Date: Tue, 30 Sep 2025 12:03:32 -0700 Subject: [PATCH 2/3] fix: ensure every variant of ClientRequest has a params field (#4512) This ensures changes the generated TypeScript type for `ClientRequest` so that instead of this: ```typescript /** * Request from the client to the server. */ export type ClientRequest = | { method: "initialize"; id: RequestId; params: InitializeParams } | { method: "newConversation"; id: RequestId; params: NewConversationParams } // ... | { method: "getUserAgent"; id: RequestId } | { method: "userInfo"; id: RequestId } // ... ``` we have this: ```typescript /** * Request from the client to the server. */ export type ClientRequest = | { method: "initialize"; id: RequestId; params: InitializeParams } | { method: "newConversation"; id: RequestId; params: NewConversationParams } // ... | { method: "getUserAgent"; id: RequestId; params: undefined } | { method: "userInfo"; id: RequestId; params: undefined } // ... ``` which makes TypeScript happier when it comes to destructuring instances of `ClientRequest` because it does not complain about `params` not being guaranteed to exist anymore. --- .../app-server/src/codex_message_processor.rs | 25 +++++++++++++++---- codex-rs/protocol/src/mcp_protocol.rs | 20 +++++++++++++++ 2 files changed, 40 insertions(+), 5 deletions(-) diff --git a/codex-rs/app-server/src/codex_message_processor.rs b/codex-rs/app-server/src/codex_message_processor.rs index 4d11aad421..6548c5f9f0 100644 --- a/codex-rs/app-server/src/codex_message_processor.rs +++ b/codex-rs/app-server/src/codex_message_processor.rs @@ -193,28 +193,43 @@ impl CodexMessageProcessor { ClientRequest::LoginApiKey { request_id, params } => { self.login_api_key(request_id, params).await; } - ClientRequest::LoginChatGpt { request_id } => { + ClientRequest::LoginChatGpt { + request_id, + params: _, + } => { self.login_chatgpt(request_id).await; } ClientRequest::CancelLoginChatGpt { request_id, params } => { self.cancel_login_chatgpt(request_id, params.login_id).await; } - ClientRequest::LogoutChatGpt { request_id } => { + ClientRequest::LogoutChatGpt { + request_id, + params: _, + } => { self.logout_chatgpt(request_id).await; } ClientRequest::GetAuthStatus { request_id, params } => { self.get_auth_status(request_id, params).await; } - ClientRequest::GetUserSavedConfig { request_id } => { + ClientRequest::GetUserSavedConfig { + request_id, + params: _, + } => { self.get_user_saved_config(request_id).await; } ClientRequest::SetDefaultModel { request_id, params } => { self.set_default_model(request_id, params).await; } - ClientRequest::GetUserAgent { request_id } => { + ClientRequest::GetUserAgent { + request_id, + params: _, + } => { self.get_user_agent(request_id).await; } - ClientRequest::UserInfo { request_id } => { + ClientRequest::UserInfo { + request_id, + params: _, + } => { self.get_user_info(request_id).await; } ClientRequest::FuzzyFileSearch { request_id, params } => { diff --git a/codex-rs/protocol/src/mcp_protocol.rs b/codex-rs/protocol/src/mcp_protocol.rs index 107055f6e0..4e01352188 100644 --- a/codex-rs/protocol/src/mcp_protocol.rs +++ b/codex-rs/protocol/src/mcp_protocol.rs @@ -158,6 +158,10 @@ pub enum ClientRequest { LoginChatGpt { #[serde(rename = "id")] request_id: RequestId, + + #[ts(type = "undefined")] + #[serde(skip_serializing_if = "Option::is_none")] + params: Option<()>, }, CancelLoginChatGpt { #[serde(rename = "id")] @@ -167,6 +171,10 @@ pub enum ClientRequest { LogoutChatGpt { #[serde(rename = "id")] request_id: RequestId, + + #[ts(type = "undefined")] + #[serde(skip_serializing_if = "Option::is_none")] + params: Option<()>, }, GetAuthStatus { #[serde(rename = "id")] @@ -176,6 +184,10 @@ pub enum ClientRequest { GetUserSavedConfig { #[serde(rename = "id")] request_id: RequestId, + + #[ts(type = "undefined")] + #[serde(skip_serializing_if = "Option::is_none")] + params: Option<()>, }, SetDefaultModel { #[serde(rename = "id")] @@ -185,10 +197,18 @@ pub enum ClientRequest { GetUserAgent { #[serde(rename = "id")] request_id: RequestId, + + #[ts(type = "undefined")] + #[serde(skip_serializing_if = "Option::is_none")] + params: Option<()>, }, UserInfo { #[serde(rename = "id")] request_id: RequestId, + + #[ts(type = "undefined")] + #[serde(skip_serializing_if = "Option::is_none")] + params: Option<()>, }, FuzzyFileSearch { #[serde(rename = "id")] From d059efe0864e27f169a1ccb176c4cef05cc35ae5 Mon Sep 17 00:00:00 2001 From: Michael Bolin Date: Tue, 30 Sep 2025 12:55:45 -0700 Subject: [PATCH 3/3] fix: clean up TypeScript exports --- codex-rs/protocol-ts/src/lib.rs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/codex-rs/protocol-ts/src/lib.rs b/codex-rs/protocol-ts/src/lib.rs index 848aeb25c7..f6990e4228 100644 --- a/codex-rs/protocol-ts/src/lib.rs +++ b/codex-rs/protocol-ts/src/lib.rs @@ -21,32 +21,34 @@ pub fn generate_ts(out_dir: &Path, prettier: Option<&Path>) -> Result<()> { codex_protocol::mcp_protocol::InputItem::export_all_to(out_dir)?; codex_protocol::mcp_protocol::ClientRequest::export_all_to(out_dir)?; codex_protocol::mcp_protocol::ServerRequest::export_all_to(out_dir)?; + + // Response types for ClientRequest (mirror enum order). codex_protocol::mcp_protocol::InitializeResponse::export_all_to(out_dir)?; codex_protocol::mcp_protocol::NewConversationResponse::export_all_to(out_dir)?; codex_protocol::mcp_protocol::ListConversationsResponse::export_all_to(out_dir)?; codex_protocol::mcp_protocol::ResumeConversationResponse::export_all_to(out_dir)?; codex_protocol::mcp_protocol::ArchiveConversationResponse::export_all_to(out_dir)?; - codex_protocol::mcp_protocol::AddConversationSubscriptionResponse::export_all_to(out_dir)?; - codex_protocol::mcp_protocol::RemoveConversationSubscriptionResponse::export_all_to(out_dir)?; codex_protocol::mcp_protocol::SendUserMessageResponse::export_all_to(out_dir)?; codex_protocol::mcp_protocol::SendUserTurnResponse::export_all_to(out_dir)?; codex_protocol::mcp_protocol::InterruptConversationResponse::export_all_to(out_dir)?; + codex_protocol::mcp_protocol::AddConversationSubscriptionResponse::export_all_to(out_dir)?; + codex_protocol::mcp_protocol::RemoveConversationSubscriptionResponse::export_all_to(out_dir)?; codex_protocol::mcp_protocol::GitDiffToRemoteResponse::export_all_to(out_dir)?; - codex_protocol::mcp_protocol::LoginApiKeyParams::export_all_to(out_dir)?; codex_protocol::mcp_protocol::LoginApiKeyResponse::export_all_to(out_dir)?; codex_protocol::mcp_protocol::LoginChatGptResponse::export_all_to(out_dir)?; codex_protocol::mcp_protocol::CancelLoginChatGptResponse::export_all_to(out_dir)?; codex_protocol::mcp_protocol::LogoutChatGptResponse::export_all_to(out_dir)?; codex_protocol::mcp_protocol::GetAuthStatusResponse::export_all_to(out_dir)?; - codex_protocol::mcp_protocol::ApplyPatchApprovalResponse::export_all_to(out_dir)?; - codex_protocol::mcp_protocol::ExecCommandApprovalResponse::export_all_to(out_dir)?; - codex_protocol::mcp_protocol::FuzzyFileSearchParams::export_all_to(out_dir)?; - codex_protocol::mcp_protocol::FuzzyFileSearchResult::export_all_to(out_dir)?; - codex_protocol::mcp_protocol::FuzzyFileSearchResponse::export_all_to(out_dir)?; codex_protocol::mcp_protocol::GetUserSavedConfigResponse::export_all_to(out_dir)?; codex_protocol::mcp_protocol::SetDefaultModelResponse::export_all_to(out_dir)?; codex_protocol::mcp_protocol::GetUserAgentResponse::export_all_to(out_dir)?; codex_protocol::mcp_protocol::UserInfoResponse::export_all_to(out_dir)?; + codex_protocol::mcp_protocol::FuzzyFileSearchResponse::export_all_to(out_dir)?; + codex_protocol::mcp_protocol::ExecArbitraryCommandResponse::export_all_to(out_dir)?; + + // Response types for ServerRequest (mirror enum order). + codex_protocol::mcp_protocol::ApplyPatchApprovalResponse::export_all_to(out_dir)?; + codex_protocol::mcp_protocol::ExecCommandApprovalResponse::export_all_to(out_dir)?; // All notification types reachable from this enum will be generated by // induction, so they do not need to be listed individually.