Skip to content

ZCode integration attaches over Chat Completions instead of the proxy's native Responses surface #4295

Description

@lidge-jun

Client or integration

Other (ZCode desktop app, client integration exported by ocx)

Provider or upstream service

ZCode (Z.ai desktop coding agent) as a client of the OpenCodex proxy

OpenCodex version

2.50.0 (installed), dev at 29d632f

Endpoint or capability

/v1/chat/completions (current) vs /v1/responses (proposed)

Current behaviour

src/clients/config-export/zcode.ts writes the provider block with kind "openai-compatible". ZCode dispatches that kind to {baseURL}/chat/completions, so every turn from ZCode lands on the proxy's Chat Completions surface.

That surface is not the proxy's native inbound. src/server/chat-completions.ts translates the Chat body into a Responses body, replays it through handleResponses, then translates the Responses stream back into Chat SSE. Two translations per turn, and tool-call deltas plus reasoning blocks change shape on the way through.

Expected behaviour

ZCode should attach over the Responses protocol, which the proxy serves natively at POST /v1/responses (src/server/index.ts:1994), so no translation happens at all.

ZCode 3.11.2 supports exactly three provider kinds and dispatches each to a fixed path. Extracted verbatim from the shipped bundle:

bt=t.enum(["anthropic","openai","openai-compatible"])

function fL(e){switch(e){case"anthropic":return"/v1/messages";case"openai":return"/responses";case"openai-compatible":return"/chat/completions"}}
s(fL,"getDefaultModelProviderEndpointPathForKind")

So kind "openai" with the existing baseURL (proxy root + /v1) resolves to /v1/responses. The reasoning block schema (enabled / variants / defaultVariant) is identical across kinds; only the wire field differs, and kind "openai" emits reasoning.effort, which /v1/responses reads natively.

The repository already anticipates this. tests/responses/responses-inbound-store-default.test.ts opens with:

Generic Responses-API clients (AI-SDK apps such as ZCode) omit `store`

Minimal redacted request or reproduction

ocx integration client enable --client zcode
python3 -c "import json,os;d=json.load(open(os.path.expanduser('~/.zcode/v2/config.json')));p=d['provider']['opencodex'];print(p['kind'], p['options']['baseURL'])"

Actual response or error

openai-compatible http://127.0.0.1:10100/v1
# ZCode therefore posts to http://127.0.0.1:10100/v1/chat/completions

Upstream documentation

ZCode publishes no configuration specification. The authority used here is the shipped application bundle, /Applications/ZCode.app/Contents/Resources/app.asar from ZCode 3.11.2, where the kind enum and the kind-to-path dispatch quoted above were extracted. The baseURL normalizer confirms suffix handling:

function lp(e,o){let r={anthropic:["/v1/messages","/messages"],openai:["/responses"],"openai-compatible":["/chat/completions"]}, ... }
s(lp,"normalizeModelProviderBaseUrlForKind")

Suggested mapping or implementation notes

Change the exported kind to "openai" and keep baseURL as it is. ZCode strips only the /responses suffix for that kind, so the /v1 root is preserved and requests land on /v1/responses.

An existing installation that has not hand-edited its config follows automatically: kind is not a refreshable path, but recordedBlockIsOwned still matches the old fingerprint, so the record becomes stale rather than conflict and a refresh rewrites the fragment. Only a user who edited kind or options themselves needs --overwrite-conflict.

Additional context and attachments

mcode already exports an anthropic-messages block and dsh already exports an openai-responses block, so this is a correction to one client rather than a new capability.

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 framestoolstool_calls, MCP, web-search / sidecar tools

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions