Skip to content

fix(opencode): alias over-length tool names (>64 chars) for OpenCode Zen/Go - #16531

Merged
diegosouzapw merged 6 commits into
release/v3.8.52from
fix/port-pr-3637-tool-name-length-alias
Oct 17, 2026
Merged

diegosouzapw merged 6 commits into
release/v3.8.52from
fix/port-pr-3637-tool-name-length-alias

Conversation

@diegosouzapw

Copy link
Copy Markdown
Collaborator

Summary

  • OpenCode Zen/Go chat-completions upstreams reject tool names longer than 64 characters (name must be at most 64 characters, got 68), which Claude Code MCP tool names routinely exceed. The registry-scoped tool-name alias only ran in DefaultExecutor; OpencodeExecutor extends BaseExecutor, so long names reached opencode.ai unchanged and the request failed with 400.
  • Extract that alias step into a reusable helper and apply it to OpenCode (opencode, opencode-zen, opencode-go now declare toolNameMaxLength: 64). Aliases are restored in responses through the existing _toolNameMap path.

Attribution

Thanks to @MakdumIbrohim for the original implementation.

Changes

  • open-sse/executors/toolNameLengthAlias.ts (new): applyToolNameLengthAliases(body, maxLength, normalize?) (pluggable normalizer, defaulting to the existing normalizeOpenAIToolNames; a missing or non-positive-integer limit is a no-op), applyRegistryToolNameLengthAliases(provider, body), and mergeToolNameAliases (copies any existing _toolNameMap, merges the new aliases, and keeps the map non-enumerable). It accepts other normalizers, so a surface with a different tool shape (for example a Responses-API 128-char cap) can reuse it.
  • open-sse/executors/default.ts: the inline alias block is replaced by applyRegistryToolNameLengthAliases(this.provider, withDefaults). Outputs are unchanged; the NVIDIA suite passes as before.
  • open-sse/executors/opencode.ts: transformRequest applies the helper as its last step, and only on the chat-completions surface. Claude and Responses surfaces are left alone.
  • open-sse/config/providers/registry/opencode/{index,zen/index,go/index}.ts: toolNameMaxLength: 64.
  • No new hashing code: aliases reuse normalizeOpenAIToolNames (prefix + _ + sha256-12, safe against collisions, consistent across tools, tool_choice, history tool_calls and role:"tool" names).

Test plan

  • New tests/unit/opencode-tool-name-length.test.ts (TDD). Against the pre-port opencode executor and registry, 4 tests failed (registry flag, distinct aliases for two 68-char names sharing a 64-char prefix, consistent history/tool_choice aliasing, response restore). After the port:
    node --import tsx/esm --test tests/unit/opencode-tool-name-length.test.ts → ℹ tests 10 ℹ pass 10 ℹ fail 0
  • Refactor neutrality: node --import tsx/esm --test tests/unit/nvidia-tool-compatibility-2840.test.ts → ℹ tests 7 ℹ pass 7 ℹ fail 0
  • Every existing test referencing the changed files or symbols (143 files: executors/opencode.ts, executors/default.ts, registry/opencode, toolNameMaxLength, normalizeOpenAIToolNames): ℹ tests 1375 ℹ pass 1374 ℹ fail 1. The one failure was a concurrency-timing assertion in opencode-transient-rotation.test.ts ("both requests must be in flight simultaneously") that showed up under box load. Rerun in isolation: ℹ tests 11 ℹ pass 11 ℹ fail 0.
  • npm run typecheck:core → exit 0
  • npx eslint --suppressions-location config/quality/eslint-suppressions.json <changed files> → exit 0; npx prettier --check <changed files> → clean
  • node scripts/check/check-pr-test-policy.mjs → Result: PASS; GITHUB_BASE_REF=release/v3.8.52 npm run check:test-masking → OK — 0 modificado(s) … sem enfraquecimento

⚠️ base-red inherited: #15306 — unit/vitest/integration/package-artifact gates on release/v3.8.52

diegosouzapw and others added 2 commits October 13, 2026 13:50
…Zen/Go

OpenCode chat-completions upstreams reject tool names longer than 64
characters ("name must be at most 64 characters, got 68"), which Claude
Code MCP tool names routinely exceed. The registry-scoped length alias
only ran in DefaultExecutor; OpencodeExecutor extends BaseExecutor, so
names reached opencode.ai unchanged.

- Extract the DefaultExecutor alias block into a reusable
  open-sse/executors/toolNameLengthAlias.ts (applyToolNameLengthAliases
  with a pluggable normalizer, applyRegistryToolNameLengthAliases, and
  mergeToolNameAliases for the non-enumerable _toolNameMap side channel).
  DefaultExecutor now calls it; behavior unchanged.
- OpencodeExecutor applies it last in transformRequest, only on the
  chat-completions surface; Claude/Responses surfaces are untouched.
- Declare toolNameMaxLength: 64 on opencode, opencode-zen, opencode-go.

Aliases reuse normalizeOpenAIToolNames (prefix + sha256-12, collision
safe) and are restored in responses through the existing _toolNameMap
consumer in chatCore.

Co-authored-by: MakdumIbrohim <makdumibrohim3@gmail.com>
Inspired-by: decolua/9router#3637
@github-actions

Copy link
Copy Markdown
Contributor

CI Coverage Report

  • Coverage job: skipped
  • PR test policy: success

Coverage artifact was not available for this run.

@diegosouzapw
diegosouzapw merged commit dea8829 into release/v3.8.52 Oct 17, 2026
7 of 9 checks passed
diegosouzapw added a commit that referenced this pull request Oct 17, 2026
Brings in #16531 (OpenCode tool-name aliasing), #16960 (Parallel provider) and
#16965 (migration 206). Count-only conflicts take the tip's numbers (re-measured
in the next commit); the OpenCode Free registry stays deleted; QUICK-START
mirrors keep this branch's retranslation (the tip's still listed OpenCode Free);
.i18n-state.json takes the tip (this branch's mirrors are re-adopted next).
diegosouzapw added a commit that referenced this pull request Oct 17, 2026
…e-measure counts

The release tip now ships 206_codex_wakeup_tasks.sql, so this branch's
migration moves to 207 (no gap); REMOVED_PROVIDERS (source + 66 mirrors), the
changelog fragment and the migration test follow.

Counts re-measured on the merged tree: 374 providers (375 on the tip minus
OpenCode Free), 204 migrations, 34 recurring pools, 52 free-forever providers;
count-only edits in AGENTS.md, llm.txt (+ mirrors via sync-llm-mirrors) and the
omni-auth/omni-providers skills. PROVIDER_REFERENCE regenerated.

#16531's tool-name-length test now covers the keyed Zen/Go entries only — the
keyless `opencode` registry it also listed is removed by this PR.

i18n: re-adopt the eight docs whose mirrors this branch retranslated or edited,
and re-hash the README/PROVIDER_REFERENCE mirror targets after the count-only
edits; the drift gate now reports exactly the tip's inherited source drift.
diegosouzapw added a commit that referenced this pull request Oct 17, 2026
)

Merged via /merge-batch (2026-10-17 wave, owner-approved rework incl. count-only edits to protected surfaces).

Reconciled on release/v3.8.52 @ 5413a24 (after #16531, #16960, #16965, #16924):
- Migration renumbered 208 -> 207 (`207_remove_opencode_free_provider.sql`; tip ships 206_codex_wakeup_tasks, no gap); test, REMOVED_PROVIDERS (+66 mirrors) and changelog fragment follow.
- #16531: the keyless `opencode` registry stays deleted; its tool-name-length test now covers the keyed Zen/Go entries only (10/10).
- Counts re-measured on the merged tree: 374 providers (375 - OpenCode Free), 204 migrations, 34 recurring pools, 52 free-forever. AGENTS.md, llm.txt (+66 mirrors), skills omni-auth/omni-providers differ from the tip only in digits (verified).
- FREE_TIERS mirrors: tip's #16924 refresh kept, OpenCode Free rows/section removed and pool count 35->34 applied mechanically (66/66); QUICK-START, TROUBLESHOOTING, FREE-TIERS-GUIDE, PROVIDERS-GUIDE, REMOVED_PROVIDERS mirrors retranslated with targeted `i18n:run --files` (330/330, table row counts equal to the source). i18n drift gate reports exactly the tip's inherited 21 drifted sources, 0 drifted targets.
- CodeQL: chat-noauth-model-cooldown test compares the exact upstream hostname instead of a URL substring.

Validation: focused node tests 1737/1737 (PR tests, migrations, blocklist, opencode*, tool-name-length, noauth cooldown) + 1052/1053 across every other test referencing opencode (1 env-only: opencode-ai postinstall binary absent locally); vitest 61/61 + 18/18; typecheck:core, api-typecheck, open-sse typecheck OK; complexity/cognitive/file-size/dead-code within baseline; boundary 774/774; changelog-integrity, docs-sync, docs-counts (strict) OK; provider-consistency OK (374); agent-skills Generated 0; eslint + prettier on changed files clean. CI runs for this head were queued (account runner backlog) and never started; remaining tip reds are the inherited base-red #15306.
@diegosouzapw
diegosouzapw deleted the fix/port-pr-3637-tool-name-length-alias branch October 18, 2026 23:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant