Repository navigation
fix(ai): satisfy OpenCode Zen free-tier gate (403 FreeTierError) (#12306) - #1
Merged
312022151125 merged 10 commits intoSep 23, 2026
Merged
Conversation
…1357#12306) OpenCode Zen/Go gate the contributor free tier on client identity: a User-Agent whose leading token is opencode/<version> and an x-opencode-session matching ses_<12 hex><14 alnum>. omp's omp/* UA and raw session/install ids failed both checks, producing 403 FreeTierError on every free-tier model. applyInferenceHeaders now sends the canonical OpenCode UA and a stable ses_ token derived from the conversation session id (routing and prompt cache stay pinned across turns); conversation-less background traffic (usage polls, model discovery) attributes through the same formatter with the install id. The shared formatter lives in packages/catalog/src/wire/opencode.ts so catalog discovery and pi-ai send identical identity. Explicit caller UA overrides (Claude OAuth fingerprint) still win. 🤖 Generated with Codebuff Co-Authored-By: Codebuff <noreply@codebuff.com>
The can1357#12306 gate is body-shaped as well as header-shaped: it requires stream:true and at least five OpenCode core tool names (bash, edit, glob, grep, read) in tools[], so tool-less auxiliary calls (advisors, one-shot helpers) were still refused with 403 FreeTierError after the client-identity fix. stream() now pads such requests with stub entries for the missing names via withOpenCodeGateTools — empty description, inert {type:"object"} schema, strict:false — wire-shape filler that never joins the caller's tool registry. Agent rosters that already carry the names pass through untouched (same array identity). When a denial does surface, isOpencodeFreeTierGateMessage classifies it as model-scoped client policy rather than a credential fault: the key keeps serving paid SKUs, so the 403 no longer rotates sibling credentials (isAuthRetryableError and stream()'s rotatable-403 arm) or reads as an auth failure, and formatMessage rewrites the raw 403 into an explanation that preserves the classifier markers. google-shared now passes provider into finalize() so the zen google-transport lane gets the same treatment. 🤖 Generated with Codebuff Co-Authored-By: Codebuff <noreply@codebuff.com>
… rotation isDirectCredentialRotationError rotated sibling keys on any 403, but the OpenCode free-tier gate denial is model-scoped client policy: sibling keys fail identically and the credential keeps serving paid SKUs. Extends the same exclusion isAuthRetryableError already applies. Live-gateway verified: old identity 403s with FreeTierError; branch identity passes the gate (rate limiter answers instead). 🤖 Generated with Codebuff Co-Authored-By: Codebuff <noreply@codebuff.com>
🤖 Generated with Codebuff Co-Authored-By: Codebuff <noreply@codebuff.com>
Addresses roboomp review on can1357#12556: stub gate tools must not become callable work on auxiliary turns that expect text.
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
312022151125
pushed a commit
that referenced
this pull request
Oct 2, 2026
composerChips() tested chip presence with text.includes(label); the compact label "<icon> #1" is a substring of "<icon> can1357#10", so keeping attachment can1357#10 also surfaced chip #1 in the attachment band. - Added referencedAttachments(), which parses the buffer once with COMPOSER_TOKEN_REGEX and collects whole #<n> indices per kind. - composerChips() now keys image, video, and paste chips on that set. Supersedes can1357#11029, which targeted the pre-move coding-agent path. Fixes can1357#13605
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.
can1357#12556 (comment)
What
I asked for the complete header fix — the canonical
opencode/<version>User-Agent plus a stableses_-shaped session token — implemented on a branch in this repo with tests, and after reviewing the diff extended it to the body-gate (tool-name) half and the 403 error scoping.OpenCode's Zen/Go gateway refuses requests that don't carry OpenCode's own client
identity. All four gate conditions were verified against the live gateway:
User-Agentleading tokenopencode/<version>— omp now sendsopencode/1.18.31(explicit caller overrides like the Claude OAuth fingerprint still win)
x-opencode-sessionmatchingses_<12 hex><14 alnum>— now a deterministic tokenderived from the conversation id, so routing and prompt-cache pinning survive
across turns; background traffic (usage polls, discovery) derives from the install id
stream: true— omp's agent turns already streamtools[](weakened from five —bash+readsuffice) —stream()(single funnel behindstream/complete/streamSimple) pads tool-less auxiliary calls (advisors,one-shot helpers) with stub entries for missing gate names; stubs are wire-inert
and never enter the tool registry
Also scoped the error: a
403 FreeTierErrordenial is model-scoped client policy, nota credential problem — it no longer rotates sibling keys or invalidates the stored
credential, and the surfaced message explains the restriction instead of reading like
an auth failure. Shared contract lives in
@oh-my-pi/pi-catalog/wire/opencode.Why
fixes can1357#12306 — community bisection there established all four conditions; two open PRs
(can1357#12325, can1357#12326) each implemented only conditions 1–2, which the bisection shows is
insufficient (tool-less auxiliary calls keep 403ing).
Testing
bun checkclean; pi-ai 4863 pass, pi-catalog 1027/1027 (the 12 pre-existingpi-ai env failures reproduce on clean main)
Live gateway A/B (byte-identical bodies, only identity headers varied):
omp/18.2.6+ raw UUID session)403 FreeTierErroropencode/1.18.31+ses_token)429 FreeUsageLimitError(gate passed; quota limiter answered)ses_session403 FreeTierError(UA independently required)402 Insufficient funds(gate only guards free SKUs)A
429is the pass condition: the quota limiter only evaluates after the identitygate accepts. Wire capture confirmed padding: tool-less requests left the client as
bash,read.bun checkpasses