Client or integration
Codex App
Provider or upstream service
Any third-party /v1/responses gateway reached through Responses passthrough. Reproduced on opencode-go / zen (deepseek-v4-flash); the same gap applies to other API-key Responses endpoints (for example DeepSeek official /responses).
OpenCodex version
2.24.2 (@bitkyc08/opencodex@2.24.2, published 2026-08-17)
Endpoint or capability
/v1/responses passthrough — Codex private tool_search (deferred tool discovery)
On the Responses passthrough line (createResponsesPassthroughAdapter in src/adapters/openai-responses.ts), OpenCodex forwards the Codex body to third-party gateways without the two translations the chat line already performs for Codex's private tool_search tool. Routed models therefore cannot start deferred-tool discovery, and a function_call[name=tool_search] that does appear is returned as-is — the Codex client never sees a tool_search_call.
This is a passthrough-line gap, not a provider-specific bug. The chat translation path already has both halves.
Current behaviour
1. Request — {type:"tool_search"} is not exposed as a callable function.
Passthrough serializes the incoming tools array without rewriting the private type. Third-party gateways do not implement type: "tool_search", so the model never sees a callable tool_search tool and cannot start discovery.
Stock 2.24.2, passthrough to zen / deepseek-v4-flash, tools include {type:"tool_search"} plus a deferred codex_app namespace tool. The model states that tool_search is not in its tool list and emits 0 tool_search_call items (live sample, translated):
I don't have a tool_search tool — the only tools in my current environment are:
- get_weather
- codex_app__set_thread_title
(tool_search_call_count: 0)
2. Response — function_call[name=tool_search] is not restored to tool_search_call.
The chat / collaboration path already restores this shape (src/bridge.ts 615–620 / 1024–1031 / 1590–1595, collector in src/server/responses/collaboration.ts 111–128). Passthrough does not run that restore: it returns the upstream item unchanged.
Observed passthrough output (with the request-side rewrite applied locally so the model can emit the call; stock 2.24.2 never reaches this step):
{
"type": "function_call",
"name": "tool_search",
"arguments": "{\"query\": \"change conversation thread title\"}"
}
The Codex client executes discovery from tool_search_call, not from a generic function_call.
Not a defect: namespace. A zen-native namespaced call already arrives as {name:"set_thread_title", namespace:"codex_app"}. Passthrough does not strip that field. This issue is only about tool_search rewrite + restore.
Expected behaviour
On the non-forward Responses line, match the chat line:
- Request: expose
{type:"tool_search"} to the routed model as a callable function (same schema as src/responses/parser.ts 199–214).
- Response: restore the model's
function_call[name=tool_search] to the private tool_search_call shape the client executes.
Canonical OpenAI forward should keep the native type: "tool_search" unchanged.
Minimal redacted request or reproduction
-
Install @bitkyc08/opencodex@2.24.2.
-
Point a provider at a third-party /v1/responses gateway via per-model modelAdapters (API key omitted):
{
"providers": {
"opencode-go": {
"adapter": "openai-chat",
"modelAdapters": {
"deepseek-v4-flash": "openai-responses"
}
}
}
}
-
In a Codex App session, ask the model to discover a deferred session tool via tool_search (for example a codex_app thread-title tool that is defer_loading: true).
-
Observe: the model reports that tool_search is not in its tool list and does not emit tool_search_call.
The same tools array sent directly to the gateway (no proxy) is not the comparison that matters here: the gateway never advertised type: "tool_search" as a function. The missing step is the proxy translation the chat line already does.
Actual response or error
Stock 2.24.2 passthrough (redacted):
HTTP 200
output types: reasoning, message
tool_search_call_count: 0
assistant: tool_search is not in the available tool list
When a tool_search function call does appear (same local request-side rewrite; stock 2.24.2 never reaches this step), passthrough returns:
function_call[name=tool_search]
not tool_search_call.
Upstream documentation
- Codex client contract: deferred discovery is a client-executed private type (
type: "tool_search" in, tool_search_call out). It is not a public OpenAI Responses hosted tool. No public specification exists for that private type.
- OpenCodex chat line already implements that contract:
src/responses/parser.ts 199–214 exposes tool_search as a function; src/bridge.ts relays the call back as tool_search_call.
- Third-party Responses gateways speak generic OpenAI Responses tools (
function, web_search, and similar) and ignore or reject unknown private types. DeepSeek Responses tools table: https://api-docs.deepseek.com/guides/responses_api
- opencode.ai zen: https://opencode.ai/docs/zen/
Expected behaviour is the Codex client contract plus the translation OpenCodex already ships on the chat line — not a claim that third-party gateways should implement type: "tool_search" natively.
Suggested mapping or implementation notes
The chat line already has both halves. Passthrough is missing the same pair:
- Request (non-forward / non-canonical-OpenAI branch only). Next to the existing
promoteClientLoadedTools / rewriteRoutedCustomToolsForUpstream calls in createResponsesPassthroughAdapter, rewrite {type:"tool_search"} to {type:"function", name:"tool_search", ...} using the schema in src/responses/parser.ts 199–214. Reuse the current gate; do not add a new flag. Leave canonical OpenAI forward on the native type.
- Response (passthrough return path). Restore
function_call[name=tool_search] to tool_search_call on the JSON and SSE rewrite chains — the same place routed custom-tool restore already hangs. The translation-line restore in src/bridge.ts / src/server/responses/collaboration.ts does not run on passthrough today.
promoteClientLoadedTools (#1602) only merges tools already returned by a prior tool_search_output. It does not convert the private type, so it cannot start discovery.
Feasibility (local experiment, not a proposed patch). On an unmodified 2.24.2 checkout plus a minimal patch at those two sites, a live third-party gateway closed the loop: the model issued tool_search_call, discovered the deferred tool, and called the namespaced tool successfully. web_search and ordinary function calls did not regress.
Additional context and attachments
Related (checked; this is not a duplicate of any of them):
Checks
Client or integration
Codex App
Provider or upstream service
Any third-party
/v1/responsesgateway reached through Responses passthrough. Reproduced on opencode-go / zen (deepseek-v4-flash); the same gap applies to other API-key Responses endpoints (for example DeepSeek official/responses).OpenCodex version
2.24.2 (
@bitkyc08/opencodex@2.24.2, published 2026-08-17)Endpoint or capability
/v1/responsespassthrough — Codex privatetool_search(deferred tool discovery)On the Responses passthrough line (
createResponsesPassthroughAdapterinsrc/adapters/openai-responses.ts), OpenCodex forwards the Codex body to third-party gateways without the two translations the chat line already performs for Codex's privatetool_searchtool. Routed models therefore cannot start deferred-tool discovery, and afunction_call[name=tool_search]that does appear is returned as-is — the Codex client never sees atool_search_call.This is a passthrough-line gap, not a provider-specific bug. The chat translation path already has both halves.
Current behaviour
1. Request —
{type:"tool_search"}is not exposed as a callable function.Passthrough serializes the incoming tools array without rewriting the private type. Third-party gateways do not implement
type: "tool_search", so the model never sees a callabletool_searchtool and cannot start discovery.Stock 2.24.2, passthrough to zen /
deepseek-v4-flash, tools include{type:"tool_search"}plus a deferredcodex_appnamespace tool. The model states thattool_searchis not in its tool list and emits 0tool_search_callitems (live sample, translated):(
tool_search_call_count: 0)2. Response —
function_call[name=tool_search]is not restored totool_search_call.The chat / collaboration path already restores this shape (
src/bridge.ts615–620 / 1024–1031 / 1590–1595, collector insrc/server/responses/collaboration.ts111–128). Passthrough does not run that restore: it returns the upstream item unchanged.Observed passthrough output (with the request-side rewrite applied locally so the model can emit the call; stock 2.24.2 never reaches this step):
The Codex client executes discovery from
tool_search_call, not from a genericfunction_call.Not a defect: namespace. A zen-native namespaced call already arrives as
{name:"set_thread_title", namespace:"codex_app"}. Passthrough does not strip that field. This issue is only abouttool_searchrewrite + restore.Expected behaviour
On the non-forward Responses line, match the chat line:
{type:"tool_search"}to the routed model as a callable function (same schema assrc/responses/parser.ts199–214).function_call[name=tool_search]to the privatetool_search_callshape the client executes.Canonical OpenAI forward should keep the native
type: "tool_search"unchanged.Minimal redacted request or reproduction
Install
@bitkyc08/opencodex@2.24.2.Point a provider at a third-party
/v1/responsesgateway via per-modelmodelAdapters(API key omitted):{
"providers": {
"opencode-go": {
"adapter": "openai-chat",
"modelAdapters": {
"deepseek-v4-flash": "openai-responses"
}
}
}
}
In a Codex App session, ask the model to discover a deferred session tool via
tool_search(for example acodex_appthread-title tool that isdefer_loading: true).Observe: the model reports that
tool_searchis not in its tool list and does not emittool_search_call.The same tools array sent directly to the gateway (no proxy) is not the comparison that matters here: the gateway never advertised
type: "tool_search"as a function. The missing step is the proxy translation the chat line already does.Actual response or error
Stock 2.24.2 passthrough (redacted):
When a
tool_searchfunction call does appear (same local request-side rewrite; stock 2.24.2 never reaches this step), passthrough returns:not
tool_search_call.Upstream documentation
type: "tool_search"in,tool_search_callout). It is not a public OpenAI Responses hosted tool. No public specification exists for that private type.src/responses/parser.ts199–214 exposestool_searchas a function;src/bridge.tsrelays the call back astool_search_call.function,web_search, and similar) and ignore or reject unknown private types. DeepSeek Responses tools table: https://api-docs.deepseek.com/guides/responses_apiExpected behaviour is the Codex client contract plus the translation OpenCodex already ships on the chat line — not a claim that third-party gateways should implement
type: "tool_search"natively.Suggested mapping or implementation notes
The chat line already has both halves. Passthrough is missing the same pair:
promoteClientLoadedTools/rewriteRoutedCustomToolsForUpstreamcalls increateResponsesPassthroughAdapter, rewrite{type:"tool_search"}to{type:"function", name:"tool_search", ...}using the schema insrc/responses/parser.ts199–214. Reuse the current gate; do not add a new flag. Leave canonical OpenAI forward on the native type.function_call[name=tool_search]totool_search_callon the JSON and SSE rewrite chains — the same place routed custom-tool restore already hangs. The translation-line restore insrc/bridge.ts/src/server/responses/collaboration.tsdoes not run on passthrough today.promoteClientLoadedTools(#1602) only merges tools already returned by a priortool_search_output. It does not convert the private type, so it cannot start discovery.Feasibility (local experiment, not a proposed patch). On an unmodified 2.24.2 checkout plus a minimal patch at those two sites, a live third-party gateway closed the loop: the model issued
tool_search_call, discovered the deferred tool, and called the namespaced tool successfully.web_searchand ordinaryfunctioncalls did not regress.Additional context and attachments
Related (checked; this is not a duplicate of any of them):
promoteClientLoadedTools: promotes tools the client has already loaded. Half of the discovery story; notool_searchto function conversion.tool_search. Maintainer confirmation that the chat / routed path exposestype: "tool_search"as a function and converts the call back totool_search_call. That is exactly the pair passthrough does not do. Documentation to use "tool_search"? #1872 is the docs thread; this issue is the passthrough implementation gap.custom). The workaround then was to switch the provider toopenai-chat. Here the private type istool_search, and the chat line already has the translation.Checks