Repository navigation
[Provider compatibility] Responses passthrough is missing the chat-line tool_search rewrite and restore #1950
Description
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 assrc/responses/parser.ts199–214). - Response: restore the model's
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):
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_callout). 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.ts199–214 exposestool_searchas a function;src/bridge.tsrelays the call back astool_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/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. - Response (passthrough return path). Restore
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 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):
- fix(responses): promote deferred tools in routed passthrough #1602 (merged 2026-08-13) —
promoteClientLoadedTools: promotes tools the client has already loaded. Half of the discovery story; notool_searchto function conversion. - Documentation to use "tool_search"? #1872 (open) — docs question about getting local models to use
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. - [Bug] Xiaomi MiMo token-plan rejects Responses custom tools (400 responses_feature_not_supported) — works fully via openai-chat adapter; add preset/guidance #1158 (closed) — same pattern: a third-party Responses gateway rejected a Codex-private tool type (
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. - [Bug]: OpenCode Go native Responses path leaks undeclared apply_patch and shows aborted #1700 (closed by fix(responses): fail closed when a routed provider calls an undeclared tool #1788, shipped in 2.23.0) — adjacent fail-closed for an undeclared routed tool on passthrough. Different gap. Not a sequel, and not the same as Documentation to use "tool_search"? #1872.
Checks
- I searched existing provider and compatibility issues.
- The request and response were redacted.
- The expected behaviour is based on an upstream specification or a concrete client requirement.
Activity
github-actions commentedon Aug 18, 2026
Issue reopened
The report now contains the information required by the automated check. Thanks for updating it.
Ingwannu commentedon Aug 18, 2026
Confirmed against current
dev. The non-canonical Responses passthrough path promotes tools from an existingtool_search_output, but it does not expose an incoming private{type:"tool_search"}declaration as a callable function and does not restore an upstreamfunction_call[name=tool_search]totool_search_call. Canonical OpenAI forward must remain native and untouched. This is a real passthrough translation gap; I will keep the implementation scoped to the non-forward request/JSON/SSE rewrite boundaries with regression coverage.Ingwannu commentedon Aug 18, 2026
Implemented in #2040 against the latest
devhead.The fix is intentionally limited to non-canonical Responses passthrough providers:
tool_searchentry to a public function tool on the upstream wire;tool_search_callitems;tool_choice;Focused exact-head verification is green: 154 tests, typecheck, privacy scan, and the 385-page docs build. The PR is awaiting independent maintainer review and exact-head CI; I have not self-approved or merged it.
lidge-jun commentedon Aug 19, 2026
리뷰 · 우선순위 54 / 80
서드파티
/v1/responses패스스루에서 Codex 전용tool_search가 번역되지 않는다. 게이트웨이는{type:"tool_search"}를 함수로 못 보고, 모델이function_call[name=tool_search]를 내도 클라이언트가 기대하는tool_search_call로 안 돌아온다. zen/deepseek-v4-flash에서 재현됐다. 기본 ChatGPT forward가 아니라 라우티드 Responses 호환이라 54다. 채팅 라인에는 이미 양쪽 번역이 있다.요청 쪽은
src/responses/parser.ts가{type:"tool_search"}를name: "tool_search"함수로 노출한다. 주석도 브리지가tool_search_call로 중계한다고 한다.createResponsesPassthroughAdapter()의buildRequest는 그 변환을 안 한다.promoteClientLoadedTools()는 이미 끝난tool_search_output의 도구를 올릴 뿐, 검색 도구 자체를 함수로 바꾸지 않는다.stripSparkCompatibility()는 Spark에서tool_search를 드롭한다. 일반 패스스루 경로가 아니다.응답 쪽은 패스스루
parseStream이 업스트림을 그대로 흘린다. 예외는 라우티드 compaction뿐이다. 채팅/collaboration은src/bridge.ts와src/server/responses/collaboration.ts가function_call[name=tool_search]를tool_search_call로 복원한다. 패스스루는 그 복원을 안 타서 Codex 클라이언트가 발견을 시작 못 한다.네임스페이스는 이 이슈가 아니다. 리포터도 zen-native
{name, namespace:"codex_app"}는 유지된다고 했다.rewriteRoutedCustomToolsForUpstream()는 custom 도구용이고 tool_search 재작성이 아니다. ChatGPTauthMode=forward에는 이 번역을 넣으면 안 된다. 네이티브는type: "tool_search"를 이해한다.해결방안
authMode !== "forward"인 패스스루buildRequest에서{type:"tool_search"}를 parser.ts와 같은 함수 스키마(query/limit)로 바꾼다. 기존 함수를 복제해 이름을 새로 짓지 말고, parser의 변환을 재사용할 수 있는지 먼저 본다. 응답은 패스스루 스트림에서function_call+name=tool_search를tool_search_call로 바꾸고, 채팅 라인 복원과 같은 필드를 유지한다. forward/Spark 드롭 경로는 그대로 둔다. 회귀는 zen에{type:"tool_search"}를 보내 모델이 함수를 보고, 나온function_call이 클라이언트에tool_search_call로 보이는 것이다.이 댓글은 grok-bot이 작성했습니다
fix(responses): bridge routed tool_search through the Responses passt…
lidge-jun commentedon Aug 20, 2026
Fixed on
devby #2145, merged as 83d5ffa.Verified present on the branch rather than assumed:
git log origin/devshows the merge commit above. These PRs targetdevrather thanmain, so GitHub does not auto-close the linked issue — this closure is manual.It ships in the next release cut from
dev. Reopen if you still see it after updating.fix(responses): bridge routed tool_search through the Responses passt…