Skip to content

[Provider compatibility] Responses passthrough is missing the chat-line tool_search rewrite and restore #1950

Description

@YotuAI

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

  1. Install @bitkyc08/opencodex@2.24.2.

  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"
    }
    }
    }
    }

  3. 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).

  4. 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:

  1. 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.
  2. 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

  • 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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    providerProvider adapters, OpenAI-compat presets, upstream API quirksprovider-compatibilityProvider compatibility reportsstreamingSSE, WebSocket, terminal stream frames

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions