diff --git a/codex-rs/core/src/client.rs b/codex-rs/core/src/client.rs index 06e167bfc3..0d2239386f 100644 --- a/codex-rs/core/src/client.rs +++ b/codex-rs/core/src/client.rs @@ -788,6 +788,7 @@ impl ModelClient { let tools = create_tools_json_for_responses_api(&prompt.tools)?; let (instructions, tools) = if model_info.use_responses_lite { let additional_tools = ResponseItem::AdditionalTools { + id: None, role: "developer".to_string(), tools, }; diff --git a/codex-rs/core/src/session/mod.rs b/codex-rs/core/src/session/mod.rs index c85898555b..7e9569fdfd 100644 --- a/codex-rs/core/src/session/mod.rs +++ b/codex-rs/core/src/session/mod.rs @@ -2712,6 +2712,7 @@ impl Session { continue; } let prefix = match item { + ResponseItem::AdditionalTools { .. } => "at", ResponseItem::Message { .. } => "msg", ResponseItem::Reasoning { .. } => "rs", ResponseItem::LocalShellCall { .. } => "lsh", diff --git a/codex-rs/core/src/session/tests.rs b/codex-rs/core/src/session/tests.rs index 6f84da3c79..1245045e31 100644 --- a/codex-rs/core/src/session/tests.rs +++ b/codex-rs/core/src/session/tests.rs @@ -221,6 +221,19 @@ fn assign_missing_response_item_ids_skips_agent_messages() { assert!(items[1].id().is_some_and(|id| id.starts_with("msg_"))); } +#[test] +fn assign_missing_response_item_ids_assigns_additional_tools_ids() { + let items = Cow::Owned(vec![ResponseItem::AdditionalTools { + id: None, + role: "developer".to_string(), + tools: Vec::new(), + }]); + + let items = Session::assign_missing_response_item_ids(items); + + assert!(items[0].id().is_some_and(|id| id.starts_with("at_"))); +} + fn assistant_message(text: &str) -> ResponseItem { ResponseItem::Message { id: None, diff --git a/codex-rs/protocol/src/models.rs b/codex-rs/protocol/src/models.rs index 02f39ede6e..182bc90644 100644 --- a/codex-rs/protocol/src/models.rs +++ b/codex-rs/protocol/src/models.rs @@ -920,6 +920,8 @@ pub enum ResponseItem { #[schemars(skip)] #[ts(skip)] AdditionalTools { + #[serde(default, skip_serializing_if = "Option::is_none")] + id: Option, role: String, tools: Vec, }, @@ -1156,7 +1158,8 @@ impl ResponseItem { /// Returns the non-empty Responses API item ID, if present. pub fn id(&self) -> Option<&str> { match self { - Self::Message { id, .. } + Self::AdditionalTools { id, .. } + | Self::Message { id, .. } | Self::AgentMessage { id, .. } | Self::LocalShellCall { id, .. } | Self::FunctionCall { id, .. } @@ -1177,7 +1180,8 @@ impl ResponseItem { /// Sets or clears the Responses API item ID for variants that carry one. pub fn set_id(&mut self, new_id: Option) { match self { - Self::Message { id, .. } + Self::AdditionalTools { id, .. } + | Self::Message { id, .. } | Self::AgentMessage { id, .. } | Self::LocalShellCall { id, .. } | Self::FunctionCall { id, .. } @@ -2145,6 +2149,14 @@ mod tests { item.set_id(/*new_id*/ None); assert_eq!(item.id(), None); + + let mut additional_tools = ResponseItem::AdditionalTools { + id: None, + role: "developer".to_string(), + tools: Vec::new(), + }; + additional_tools.set_id(Some("at_test".to_string())); + assert_eq!(additional_tools.id(), Some("at_test")); } fn response_item_with_metadata(metadata: Option) -> ResponseItem {