Repository navigation
fix(providers): remove OpenCode Free while preserving Zen and Go - #16310
Conversation
…ntract Preventive removal of the keyless "OpenCode Free" provider (id `opencode`, alias `oc`) and of the free-tier request contract that shaped requests to look like the OpenCode client (placeholder/borrowed tools, forced stream and JSON rebuild, shape replay, observed-tool retry, keyless premium gate), honoring the OpenCode operator's public removal request. - registry entry, no-auth catalog entry, executor alias, free-apikey and no-auth proxy allowlists, auto-combo no-auth allowlist, fingerprint expansion member, egress-lock default, search pair, usage fetcher ids - free-model catalog rows for `opencode` and the opencode-zen free rows, ToS verdict entry, models.dev mapping and synced-capability fallback - opencode-zen / opencode-go no longer offer the keyless anonymous fallback or hydrate from the removed sibling id - combo `opencode/` -> `oc/` target rewrite (#11912) removed - migration 206 cleans stored `opencode` / `oc` provider state - REMOVED_PROVIDERS.md entry + blocklist guard (declaration-shaped needles, so opencode-zen / opencode-go and the OpenCode client integrations stay untouched) The paid opencode-zen / opencode-go providers keep the OpencodeExecutor; a free model requested through opencode-zen is sent without the contract.
Tests that only exercised the removed keyless provider or the free-tier contract were dropped with it; tests that used `opencode` / `oc` as a vehicle for a mechanism that stays are re-pointed at a provider that still has the property under test (uncloseai / duckduckgo-web for no-auth paths, pollinations / kilocode for the anonymous fallback, opencode-go for the egress-bucketed lock, opencode-zen for the family error rules and the executor). Removed ids are asserted absent where a list used to include them. Goldens (executor map, provider translate-path) drop exactly the `opencode` entry, and the two stale ESLint suppressions for deleted or cleaned test files are pruned.
Remove the keyless OpenCode Free provider from the getting-started guides, tiers/free-tier references, troubleshooting and strict-zero-cost notes, the README free-forever table and their i18n mirrors (rows and mentions removed, surrounding text kept), regenerate PROVIDER_REFERENCE.md, and re-sync the counts the docs-counts gate pins: 371 providers (153 with hasFree), 203 migrations, 476 free-tier catalog entries, 34 recurring pools, 52 recurring/keyless free-forever providers.
…sking gate Record each deleted test file as `sourceRemoved` (the keyless registry entry, the free-tier contract modules or the #11912 combo-target alias, all absent at HEAD) and each net-assert reduction with the case it dropped and why, so check:test-masking can tell the removal apart from weakening.
…ode Free catalog removal
…the OpenCode Free removal
…opencode-zen The removed `opencode` id now resolves to `opencode-zen` as a user-typed alias, so a hidden-model override or alias stored under the old id would hide models on the paid Zen provider. Migration 206 now also clears modelCompatOverrides, providerAliases and syncedAvailableModels rows keyed by the removed ids, and the hidden-model combo test that targeted the removed provider is re-pointed at opencode-zen's own id.
| }); | ||
| await lockModelWithAnUpstream502(); | ||
| await settingsDb.updateSettings({ requestRetry: 3, maxRetryIntervalSec: 10 }); | ||
| let noAuthCalls = 0; | ||
| let keyedCalls = 0; | ||
| globalThis.fetch = async (url) => { | ||
| if (String(url).includes("opencode.ai")) { | ||
| if (String(url).includes("unturf.com")) { |
CI Coverage Report
Coverage artifact was not available for this run. |
… of deleting them The OpenCode Free tier was keyless, so a connection on the old card that carries an API key is a paid Zen key the user pasted there. Migration 206 now re-homes those rows to opencode-zen (same id, same key) and deletes only the keyless ones.
…ors where both sides changed them
|
A narrower option before removing everything: keep The upstream message is "can only be used from within OpenCode", and the request in #14227 was about traffic from outside OpenCode reaching these models. A pass-through only carries what someone's own OpenCode session already sent, for people who run OpenCode and point it at OmniRoute for caching, fallback or logs. It stops:
I'd detect the client by its User-Agent plus the session headers it sends. That isn't proof, and it doesn't need to be: the point is that OmniRoute never makes up a client. It's also the version that doesn't break when upstream moves. Today's 403s come from what OmniRoute changes in the request. The current client (2.0.29) now talks to To keep the risk down while #16310 is on hold, I'll open this as a PR: it removes the client-imitation code and the non-OpenCode access, so you can compare the two side by side. If the request in #14227 gets confirmed and asks gateways to stop carrying these models at all, #16310 as it stands is still the right answer and the PR can simply be closed. |
The release tip's highest migration is 205, so 208 left a two-number gap. Rename the migration, its test and every pinned reference (REMOVED_PROVIDERS, changelog fragment). Also compare the no-auth cooldown test's fetch stub on the exact upstream hostname instead of a substring match (CodeQL incomplete URL substring sanitization).
…Zen/Go (#16531) Merge-batch Track B (owner-approved). Head 214d075 = e42619d + release/v3.8.52 merged (clean, no code changes by me). OpenCode keyless 'opencode' registry entry kept as-is (#16310 on HOLD). Validation on the merged head: tests/unit/opencode-tool-name-length.test.ts + 14 opencode/default-executor suites (opencode-executor, opencode-tools-no-truncation, executor-default-*, repro-10119, opencode-free-tier-request-contract, target-format/responses-format, buildHeaders, enum-trim) 240/240; nvidia-tool-compatibility-2840 (other toolNameMaxLength consumer) 7/7; re-run after the final tip merge 120/120. Gates: typecheck:core 0, open-sse typecheck OK, complexity 3079<=3218, cognitive 1422<=1437, file-size OK, skills Generated 0, boundary 774/774, changelog-integrity OK, eslint (suppressions) 0.
Targeted i18n:run --files for QUICK-START, TROUBLESHOOTING, FREE-TIERS-GUIDE, PROVIDERS-GUIDE and REMOVED_PROVIDERS (only the changed sections; table row counts verified equal to the English source in all 66 locales).
…T_ZERO_COST mirrors Their mirrors carry the same row/section deletions as the English source, so record the current source and target hashes instead of retranslating.
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).
…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.
|
Hey, before acting rash. I did an investigation into 3xp10its account and couldn't confirm his affiliation with Anomalyco or OpenCode. I found that he studied pharmacy and worked in related field, none of his profiles (ResearchGate and LinkedIn) links toward any OpenCode or Anomalyco jobs. Beside, Anomaly's own GitHub org doesn't have him as a member. |
…isabled (#16976) Merged via /merge-batch (2026-10-17 wave, round 3). Reconciled with #16503 (re-translated ROUTE_GUARD_TIERS mirrors) and #16310: every mirror takes the tip's translation plus this PR's new Tier-2 row for /api/relay/tokens (66/66 locales), and .i18n-state.json keeps the tip's records with only the ROUTE_GUARD_TIERS record re-adopted from disk (i18n drift: 20 drifted sources vs 21 on the pure tip, 0 drifted targets). Behaviour change (intended, security): relay token mint/list/read/edit/revoke now requires a credential even with requireLogin=false; dashboard session and manage keys keep working. Focused tests 15/15 (relay-tokens-always-protected + credential-export-always-protected); openapi-security-tiers PASS; api-typecheck, typecheck:core, docs-sync, changelog-integrity, skills-sync (Generated 0), boundary 774/774 green. route-guard-import-locales fails identically on the pure tip (inherited base-red #15306).
…t credential columns (#17006) Base-red hotfix (#15306), regression from #16310: migration 207 aborted the run on a provider_connections table without credential columns. The runner adds the missing nullable TEXT columns first. New legacy-schema test RED on the tip runner / GREEN with the fix; db-core-init 14/14; migration-207 + keyed-preservation suites green; typecheck, file-size, complexity, changelog-integrity, boundary 774/774, eslint, prettier green.
…h provider that still exists The fixture used the keyless "opencode" provider, removed by cheaper-inference#16310, so the synced model never reached the combo builder and both cases failed before reaching the visibility logic. aihorde is still a no-auth provider listed without a provider_connections row.
…s connection id - opencodeQuotaFetcher: keyless OpenCode Free is gone (cheaper-inference#16310), so the 'noauth' fallback id resolved a proxy for a connection that cannot exist and shared one cache entry across every empty-id reading. Key the cache, the session seed and the proxy lookup on the real connection id. - grokQuotaFetcher: the billing/OIDC helpers took an optional id and passed 'connectionId ?? ""' to fetchWithConnectionProxy, which reports an unknown reading for an empty id. Every caller already has the id (fetchGrokWebQuota, xaiOauthQuotaFetcher), so make it required.
…n proxy (#16344) Routes the remaining console quota readings (jina, llmgateway, lyceum, moonshot, new-api aggregator, opencode, openrouter, tavily, v0) through the connection's assigned egress path via fetchWithConnectionProxy, completing #15885 (merged first; this branch was stacked on it and now carries only its own delta). Maintainer fixes on top: - open-sse/services/opencodeQuotaFetcher.ts: dropped the stale effectiveConnectionId = connectionId || "noauth" sentinel. Keyless OpenCode Free is gone (#16310), so 'noauth' resolved a proxy for a connection that cannot exist and shared one cache entry/session seed across every empty-id reading. Cache, session seed, proxy lookup and invalidation now use the real connection id; an empty id reports no reading like every other quota fetcher (the policy #16773/#15885 test for the helper). - open-sse/services/grokQuotaFetcher.ts: the OIDC/refresh/billing helpers took an optional id and passed connectionId ?? "" to the helper, which turns an empty id into an unknown reading. Every caller already has the id (fetchGrokWebQuota, xaiOauthQuotaFetcher), so the parameter is now required and the ?? "" fallback is gone. The helper's empty-id policy itself is unchanged (merged + tested in #16773/#15885). - tests/unit/quota-fetchers-connection-proxy.test.ts: the 'shared no-auth reading' case replaced by (a) an empty id makes no fetch outside the proxy — red on the PR head before the fix — and (b) the cache is keyed and invalidated by the connection id, not a shared no-auth key. Evidence: focused node:test 324/324 over 29 quota-fetcher / usage-proxy files, 66/66 re-run after the final tip merge. Gates: typecheck:core, open-sse and API typechecks, file-size, complexity, cognitive-complexity, dead-code, boundary, agent-skills (Generated: 0), changelog-integrity, env-doc-sync, docs-sync, eslint all exit 0; prettier clean on the PR's hunks (the one flagged file, newApiAggregatorQuotaFetcher.ts, is unformatted on the release tip in an untouched function). Remaining CI reds are the inherited base-red #15306. Thanks @maxmad64bis!
…lidator keys After cheaper-inference#16310 the OpenCode quota fetcher registers only opencode-go and opencode-zen, so asserting an invalidator for 'opencode' failed on the merged tree.
Adds a per-provider registry of quota-cache invalidators (registerQuotaCacheInvalidator / getQuotaCacheInvalidator in quotaPreflight.ts). Every bespoke quota fetcher registers how to drop its cached snapshot under the same provider ids (aliases included) it registers its fetcher with, and the upstream-429 site in executeProviderRequest now calls invalidateQuotaCacheOnStatus, which uses the provider's invalidator and falls back to the generic cache drop. Before, a 429 only dropped the Codex and generic caches, so bespoke providers kept serving a stale 'quota available' snapshot for up to a minute. Behaviour note: a 429 now drops every bespoke fetcher's cache for that connection, so the next preflight re-reads usage — more quota reads after 429s, each still throttled by throttleQuotaFetch. Maintainer fixes on top: - Merged #15885 and #16344 (landed first, same fetcher files) into the branch: the only conflict was the grokQuotaFetcher import block (kept both the invalidator registration and the connection-proxy import). The combined fetchers both register their invalidator and read through the connection proxy. - tests/unit/quota-cache-invalidation-on-429.test.ts (~L364): removed "opencode" from the expected invalidator keys — after #16310 the OpenCode fetcher registers only opencode-go and opencode-zen; the assertion was red on the merged tree ('missing quota cache invalidator for opencode') and is green now. Evidence: focused node:test 332/332 over 29 files (quota-cache-invalidation-on-429, chatcore/execute-provider-request, generic-quota-fetcher, quota-fetchers-connection-proxy, every *-quota-fetcher suite, usage-connection-proxy, bailian-usage, newapi preflight dispatch), 74/74 re-run after the final tip merge. Gates: typecheck:core, open-sse and API typechecks, file-size, complexity, cognitive-complexity, dead-code, boundary, agent-skills (Generated: 0), changelog-integrity, env-doc-sync, docs-sync, eslint all exit 0; prettier clean on the PR's hunks (the flagged lines in tests/unit/chatcore/execute-provider-request.test.ts are pre-existing on the release tip). Remaining CI reds are the inherited base-red #15306. Thanks @maxmad64bis!
…ter #12608, stale budget card SVG (#17054) * test(antigravity): the warmup quota fixture follows the #12608 live 3.8 tier `git bisect` (d3fa8bd..89ebf6a, this file only) lands on #12608 (76e905b): the per-tier Gemini 3.7 ids left the live roster and the usage parser now hides them from Provider Limits (`isUserVisibleAntigravityQuotaModelId` is false for gemini-3.7-flash-high). #12608 moved its own usage fixtures to gemini-3.8-flash-high, but it was rebased on a 2026-10-03 tip and never saw this test (#16512, 2026-10-13), so "unreachable release feed falls back to the 2.5.5 floor" kept asserting a bucket the parser now drops. Use the live 3.8 tier, like #12608 did for its siblings; the version-floor assertions are unchanged. Refs #15306 * test(antigravity): the AGY static quota key uses the live 3.8 tier (#12608) #12608 retired gemini-3.7-flash{,-high,-medium,-low} from AGY (absent from the live roster, discussion #15568), so isUsageQuotaKeyAllowed("agy", "gemini-3.7-flash-medium") is false by design. "quota key allowed: static model IDs are always allowed" (#13739, 2026-10-08) predates #12608's rebase base and still used the retired id. Use gemini-3.8-flash-medium, the live tier the shared catalog now exposes; the other assertions are unchanged. Refs #15306 * docs(screenshots): regenerate the free-tier budget card for the current catalog tests/unit/gen-budget-card-svg.test.ts compares the committed SVG with `scripts/research/gen-budget-card-svg.mjs` output. Since the last regeneration (6aec22c, 2026-10-15) the catalog changed twice — #16310 removed OpenCode Free and #12608 retired the per-tier Gemini 3.6/3.7 Flash ids — so the card still said "485 models" and "13 providers". Regenerated with the script: 478 models, 12 providers; nothing else in the SVG moves. Refs #15306
…restore tip defaults Conflicts in providerErrorRules.ts, four egress/rule tests and .i18n-state.json resolved to the release tip: cheaper-inference#16310 already dropped opencode from the egress default set, and the opencode-zen / full-text widenings are re-applied behind default-off feature flags in the follow-up commits so the default path stays byte-identical to the tip.
Keep only the generic combo exhaustion guards; the keyless OpenCode Free provider was removed by cheaper-inference#16310, so its catalog delisting, OPENCODE_FREE_MODELS roster edit, premium 402 classification and the tests written for them take the tip version.
…ence#14313 exhaustion fixes The request-scoped, model-unavailable and stream-early-EOF guards in targetExhaustion.ts are generic; their tests no longer lean on the keyless OpenCode Free connection removed by cheaper-inference#16310. Adds controls proving a plain 403 and a plain 502 still exhaust the connection.
…p the keyless auth Rework of cheaper-inference#16571 on the release tip: - the v2 identity (UA opencode/latest/<2.x>/cli, client cli, 2.x UA rule), the gated free-tier v2 session headers (no x-opencode-request, session mirrored), the v2 CLI read/shell placeholder schemas and the tool_choice "none" strip are applied only when OPENCODE_V2_CLIENT_FINGERPRINT_ENABLED is on; with it off every OpenCode request is byte-identical to the tip (UA opencode/1.18.31, client desktop, 1.17 UA rule, tip placeholder schemas and tool_choice); - the flag is read in the executor (existing featureFlags import) and passed down as a parameter, so no new open-sse -> src import; - drop the Authorization: Bearer public injection: it only served the keyless OpenCode Free provider removed by cheaper-inference#16310, and it read gatedScope before its declaration (TDZ ReferenceError on every keyless buildHeaders call, TS2448); - move the fingerprint import to the top of opencodeFreeTierContract.ts and drop the unused FINGERPRINT_TOOL_DEFINITIONS / DEFAULT_PLACEHOLDER_TOOL_NAMES exports; restore the tip contract test assertions (default unchanged).
…ed catalog declares (#16798) Merged via /merge-batch (2026-10-18, uplift round). Thanks @maxmad64bis for the idea and the original implementation (commit by Dizzle, carried with authorship as 2637fc7)! This PR was stacked on #16608 (on HOLD: the keyless OpenCode Free path it restored was removed in #16310), so the branch was re-cut standalone off release/v3.8.52 with owner approval (force-push with lease): only this PR's own commit was cherry-picked, without #16608's endpoint module. Maintainer rework on top: - refactor(opencode): read the published model surfaces standalone (in-memory, 6 h cache, 30 s attempt floor, 5 s timeout, never throws, no src/ imports) - feat(flags): add OPENCODE_PUBLISHED_CATALOG_ROUTING_ENABLED (default off) - fix(opencode): gate the published-catalog surface lookup on that flag — with it off, getModelTargetFormat returns exactly the tip's answer and the catalog is never fetched Evidence: validated on the combined board of the 34 reworked PRs, then on the current tip: focused tests 85/85 (opencode-model-surface-from-catalog 13/13 + flag registry), typecheck core/open-sse, boundary 774/774, cognitive-complexity within baseline, changelog-integrity, skills-sync Generated 0. Remaining CI reds are the inherited base-red #15306.
The fork's v3.8.52 sync commit (39df1d7) rewrote the OMNIROUTE_EGRESS_IP_LOCK_PROVIDERS documentation in place - one block in .env.example and one row in docs/reference/ENVIRONMENT.md. The merge kept the fork's rewrite AND upstream's original, so the merged tree carried the variable twice in both files: upstream has one occurrence, the fork tip has one, the merge had two. Removes upstream's now-obsolete copy and keeps the fork's rewrite, which is the more complete of the two. Also corrects the provider list in the surviving copy. It named a bare `opencode` alongside `opencode-go` and `opencode-cli`, but the default allowlist is exactly the latter two: EGRESS_BUCKETED_LOCK_PROVIDERS_DEFAULT = { opencode-go, opencode-cli } which tests/unit/egress-ip-lock-env-override.test.ts pins directly (assert.deepEqual([...set].sort(), ["opencode-cli", "opencode-go"])). The bare `opencode` id is not a registered provider - upstream retired it in cheaper-inference#16310 - so the docs named a provider the lock never applies to. The stale wording was inherited from upstream's own text; this corrects the copy the fork owns. Doc-only: no runtime behaviour changes.
Reconciles the Muse Code consolidation with the v3.8.53 base: - managedLeaseFence.ts: keep the base's createManagedLeaseFence factory and add the MUSE_OWNERSHIP_REJECTED code/result to it (no lease needed for that fence). - chat.ts: fold the Muse owner pin (allowed connection, no occupancy session key, forced connection, no preselected/empty-turn sibling exclusion) into the base's selectCredentials() helper. - selectedConnectionHeader.ts: clone path now goes through inheritResponsePolicies (probe marker cheaper-inference#15594 + trusted empty-turn record cheaper-inference#16072), with a regression test. - providerModels.ts / registeredEffortVariants.ts: keep the base (OpenCode Free removed in cheaper-inference#16310, `ultra` tier) plus the PR's ocg/ocz aliases and `minimal` suffix. - oauth-polltoken test: assert both the bare invalid_response shape and the no-upstream-echo guard (cheaper-inference#15869); stryker list and file-size baseline take the base side. - opencode-muse-spark-1.3-support test: the removed `opencode` alias is replaced by `ocz`. - museSessionOwnership.ts: split ownContinuationIds() out of continuationIds() so the PR does not add a cognitive-complexity violation over the ratchet baseline.
…keep the refreshed Zen catalog list The keyed-preservation guard (cheaper-inference#16310) conflicted: the release's catalog refresh (cheaper-inference#17345) already lists mimo-v2.6-flash-free alongside the new Zen rows, which is the only change this PR made to that list, so the hunk takes the release list. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
Retires only the keyless OpenCode Free provider (
opencode/oc) while preserving authenticated OpenCode Zen and Go, their six existing Zen catalog entries, shared execution contracts and OpenCode client/plugin integrations.Behavior
208_remove_opencode_free_provider.sqlmoves legacyopencode/occonnections to Zen when they contain an API key, access/refresh/id token or nonempty provider metadata. It keeps connection IDs and credential bytes, deletes demonstrably empty legacy connections, and leaves existing Zen/Go connections and historical usage intact.3c3edb9ef32f7273c0de848af261f2cc7926f9fb, preserving DaoXE, selective provider transfer and nested provider credential encryption. Current isolated counts are 373 providers and 203 SQL migration files.Validation
Prepared head:
81deeda6228f624726223cc507847776edcfe1bc; tree:5a4c93ab32554e0a7a585fba2294b254acc7eb3f. Original PR head and the incorporated release base remain ancestors; commits use normal hooks.Completed focused phases on the earlier incorporated base
a139bce…(recorded heads in local receipts): 100/100 Node cases across seven files; 138/138 across eleven restored/reconciled files; 565/565 across the remaining 71 Node files; 21/21 Vitest AutoCombo cases; 3/3 UI cases. These phases overlap and are not summed as a unique-test total. The two further coverage restorations passed 16/16. Typecheck core passed on both incorporated bases; documentation counts, file-size, changelog and normal commit hooks passed.The catalog regression was reproduced before restoring six authenticated Zen rows. The migration regression was reproduced before preserving token-only and metadata-only accounts. Independent SQLite checks preserved 18 credential-bearing rows, removed 14 empty rows, retained three controls, and exercised idempotence and transactional rollback. No production database migration, live-provider smoke validation or deployment was performed.
After incorporating credential-encryption base
3c3edb9…, six focused files produced 37/38 passes. The failure was in the new test fixture: a raw insert after a cached read omitted the second legacy row from the cached result. Setup now inserts all legacy rows before migration/read; production SQL is unchanged. The corrected migration file rerun passed 2/2 cases, including the real encrypted roundtrip through eager/lazy Zen reads and top-level-token Authorization; its focused ESLint run also passed.A prior 294-case diagnostic run had 292 passes and two fixture failures; their repair reruns passed. That earlier failure remains recorded and is not described as a full 294-case pass. Local dependencies match the candidate lockfile but were copied by hardlink; the broad Node probe used a dedicated run data directory, with individual suites overriding it where configured. Clean installation, standard CI isolation and full candidate acceptance remain CI requirements.
Admission holds
check:docs-allcurrently fails on the/api/balancereference indocs/reference/ENVIRONMENT.md; that document is byte-identical to the incorporated base. The failure is not waived. docs(usage): qualify upstream Ollama balance endpoint #16936 tracks the separate checker repair.Gate / CI,Gate / Quality, applicable external checks and current combined-candidate identity remain required. Local focused passes do not authorize merge, queue activation or deployment.