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
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:
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:
Minimal redacted request or reproduction
Actual response or error
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:
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