patch-tools

This commit is contained in:
Ahmed Ibrahim
2025-09-12 11:44:40 -04:00
parent 3318cf9369
commit 4fee2ca3fd

View File

@@ -609,178 +609,6 @@ async fn per_turn_overrides_keep_cached_prefix_and_key_constant() {
assert_eq!(body2["input"], expected_body2);
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn changing_approval_policy_mid_session_preserves_shell_tool_description_and_json() {
use pretty_assertions::assert_eq;
let server = MockServer::start().await;
let sse = sse_completed("resp");
let template = ResponseTemplate::new(200)
.insert_header("content-type", "text/event-stream")
.set_body_raw(sse, "text/event-stream");
// Expect two POSTs to /v1/responses
Mock::given(method("POST"))
.and(path("/v1/responses"))
.respond_with(template)
.expect(2)
.mount(&server)
.await;
let model_provider = ModelProviderInfo {
base_url: Some(format!("{}/v1", server.uri())),
..built_in_model_providers()["openai"].clone()
};
let cwd = TempDir::new().unwrap();
let codex_home = TempDir::new().unwrap();
let mut config = load_default_config_for_test(&codex_home);
config.cwd = cwd.path().to_path_buf();
config.model_provider = model_provider;
config.user_instructions = Some("be consistent and helpful".to_string());
// Keep toolset minimal/deterministic for this assertion
config.include_apply_patch_tool = false;
config.include_plan_tool = false;
let conversation_manager =
ConversationManager::with_auth(CodexAuth::from_api_key("Test API Key"));
let codex = conversation_manager
.new_conversation(config)
.await
.expect("create new conversation")
.conversation;
// First turn with default approval policy (on-request)
codex
.submit(Op::UserInput {
items: vec![InputItem::Text {
text: "hello 1".into(),
}],
})
.await
.unwrap();
wait_for_event(&codex, |ev| matches!(ev, EventMsg::TaskComplete(_))).await;
// Change approval policy mid-session to Never
codex
.submit(Op::OverrideTurnContext {
cwd: None,
approval_policy: Some(AskForApproval::Never),
sandbox_policy: None,
model: None,
effort: None,
summary: None,
})
.await
.unwrap();
// Second turn after overrides
codex
.submit(Op::UserInput {
items: vec![InputItem::Text {
text: "hello 2".into(),
}],
})
.await
.unwrap();
wait_for_event(&codex, |ev| matches!(ev, EventMsg::TaskComplete(_))).await;
let requests = server.received_requests().await.unwrap();
assert_eq!(requests.len(), 2, "expected two POST requests");
let body1 = requests[0].body_json::<serde_json::Value>().unwrap();
let body2 = requests[1].body_json::<serde_json::Value>().unwrap();
// Extract the shell tool from the tools array in each request
let find_shell = |body: &serde_json::Value| -> serde_json::Value {
body["tools"]
.as_array()
.unwrap()
.iter()
.find(|t| t["type"] == "function" && t["name"] == "shell")
.cloned()
.expect("shell tool present")
};
let shell1 = find_shell(&body1);
let shell2 = find_shell(&body2);
// Description must be stable across approval policy changes
assert_eq!(
shell1["description"],
serde_json::json!("Runs a shell command and returns its output")
);
assert_eq!(shell1["description"], shell2["description"]);
// Raw JSON expectation for shell tool under on-request (ShellWithRequest)
let expected_shell_on_request = serde_json::json!({
"type": "function",
"name": "shell",
"description": "Runs a shell command and returns its output",
"strict": false,
"parameters": {
"type": "object",
"properties": {
"command": {
"type": "array",
"items": { "type": "string" },
"description": "The command to execute"
},
"workdir": {
"type": "string",
"description": "Working directory to execute the command in."
},
"timeout_ms": {
"type": "number",
"description": "Timeout for the command in milliseconds."
},
"with_escalated_permissions": {
"type": "boolean",
"description": "Request escalated permissions, only for when a command would otherwise be blocked by the sandbox."
},
"justification": {
"type": "string",
"description": "Required if and only if with_escalated_permissions == true. One sentence explaining why escalation is needed (e.g., write outside CWD, network fetch, git commit)."
}
},
"required": ["command"],
"additionalProperties": false
}
});
// Raw JSON expectation for shell tool under never (DefaultShell)
let expected_shell_never = serde_json::json!({
"type": "function",
"name": "shell",
"description": "Runs a shell command and returns its output",
"strict": false,
"parameters": {
"type": "object",
"properties": {
"command": {
"type": "array",
"items": { "type": "string" },
"description": "The command to execute"
},
"workdir": {
"type": "string",
"description": "The working directory to execute the command in"
},
"timeout_ms": {
"type": "number",
"description": "The timeout for the command in milliseconds"
}
},
"required": ["command"],
"additionalProperties": false
}
});
assert_eq!(shell1, expected_shell_on_request);
assert_eq!(shell2, expected_shell_never);
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn tools_stable_across_all_approval_policy_transitions() {
use pretty_assertions::assert_eq;