You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Multi-family gateways (cline-pass, opendesign, opencode-go) get zero per-family tool adaptation #1706
Multi-family gateways (cline-pass, opendesign, opencode-go) get zero per-family tool adaptation — detectors key on provider id/baseURL, not the model family
From the 2026-11-05 tool-call audit (~/workspace/scratch/toolcall-audit-2026-11-04/code-inventory.md §7A, §12–§14).
cline-pass, opendesign, and opencode-go are all multi-family gateways: one provider id fronts GLM, Kimi, DeepSeek, MiniMax, Claude, Gemini and more. But every dialect adaptation in the fork's OpenAI Chat protocol is keyed on provider id or baseURL, so behind a gateway none of it fires:
ZAI tool_stream:true is added only when the provider/baseURL looks like ZAI (detectZaiToolStream, openai-chat.ts) — GLM models reached via cline-pass / opendesign / opencode-go never get it.
Gemini thought-signature custody (extra_content.google.thought_signature decode + replay in openai-chat.ts) depends on the gateway forwarding extra_content verbatim — nothing verifies or repairs this per gateway.
Moonshot/Kimi reasoning-details handling and strict-mode detection (detectSupportsStrictMode) take generic defaults for these ids; strict:false is sent to gateways that forward to strict validators.
Parallel-call recovery in the chat protocol is heuristic (index → id → batch position → latest). If a gateway strips both index and id on later parallel deltas, arguments can concatenate onto the wrong call. No fixture pins any of these gateways' actual delta shapes.
opencode-go compounds it: its dialect is catalog data (per-model pinned api URL + package from models.dev). A catalog refresh can flip a model's dialect (chat ↔ messages ↔ responses) with no code review, silently changing its tool-call handling.
Ask
Key the compatibility detectors on the model family (model id) as well as provider/baseURL, so GLM/Kimi/Gemini models get their adaptations behind any gateway.
Recorded-fixture tests for the seated models behind each gateway (delta shapes, parallel calls, signature forwarding), so gateway behavior changes are caught as test failures instead of session failures.
For opencode-go: a pinned expectation per seated Go model (package + api family) asserted in tests, so a catalog refresh that changes a dialect shows up as a deliberate diff.
Verdict: CONFIRMED by reading on current main 44fc2ba5d52730283a4ced0e37938146008b2018 (gh api raw reads, verified x2 — raw Accept read + contents base64 decode, diff-identical).
What I checked in packages/ai/src/protocols/openai-chat.ts:
:771-784 — detectZaiToolStream(provider, baseURL, modelID): the ZAI gate is provider ∈ {zai, zai-coding-plan, zhipuai, zhipuai-coding-plan} or baseURL containing api.z.ai / open.bigmodel.cn. If neither matches it returns false immediately — modelID is consulted only after the gate, for the glm-4.5 exclusion list. A GLM model reached via cline-pass, opendesign, or opencode-go therefore never gets tool_stream: true (:828, :844), exactly as the issue states.
:761-769 — detectSupportsStrictMode(provider, baseURL) takes no model input at all: Moonshot/Together/Cloudflare/NVIDIA keyed purely on provider id or baseURL, so gateway-fronted models take generic defaults.
:62-76, :363, :1143 — Gemini thought-signature custody (extra_content.google.thought_signature decode + replay) exists in this protocol, but nothing verifies or repairs its forwarding per gateway, and no recorded fixture pins any of these gateways' delta shapes.
What "done" looks like (per the issue's ask): (1) compatibility detectors keyed on model family (model id) as well as provider/baseURL; (2) recorded-fixture tests for the seated models behind each gateway (delta shapes, parallel calls, signature forwarding); (3) for opencode-go, a pinned per-model expectation (package + api family) asserted in tests so a catalog refresh that flips a dialect shows as a deliberate diff.
Enqueuing a triage packet for this issue into repo-health-opencode-pilot in the same pass; the queue lane owns detector/fixture work and any execution environment.
Task: Enqueued a triage packet for this issue into repo-health-opencode-pilot (dedupe repo-health:v1:triage:developing-today/opencode:issue:1706, packet hidden_files/packet-opencode-1706.json in the goal workspace; queue-list verified queued).
Found: CONFIRMED by reading on main 44fc2ba5 — see the triage evidence comment above (6652420851): openai-chat.ts:771-784detectZaiToolStream returns false unless provider/baseURL looks like ZAI (modelID consulted only for the glm-4.5 exclusion after the gate); :761-769detectSupportsStrictMode takes no model input at all.
Expectation: the queue lane inventories the detectors, designs model-family keying that composes with the compatibility overrides, and scopes the fixture + opencode-go pinned-dialect work; done when GLM/Kimi/Gemini models behind the three gateways get their adaptations with recorded-fixture tests.
🤖 Triage verdict: CONFIRMED — the OpenAI Chat compatibility detectors are provider/baseURL keyed, so the three multi-family gateway IDs miss model-specific behavior.
Verified against the fresh fetched main commit 13ab3dc5c2d51fa35ff0273e18f5ef647eca83db in the task worktree /home/user/git/fleet/9929. The feeder's prior read at 44fc2ba5d52730283a4ced0e37938146008b2018 matches the reported paths; the current source still has the same detector structure.
detectMaxTokensField(provider, baseURL) (:683-715): maps DeepSeek, Moonshot, Together, ZAI/Zhipu, NVIDIA, Cerebras, Chutes and Cloudflare to max_tokens; defaults to max_completion_tokens.
detectSupportsStore(provider, baseURL) (:717-757): returns false for the nonstandard provider/baseURL set (NVIDIA, Moonshot, Together, ZAI, DeepSeek, Cerebras, xAI, Chutes, Cloudflare, Vercel, Ant Ling, OpenCode and Gemini); defaults true.
detectSupportsStrictMode(provider, baseURL) (:761-769): returns false only for Moonshot, Together, Cloudflare AI Gateway and NVIDIA; it has no model-ID input, so Kimi behind a gateway receives the generic default.
detectZaiToolStream(provider, baseURL, modelID) (:771-784): first requires a ZAI/Zhipu provider or ZAI base URL, then excludes glm-4.5, glm-4.5-air, glm-4.5-flash and glm-4.5v. A GLM model behind cline-pass, opendesign or opencode-go returns false before its ID is considered.
fromRequest applies request.model.compatibility at :821-828 using nullish-coalescing overrides. Explicit values, especially false, must remain authoritative. reasoningField is also explicit compatibility configuration, not an inferred detector.
Kimi reasoning details are handled structurally (:80-115, :390-410, :470-480): summary/encrypted stream indices are stripped on replay and reasoning_content is retained. Gemini extra_content.google.thought_signature is decoded, attached to the tool call and replayed (:62-76, :363, :1138-1165). The direct Gemini parallel-signature test already covers that path, but not these gateway IDs.
Design: normalize model IDs and recognize family patterns case-insensitively with anchored/canonical matches (GLM, Kimi/Moonshot, Gemini). Keep known direct provider/baseURL behavior intact; use model-family defaults as the fallback for multi-family gateway IDs. In every case explicit compatibility overrides win, including false. Do not invent provider-level rules or live-call gateways before the reset windows. Keep Kimi reasoning-detail transformations and Gemini signature custody structurally driven where possible; pin opencode-go's provider package and model API family for every seated model so a catalog flip becomes a deliberate diff.
Follow-up work queued: task 9960 implements the model-family defaults and override tests. Task 9961, linked from #1708 and not claimable before 2026-11-10T12:02:21Z, coordinates the three post-reset gateway recordings/tests: cline-pass after 2026-11-06T01:22:00Z, opencode-go after its 2026-11-09 reset, and opendesign after 2026-11-10T12:02:21Z. That fixture task pins per-model opencode-go package/API identity and tests actual deltas, parallel calls and signature forwarding where present. Existing direct fixtures are references, not gateway proof.
No code changes or tests were run in this triage task. The local primary clone was clean but diverged (one local repo-keeper commit ahead and 267 commits behind); it was preserved, and the task worktree was based on fetched current main.
State: resumed the prior attempt's laptop worktree (branch queue/9960-1706-model-family-gateway); the family classifier + gateway parity tests were in place, but the new Gemini thought-signature gateway test failed (79 pass / 1 fail) because it stored the signature under the base route's openai metadata key instead of the gateway's provider key (opencode-go), which is the key the route actually stamps and lowering reads. Fixed the test to the real gateway round-trip key with an explanatory comment; no signature rewriting added.
Tests:bun test test/provider/openai-chat.test.ts test/provider/openai-compatible-chat.test.ts — 101 pass / 0 fail / 397 expects (after rebase onto main 255fa09); bun run typecheck in packages/ai — pass; git diff --check — clean.
Activity
dezren39 commentedon Nov 5, 2026
🤖 Triage evidence · issue-sweep-rotating feeder (Make triage feeder), 2026-11-05
Verdict: CONFIRMED by reading on current main
44fc2ba5d52730283a4ced0e37938146008b2018(gh api raw reads, verified x2 — raw Accept read + contents base64 decode, diff-identical).What I checked in
packages/ai/src/protocols/openai-chat.ts::771-784—detectZaiToolStream(provider, baseURL, modelID): the ZAI gate isprovider ∈ {zai, zai-coding-plan, zhipuai, zhipuai-coding-plan}or baseURL containingapi.z.ai/open.bigmodel.cn. If neither matches it returnsfalseimmediately —modelIDis consulted only after the gate, for the glm-4.5 exclusion list. A GLM model reached viacline-pass,opendesign, oropencode-gotherefore never getstool_stream: true(:828,:844), exactly as the issue states.:761-769—detectSupportsStrictMode(provider, baseURL)takes no model input at all: Moonshot/Together/Cloudflare/NVIDIA keyed purely on provider id or baseURL, so gateway-fronted models take generic defaults.:62-76,:363,:1143— Gemini thought-signature custody (extra_content.google.thought_signaturedecode + replay) exists in this protocol, but nothing verifies or repairs its forwarding per gateway, and no recorded fixture pins any of these gateways' delta shapes.What "done" looks like (per the issue's ask): (1) compatibility detectors keyed on model family (model id) as well as provider/baseURL; (2) recorded-fixture tests for the seated models behind each gateway (delta shapes, parallel calls, signature forwarding); (3) for opencode-go, a pinned per-model expectation (package + api family) asserted in tests so a catalog refresh that flips a dialect shows as a deliberate diff.
Enqueuing a triage packet for this issue into
repo-health-opencode-pilotin the same pass; the queue lane owns detector/fixture work and any execution environment.dezren39 commentedon Nov 5, 2026
🤖 Agent note · queued
triagepacket for this issue intorepo-health-opencode-pilot(deduperepo-health:v1:triage:developing-today/opencode:issue:1706, packethidden_files/packet-opencode-1706.jsonin the goal workspace; queue-list verified queued).44fc2ba5— see the triage evidence comment above (6652420851):openai-chat.ts:771-784detectZaiToolStreamreturns false unless provider/baseURL looks like ZAI (modelID consulted only for the glm-4.5 exclusion after the gate);:761-769detectSupportsStrictModetakes no model input at all.compatibilityoverrides, and scopes the fixture + opencode-go pinned-dialect work; done when GLM/Kimi/Gemini models behind the three gateways get their adaptations with recorded-fixture tests.dezren39 commentedon Nov 5, 2026
🤖 Agent note · claimed
3fe37e8a-c61b-419c-9e97-769ecd264d5ddezren39 commentedon Nov 5, 2026
🤖 Triage verdict: CONFIRMED — the OpenAI Chat compatibility detectors are provider/baseURL keyed, so the three multi-family gateway IDs miss model-specific behavior.
Verified against the fresh fetched
maincommit13ab3dc5c2d51fa35ff0273e18f5ef647eca83dbin the task worktree/home/user/git/fleet/9929. The feeder's prior read at44fc2ba5d52730283a4ced0e37938146008b2018matches the reported paths; the current source still has the same detector structure.Detector inventory (
packages/ai/src/protocols/openai-chat.ts):detectMaxTokensField(provider, baseURL)(:683-715): maps DeepSeek, Moonshot, Together, ZAI/Zhipu, NVIDIA, Cerebras, Chutes and Cloudflare tomax_tokens; defaults tomax_completion_tokens.detectSupportsStore(provider, baseURL)(:717-757): returns false for the nonstandard provider/baseURL set (NVIDIA, Moonshot, Together, ZAI, DeepSeek, Cerebras, xAI, Chutes, Cloudflare, Vercel, Ant Ling, OpenCode and Gemini); defaults true.detectSupportsStrictMode(provider, baseURL)(:761-769): returns false only for Moonshot, Together, Cloudflare AI Gateway and NVIDIA; it has no model-ID input, so Kimi behind a gateway receives the generic default.detectZaiToolStream(provider, baseURL, modelID)(:771-784): first requires a ZAI/Zhipu provider or ZAI base URL, then excludesglm-4.5,glm-4.5-air,glm-4.5-flashandglm-4.5v. A GLM model behind cline-pass, opendesign or opencode-go returns false before its ID is considered.fromRequestappliesrequest.model.compatibilityat:821-828using nullish-coalescing overrides. Explicit values, especiallyfalse, must remain authoritative.reasoningFieldis also explicit compatibility configuration, not an inferred detector.:80-115,:390-410,:470-480): summary/encrypted stream indices are stripped on replay andreasoning_contentis retained. Geminiextra_content.google.thought_signatureis decoded, attached to the tool call and replayed (:62-76,:363,:1138-1165). The direct Gemini parallel-signature test already covers that path, but not these gateway IDs.Design: normalize model IDs and recognize family patterns case-insensitively with anchored/canonical matches (GLM, Kimi/Moonshot, Gemini). Keep known direct provider/baseURL behavior intact; use model-family defaults as the fallback for multi-family gateway IDs. In every case explicit
compatibilityoverrides win, includingfalse. Do not invent provider-level rules or live-call gateways before the reset windows. Keep Kimi reasoning-detail transformations and Gemini signature custody structurally driven where possible; pin opencode-go's provider package and model API family for every seated model so a catalog flip becomes a deliberate diff.Follow-up work queued: task
9960implements the model-family defaults and override tests. Task9961, linked from #1708 and not claimable before2026-11-10T12:02:21Z, coordinates the three post-reset gateway recordings/tests: cline-pass after2026-11-06T01:22:00Z, opencode-go after its2026-11-09reset, and opendesign after2026-11-10T12:02:21Z. That fixture task pins per-model opencode-go package/API identity and tests actual deltas, parallel calls and signature forwarding where present. Existing direct fixtures are references, not gateway proof.No code changes or tests were run in this triage task. The local primary clone was clean but diverged (one local repo-keeper commit ahead and 267 commits behind); it was preserved, and the task worktree was based on fetched current
main.6 remaining items
dezren39 commentedon Nov 5, 2026
🤖 Agent note · picked up / PR opened
queue/9960-1706-model-family-gateway); the family classifier + gateway parity tests were in place, but the new Gemini thought-signature gateway test failed (79 pass / 1 fail) because it stored the signature under the base route'sopenaimetadata key instead of the gateway's provider key (opencode-go), which is the key the route actually stamps and lowering reads. Fixed the test to the real gateway round-trip key with an explanatory comment; no signature rewriting added.bun test test/provider/openai-chat.test.ts test/provider/openai-compatible-chat.test.ts— 101 pass / 0 fail / 397 expects (after rebase onto main 255fa09);bun run typecheckin packages/ai — pass;git diff --check— clean.dezren39 commentedon Nov 5, 2026
🤖 Agent note · picked up
bun test test/provider/openai-chat.test.ts test/provider/openai-compatible-chat.test.ts— 101 pass / 0 fail / 397 expects. Proceeding to merge.queue/9960-1706-model-family-gatewaydezren39 commentedon Nov 5, 2026
🤖 Agent note · done
bun test test/provider/openai-chat.test.ts test/provider/openai-compatible-chat.test.ts— 101 pass / 0 fail / 397 expects; typecheck passed in the prior attempt (recorded on PR fix(ai): detect OpenAI Chat compatibility by model family behind gateways #1731).