Skip to content

[Codex effort format] - Step 3: Drop opencode's hardcoded effort list - #2768

Open
zeroliu wants to merge 1 commit into
codex-effort-2-base-blurbfrom
codex-effort-3-opencode-vocabulary
Open

[Codex effort format] - Step 3: Drop opencode's hardcoded effort list#2768
zeroliu wants to merge 1 commit into
codex-effort-2-base-blurbfrom
codex-effort-3-opencode-vocabulary

Conversation

@zeroliu

@zeroliu zeroliu commented Aug 7, 2026

Copy link
Copy Markdown
Collaborator

Fixes logancyang/obsidian-copilot-preview#271

Why

Codex's hardcoded effort list is gone from this stack's first step. opencode carries the same construct — a KNOWN_OPENCODE_EFFORTS set of none · minimal · low · medium · high · xhigh · max — and it is missing ultra in exactly the way Codex's was. The issue asked whether to fix it too.

It needs no fixing, because it does nothing. opencode stopped putting effort in model ids: it reports plain ids like opencode/big-pickle and openrouter/anthropic/claude-3.5-haiku, and offers effort as a separate thought_level selector scoped to whichever model is active. Copilot already reads it from there — that is what the effort dropdown in an opencode chat is driven by.

The suffix-splitting branch that the vocabulary guards can no longer run for any opencode Copilot will talk to. Installs below 1.16.0 resolve to incompatible, and both the model preloader and the session manager require ready before spawning, so an older binary never starts and never reports a catalog. Persisted state cannot reach the branch either: enabled opencode models derive their id from the provider mapping, not from the decoder, whose only callers translate ids the running agent just reported.

So the vocabulary is a list of magic strings that reads as load-bearing, sitting on a decode path where the effort branch cannot fire. The next person to hit an effort bug will find it and widen it, exactly as the issue proposed.

What

No user-visible change. Removing the branch also removes the reason the vocabulary existed: it was there to stop a model whose id legitimately ends in a third segment — openrouter/anthropic/claude-3.5-haiku — from being mis-split into a base model plus an effort called claude-3.5-haiku. With no trailing segment ever peeled off, that hazard is gone rather than guarded.

opencode reports Base model Effort
opencode/big-pickle opencode/big-pickle from the thought_level selector
openrouter/anthropic/claude-3.5-haiku openrouter/anthropic/claude-3.5-haiku from the thought_level selector
byok-id/lmstudio-community/Qwen2.5-7B-GGUF the whole id none advertised

Encoding drops effort to match, as the Claude backend's codec already does — for opencode the model id and the effort travel as two separate calls, and only the model id was ever sent.

This is the last step in the stack, so with it the reported behavior is fully addressed: Codex effort levels come from the CLI, the settings list shows one row per base model, and neither backend keeps a list of effort names.

Non goal

  • opencode's effort behavior itself is unchanged: it still comes from the thought_level option, still gets prefetched per enabled model, and still applies after the model switch.
  • The minimum supported opencode version is not moved; this relies on the existing 1.16.0 floor rather than raising it.
  • Claude's effortConfigFor path is untouched — it synthesizes its option from the bundled SDK catalog and has no id-suffix branch to remove.
  • No shared abstraction is introduced across the three backends' codecs; each keeps its own, now each without a vocabulary.

Screenshot

Not applicable — this PR deletes unreachable code and changes nothing a user sees.

Risk

Medium

Criterion Status Reason
No behavior change, or a cosmetic/copy/docs/config change visible where it renders, or deterministic tests cover the changed behavior opencode/descriptor.test.ts covers decoding umbrella ids whole, keeping a trailing segment that reads like an effort, provider attribution, and encode/decode round-trips; its applySelection and prefetchEffortCatalog suites still assert the two-call model-then-effort path
A defect would fail CI or be obvious on first use Those tests run in CI, and a mis-decoded id would show as a wrong or duplicated opencode model on the first picker open
A revert fully restores prior state, including persisted data Code-only; nothing is written or migrated
No auth, permissions, secrets, or input-handling surface changes Model id parsing only
No public API, plugin API, message, or on-disk contract changes Wire ids sent to opencode are unchanged — effort was already applied through its own option, never the id
No core-path concurrency, async-lifecycle, or state-machine changes The codec is pure; the apply sequence is unchanged
No hot-path behavior lacks deterministic coverage Every remaining decode and encode branch is covered
No new dependency Dependency manifests are unchanged
Human-only behavior stays in one feature area and surfaces quickly Correctness rests on the claim that no supported opencode reports an effort-suffixed id — a reachability argument CI cannot prove. If it is wrong, an opencode model would switch to the wrong effort silently, without an error

