Repository navigation
ZCode integration attaches over Chat Completions instead of the proxy's native Responses surface #4295
Description
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
lidge-jun commentedon Sep 11, 2026
리뷰 · 우선순위 61 / 80
설명
이 이슈는 ZCode 데스크톱 연동 export가 provider
kind를openai-compatible으로 써서, ZCode가 프록시의 Chat Completions면({baseURL}/chat/completions)으로 붙는 문제를 말합니다. 지금devHEADcb7f96cbc의src/clients/config-export/zcode.ts는 주석부터 “ZCode dials the OpenAI Chat Completions surface (openai-compatible)”라고 적고, 생성 문서도kind: "openai-compatible"입니다. 프록시의 네이티브 inbound는POST /v1/responses이고, Chat 면은src/server/chat-completions.ts가 Chat→Responses→Chat으로 두 번 번역합니다. 도구 델타·reasoning 블록 모양이 그 과정에서 바뀌기 쉽습니다.이슈가 가져온 ZCode 3.11.2 번들 근거는 명확합니다. kind enum이
anthropic/openai/openai-compatible세 가지뿐이고,openai는/responses,openai-compatible는/chat/completions로 고정됩니다. 기존baseURL(프록시 루트 +/v1)을 유지한 채 kind만openai로 바꾸면/v1/responses로 붙습니다. reasoning 스키마(enabled/variants/defaultVariant)는 kind 공통이고,openai는reasoning.effort를 내며 프록시 Responses가 그걸 읽습니다. 저장소 테스트 주석(tests/responses/responses-inbound-store-default.test.ts)도 “Generic Responses-API clients (… ZCode)”를 이미 전제로 적습니다.왜 지금 중요한가. tip에 omo가 14번째 export 클라이언트로 들어갔고(#4290/#4294), zcode는 그 목록의 loopback-only 클라이언트 중 하나입니다. ZCode 사용자가 Chat 번역층을 항상 거치면, “프록시가 Responses를 잘하는데 클라이언트만 우회한다”는 상태가 됩니다. #4297(Z.AI provider가 Chat에 고정)과 이름이 비슷해 보이지만 레이어가 다릅니다. 이쪽은 클라이언트 export kind, 저쪽은 업스트림 provider adapter입니다.
우선순위 61입니다. 변경 후보는
zcode.ts의 kind 한 줄과 문서/테스트 정도라 범위가 작고, 이득(번역 제거)이 큽니다. 다만 이미openai-compatible로 저장된 사용자~/.zcode/v2/config.json을 export가 덮어쓸지, 병합 규칙이 기존 kind를 보존하는지를 확인해야 합니다. 업그레이드 시 조용히 kind가 바뀌면 동작이 급변합니다.라인 -
src/clients/config-export/zcode.ts-kind: "openai-compatible"이 직접 원인입니다.openai로 바꾸는 제안이 ZCode 번들 디스패치와 맞습니다.경로/심볼 -
buildZcodeClientConfig/ contribution fragment - fragment가["provider", OPENCODE_PROVIDER_ID]만 쓰므로, kind 변경은 그 provider 블록 전체에 적용됩니다. 다른 필드(baseURL, models, modalities)와의 정합 테스트를 같이 갱신하세요.경로/심볼 - 기존 설치 업그레이드 -
ocx integration client enable --client zcode가 기존 kind를 덮어쓰는지, 사용자가 수동으로 고친 값을 보존하는지 확인이 필요합니다.관련 - #4296 - ZCode가 modalities를 읽어 첨부를 막으므로, kind를 Responses로 바꿔도 flash modality가 text면 첨부는 계속 막힙니다. 이 이슈만으로 첨부 버그가 끝나지 않습니다.
메인테이너의 판단이 필요한 지점
openai로 바꿀지, 설정 플래그로 Responses/Chat을 선택하게 할지openai-compatible으로 저장된 사용자 설정을 자동 이주할지, 문서에 수동 변경만 안내할지너의 추천
수락하세요.
zcode.ts기본 kind를openai로 바꾸고, export/integration 테스트에 “baseURL + kind → /v1/responses” 기대를 잠근 뒤, 릴리즈 노트에 “기존 zcode 설정은 kind를 openai로 바꾸라”는 한 줄을 넣으세요. #4296과 함께 보면 첨부 증상까지 같이 사라집니다. #4297과는 PR을 섞지 마세요.이 댓글은 grok-bot이 작성했습니다
lidge-jun commentedon Sep 11, 2026
Landed on `dev` in #4302 (merge commit 5edb7b7). PRs here target `dev`, so GitHub does not auto-close; closing by hand. The export now writes kind "openai", which ZCode dispatches to {baseURL}/responses, and the serialized /v1 root survives its per-kind suffix normalization, so turns land on the proxy route registered at src/server/index.ts:1994 with no translation.
docs(devlog): plan ZCode Responses attachment, GLM-5.3 modality fix, …
fix(zcode): attach over the proxy Responses route instead of Chat Com…
docs(devlog): plan ZCode Responses attachment, GLM-5.3 modality fix, …
fix(zcode): attach over the proxy Responses route instead of Chat Com…