Summary
Newer Codex builds (Codex Desktop 26.901 / codex.exe v0.153.4) no longer send tools in the top-level tools field of Responses requests. Instead, tools arrive as an input item of type additional_tools (with role: "developer"), containing the namespace/custom/function tool definitions.
The Responses→Chat Completions translation only collects tools from the top-level tools array, so every tool is silently dropped before forwarding to the upstream Chat backend. The model then receives a request with zero tools and truthfully answers that it has no callable tools — Codex effectively becomes text-only through the proxy.
This appears to be the same compatibility class as theagentrouter/agent-router#2586, which resolved it by adding support for the additional_tools input type.
Environment
- cc-switch CLI 5.10.5 (latest release; also reproduced against
main @ 1ae36f7), Windows x64
- Codex: Codex Desktop 26.901 / codex.exe v0.153.4, provider with
wire_api = "responses" pointing at the local proxy (http://127.0.0.1:15722/v1)
- Upstream: Chat Completions backend (Azure OpenAI deployment)
Evidence
Captured request from Codex to the proxy (POST /v1/responses):
- Top-level keys:
client_metadata, include, input, model, parallel_tool_calls, prompt_cache_key, reasoning, store, stream, text, tool_choice — no tools key
input[0] is:
{
"type": "additional_tools",
"id": "at_...",
"role": "developer",
"tools": [
{
"type": "namespace",
"name": "functions",
"tools": [
{ "type": "custom", "name": "exec", "..." : "..." },
...
]
}
]
}
A/B repro against the local proxy — identical tool and prompt, only tool placement differs:
-
Tool in top-level tools → model returns a proper function_call:
curl -s http://127.0.0.1:15722/v1/responses \
-H "Content-Type: application/json" \
-d '{"model":"<model>","stream":false,"tool_choice":"auto","tools":[{"type":"function","name":"get_weather","description":"Get current weather for a city","parameters":{"type":"object","properties":{"city":{"type":"string"}},"required":["city"]}}],"input":[{"type":"message","role":"user","content":"What is the weather in Hong Kong? You MUST use the get_weather tool."}]}'
→ output item {"type":"function_call","name":"get_weather","arguments":"{\"city\":\"Hong Kong\"}"}
-
Same tool inside an additional_tools input item → model replies text-only:
curl -s http://127.0.0.1:15722/v1/responses \
-H "Content-Type: application/json" \
-d '{"model":"<model>","stream":false,"tool_choice":"auto","input":[{"type":"additional_tools","id":"at_test_1","role":"developer","tools":[{"type":"function","name":"get_weather","description":"Get current weather for a city","parameters":{"type":"object","properties":{"city":{"type":"string"}},"required":["city"]}}]},{"type":"message","role":"user","content":"What is the weather in Hong Kong? You MUST use the get_weather tool."}]}'
→ assistant message: "I'm unable to access the get_weather tool in this chat" (no function_call)
Root cause
build_codex_tool_context_from_request in src-tauri/src/proxy/providers/transform_codex_chat.rs collects tools only from:
body["tools"], and
tool_search tool outputs found in input
additional_tools input items are never inspected, so add_response_tool never sees the tools nested inside them. grep -rn additional_tools src-tauri/src/ returns nothing, including on current main.
Suggested fix
Walk input for type == "additional_tools" and feed each nested tool through the existing add_response_tool (the nested shapes — namespace, custom, function — are already supported):
if let Some(input) = body.get("input").and_then(|v| v.as_array()) {
for item in input {
if item.get("type").and_then(|v| v.as_str()) == Some("additional_tools") {
if let Some(tools) = item.get("tools").and_then(|v| v.as_array()) {
for tool in tools {
context.add_response_tool(tool);
}
}
}
}
}
The response side (function_call → Chat tool_calls → function_call_output) looks already handled, so request-side tool extraction should restore the full loop.
Impact
Any user on the current Codex Desktop gets zero tool functionality (shell, file edits, MCP, plugins) through the proxy whenever the provider requires Responses→Chat translation.
Side note: the null-content fix from #448 is verified working in 5.10.5 — thank you. This is a separate gap exposed by the newer Codex.
Summary
Newer Codex builds (Codex Desktop 26.901 /
codex.exev0.153.4) no longer send tools in the top-leveltoolsfield of Responses requests. Instead, tools arrive as aninputitem of typeadditional_tools(withrole: "developer"), containing the namespace/custom/function tool definitions.The Responses→Chat Completions translation only collects tools from the top-level
toolsarray, so every tool is silently dropped before forwarding to the upstream Chat backend. The model then receives a request with zero tools and truthfully answers that it has no callable tools — Codex effectively becomes text-only through the proxy.This appears to be the same compatibility class as theagentrouter/agent-router#2586, which resolved it by adding support for the
additional_toolsinput type.Environment
main@1ae36f7), Windows x64wire_api = "responses"pointing at the local proxy (http://127.0.0.1:15722/v1)Evidence
Captured request from Codex to the proxy (
POST /v1/responses):client_metadata,include,input,model,parallel_tool_calls,prompt_cache_key,reasoning,store,stream,text,tool_choice— notoolskeyinput[0]is:{ "type": "additional_tools", "id": "at_...", "role": "developer", "tools": [ { "type": "namespace", "name": "functions", "tools": [ { "type": "custom", "name": "exec", "..." : "..." }, ... ] } ] }A/B repro against the local proxy — identical tool and prompt, only tool placement differs:
Tool in top-level
tools→ model returns a properfunction_call:→ output item
{"type":"function_call","name":"get_weather","arguments":"{\"city\":\"Hong Kong\"}"}Same tool inside an
additional_toolsinput item → model replies text-only:→ assistant message: "I'm unable to access the
get_weathertool in this chat" (nofunction_call)Root cause
build_codex_tool_context_from_requestinsrc-tauri/src/proxy/providers/transform_codex_chat.rscollects tools only from:body["tools"], andtool_searchtool outputs found ininputadditional_toolsinput items are never inspected, soadd_response_toolnever sees the tools nested inside them.grep -rn additional_tools src-tauri/src/returns nothing, including on currentmain.Suggested fix
Walk
inputfortype == "additional_tools"and feed each nested tool through the existingadd_response_tool(the nested shapes —namespace,custom,function— are already supported):The response side (
function_call→ Chattool_calls→function_call_output) looks already handled, so request-side tool extraction should restore the full loop.Impact
Any user on the current Codex Desktop gets zero tool functionality (shell, file edits, MCP, plugins) through the proxy whenever the provider requires Responses→Chat translation.
Side note: the null-content fix from #448 is verified working in 5.10.5 — thank you. This is a separate gap exposed by the newer Codex.