Review: confirm the reachability claim before the code. In src/agentMode/backends/opencode/OpencodeBinaryManager.ts, check isOpencodeVersionOutdated and toOpencodeInstallState return incompatible below OPENCODE_MIN_ACP_VERSION, then that AgentModelPreloader.preload and AgentSessionManager.isBackendInstalled both gate on ready. Confirm opencodeEnabledModelEntries and opencodeWireBaseId in src/agentMode/backends/opencode/opencodeModelResolve.ts never call wire.decode. Then read opencodeWire in src/agentMode/backends/opencode/descriptor.ts alongside its applySelection. Run Verification steps 2–4, including step 4's downgrade attempt.

Verification

Requires opencode installed and at least one provider configured, plus a reasoning-capable model such as an Anthropic or OpenRouter one.

  1. Build and load this branch.
  2. Open an opencode chat and the model picker. Every configured model appears exactly once — in particular an OpenRouter model such as openrouter/anthropic/claude-3.5-haiku is one row, not several, and is not renamed.
  3. Select a reasoning-capable model, choose a non-default effort from its dropdown, and send a message. The turn runs, and reopening the picker shows the effort you chose still selected. Switch to a different model and back, and confirm the effort dropdown reflects the newly active model rather than the previous one.
  4. Point Settings → Agent → opencode at an opencode binary older than 1.16.0. It is reported as unsupported with an upgrade prompt and no chat starts against it — the state this PR relies on to make the removed branch unreachable.

@zeroliu
zeroliu marked this pull request as ready for review August 8, 2026 04:24
`KNOWN_OPENCODE_EFFORTS` and the trailing-segment branch it gated are
unreachable on every opencode version Copilot will talk to. opencode reports
effort as a sibling `category:"thought_level"` config option and its model ids
carry none, which `applySelection` and `prefetchEffortCatalog` already rely on;
installs below `OPENCODE_MIN_ACP_VERSION` (1.16.0) resolve to `incompatible`,
and both `AgentModelPreloader` and `isBackendInstalled` require `ready`, so an
older binary never spawns or reports a catalog. The suffix branch is also never
reached from persisted state: `opencodeEnabledModelEntries` builds its base id
from the provider mapping, not `wire.decode`, whose only callers are
`translateBackendState` over live agent-reported ids.

Deleting it also removes the reason the vocabulary existed — with no trailing
segment peeled off, an umbrella id like `openrouter/anthropic/claude-3.5-haiku`
can no longer be mis-split, so nothing needs to gate against it. `encode` drops
effort to match, as Claude's codec already does.

Net -68 lines.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@zeroliu
zeroliu force-pushed the codex-effort-3-opencode-vocabulary branch from d1f0946 to f142ce1 Compare August 8, 2026 04:38

zeroliu commented Aug 8, 2026

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f142ce1019

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

const opencodeWire: ModelWireCodec = {
encode: (selection: ModelSelection) =>
selection.effort ? `${selection.baseModelId}/${selection.effort}` : selection.baseModelId,
encode: (selection: ModelSelection) => selection.baseModelId,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3 Badge Align the shared wire contract with out-of-band OpenCode effort

Once encode drops effort here, the shared contract still explicitly classifies OpenCode as suffix-style (src/agentMode/session/types.ts:225-229), seedSelectionIntoState still says a later setModel carries OpenCode effort in the encoded ID (src/agentMode/session/AgentSession.ts:110-116), and designdocs/todo/subscription_models_via_agent_backends.md:128-130 still documents <provider>/<model>[/<effort>]. A maintainer extending generic model dispatch from these contract comments would use the wrong channel and silently lose effort on supported OpenCode, so these stale references should be updated with the codec change.

Useful? React with 👍 / 👎.

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