mirror of
https://github.com/openai/codex.git
synced 2026-10-21 05:22:21 +00:00
Remove the legacy code-mode tool metadata inventory (#37500)
## What changed - Stop adding `code_mode_tool_names` to Responses Lite turn metadata. - Keep `code_mode_tool_names` reserved so client metadata cannot reintroduce the removed, unbounded inventory. - Use the opt-in `tool_namespaces_info` metadata, including each function's `code_mode_name`, when authoritative tool details are requested. ## Testing - Update Responses Lite and code-mode tests to verify the opt-in namespace metadata and normalized code-mode names. GitOrigin-RevId: cbfb9af984de71c7cc175d0cd47ae799a5275309
This commit is contained in:
@@ -7,7 +7,6 @@ use codex_analytics::CompactionReason;
|
||||
use codex_analytics::CompactionStrategy;
|
||||
use codex_analytics::CompactionTrigger;
|
||||
use codex_protocol::ThreadId;
|
||||
use codex_protocol::ToolName;
|
||||
use codex_protocol::protocol::InternalSessionSource;
|
||||
use codex_protocol::protocol::SessionSource;
|
||||
use codex_protocol::protocol::SubAgentSource;
|
||||
@@ -31,7 +30,8 @@ pub(crate) const TURN_ID_KEY: &str = "turn_id";
|
||||
pub(crate) const WINDOW_ID_KEY: &str = "window_id";
|
||||
pub(crate) const REQUEST_KIND_KEY: &str = "request_kind";
|
||||
pub(crate) const COMPACTION_KEY: &str = "compaction";
|
||||
pub(crate) const CODE_MODE_TOOL_NAMES_KEY: &str = "code_mode_tool_names";
|
||||
// Keep the removed inventory reserved so callers cannot reintroduce oversized metadata.
|
||||
pub(crate) const LEGACY_CODE_MODE_TOOL_NAMES_KEY: &str = "code_mode_tool_names";
|
||||
pub(crate) const TOOL_NAMESPACES_INFO_KEY: &str = "tool_namespaces_info";
|
||||
pub(crate) const TURN_STARTED_AT_UNIX_MS_KEY: &str = "turn_started_at_unix_ms";
|
||||
|
||||
@@ -58,7 +58,7 @@ const RESERVED_METADATA_KEYS: &[&str] = &[
|
||||
X_OPENAI_SUBAGENT_HEADER,
|
||||
REQUEST_KIND_KEY,
|
||||
COMPACTION_KEY,
|
||||
CODE_MODE_TOOL_NAMES_KEY,
|
||||
LEGACY_CODE_MODE_TOOL_NAMES_KEY,
|
||||
TOOL_NAMESPACES_INFO_KEY,
|
||||
TURN_STARTED_AT_UNIX_MS_KEY,
|
||||
FORKED_FROM_THREAD_ID_KEY,
|
||||
@@ -202,7 +202,6 @@ pub struct CodexResponsesMetadata {
|
||||
pub(crate) thread_source: Option<ThreadSource>,
|
||||
pub(crate) sandbox: Option<String>,
|
||||
pub(crate) workspaces: BTreeMap<String, TurnMetadataWorkspace>,
|
||||
pub(crate) code_mode_tool_names: Option<BTreeMap<String, ToolName>>,
|
||||
pub(crate) tool_namespaces_info: Option<TurnToolNamespacesInfo>,
|
||||
pub(crate) turn_started_at_unix_ms: Option<i64>,
|
||||
pub(crate) extra: BTreeMap<String, String>,
|
||||
@@ -231,7 +230,6 @@ impl CodexResponsesMetadata {
|
||||
thread_source: None,
|
||||
sandbox: None,
|
||||
workspaces: BTreeMap::new(),
|
||||
code_mode_tool_names: None,
|
||||
tool_namespaces_info: None,
|
||||
turn_started_at_unix_ms: None,
|
||||
extra: BTreeMap::new(),
|
||||
@@ -289,11 +287,10 @@ impl CodexResponsesMetadata {
|
||||
pub(crate) fn compatibility_headers(&self) -> ApiHeaderMap {
|
||||
let mut headers = ApiHeaderMap::new();
|
||||
insert_header(&mut headers, X_CODEX_WINDOW_ID_HEADER, &self.window_id);
|
||||
// Direct x-codex-turn-metadata is compatibility output. Keep unbounded tool inventories
|
||||
// Direct x-codex-turn-metadata is compatibility output. Keep the unbounded tool inventory
|
||||
// in client_metadata only so HTTP and WebSocket compatibility headers remain bounded.
|
||||
if self.has_turn_metadata()
|
||||
&& let Ok(turn_metadata_json) = to_ascii_json_string(&CodexTurnMetadataPayload {
|
||||
code_mode_tool_names: None,
|
||||
tool_namespaces_info: None,
|
||||
..self.turn_metadata_payload()
|
||||
})
|
||||
@@ -343,7 +340,6 @@ impl CodexResponsesMetadata {
|
||||
thread_source: self.thread_source.as_ref(),
|
||||
sandbox: self.sandbox.as_deref(),
|
||||
workspaces: non_empty_workspaces(&self.workspaces),
|
||||
code_mode_tool_names: self.code_mode_tool_names.as_ref(),
|
||||
tool_namespaces_info: self.tool_namespaces_info.as_ref(),
|
||||
turn_started_at_unix_ms: self.turn_started_at_unix_ms,
|
||||
compaction,
|
||||
@@ -437,8 +433,6 @@ struct CodexTurnMetadataPayload<'a> {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
workspaces: Option<&'a BTreeMap<String, TurnMetadataWorkspace>>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
code_mode_tool_names: Option<&'a BTreeMap<String, ToolName>>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
tool_namespaces_info: Option<&'a TurnToolNamespacesInfo>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
turn_started_at_unix_ms: Option<i64>,
|
||||
|
||||
@@ -782,12 +782,6 @@ fn register_code_mode_executors(
|
||||
code_mode_nested_tool_specs.push((spec, cached_runtime));
|
||||
}
|
||||
|
||||
if turn_context.model_info.use_responses_lite {
|
||||
turn_context
|
||||
.turn_metadata_state
|
||||
.set_code_mode_tool_names(code_mode_tool_names.clone());
|
||||
}
|
||||
|
||||
let namespace_descriptions = code_mode_namespace_descriptions(&exec_prompt_tool_specs);
|
||||
let mut enabled_tools =
|
||||
collect_code_mode_exec_prompt_tool_definitions(exec_prompt_tool_specs.iter());
|
||||
|
||||
@@ -1696,6 +1696,9 @@ async fn code_mode_uses_the_first_normalized_tool_identity() {
|
||||
let mut metadata_state = None;
|
||||
let plan = probe_with(
|
||||
|turn| {
|
||||
update_config(turn, |config| {
|
||||
config.tool_registry.turn_metadata_includes_tool_info = true;
|
||||
});
|
||||
set_feature(turn, Feature::CodeMode, /*enabled*/ true);
|
||||
if code_mode_only {
|
||||
set_feature(turn, Feature::CodeModeOnly, /*enabled*/ true);
|
||||
@@ -1741,15 +1744,23 @@ async fn code_mode_uses_the_first_normalized_tool_identity() {
|
||||
"window".to_string(),
|
||||
CodexResponsesRequestKind::Turn,
|
||||
);
|
||||
let code_mode_tool_names = metadata
|
||||
.code_mode_tool_names
|
||||
let tool_namespaces = metadata
|
||||
.tool_namespaces_info
|
||||
.as_ref()
|
||||
.expect("Responses Lite should receive the supported code-mode tools");
|
||||
.expect("opted-in Responses Lite should receive the supported tools");
|
||||
assert_eq!(
|
||||
code_mode_tool_names.get("normalized_alias__lookup"),
|
||||
Some(&winner_name),
|
||||
tool_namespaces["normalized-alias"].functions["lookup"]
|
||||
.code_mode_name
|
||||
.as_deref(),
|
||||
Some("normalized_alias__lookup"),
|
||||
);
|
||||
assert!(
|
||||
tool_namespaces
|
||||
.get("normalized_alias")
|
||||
.and_then(|namespace| namespace.functions.get("lookup"))
|
||||
.and_then(|function| function.code_mode_name.as_ref())
|
||||
.is_none()
|
||||
);
|
||||
assert!(!code_mode_tool_names.contains_key(codex_tools::TOOL_SEARCH_TOOL_NAME));
|
||||
|
||||
if !code_mode_only && !shadow_is_deferred {
|
||||
let ToolSpec::Namespace(namespace) = plan.visible_spec("normalized_alias") else {
|
||||
|
||||
@@ -26,7 +26,6 @@ use codex_git_utils::get_git_repo_root;
|
||||
use codex_git_utils::get_has_changes_in_repo;
|
||||
use codex_git_utils::get_head_commit_hash;
|
||||
use codex_protocol::ThreadId;
|
||||
use codex_protocol::ToolName;
|
||||
use codex_protocol::config_types::WindowsSandboxLevel;
|
||||
use codex_protocol::models::PermissionProfile;
|
||||
use codex_protocol::openai_models::ReasoningEffort as ReasoningEffortConfig;
|
||||
@@ -103,7 +102,6 @@ pub(crate) struct TurnMetadataState {
|
||||
turn_id: String,
|
||||
sandbox: Option<String>,
|
||||
enriched_workspaces: RwLock<Option<BTreeMap<String, TurnMetadataWorkspace>>>,
|
||||
code_mode_tool_names: RwLock<Option<BTreeMap<String, ToolName>>>,
|
||||
tool_namespaces_info: RwLock<Option<TurnToolNamespacesInfo>>,
|
||||
turn_started_at_unix_ms: RwLock<Option<i64>>,
|
||||
responsesapi_client_metadata: RwLock<BTreeMap<String, String>>,
|
||||
@@ -149,7 +147,6 @@ impl TurnMetadataState {
|
||||
turn_id,
|
||||
sandbox,
|
||||
enriched_workspaces: RwLock::new(None),
|
||||
code_mode_tool_names: RwLock::new(None),
|
||||
tool_namespaces_info: RwLock::new(None),
|
||||
turn_started_at_unix_ms: RwLock::new(None),
|
||||
responsesapi_client_metadata: RwLock::new(BTreeMap::new()),
|
||||
@@ -164,7 +161,6 @@ impl TurnMetadataState {
|
||||
) -> Option<serde_json::Value> {
|
||||
let mut responses_metadata = self.responses_metadata_template();
|
||||
// Never serialize harness-owned tool inventory for external MCP servers.
|
||||
responses_metadata.code_mode_tool_names = None;
|
||||
responses_metadata.tool_namespaces_info = None;
|
||||
let Value::Object(mut metadata) = responses_metadata.turn_metadata_value()? else {
|
||||
return None;
|
||||
@@ -218,17 +214,6 @@ impl TurnMetadataState {
|
||||
.store(true, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
pub(crate) fn set_code_mode_tool_names(
|
||||
&self,
|
||||
code_mode_tool_names: BTreeMap<String, ToolName>,
|
||||
) {
|
||||
*self
|
||||
.code_mode_tool_names
|
||||
.write()
|
||||
.unwrap_or_else(std::sync::PoisonError::into_inner) =
|
||||
(!code_mode_tool_names.is_empty()).then_some(code_mode_tool_names);
|
||||
}
|
||||
|
||||
pub(crate) fn set_tool_namespaces_info(&self, tool_namespaces_info: TurnToolNamespacesInfo) {
|
||||
*self
|
||||
.tool_namespaces_info
|
||||
@@ -274,11 +259,6 @@ impl TurnMetadataState {
|
||||
thread_source: self.thread_source.clone(),
|
||||
sandbox: self.sandbox.clone(),
|
||||
workspaces: self.current_workspaces(),
|
||||
code_mode_tool_names: self
|
||||
.code_mode_tool_names
|
||||
.read()
|
||||
.unwrap_or_else(std::sync::PoisonError::into_inner)
|
||||
.clone(),
|
||||
tool_namespaces_info: self
|
||||
.tool_namespaces_info
|
||||
.read()
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use super::*;
|
||||
|
||||
use crate::responses_metadata::CODE_MODE_TOOL_NAMES_KEY;
|
||||
use crate::responses_metadata::CodexResponsesRequestKind;
|
||||
use crate::responses_metadata::CompactionTurnMetadata;
|
||||
use crate::responses_metadata::INSTALLATION_ID_KEY;
|
||||
use crate::responses_metadata::LEGACY_CODE_MODE_TOOL_NAMES_KEY;
|
||||
use crate::responses_metadata::PARENT_TURN_ID_KEY;
|
||||
use crate::responses_metadata::TOOL_NAMESPACES_INFO_KEY;
|
||||
use crate::responses_metadata::TurnToolFunctionInfo;
|
||||
@@ -15,7 +15,6 @@ use codex_analytics::CompactionImplementation;
|
||||
use codex_analytics::CompactionPhase;
|
||||
use codex_analytics::CompactionReason;
|
||||
use codex_analytics::CompactionTrigger;
|
||||
use codex_protocol::ToolName;
|
||||
use codex_protocol::models::PermissionProfile;
|
||||
use codex_protocol::openai_models::ReasoningEffort as ReasoningEffortConfig;
|
||||
use codex_protocol::protocol::SessionSource;
|
||||
@@ -550,7 +549,7 @@ fn turn_metadata_state_ignores_client_reserved_metadata_before_start() {
|
||||
);
|
||||
state.set_responsesapi_client_metadata(HashMap::from([
|
||||
(
|
||||
CODE_MODE_TOOL_NAMES_KEY.to_string(),
|
||||
LEGACY_CODE_MODE_TOOL_NAMES_KEY.to_string(),
|
||||
"client-supplied".to_string(),
|
||||
),
|
||||
(
|
||||
@@ -576,7 +575,7 @@ fn turn_metadata_state_ignores_client_reserved_metadata_before_start() {
|
||||
let header = test_turn_metadata_header(&state);
|
||||
let json: Value = serde_json::from_str(&header).expect("json");
|
||||
|
||||
assert!(json.get(CODE_MODE_TOOL_NAMES_KEY).is_none());
|
||||
assert!(json.get(LEGACY_CODE_MODE_TOOL_NAMES_KEY).is_none());
|
||||
assert!(json.get(TOOL_NAMESPACES_INFO_KEY).is_none());
|
||||
assert!(json.get("turn_started_at_unix_ms").is_none());
|
||||
assert!(json.get("forked_from_thread_id").is_none());
|
||||
@@ -650,7 +649,7 @@ fn turn_metadata_state_merges_client_metadata_without_replacing_reserved_fields(
|
||||
("parent_turn_id".to_string(), "client-supplied".to_string()),
|
||||
("subagent_kind".to_string(), "client-supplied".to_string()),
|
||||
(
|
||||
CODE_MODE_TOOL_NAMES_KEY.to_string(),
|
||||
LEGACY_CODE_MODE_TOOL_NAMES_KEY.to_string(),
|
||||
"client-supplied".to_string(),
|
||||
),
|
||||
(
|
||||
@@ -667,13 +666,6 @@ fn turn_metadata_state_merges_client_metadata_without_replacing_reserved_fields(
|
||||
),
|
||||
]));
|
||||
state.set_turn_started_at_unix_ms(/*turn_started_at_unix_ms*/ 1_700_000_000_123);
|
||||
state.set_code_mode_tool_names(BTreeMap::from([
|
||||
("exec_command".to_string(), ToolName::plain("exec_command")),
|
||||
(
|
||||
"mcp__calendar__lookup".to_string(),
|
||||
ToolName::namespaced("mcp__calendar", "lookup"),
|
||||
),
|
||||
]));
|
||||
state.set_tool_namespaces_info(BTreeMap::from([(
|
||||
"mcp__calendar".to_string(),
|
||||
TurnToolNamespaceInfo {
|
||||
@@ -705,19 +697,7 @@ fn turn_metadata_state_merges_client_metadata_without_replacing_reserved_fields(
|
||||
assert_eq!(json["reasoning_effort"].as_str(), Some("client-supplied"));
|
||||
assert_eq!(json["session_id"].as_str(), Some("session-a"));
|
||||
assert_eq!(json["thread_id"].as_str(), Some("thread-a"));
|
||||
assert_eq!(
|
||||
json[CODE_MODE_TOOL_NAMES_KEY],
|
||||
serde_json::json!({
|
||||
"exec_command": {
|
||||
"name": "exec_command",
|
||||
"namespace": null,
|
||||
},
|
||||
"mcp__calendar__lookup": {
|
||||
"name": "lookup",
|
||||
"namespace": "mcp__calendar",
|
||||
},
|
||||
})
|
||||
);
|
||||
assert!(json.get(LEGACY_CODE_MODE_TOOL_NAMES_KEY).is_none());
|
||||
assert_eq!(
|
||||
json[TOOL_NAMESPACES_INFO_KEY],
|
||||
serde_json::json!({
|
||||
@@ -795,7 +775,7 @@ fn turn_metadata_state_merges_client_metadata_without_replacing_reserved_fields(
|
||||
.expect("compatibility metadata json");
|
||||
assert!(
|
||||
compatibility_metadata
|
||||
.get(CODE_MODE_TOOL_NAMES_KEY)
|
||||
.get(LEGACY_CODE_MODE_TOOL_NAMES_KEY)
|
||||
.is_none()
|
||||
);
|
||||
assert!(
|
||||
@@ -809,7 +789,7 @@ fn turn_metadata_state_merges_client_metadata_without_replacing_reserved_fields(
|
||||
.expect("turn metadata should be present");
|
||||
assert_eq!(meta["model"].as_str(), Some("gpt-5.4"));
|
||||
assert_eq!(meta["reasoning_effort"].as_str(), Some("high"));
|
||||
assert!(meta.get(CODE_MODE_TOOL_NAMES_KEY).is_none());
|
||||
assert!(meta.get(LEGACY_CODE_MODE_TOOL_NAMES_KEY).is_none());
|
||||
assert!(meta.get(TOOL_NAMESPACES_INFO_KEY).is_none());
|
||||
assert!(meta.get(PARENT_TURN_ID_KEY).is_none());
|
||||
assert!(meta.get(WINDOW_ID_KEY).is_none());
|
||||
|
||||
@@ -79,29 +79,6 @@ async fn websocket_model_switch_to_responses_lite_omits_top_level_tools() -> Res
|
||||
|
||||
assert_eq!(non_lite_turn["model"].as_str(), Some("gpt-5.2"));
|
||||
assert_eq!(lite_turn["model"].as_str(), Some("gpt-5.4"));
|
||||
let non_lite_turn_metadata: Value = serde_json::from_str(
|
||||
non_lite_turn["client_metadata"]["x-codex-turn-metadata"]
|
||||
.as_str()
|
||||
.expect("non-lite turn metadata"),
|
||||
)?;
|
||||
assert!(non_lite_turn_metadata.get("code_mode_tool_names").is_none());
|
||||
let lite_turn_metadata: Value = serde_json::from_str(
|
||||
lite_turn["client_metadata"]["x-codex-turn-metadata"]
|
||||
.as_str()
|
||||
.expect("Responses Lite turn metadata"),
|
||||
)?;
|
||||
assert_eq!(
|
||||
lite_turn_metadata["code_mode_tool_names"]["view_image"],
|
||||
serde_json::json!({
|
||||
"name": "view_image",
|
||||
"namespace": null,
|
||||
})
|
||||
);
|
||||
assert!(
|
||||
lite_turn["client_metadata"]
|
||||
.get("x-codex-code-mode-tool-names")
|
||||
.is_none()
|
||||
);
|
||||
assert!(
|
||||
non_lite_turn
|
||||
.get("tools")
|
||||
|
||||
@@ -4853,6 +4853,7 @@ async fn code_mode_uses_the_first_dynamic_tool_for_a_normalized_name() -> Result
|
||||
model_info.tool_mode = Some(ToolMode::CodeMode);
|
||||
})
|
||||
.with_config(|config| {
|
||||
config.tool_registry.turn_metadata_includes_tool_info = true;
|
||||
config
|
||||
.features
|
||||
.enable(Feature::CodeMode)
|
||||
@@ -5041,18 +5042,9 @@ text(JSON.stringify({
|
||||
.as_str()
|
||||
.expect("Responses Lite should contain serialized turn metadata"),
|
||||
)?;
|
||||
assert_eq!(
|
||||
metadata["code_mode_tool_names"]["foo_bar"],
|
||||
serde_json::json!({
|
||||
"name": "foo-bar",
|
||||
"namespace": null,
|
||||
}),
|
||||
);
|
||||
assert!(
|
||||
metadata["code_mode_tool_names"]
|
||||
.get("tool_search")
|
||||
.is_none()
|
||||
);
|
||||
let functions = &metadata["tool_namespaces_info"]["functions"]["functions"];
|
||||
assert_eq!(functions["foo-bar"]["code_mode_name"], "foo_bar");
|
||||
assert!(functions["foo_bar"]["code_mode_name"].is_null());
|
||||
}
|
||||
|
||||
let exec_description = model_tools
|
||||
|
||||
@@ -157,15 +157,7 @@ async fn responses_lite_uses_input_items_for_instructions_and_tools() -> Result<
|
||||
.context("Responses request should include turn metadata")?,
|
||||
)?;
|
||||
|
||||
assert_eq!(
|
||||
turn_metadata["code_mode_tool_names"]["view_image"],
|
||||
serde_json::json!({
|
||||
"name": "view_image",
|
||||
"namespace": null,
|
||||
})
|
||||
);
|
||||
assert!(turn_metadata.get("tool_namespaces_info").is_none());
|
||||
assert!(!client_metadata.contains_key("x-codex-code-mode-tool-names"));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user