Code Mode: support function-backed `exec` for providers without custom/freeform tool support
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 125k
- Forks
- 19.5k
- PR merge metrics
- PR metrics pending
Description
Summary
Codex Code Mode currently exposes its public exec entrypoint as a custom/freeform tool.
This works with providers that support Codex/OpenAI custom tools, but prevents Code Mode from working with Responses-compatible providers that support ordinary function calling while rejecting type: "custom" / freeform tools.
A provider-capability-backed function transport for Code Mode exec could solve this without introducing a second Code Mode runtime.
Reproduction
Environment:
- Codex CLI
- Responses API provider: DeepSeek official endpoint
- Model:
deepseek-v4-flash
Enable Code Mode:
codex -c 'features.code_mode.enable=true'
Prompt:
Use code mode to analyze this project.
Please use JavaScript tool execution to inspect multiple files programmatically.
Observed:
Code Mode is enabled in configuration, but model `deepseek-v4-flash` does not advertise Code Mode support.
Unsupported custom tool: 'exec'. Only 'apply_patch' is supported.
The failure happens at the provider tool schema boundary before the Code Mode runtime is reached.
Current behavior
The current flow is effectively:
model
-> custom/freeform exec(JavaScript)
-> CodeModeExecuteHandler
-> existing V8 runtime
The runtime itself is not the compatibility problem. The provider cannot accept the public representation of exec.
Proposed direction
Add a capability-based Code Mode exec transport:
CodeModeExecTransport:
Freeform
Function
For providers supporting custom/freeform tools, keep the existing behavior.
For providers supporting only function tools, expose something like:
{
"type": "function",
"name": "exec",
"parameters": {
"type": "object",
"properties": {
"code": {
"type": "string"
}
},
"required": ["code"]
}
}
Then normalize the function payload at the handler boundary:
ToolPayload::Function
-> extract code
-> existing CodeModeExecuteHandler
-> existing V8 execution path
This keeps one Code Mode implementation and only changes the model-facing tool representation.
Motivation
Responses API compatibility does not always mean full support for every OpenAI-specific tool type.
Many providers support:
- Responses API
- function calling
- reasoning/tool use
but not arbitrary custom/freeform tools.
A function-backed Code Mode transport would allow more providers and models to reuse the existing Code Mode runtime without provider-specific hacks.
Potentially affected providers include:
- DeepSeek
- local Responses-compatible servers
- other third-party model providers
Related areas
This appears related to existing provider compatibility discussions:
- namespace tool flattening for providers without namespace support
- Code Mode tool conversion
- provider capability detection
- custom tool compatibility
The main question is whether exec is intentionally tied to custom/freeform tools or whether it should support multiple wire representations depending on provider capabilities.
Suggested acceptance criteria
- Existing custom/freeform-capable providers continue using the current
execrepresentation. - Function-only providers can select a function-backed
execrepresentation. - Both paths reach the same Code Mode runtime.
- JavaScript execution, nested
tools.*calls, MCP routing, and yielded execution continue to work unchanged. - No model-specific checks such as
if deepseekare required.
A small proof of concept should only need changes around the public tool specification and payload normalization, while keeping the existing Code Mode runtime untouched.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start at the public Code Mode exec tool specification and provider capability detection, then trace function payload normalization into CodeModeExecuteHandler and the existing V8 execution path. Compare the Freeform and Function representations while preserving the shared runtime. Done means custom-tool providers remain unchanged and function-only providers pass JavaScript execution, nested tools.* calls, MCP routing, and yielded execution through the same runtime.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- api, cli, devtools
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100