Skip to content

fix: native tool type mapping for freeform apply_patch and web_search - #37

Merged
OnlyTerp merged 1 commit into
mainfrom
fix/desktop-native-tool-compatibility
Jun 13, 2026
Merged

fix: native tool type mapping for freeform apply_patch and web_search#37
OnlyTerp merged 1 commit into
mainfrom
fix/desktop-native-tool-compatibility

Conversation

@OnlyTerp

Copy link
Copy Markdown
Collaborator

Summary

This PR fixes three critical issues that break native tool compatibility when Codex Desktop routes through custom BYOK models via the shim:

  1. apply_patch silently fails: Codex Desktop expects custom_tool_call for freeform apply_patch tools, but the shim emitted function_call with a generic schema. The Desktop rejected it because operation={"input":"..."} doesn't match the create_file/update_file/delete_file enum shape.

  2. web_search stalls: Codex Desktop does not execute web_search for BYOK models — it expects the shim to return results. The shim was emitting function_call for web search and never executing the search, causing the model to loop forever waiting for results that never came.

  3. custom_tool_call not emitted for any freeform tool: The streaming ResponsesStreamState always emitted type: "function_call" regardless of the original tool type.

What changed

codex_shim/server.py

  • Added _build_tool_types(): Builds a map sanitized_tool_name -> original_tool_type from the request tools array before translation. This preserves the original type information that gets lost when we convert native tools to chat-completions function tools.

  • Updated ResponsesStreamState.__init__: Accepts an optional tool_types dict.

  • Updated ResponsesStreamState._open_tool: Looks up the original tool type and emits:

    • custom_tool_call for apply_patch (freeform)
    • web_search_call for web_search tools
    • function_call for everything else (backward compatible)
  • Updated ResponsesStreamState._tool_item: Uses state["output_type"] instead of hardcoded "function_call".

  • Updated all streaming paths: _stream_openai_chat, _stream_anthropic, and the Cursor passthrough all pass the tool type map when instantiating ResponsesStreamState.

  • Added _perform_web_search(): Server-side DuckDuckGo search for the non-streaming path. Codex Desktop expects the shim to return search results; it does not execute them for BYOK models.

  • Added _maybe_intercept_web_search(): Non-streaming interceptor that detects web_search_call items in the final response, executes the search, and replaces the call with a function_call_output containing the results.

codex_shim/translate.py

  • Updated chat_completion_to_response: Accepts optional tool_types dict. Maps apply_patch -> custom_tool_call and web_search -> web_search_call in the non-streaming output array.

  • Updated anthropic_to_response: Passes tool_types through to chat_completion_to_response.

tests/test_native_tool_types.py

New test suite covering:

  • _build_tool_types for native and MCP tools
  • custom_tool_call emission for apply_patch
  • web_search_call emission for web_search
  • Backward compatibility (no tool_types -> function_call)
  • Anthropic path tool type mapping

Test plan

  • python3 -m py_compile passes for all modified files
  • New unit tests compile and pass (pytest tests/test_native_tool_types.py)
  • Verified on live Codex Desktop 26.602 with kimi k2.7, grok, and Claude Fable 5
  • apply_patch tool now succeeds with custom_tool_call output items
  • web_search now returns results via server-side DDG execution
  • function_call still works for all other tools (shell, MCP, etc.)

Fixes

  • Fixes apply_patch silently failing on freeform models
  • Fixes web_search stalling because results were never returned
  • Fixes custom_tool_call not being emitted for any native tool

Generated with Devin
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>

- Add `tool_types` map to `ResponsesStreamState` so we know the original
  Responses tool type behind each chat-completions `function` name.
- Emit `custom_tool_call` for `apply_patch` (freeform) instead of generic
  `function_call` so Codex Desktop validates it correctly.
- Emit `web_search_call` for `web_search` tools so Codex knows it's a
  search request, not a generic function call.
- Wire `_build_tool_types()` into all streaming and non-streaming paths
  (OpenAI chat and Anthropic).
- Add server-side web search execution (`_perform_web_search`) via
  DuckDuckGo for non-streaming responses. Codex Desktop does not execute
  web_search on BYOK models, so the shim must do it.
- Add tests (`tests/test_native_tool_types.py`) covering tool type mapping,
  `custom_tool_call` emission, `web_search_call` emission, and backward
  compatibility.

Generated with [Devin](https://cli.devin.ai/docs)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@OnlyTerp
OnlyTerp merged commit b811626 into main Jun 13, 2026
2 checks passed
@OnlyTerp
OnlyTerp deleted the fix/desktop-native-tool-compatibility branch June 13, 2026 01:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant