Skip to content

Multi-family gateways (cline-pass, opendesign, opencode-go) get zero per-family tool adaptation #1706

Closed

Description

@dezren39

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

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

Activity

  1. dezren39 commented on Nov 5, 2026

    @dezren39
    Author

    🤖 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 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.

  2. dezren39 commented on Nov 5, 2026

    @dezren39
    Author

    🤖 Agent note · queued

    • Agent: issue-sweep-rotating feeder (Make triage feeder)
    • Session / ID: cron job issue-sweep-rotating, run 2026-11-05T07:35Z; queue task 9929
    • 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-784 detectZaiToolStream returns false unless provider/baseURL looks like ZAI (modelID consulted only for the glm-4.5 exclusion after the gate); :761-769 detectSupportsStrictMode 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.
  3. added
    work/assignedAn agent holds this item via the queue (claimed task)
    queue/claimedA queue worker has claimed the task for this item
    and removed
    queue/queuedHas a queued task in the Supabase agent queue
    on Nov 5, 2026
  4. dezren39 commented on Nov 5, 2026

    @dezren39
    Author

    🤖 Agent note · claimed

    • Agent: repo-worker-opencode (OpenChamber scheduled lane)
    • Session / ID: OpenCode scheduled run 2026-11-05; queue task 9929; lane 3fe37e8a-c61b-419c-9e97-769ecd264d5d
    • Task: Claimed triage packet; validating current GitHub state and mapping detector inventory/model-family design.
    • Found: Issue remains open; no matching open PR found in repository search.
    • Next: Complete detector and fixture scope triage, post the verdict and create follow-up work packet(s) if supported.
  5. dezren39 commented on Nov 5, 2026

    @dezren39
    Author

    🤖 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.

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

  6. added
    queue/queuedHas a queued task in the Supabase agent queue
    task/small-fixQueue packet kind: bounded fix with reproduction and targeted tests
    source/queue-workerFiled or worked by the queue/worker pilot
    and removed
    queue/claimedA queue worker has claimed the task for this item
    work/assignedAn agent holds this item via the queue (claimed task)
    on Nov 5, 2026
  7. 6 remaining items

  8. dezren39 commented on Nov 5, 2026

    @dezren39
    Author

    🤖 Agent note · picked up / PR opened

  9. added
    queue/parkedQueue task is parked (waiting on CI, time, or an event)
    status/parkedDeliberately paused with a recorded resume condition
    queue/claimedA queue worker has claimed the task for this item
    and removed
    queue/claimedA queue worker has claimed the task for this item
    status/parkedDeliberately paused with a recorded resume condition
    queue/parkedQueue task is parked (waiting on CI, time, or an event)
    on Nov 5, 2026
  10. dezren39 commented on Nov 5, 2026

    @dezren39
    Author

    🤖 Agent note · picked up

  11. dezren39 commented on Nov 5, 2026

    @dezren39
    Author

    🤖 Agent note · done

  12. removed
    queue/claimedA queue worker has claimed the task for this item
    work/assignedAn agent holds this item via the queue (claimed task)
    on Nov 5, 2026
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

    source/queue-workerFiled or worked by the queue/worker pilottask/small-fixQueue packet kind: bounded fix with reproduction and targeted tests

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions