Skip to content

fix(opencode): session stability, free-tier routing, and CLI defaults - #10571

Merged
diegosouzapw merged 2 commits into
diegosouzapw:release/v3.8.50from
CyrixJD115:fix/opencode-defaults
Aug 18, 2026
Merged

diegosouzapw merged 2 commits into
diegosouzapw:release/v3.8.50from
CyrixJD115:fix/opencode-defaults

Conversation

@CyrixJD115

Copy link
Copy Markdown
Contributor

Summary

Fix OpenCode provider defaults: stable session IDs for prompt caching, correct free-tier model routing, and CLI header synthesis enabled by default.

Problem

Three issues affecting OpenCode provider reliability on VPS deployments:

  1. Prompt caching broken — x-opencode-session was set to randomUUID() per request, so opencode.ai's upstream prompt cache never hit across a conversation
  2. Free-tier model misrouting — models like big-pickle and *-free could be incorrectly routed to non-opencode providers when the live catalog was temporarily unreachable
  3. CLI header synthesis off by default — Cloudflare in front of opencode.ai 429s VPS requests lacking CLI identity, but synthesis was opt-in instead of opt-out

Fix

  • Wired generateSessionId() from sessionManager.ts into opencodeHeaders.ts — session ID is now a deterministic SHA-256 fingerprint of model + system prompt + first user message + tools
  • Threaded request body through buildHeaders() in base executor and OpencodeExecutor override so the fingerprint has access to conversation context
  • Flipped CLI header synthesis to default ON (opt-out via OPENCODE_SYNTHESIZE_CLI_HEADERS=false), with production-proven defaults (opencode/desktop/global)
  • Added auto-echo of listing-valid model names for noAuth providers so response.model matches /v1/models listing
  • Short-circuited free-tier model resolution to opencode provider first to prevent prefix inference misrouting

Results

  • Same conversation = same session ID = prompt cache hits on opencode.ai
  • Free-tier models (big-pickle, *-free) correctly route to opencode when available
  • VPS requests no longer 429'd by Cloudflare due to missing CLI identity headers
  • Client-supplied headers still take precedence over all synthesized defaults

AI Assisted changes notice

This change was developed with AI assistance (ZCode agent).

- Wire generateSessionId() into opencodeHeaders so x-opencode-session
  is a deterministic fingerprint instead of randomUUID() per request,
  enabling upstream prompt caching across a conversation
- Thread request body through buildHeaders() so session fingerprint
  has access to model, system, messages, and tools
- Default CLI header synthesis to ON (opt-out via false), align
  values with 9router proven defaults (opencode/desktop/global)
- Auto-echo listing-valid model names for noAuth providers so
  response.model matches /v1/models listing
- Short-circuit free-tier model resolution to opencode provider first
  to prevent prefix inference misrouting when catalog is unreachable
@diegosouzapw

Copy link
Copy Markdown
Owner

The underlying problems (random session IDs breaking prompt-cache hits, header-synthesis being opt-in) look real, but this PR flips OPENCODE_SYNTHESIZE_CLI_HEADERS's default from off to on — a decision the existing test file documents as deliberate and owner-requested to stay off pending live validation — and breaks 2 of that file's assertions (I ran them: pass 5 / fail 2 on this branch). It also changes the synthesized UA/client/project default values (opencode-cli/1.0.0 → opencode, cli → desktop, default → global) without updating the test that pins those. No new or updated tests ship for the session-fingerprinting, free-tier routing, or echo-model changes either. Could you: (1) confirm with the maintainer that flipping the default is intended, (2) update tests/unit/opencode-cli-headers-synthesis-5997.test.ts accordingly, and (3) add coverage for the three new behaviors?

…rage

PR diegosouzapw#10571 flipped OPENCODE_SYNTHESIZE_CLI_HEADERS to on-by-default and
changed the synthesized UA/client/project default values, but shipped
with 2 broken assertions in the existing diegosouzapw#5997 regression test and no
coverage for the new session-fingerprinting, free-tier routing, or
noAuth echoModel logic (Hard Rule diegosouzapw#18).

- Update tests/unit/opencode-cli-headers-synthesis-5997.test.ts to match
  the new on-by-default behavior and new default values; add an explicit
  opt-out coverage test so the forward-only path is still guarded.
- Fix 20 further test failures in tests/unit/opencode-executor.test.ts
  and tests/unit/refactor-buildHeaders-opencode.test.ts caused by the
  same default flip (pin OPENCODE_SYNTHESIZE_CLI_HEADERS=false for the
  characterization suites that predate diegosouzapw#10571; use a genuinely
  CLI-looking UA where the preserved-UA test requires one).
- Fix a real bug found via TDD while adding the mandated free-tier
  routing regression test: the big-pickle/*-free short-circuit in
  open-sse/services/model.ts checked activeProviders?.has("opencode")
  literally, but getActiveProviderSet() canonicalizes every connection's
  provider id through resolveProviderAlias(), which rewrites "opencode"
  to "opencode-zen" via a manual override — so an active no-auth
  opencode connection could never satisfy the check. Now checks both
  opencode-family candidate ids. Proven with a test that fails on the
  original code and passes with the fix (both connections active with a
  stale synced catalog omitting big-pickle).
- Extract the noAuth-provider echoModel aliasing in chatCore.ts into a
  pure, directly-testable helper (open-sse/handlers/chatCore/noAuthEchoModel.ts),
  matching the existing chatCore god-file decomposition pattern.
- Add regression tests for generateSessionId()-based x-opencode-session
  fingerprinting (stable within a conversation, changes on model/message
  changes), the free-tier routing short-circuit, and the noAuth echoModel
  aliasing.
- Add the changelog.d/ fragment and sync docs/reference/ENVIRONMENT.md's
  OPENCODE_SYNTHESIZE_CLI_HEADERS/OPENCODE_USER_AGENT/OPENCODE_CLIENT/
  OPENCODE_PROJECT rows to the new defaults.

Does NOT resolve whether flipping OPENCODE_SYNTHESIZE_CLI_HEADERS's
default was the right call, and does NOT touch the separate open PR
diegosouzapw#10357 which flips the same flag with a different literal default value
- that decision is left to the maintainer at merge time.

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
@diegosouzapw
diegosouzapw merged commit 9222528 into diegosouzapw:release/v3.8.50 Aug 18, 2026
4 checks passed
HouMinXi pushed a commit to HouMinXi/OmniRoute that referenced this pull request Aug 20, 2026
…diegosouzapw#10571)

* fix(opencode): session stability, free-tier routing, and CLI defaults

- Wire generateSessionId() into opencodeHeaders so x-opencode-session
  is a deterministic fingerprint instead of randomUUID() per request,
  enabling upstream prompt caching across a conversation
- Thread request body through buildHeaders() so session fingerprint
  has access to model, system, messages, and tools
- Default CLI header synthesis to ON (opt-out via false), align
  values with 9router proven defaults (opencode/desktop/global)
- Auto-echo listing-valid model names for noAuth providers so
  response.model matches /v1/models listing
- Short-circuit free-tier model resolution to opencode provider first
  to prevent prefix inference misrouting when catalog is unreachable

* fix(opencode): make free-tier default flip self-consistent + add coverage

PR diegosouzapw#10571 flipped OPENCODE_SYNTHESIZE_CLI_HEADERS to on-by-default and
changed the synthesized UA/client/project default values, but shipped
with 2 broken assertions in the existing diegosouzapw#5997 regression test and no
coverage for the new session-fingerprinting, free-tier routing, or
noAuth echoModel logic (Hard Rule diegosouzapw#18).

- Update tests/unit/opencode-cli-headers-synthesis-5997.test.ts to match
  the new on-by-default behavior and new default values; add an explicit
  opt-out coverage test so the forward-only path is still guarded.
- Fix 20 further test failures in tests/unit/opencode-executor.test.ts
  and tests/unit/refactor-buildHeaders-opencode.test.ts caused by the
  same default flip (pin OPENCODE_SYNTHESIZE_CLI_HEADERS=false for the
  characterization suites that predate diegosouzapw#10571; use a genuinely
  CLI-looking UA where the preserved-UA test requires one).
- Fix a real bug found via TDD while adding the mandated free-tier
  routing regression test: the big-pickle/*-free short-circuit in
  open-sse/services/model.ts checked activeProviders?.has("opencode")
  literally, but getActiveProviderSet() canonicalizes every connection's
  provider id through resolveProviderAlias(), which rewrites "opencode"
  to "opencode-zen" via a manual override — so an active no-auth
  opencode connection could never satisfy the check. Now checks both
  opencode-family candidate ids. Proven with a test that fails on the
  original code and passes with the fix (both connections active with a
  stale synced catalog omitting big-pickle).
- Extract the noAuth-provider echoModel aliasing in chatCore.ts into a
  pure, directly-testable helper (open-sse/handlers/chatCore/noAuthEchoModel.ts),
  matching the existing chatCore god-file decomposition pattern.
- Add regression tests for generateSessionId()-based x-opencode-session
  fingerprinting (stable within a conversation, changes on model/message
  changes), the free-tier routing short-circuit, and the noAuth echoModel
  aliasing.
- Add the changelog.d/ fragment and sync docs/reference/ENVIRONMENT.md's
  OPENCODE_SYNTHESIZE_CLI_HEADERS/OPENCODE_USER_AGENT/OPENCODE_CLIENT/
  OPENCODE_PROJECT rows to the new defaults.

Does NOT resolve whether flipping OPENCODE_SYNTHESIZE_CLI_HEADERS's
default was the right call, and does NOT touch the separate open PR
diegosouzapw#10357 which flips the same flag with a different literal default value
- that decision is left to the maintainer at merge time.

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>

---------

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
giauphan pushed a commit to giauphan/OmniRoute that referenced this pull request Aug 20, 2026
…diegosouzapw#10571)

* fix(opencode): session stability, free-tier routing, and CLI defaults

- Wire generateSessionId() into opencodeHeaders so x-opencode-session
  is a deterministic fingerprint instead of randomUUID() per request,
  enabling upstream prompt caching across a conversation
- Thread request body through buildHeaders() so session fingerprint
  has access to model, system, messages, and tools
- Default CLI header synthesis to ON (opt-out via false), align
  values with 9router proven defaults (opencode/desktop/global)
- Auto-echo listing-valid model names for noAuth providers so
  response.model matches /v1/models listing
- Short-circuit free-tier model resolution to opencode provider first
  to prevent prefix inference misrouting when catalog is unreachable

* fix(opencode): make free-tier default flip self-consistent + add coverage

PR diegosouzapw#10571 flipped OPENCODE_SYNTHESIZE_CLI_HEADERS to on-by-default and
changed the synthesized UA/client/project default values, but shipped
with 2 broken assertions in the existing diegosouzapw#5997 regression test and no
coverage for the new session-fingerprinting, free-tier routing, or
noAuth echoModel logic (Hard Rule diegosouzapw#18).

- Update tests/unit/opencode-cli-headers-synthesis-5997.test.ts to match
  the new on-by-default behavior and new default values; add an explicit
  opt-out coverage test so the forward-only path is still guarded.
- Fix 20 further test failures in tests/unit/opencode-executor.test.ts
  and tests/unit/refactor-buildHeaders-opencode.test.ts caused by the
  same default flip (pin OPENCODE_SYNTHESIZE_CLI_HEADERS=false for the
  characterization suites that predate diegosouzapw#10571; use a genuinely
  CLI-looking UA where the preserved-UA test requires one).
- Fix a real bug found via TDD while adding the mandated free-tier
  routing regression test: the big-pickle/*-free short-circuit in
  open-sse/services/model.ts checked activeProviders?.has("opencode")
  literally, but getActiveProviderSet() canonicalizes every connection's
  provider id through resolveProviderAlias(), which rewrites "opencode"
  to "opencode-zen" via a manual override — so an active no-auth
  opencode connection could never satisfy the check. Now checks both
  opencode-family candidate ids. Proven with a test that fails on the
  original code and passes with the fix (both connections active with a
  stale synced catalog omitting big-pickle).
- Extract the noAuth-provider echoModel aliasing in chatCore.ts into a
  pure, directly-testable helper (open-sse/handlers/chatCore/noAuthEchoModel.ts),
  matching the existing chatCore god-file decomposition pattern.
- Add regression tests for generateSessionId()-based x-opencode-session
  fingerprinting (stable within a conversation, changes on model/message
  changes), the free-tier routing short-circuit, and the noAuth echoModel
  aliasing.
- Add the changelog.d/ fragment and sync docs/reference/ENVIRONMENT.md's
  OPENCODE_SYNTHESIZE_CLI_HEADERS/OPENCODE_USER_AGENT/OPENCODE_CLIENT/
  OPENCODE_PROJECT rows to the new defaults.

Does NOT resolve whether flipping OPENCODE_SYNTHESIZE_CLI_HEADERS's
default was the right call, and does NOT touch the separate open PR
diegosouzapw#10357 which flips the same flag with a different literal default value
- that decision is left to the maintainer at merge time.

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>

---------

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
Moseyuh333 added a commit to Moseyuh333/OmniRoute that referenced this pull request Sep 15, 2026
…e only filler

Review follow-up (PR diegosouzapw#12719): under the default configuration applyCliDefaults
(step 4, diegosouzapw#10571) backfills x-opencode-session, so the step-3 gap this PR fixes
is only observable when an operator sets OPENCODE_SYNTHESIZE_CLI_HEADERS=false
— step 4 never runs there and step 3 is the only filler left.

The new test isolates exactly that scenario and fails on the unpatched tip
(verified: 10/11 pass there, only this test red), proving it pins the fix.
muhamadgalihsaputra pushed a commit to niyatna/NiyatnaRoute that referenced this pull request Sep 27, 2026
…diegosouzapw#10571)

* fix(opencode): session stability, free-tier routing, and CLI defaults

- Wire generateSessionId() into opencodeHeaders so x-opencode-session
  is a deterministic fingerprint instead of randomUUID() per request,
  enabling upstream prompt caching across a conversation
- Thread request body through buildHeaders() so session fingerprint
  has access to model, system, messages, and tools
- Default CLI header synthesis to ON (opt-out via false), align
  values with 9router proven defaults (opencode/desktop/global)
- Auto-echo listing-valid model names for noAuth providers so
  response.model matches /v1/models listing
- Short-circuit free-tier model resolution to opencode provider first
  to prevent prefix inference misrouting when catalog is unreachable

* fix(opencode): make free-tier default flip self-consistent + add coverage

PR diegosouzapw#10571 flipped OPENCODE_SYNTHESIZE_CLI_HEADERS to on-by-default and
changed the synthesized UA/client/project default values, but shipped
with 2 broken assertions in the existing diegosouzapw#5997 regression test and no
coverage for the new session-fingerprinting, free-tier routing, or
noAuth echoModel logic (Hard Rule diegosouzapw#18).

- Update tests/unit/opencode-cli-headers-synthesis-5997.test.ts to match
  the new on-by-default behavior and new default values; add an explicit
  opt-out coverage test so the forward-only path is still guarded.
- Fix 20 further test failures in tests/unit/opencode-executor.test.ts
  and tests/unit/refactor-buildHeaders-opencode.test.ts caused by the
  same default flip (pin OPENCODE_SYNTHESIZE_CLI_HEADERS=false for the
  characterization suites that predate diegosouzapw#10571; use a genuinely
  CLI-looking UA where the preserved-UA test requires one).
- Fix a real bug found via TDD while adding the mandated free-tier
  routing regression test: the big-pickle/*-free short-circuit in
  open-sse/services/model.ts checked activeProviders?.has("opencode")
  literally, but getActiveProviderSet() canonicalizes every connection's
  provider id through resolveProviderAlias(), which rewrites "opencode"
  to "opencode-zen" via a manual override — so an active no-auth
  opencode connection could never satisfy the check. Now checks both
  opencode-family candidate ids. Proven with a test that fails on the
  original code and passes with the fix (both connections active with a
  stale synced catalog omitting big-pickle).
- Extract the noAuth-provider echoModel aliasing in chatCore.ts into a
  pure, directly-testable helper (open-sse/handlers/chatCore/noAuthEchoModel.ts),
  matching the existing chatCore god-file decomposition pattern.
- Add regression tests for generateSessionId()-based x-opencode-session
  fingerprinting (stable within a conversation, changes on model/message
  changes), the free-tier routing short-circuit, and the noAuth echoModel
  aliasing.
- Add the changelog.d/ fragment and sync docs/reference/ENVIRONMENT.md's
  OPENCODE_SYNTHESIZE_CLI_HEADERS/OPENCODE_USER_AGENT/OPENCODE_CLIENT/
  OPENCODE_PROJECT rows to the new defaults.

Does NOT resolve whether flipping OPENCODE_SYNTHESIZE_CLI_HEADERS's
default was the right call, and does NOT touch the separate open PR
diegosouzapw#10357 which flips the same flag with a different literal default value
- that decision is left to the maintainer at merge time.

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>

---------

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
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.

2 participants