core: add local input metadata strip for responses compatibility

This commit is contained in:
Roy Han
2026-03-16 18:41:03 -07:00
parent 6079029dd3
commit ee59a8aac8
2 changed files with 111 additions and 0 deletions

View File

@@ -21,6 +21,7 @@ pub const REVIEW_PROMPT: &str = include_str!("../review_prompt.md");
pub const REVIEW_EXIT_SUCCESS_TMPL: &str = include_str!("../templates/review/exit_success.xml");
pub const REVIEW_EXIT_INTERRUPTED_TMPL: &str =
include_str!("../templates/review/exit_interrupted.xml");
const STRIP_INPUT_ITEM_METADATA_ENV_VAR: &str = "CODEX_STRIP_INPUT_ITEM_METADATA";
/// API request payload for a single model turn
#[derive(Default, Debug, Clone)]
@@ -60,10 +61,37 @@ impl Prompt {
reserialize_shell_outputs(&mut input);
}
if should_strip_input_item_metadata() {
strip_input_item_metadata(&mut input);
}
input
}
}
fn should_strip_input_item_metadata() -> bool {
std::env::var(STRIP_INPUT_ITEM_METADATA_ENV_VAR)
.ok()
.is_some_and(|value| {
matches!(
value.trim().to_ascii_lowercase().as_str(),
"1" | "true" | "yes" | "on"
)
})
}
fn strip_input_item_metadata(items: &mut [ResponseItem]) {
items.iter_mut().for_each(|item| match item {
ResponseItem::Message { metadata, .. }
| ResponseItem::LocalShellCall { metadata, .. }
| ResponseItem::FunctionCall { metadata, .. }
| ResponseItem::CustomToolCall { metadata, .. } => {
*metadata = None;
}
_ => {}
});
}
fn reserialize_shell_outputs(items: &mut [ResponseItem]) {
let mut shell_call_ids: HashSet<String> = HashSet::new();

View File

@@ -245,3 +245,86 @@ fn tool_search_output_namespace_serializes_with_deferred_child_tools() {
})
);
}
#[test]
fn strip_input_item_metadata_clears_supported_variants() {
let metadata = Some(codex_protocol::models::ResponseItemMetadata {
user_message_type: Some(codex_protocol::models::UserMessageType::Prompt),
sandbox_policy: Some(codex_protocol::models::SandboxPolicyMetadata::Sandbox),
is_tool_call_escalated: Some(true),
review_decision: Some(codex_protocol::models::ReviewDecisionMetadata::Denied),
});
let mut items = vec![
ResponseItem::Message {
id: None,
role: "user".to_string(),
content: vec![codex_protocol::models::ContentItem::InputText {
text: "hello".to_string(),
}],
metadata: metadata.clone(),
end_turn: None,
phase: None,
},
ResponseItem::LocalShellCall {
id: None,
call_id: Some("call-1".to_string()),
status: codex_protocol::models::LocalShellStatus::Completed,
action: codex_protocol::models::LocalShellAction::Exec(
codex_protocol::models::LocalShellExecAction {
command: vec!["echo".to_string(), "hello".to_string()],
env: None,
timeout_ms: None,
user: None,
working_directory: None,
},
),
metadata: metadata.clone(),
},
ResponseItem::FunctionCall {
id: None,
name: "shell".to_string(),
namespace: None,
arguments: "{}".to_string(),
call_id: "call-2".to_string(),
metadata: metadata.clone(),
},
ResponseItem::CustomToolCall {
id: None,
status: None,
call_id: "call-3".to_string(),
name: "apply_patch".to_string(),
input: "*** Begin Patch".to_string(),
metadata: metadata.clone(),
},
];
strip_input_item_metadata(&mut items);
for item in items {
match item {
ResponseItem::Message { metadata, .. }
| ResponseItem::LocalShellCall { metadata, .. }
| ResponseItem::FunctionCall { metadata, .. }
| ResponseItem::CustomToolCall { metadata, .. } => assert_eq!(metadata, None),
_ => {}
}
}
}
#[test]
fn strip_input_item_metadata_does_not_change_non_metadata_items() {
let mut items = vec![
ResponseItem::FunctionCallOutput {
call_id: "call-4".to_string(),
output: FunctionCallOutputPayload::from_text("ok".to_string()),
},
ResponseItem::CustomToolCallOutput {
call_id: "call-5".to_string(),
output: FunctionCallOutputPayload::from_text("ok".to_string()),
},
];
let expected = items.clone();
strip_input_item_metadata(&mut items);
assert_eq!(items, expected);
}