mirror of
https://github.com/openai/codex.git
synced 2026-10-18 17:18:52 +00:00
Keep shared MCP types stable in Code Mode exec descriptions (#50536)
## Why Changing a deferred tool's output schema could add or remove the shared MCP type preamble from the `exec` description. ## What changed Include the default or configured MCP TypeScript preamble whenever `code_mode_only` is enabled, regardless of the available tools' output schemas. Preserve an empty preamble override as a way to omit the section. ## Testing Add a regression test that checks description stability when a deferred tool gains an MCP output schema, covering default, custom, and empty preambles. Update description expectations, request snapshots, and two Guardian context-budget test thresholds for the added preamble. GitOrigin-RevId: 725035d6159e9da3c4beff5482c426c082e768b4
This commit is contained in:
@@ -304,17 +304,11 @@ pub fn build_exec_tool_description(
|
||||
return sections.join("\n\n");
|
||||
}
|
||||
|
||||
let has_mcp_tools = enabled_tools
|
||||
.iter()
|
||||
.chain(deferred_tools)
|
||||
.any(|tool| mcp_structured_content_schema(tool.output_schema.as_ref()).is_some());
|
||||
if has_mcp_tools {
|
||||
let preamble = messages
|
||||
.and_then(|messages| messages.mcp_typescript_preamble.as_deref())
|
||||
.unwrap_or(MCP_TYPESCRIPT_PREAMBLE);
|
||||
if !preamble.is_empty() {
|
||||
sections.push(format!("Shared MCP Types:\n```ts\n{preamble}\n```"));
|
||||
}
|
||||
let preamble = messages
|
||||
.and_then(|messages| messages.mcp_typescript_preamble.as_deref())
|
||||
.unwrap_or(MCP_TYPESCRIPT_PREAMBLE);
|
||||
if !preamble.is_empty() {
|
||||
sections.push(format!("Shared MCP Types:\n```ts\n{preamble}\n```"));
|
||||
}
|
||||
|
||||
if !enabled_tools.is_empty() {
|
||||
|
||||
@@ -75,7 +75,14 @@ fn exec_override_preserves_empty_and_whitespace_only_text() {
|
||||
..Default::default()
|
||||
}),
|
||||
),
|
||||
description_override,
|
||||
[
|
||||
description_override,
|
||||
"Shared MCP Types:\n```ts\nNo MCP tools; omit this.\n```"
|
||||
]
|
||||
.into_iter()
|
||||
.filter(|section| !section.is_empty())
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n\n"),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -159,3 +166,45 @@ fn exec_override_preserves_runtime_sections() {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mcp_types_stay_stable_when_a_deferred_tool_changes_its_output_schema() {
|
||||
let mut tool = ToolDefinition {
|
||||
name: "sample".to_string(),
|
||||
tool_name: ToolName::plain("sample"),
|
||||
description: "Deferred tool".to_string(),
|
||||
kind: CodeModeToolKind::Function,
|
||||
input_schema: None,
|
||||
input_schema_max_bytes: None,
|
||||
output_schema: None,
|
||||
};
|
||||
let render = |tool: &ToolDefinition, preamble: Option<&str>| {
|
||||
build_exec_tool_description(
|
||||
&[],
|
||||
std::slice::from_ref(tool),
|
||||
&BTreeMap::new(),
|
||||
crate::DEFAULT_EXEC_YIELD_TIME_MS,
|
||||
/*code_mode_only*/ true,
|
||||
ImageDetailVisibility::Visible,
|
||||
Some(&CodeModeToolMessages {
|
||||
mcp_typescript_preamble: preamble.map(str::to_string),
|
||||
..Default::default()
|
||||
}),
|
||||
)
|
||||
};
|
||||
// Keep one deferred tool throughout to isolate this from the independent
|
||||
// discovery guidance transition when the entire deferred catalog empties.
|
||||
for preamble in [None, Some("type CustomMcpResult = string;"), Some("")] {
|
||||
tool.output_schema = None;
|
||||
let before = render(&tool, preamble);
|
||||
tool.output_schema = Some(json!({
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"content": { "type": "array", "items": { "type": "object" } },
|
||||
"isError": { "type": "boolean" },
|
||||
"_meta": { "type": "object" }
|
||||
}
|
||||
}));
|
||||
assert_eq!(before, render(&tool, preamble));
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -6,8 +6,8 @@ Scenario: Guardian follow-up review request layout
|
||||
|
||||
## Window 1
|
||||
-- request 1 (request; Initial Guardian Review Request) --
|
||||
00:additional_tools/developer (1; hash=ae2f2f1f9dcd0cd9):
|
||||
- namespace/functions; hash=fd8638eaf7cabb95
|
||||
00:additional_tools/developer (1; hash=a8891bdc414756a2):
|
||||
- namespace/functions; hash=af55b81b2cec6cc2
|
||||
- custom/exec
|
||||
- function/wait
|
||||
01:message/developer:
|
||||
|
||||
+2
-2
@@ -6,8 +6,8 @@ Scenario: Guardian review request layout
|
||||
|
||||
## Window 1
|
||||
-- request 1 (request; Guardian Review Request) --
|
||||
00:additional_tools/developer (1; hash=ae2f2f1f9dcd0cd9):
|
||||
- namespace/functions; hash=fd8638eaf7cabb95
|
||||
00:additional_tools/developer (1; hash=a8891bdc414756a2):
|
||||
- namespace/functions; hash=af55b81b2cec6cc2
|
||||
- custom/exec
|
||||
- function/wait
|
||||
01:message/developer:
|
||||
|
||||
@@ -216,8 +216,8 @@ enum ReviewerResponse {
|
||||
#[test_case(1, ReviewerResponse::Decision; "required_context_fails_closed")]
|
||||
#[test_case(4_500, ReviewerResponse::ToolContinuation; "oversized_tool_continuation_compacts")]
|
||||
#[test_case(4_500, ReviewerResponse::FileImageContinuation; "uploaded_original_image_history_compacts")]
|
||||
#[test_case(4_500, ReviewerResponse::UncompactableContinuation; "ineffective_compaction_fails_closed")]
|
||||
#[test_case(4_500, ReviewerResponse::CompactionError; "compaction_service_error_does_not_request_user_approval")]
|
||||
#[test_case(5_000, ReviewerResponse::UncompactableContinuation; "ineffective_compaction_fails_closed")]
|
||||
#[test_case(5_000, ReviewerResponse::CompactionError; "compaction_service_error_does_not_request_user_approval")]
|
||||
#[test_case(6_000, ReviewerResponse::NextReview; "incoming_review_compacts_existing_history")]
|
||||
async fn review_respects_complete_context_budget(
|
||||
window: i64,
|
||||
|
||||
+2
-2
@@ -6,8 +6,8 @@ Scenario: Astra starts in one workspace, switches for the next step of the activ
|
||||
|
||||
## Window 1
|
||||
-- request 1 (turn) --
|
||||
00:additional_tools/developer (3; hash=a54e67018899e049):
|
||||
- namespace/functions; hash=dead25325ac79c67
|
||||
00:additional_tools/developer (3; hash=5c3ec9cbf2000b64):
|
||||
- namespace/functions; hash=e6c4aaa15e6d6f30
|
||||
- custom/exec
|
||||
- function/wait
|
||||
- function/request_user_input
|
||||
|
||||
+2
-2
@@ -7,8 +7,8 @@ Scenario: Astra uses the catalog question schema to ask who a launch update is f
|
||||
|
||||
## Window 1
|
||||
-- request 1 (request) --
|
||||
00:additional_tools/developer (3; hash=ae153e78f6722d2b):
|
||||
- namespace/functions; hash=f1de44f63c977f3d
|
||||
00:additional_tools/developer (3; hash=f19fc129e930a86e):
|
||||
- namespace/functions; hash=b5c27b6b8551a052
|
||||
- custom/exec
|
||||
- function/wait
|
||||
- function/request_user_input
|
||||
|
||||
+2
-2
@@ -6,8 +6,8 @@ Scenario: Astra receives host and handler timings on completed and failed code-m
|
||||
|
||||
## Window 1
|
||||
-- request 1 (turn) --
|
||||
00:additional_tools/developer (3; hash=a1bfbfab4176467b):
|
||||
- namespace/functions; hash=e7db0a45c4ec3157
|
||||
00:additional_tools/developer (3; hash=10256ca2e2118704):
|
||||
- namespace/functions; hash=f01b11ec77d41982
|
||||
- custom/exec
|
||||
- function/wait
|
||||
- function/request_user_input
|
||||
|
||||
+2
-2
@@ -16,8 +16,8 @@ Settings:
|
||||
text: {"verbosity":"low"}
|
||||
tool_choice: "auto"
|
||||
-- request 1 (request) --
|
||||
00:additional_tools/developer (3; hash=e33e09a2c1478914):
|
||||
- namespace/functions; hash=99a630cff25563aa
|
||||
00:additional_tools/developer (3; hash=1b07ea833720903d):
|
||||
- namespace/functions; hash=986d556577441f79
|
||||
- custom/exec
|
||||
- function/wait
|
||||
- function/request_user_input
|
||||
|
||||
+2
-2
@@ -6,8 +6,8 @@ Scenario: Astra receives new user input after its unfinished response is interru
|
||||
|
||||
## Window 1
|
||||
-- request 1 (request) --
|
||||
00:additional_tools/developer (3; hash=a1bfbfab4176467b):
|
||||
- namespace/functions; hash=e7db0a45c4ec3157
|
||||
00:additional_tools/developer (3; hash=10256ca2e2118704):
|
||||
- namespace/functions; hash=f01b11ec77d41982
|
||||
- custom/exec
|
||||
- function/wait
|
||||
- function/request_user_input
|
||||
|
||||
+2
-2
@@ -6,8 +6,8 @@ Scenario: Astra continues after new user input yields a running code cell, then
|
||||
|
||||
## Window 1
|
||||
-- request 1 (turn) --
|
||||
00:additional_tools/developer (3; hash=b035de0e31ab087e):
|
||||
- namespace/functions; hash=a0b6a7ae1ecf9f28
|
||||
00:additional_tools/developer (3; hash=7125a91d9e3d997f):
|
||||
- namespace/functions; hash=5cf23dadab8c4adb
|
||||
- custom/exec
|
||||
- function/wait
|
||||
- function/request_user_input
|
||||
|
||||
+4
-4
@@ -16,8 +16,8 @@ Settings:
|
||||
text: {"verbosity":"low"}
|
||||
tool_choice: "auto"
|
||||
-- request 1 (turn) --
|
||||
00:additional_tools/developer (3; hash=a1bfbfab4176467b):
|
||||
- namespace/functions; hash=e7db0a45c4ec3157
|
||||
00:additional_tools/developer (3; hash=10256ca2e2118704):
|
||||
- namespace/functions; hash=f01b11ec77d41982
|
||||
- custom/exec
|
||||
- function/wait
|
||||
- function/request_user_input
|
||||
@@ -187,8 +187,8 @@ Settings:
|
||||
## Window 2 (after request 3: input diverged at item 02)
|
||||
Settings: same as window 1
|
||||
-- request 4 (turn) --
|
||||
00:additional_tools/developer (3; hash=a1bfbfab4176467b):
|
||||
- namespace/functions; hash=e7db0a45c4ec3157
|
||||
00:additional_tools/developer (3; hash=10256ca2e2118704):
|
||||
- namespace/functions; hash=f01b11ec77d41982
|
||||
- custom/exec
|
||||
- function/wait
|
||||
- function/request_user_input
|
||||
|
||||
+2
-2
@@ -16,8 +16,8 @@ Settings:
|
||||
text: {"verbosity":"low"}
|
||||
tool_choice: "auto"
|
||||
-- request 1 (turn) --
|
||||
00:additional_tools/developer (3; hash=a1bfbfab4176467b):
|
||||
- namespace/functions; hash=e7db0a45c4ec3157
|
||||
00:additional_tools/developer (3; hash=10256ca2e2118704):
|
||||
- namespace/functions; hash=f01b11ec77d41982
|
||||
- custom/exec
|
||||
- function/wait
|
||||
- function/request_user_input
|
||||
|
||||
+4
-4
@@ -16,8 +16,8 @@ Settings:
|
||||
text: {"verbosity":"low"}
|
||||
tool_choice: "auto"
|
||||
-- request 1 (turn; Before /compact) --
|
||||
00:additional_tools/developer (3; hash=a1bfbfab4176467b):
|
||||
- namespace/functions; hash=e7db0a45c4ec3157
|
||||
00:additional_tools/developer (3; hash=10256ca2e2118704):
|
||||
- namespace/functions; hash=f01b11ec77d41982
|
||||
- custom/exec
|
||||
- function/wait
|
||||
- function/request_user_input
|
||||
@@ -115,8 +115,8 @@ Settings: unavailable (items only)
|
||||
## Window 3 (after request 3: input diverged at item 00)
|
||||
Settings: same as window 1
|
||||
-- request 4 (turn; First turn after /compact) --
|
||||
00:additional_tools/developer (3; hash=a1bfbfab4176467b):
|
||||
- namespace/functions; hash=e7db0a45c4ec3157
|
||||
00:additional_tools/developer (3; hash=10256ca2e2118704):
|
||||
- namespace/functions; hash=f01b11ec77d41982
|
||||
- custom/exec
|
||||
- function/wait
|
||||
- function/request_user_input
|
||||
|
||||
+4
-4
@@ -20,8 +20,8 @@ Scenario: An encrypted parent reply is delivered as a native agent message. Both
|
||||
|
||||
## Window 2 (after request 2: input diverged at item 00, settings changed (instructions, model, parallel_tool_calls, prompt_cache_key, reasoning, text, tools))
|
||||
-- request 3 (turn) --
|
||||
00:additional_tools/developer (1; hash=ae2f2f1f9dcd0cd9):
|
||||
- namespace/functions; hash=fd8638eaf7cabb95
|
||||
00:additional_tools/developer (1; hash=a8891bdc414756a2):
|
||||
- namespace/functions; hash=af55b81b2cec6cc2
|
||||
- custom/exec
|
||||
- function/wait
|
||||
01:message/developer:
|
||||
@@ -106,8 +106,8 @@ Scenario: An encrypted parent reply is delivered as a native agent message. Both
|
||||
|
||||
## Window 4 (after request 4: input diverged at item 00, settings changed (instructions, model, parallel_tool_calls, prompt_cache_key, reasoning, text, tools))
|
||||
-- request 5 (turn) --
|
||||
00:additional_tools/developer (1; hash=ae2f2f1f9dcd0cd9):
|
||||
- namespace/functions; hash=fd8638eaf7cabb95
|
||||
00:additional_tools/developer (1; hash=a8891bdc414756a2):
|
||||
- namespace/functions; hash=af55b81b2cec6cc2
|
||||
- custom/exec
|
||||
- function/wait
|
||||
01:message/developer:
|
||||
|
||||
+12
-12
@@ -50,8 +50,8 @@ Settings: relative to window 1
|
||||
reasoning: {"context":"all_turns","effort":"medium"}
|
||||
tools: <removed>
|
||||
-- request 4 (turn) --
|
||||
00:additional_tools/developer (1; hash=2e71c3c832baebb6):
|
||||
- namespace/functions; hash=cea33529f2099460
|
||||
00:additional_tools/developer (1; hash=803cb4ba2a73d531):
|
||||
- namespace/functions; hash=4b4ed79c9bae9f27
|
||||
- custom/exec
|
||||
- function/wait
|
||||
- function/request_user_input
|
||||
@@ -79,8 +79,8 @@ Settings: relative to window 2
|
||||
reasoning: {"context":"all_turns","effort":"low"}
|
||||
text: {"format":{"name":"codex_output_schema","schema":{"additionalProperties":false,"properties":{"outcome":{"enum":["allow","deny"],"type":"string"},"rationale":{"type":"string"},"risk_level":{"enum":["low","medium","high","critical"],"type":"string"},"user_authorization":{"enum":["unknown","low","medium","high"],"type":"string"}},"required":["outcome"],"type":"object"},"strict":false,"type":"json_schema"},"verbosity":"low"}
|
||||
-- request 6 (turn) --
|
||||
00:additional_tools/developer (1; hash=ae2f2f1f9dcd0cd9):
|
||||
- namespace/functions; hash=fd8638eaf7cabb95
|
||||
00:additional_tools/developer (1; hash=a8891bdc414756a2):
|
||||
- namespace/functions; hash=af55b81b2cec6cc2
|
||||
- custom/exec
|
||||
- function/wait
|
||||
01:message/developer:
|
||||
@@ -187,8 +187,8 @@ Settings: relative to window 2
|
||||
## Window 4 (after request 6: input diverged at item 00)
|
||||
Settings: same as window 2
|
||||
-- request 7 (turn) --
|
||||
00:additional_tools/developer (1; hash=2e71c3c832baebb6):
|
||||
- namespace/functions; hash=cea33529f2099460
|
||||
00:additional_tools/developer (1; hash=803cb4ba2a73d531):
|
||||
- namespace/functions; hash=4b4ed79c9bae9f27
|
||||
- custom/exec
|
||||
- function/wait
|
||||
- function/request_user_input
|
||||
@@ -216,8 +216,8 @@ Settings: same as window 2
|
||||
## Window 5 (after request 8: input diverged at item 04)
|
||||
Settings: same as window 2
|
||||
-- request 9 (turn) --
|
||||
00:additional_tools/developer (1; hash=2e71c3c832baebb6):
|
||||
- namespace/functions; hash=cea33529f2099460
|
||||
00:additional_tools/developer (1; hash=803cb4ba2a73d531):
|
||||
- namespace/functions; hash=4b4ed79c9bae9f27
|
||||
- custom/exec
|
||||
- function/wait
|
||||
- function/request_user_input
|
||||
@@ -242,8 +242,8 @@ Settings: same as window 2
|
||||
## Window 6 (after request 9: input diverged at item 00)
|
||||
Settings: same as window 3
|
||||
-- request 10 (turn) --
|
||||
00:additional_tools/developer (1; hash=ae2f2f1f9dcd0cd9):
|
||||
- namespace/functions; hash=fd8638eaf7cabb95
|
||||
00:additional_tools/developer (1; hash=a8891bdc414756a2):
|
||||
- namespace/functions; hash=af55b81b2cec6cc2
|
||||
- custom/exec
|
||||
- function/wait
|
||||
01:message/developer:
|
||||
@@ -340,8 +340,8 @@ Settings: same as window 3
|
||||
## Window 7 (after request 10: input diverged at item 00)
|
||||
Settings: same as window 2
|
||||
-- request 11 (turn) --
|
||||
00:additional_tools/developer (1; hash=2e71c3c832baebb6):
|
||||
- namespace/functions; hash=cea33529f2099460
|
||||
00:additional_tools/developer (1; hash=803cb4ba2a73d531):
|
||||
- namespace/functions; hash=4b4ed79c9bae9f27
|
||||
- custom/exec
|
||||
- function/wait
|
||||
- function/request_user_input
|
||||
|
||||
+4
-4
@@ -83,8 +83,8 @@ Scenario: A root uses Code Mode to send a confirmation and receives a real user
|
||||
|
||||
## Window 3 (after request 6: input diverged at item 00, settings changed (instructions, model, parallel_tool_calls, prompt_cache_key, reasoning, text, tools))
|
||||
-- request 7 (turn) --
|
||||
00:additional_tools/developer (1; hash=ae2f2f1f9dcd0cd9):
|
||||
- namespace/functions; hash=fd8638eaf7cabb95
|
||||
00:additional_tools/developer (1; hash=a8891bdc414756a2):
|
||||
- namespace/functions; hash=af55b81b2cec6cc2
|
||||
- custom/exec
|
||||
- function/wait
|
||||
01:message/developer:
|
||||
@@ -153,8 +153,8 @@ Scenario: A root uses Code Mode to send a confirmation and receives a real user
|
||||
|
||||
## Window 4 (after request 7: input diverged at item 04, settings changed (prompt_cache_key))
|
||||
-- request 8 (turn) --
|
||||
00:additional_tools/developer (1; hash=ae2f2f1f9dcd0cd9):
|
||||
- namespace/functions; hash=fd8638eaf7cabb95
|
||||
00:additional_tools/developer (1; hash=a8891bdc414756a2):
|
||||
- namespace/functions; hash=af55b81b2cec6cc2
|
||||
- custom/exec
|
||||
- function/wait
|
||||
01:message/developer:
|
||||
|
||||
+2
-2
@@ -84,8 +84,8 @@ Settings: relative to window 1
|
||||
text: {"format":{"name":"codex_output_schema","schema":{"additionalProperties":false,"properties":{"outcome":{"enum":["allow","deny"],"type":"string"},"rationale":{"type":"string"},"risk_level":{"enum":["low","medium","high","critical"],"type":"string"},"user_authorization":{"enum":["unknown","low","medium","high"],"type":"string"}},"required":["outcome"],"type":"object"},"strict":false,"type":"json_schema"},"verbosity":"low"}
|
||||
tools: <removed>
|
||||
-- request 2 (turn) --
|
||||
00:additional_tools/developer (1; hash=ae2f2f1f9dcd0cd9):
|
||||
- namespace/functions; hash=fd8638eaf7cabb95
|
||||
00:additional_tools/developer (1; hash=a8891bdc414756a2):
|
||||
- namespace/functions; hash=af55b81b2cec6cc2
|
||||
- custom/exec
|
||||
- function/wait
|
||||
01:message/developer:
|
||||
|
||||
+2
-2
@@ -7,8 +7,8 @@ Scenario: Complete Guardian request history: root delegates through alpha to lea
|
||||
|
||||
## Window 1
|
||||
-- request 1 (turn) --
|
||||
00:additional_tools/developer (1; hash=ae2f2f1f9dcd0cd9):
|
||||
- namespace/functions; hash=fd8638eaf7cabb95
|
||||
00:additional_tools/developer (1; hash=a8891bdc414756a2):
|
||||
- namespace/functions; hash=af55b81b2cec6cc2
|
||||
- custom/exec
|
||||
- function/wait
|
||||
01:message/developer:
|
||||
|
||||
+2
-2
@@ -56,8 +56,8 @@ Scenario: Three scheduled runs share one instruction body, a human overrides its
|
||||
|
||||
## Window 2 (after request 5: input diverged at item 00, settings changed (instructions, model, parallel_tool_calls, prompt_cache_key, reasoning, text, tools))
|
||||
-- request 6 (request) --
|
||||
00:additional_tools/developer (1; hash=ae2f2f1f9dcd0cd9):
|
||||
- namespace/functions; hash=fd8638eaf7cabb95
|
||||
00:additional_tools/developer (1; hash=a8891bdc414756a2):
|
||||
- namespace/functions; hash=af55b81b2cec6cc2
|
||||
- custom/exec
|
||||
- function/wait
|
||||
01:message/developer:
|
||||
|
||||
+2
-2
@@ -16,8 +16,8 @@ Settings:
|
||||
text: {"verbosity":"low"}
|
||||
tool_choice: "auto"
|
||||
-- request 1 (turn) --
|
||||
00:additional_tools/developer (3; hash=78c6350cb8fdad81):
|
||||
- namespace/functions; hash=e7db0a45c4ec3157
|
||||
00:additional_tools/developer (3; hash=954d592d9c1e75d8):
|
||||
- namespace/functions; hash=f01b11ec77d41982
|
||||
- custom/exec
|
||||
- function/wait
|
||||
- function/request_user_input
|
||||
|
||||
+2
-2
@@ -16,8 +16,8 @@ Settings:
|
||||
text: {"verbosity":"low"}
|
||||
tool_choice: "auto"
|
||||
-- request 1 (turn) --
|
||||
00:additional_tools/developer (3; hash=853a5788ad239c35):
|
||||
- namespace/functions; hash=48b2ce07cd26a39b
|
||||
00:additional_tools/developer (3; hash=a1c4ca6659851eb8):
|
||||
- namespace/functions; hash=8741ef7a7ba4ed8c
|
||||
- custom/exec
|
||||
- function/wait
|
||||
- function/request_user_input
|
||||
|
||||
Reference in New Issue
Block a user