fix(opencode): append configured placeholder tools to client tools & protect precedence - #14156
Merged
diegosouzapw merged 1 commit intoSep 29, 2026
Conversation
adevwithpurpose
force-pushed
the
fix/opencode-free-tier-client-tools
branch
from
September 19, 2026 02:06
d337206 to
f374fc3
Compare
This was referenced Sep 19, 2026
…protect precedence Cherry-picked from f374fc3 (branch fix/opencode-free-tier-client-tools, 2026-09-19) onto the deployed line fix/purify-notice-cache-stable. The upstream free tier validates the request's declared tool NAMES, and refuses a request that does not match the OpenCode client contract with 403 "free tier can only be used from within OpenCode". `applyFreeTierRequestContract` early returned on `hasTools()`, so the placeholders were only injected when the caller sent NO tools at all -- a client that sends its own tools (DeepSeek Harness sends `run_code`) shipped without them and was refused. - Merge the configured placeholder names (OPENCODE_FREE_TIER_PLACEHOLDER_TOOLS) into the client's tools instead of only filling an empty set. Client tools are preserved and keep their order; only names not already declared are appended, so the operation stays idempotent. - Resolve configured names ahead of un-scoped observed tools in resolvePlaceholderNames, so a foreign caller cannot poison the observation store and change which names a later request borrows. - Cover both with unit tests (multi-placeholder client appending, observation precedence). Measured on the real entry point `prepareFreeTierRequest` with OPENCODE_FREE_TIER_PLACEHOLDER_TOOLS=glob,grep,read,edit,write,bash: in : tools=[run_code] out: tools=[run_code, glob, grep, read, edit, write, bash] Paid models and OPENCODE_FREE_TIER_REQUEST_CONTRACT=off are untouched.
adevwithpurpose
force-pushed
the
fix/opencode-free-tier-client-tools
branch
from
September 23, 2026 06:07
f374fc3 to
8ea6d25
Compare
diegosouzapw
merged commit Sep 29, 2026
2acb430
into
diegosouzapw:release/v3.8.51
9 of 16 checks passed
diegosouzapw
added a commit
that referenced
this pull request
Sep 29, 2026
…tool-map side channel, audio id, CI cache key, stale contracts) (#15112) Release-captain base-red fix (v3.8.51 release PR #11442, unit shards 5-6): three production defects (OpenCode free-tier retry could resend the exact refused body; _toolNameMap side channel left in the Gemini/Antigravity body; unsafe audio model ids reported as 'no provider found' instead of 400) plus contract propagations (#12961 cache key, #14627, #14156, #14273, #14959, #15002). All twelve reds reproduced on the tip and pass; typecheck clean.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Default change: None for standard requests.
Behavior change: Clients supplying custom tools (e.g. DeepSeek Harness, Claude Code, Cursor, Cline sending
run_codeor custom functions) to OpenCode Zen free-tier models (oc/big-pickle,oc/muse-spark-1.2-contributor-free) no longer fail with HTTP 403FreeTierError. Furthermore, un-scoped tool observations from foreign callers without session IDs no longer poison the tool cache and break subsequent requests.Background & Root Cause
/zen/v1) enforces the presence of OpenCode's official client tools (glob, grep, read, edit, write, bash) for free-tier access. In fix(opencode): match the upstream free-tier request contract #14013,applyFreeTierRequestContractcheckedhasTools(next): if client tools existed, it fell back tobaseNames = [PLACEHOLDER_TOOL_NAME](_noop). When OpenCode Zen receivedtools: [run_code, _noop], it returned403 FreeTierError: OpenCode's free tier can only be used from within OpenCode.next.tools, leaving caller tools intact and satisfying upstream.resolvePlaceholderNames, un-scopedgetObservedToolNames(provider, model)was checked beforeconfigured. When a generic caller succeeded without a session tag, its tool list (["run_code"]) was recorded as observed and subsequently overwrote the 6 configured tools, breaking later turns and bare chat requests with 403.Related Issues
Validation
node --import tsx --test tests/unit/opencode-free-tier-request-contract.test.ts(39/39 passing)npx eslinton modified files passes with 0 errorsupstream/release/v3.8.51Tests Added Or Updated
tests/unit/opencode-free-tier-request-contract.test.ts:when client-supplied tools are present, multiple configured placeholders are appended(verifies client tools likerun_codeare preserved while all 6 configured tools are appended).configured placeholder names take precedence over un-scoped observed tools for generic clients(verifies un-scoped observations cannot override configured official placeholders).Coverage Notes
open-sse/executors/opencodeFreeTierContract.tsandopen-sse/executors/opencodeToolObservation.ts.opencode-free-tier-request-contract.test.tspass cleanly.Reviewer Notes
changelog.d/fixes/14156-opencode-free-tier-client-tools.md.