Skip to content

feat: add @omniroute/opencode-plugin-v2 with unified model catalog - #4

Open
HouMinXi wants to merge 4990 commits into
mainfrom
fix/codex-responses-subpath-encoded-traversal
Open

HouMinXi wants to merge 4990 commits into
mainfrom
fix/codex-responses-subpath-encoded-traversal

Conversation

@HouMinXi

Copy link
Copy Markdown
Owner

Summary

Introduces @omniroute/opencode-plugin-v2, a new major version of the OpenCode plugin with a unified model catalog system. This release includes:

  • Unified model catalog (catalog.ts): Consolidates model definitions from multiple sources (legacy models, combos, auto-combos) into a single queryable interface
  • Enhanced enrichment pipeline (enrich.ts): Improved model metadata enrichment with better error handling and source attribution
  • Shared utilities: Naming conventions, fingerprinting, Gemini language filtering, usability checks, and combo/model mapping
  • Antigravity bridge (bin/antigravity-bridge.mjs): New CLI tool for bridging OmniRoute and Antigravity systems
  • Comprehensive test suite: 50+ test files covering catalog operations, enrichment, caching, management tokens, and v1 parity
  • Configuration updates: New environment variables for executor thread management and cache TTL settings
  • Internationalization: Expanded locale support (Bosnian, Greek, Estonian, Georgian, Khmer, Kannada, Lithuanian, Latvian, Malayalam, Maltese, Burmese, Nepali, Odia, Punjabi, Sinhala, Slovenian, Serbian, Uzbek, Yoruba, Amharic, Hausa, Igbo)

The v2 plugin maintains backward compatibility while providing a more robust foundation for model discovery, caching, and provider integration.

Related Issues

Validation

  • Change type: build-deploy / plugin infrastructure
  • Focused tests: 50+ new test files covering catalog, enrichment, caching, and compatibility
  • npm run lint (CI gates)
  • Production-code changes include comprehensive automated tests
  • Backward compatibility maintained with v1 plugin

Tests Added Or Updated

New test files (50+):

  • @omniroute/opencode-plugin-v2/tests/catalog.test.ts — model catalog operations
  • @omniroute/opencode-plugin-v2/tests/index.test.ts — plugin initialization
  • @omniroute/opencode-plugin-v2/tests/enrichment.test.ts — metadata enrichment
  • @omniroute/opencode-plugin-v2/tests/cache-ttl-snapshot.test.ts — cache TTL behavior
  • @omniroute/opencode-plugin-v2/tests/management-token.test.ts — token management
  • @omniroute/opencode-plugin-v2/tests/staged-refresh.test.ts — refresh lifecycle
  • @omniroute/opencode-plugin-v2/tests/parity.test.ts — v1 compatibility
  • @omniroute/opencode-plugin-v2/tests/auto-combos.test.ts — automatic combo generation
  • @omniroute/opencode-plugin-v2/tests/nested-combos.test.ts — nested combo handling
  • Plus 40+ additional focused tests for shared utilities, gemini filtering, usability checks, etc.

Updated test files:

  • @omniroute/opencode-plugin/tests/config-shim.test.ts — added management token env tests
  • @omniroute/opencode-plugin/tests/options-schema.test.ts — schema validation updates
  • @omniroute/opencode-plugin/tests/combos.test.ts — combo snapshot updates
  • @omniroute/opencode-plugin/tests/naming.test.ts — new naming convention tests
  • @omniroute/opencode-plugin/tests/telemetry.test.ts — telemetry hook tests

Coverage Notes

New production code in @omniroute/opencode-plugin-v2/src/ is covered by 50+ focused unit tests:

  • Catalog operations (model discovery, filtering, caching)
  • Enrichment pipeline (metadata fetching

https://claude.ai/code/session_01JaQAXF3Bv4dCDM82X1Jjku

maxmad64bis and others added 30 commits September 25, 2026 18:23
…l logs (diegosouzapw#14810)

Maintainer rework: reconciled with release/v3.8.51 after diegosouzapw#14795 (migration 190) landed — migration 191 now sits contiguous (temporary 190 KNOWN_GAPS reservation dropped), the INSERT keeps has_content/usage_provenance plus the optional resilience_actions column (spliced instead of a duplicated statement) and the diegosouzapw#14474 id-collision retry loop, buildContinuationLogHooks takes (log, correlationId, resilience), and the resilience-actions parser moved to src/lib/usage/resilienceActionsParse.ts to keep callLogs.ts under the cap. Tests: 131/131 node (resilience-actions context/sink/notes/badges, migration-191, stream-recovery-trace-logging, call-log id-collision/persistence/reasoning/provenance) + 2/2 vitest UI badges; typecheck:core and check:open-sse-typecheck clean; check-file-size and check-migration-numbering OK. Thank you @maxmad64bis!
…4688)

Flips PROXY_SKIP_RECENTLY_FAILED to default on (=false restores plain rotation). Maintainer rework: reconciled with release/v3.8.51 after diegosouzapw#14809 (kept its lastResort429 spare alongside the default-on comment) and diegosouzapw#14802/diegosouzapw#14755 (their flag-off test cases re-pinned from 'delete env' to an explicit =false, since unset now means on). Red->green: proxy-refusal-memory-default 2/3 red on base, 3/3 green. Focused proxy/opencode/refusal-memory suites 258/261 then 36/36 on the 3 reruns, plus 127/127 after the diegosouzapw#14802 merge; typecheck:core and open-sse typecheck clean; file-size only the inherited opencode.ts tip ceiling (1354>1338, red on the pure tip). Thank you @maxmad64bis!
… log (diegosouzapw#14656)

Surfaces the strict zero-cost exclusion diagnosis in the auto-combo pool log. Maintainer rework: reconciled with diegosouzapw#14707 (kept its exhausted/state-unknown breakdown via describeStrictExclusions on top of this PR's WithDiagnosis filter; StrictFilterDiagnosis type widened accordingly). strict-zero-cost vitest suites 35/35 + unknown-visibility node tests green, open-sse typecheck clean, eslint clean. Thank you @maxmad64bis!
…apw#14807)

Maintainer rework: reconciled with release/v3.8.51 — scheduler.ts re-applied on the tip's two-phase sweep (diegosouzapw#14805: verdict recorded in decideOneResult from the final, possibly promoted, outcome; dropped on auto-remove); ProxyHealthCell/proxyRegistryConstants keep the tip's transport/upstream fields plus the sweep verdict. Locales: tip keys + only proxyRegistry.sweepVerdict/sweepNoData/sweepLabel.* (nested), no reorder, no __MISSING__. Tests: sweep-verdict + route 13/13, proxy-health scheduler suites 55/55 on tip+head, UI vitest 3/3; typecheck:core clean; i18n ratio/new-keys PASS (check-keys only reports the tip's inherited bs requestTimeline gap); file-size only the inherited opencode.ts tip ceiling. Thank you @maxmad64bis!
…egosouzapw#14659)

Adds OPENCODE_POOL_RESELECT (default off): after a per-address 429 on the plain-rotation path, the opencode executor asks the pool for another member per attempt instead of replaying the refused egress. Maintainer rework: reconciled with release/v3.8.51 (kept the tip's applied-egress tracker and served-account tracker alongside the reselect cell; re-measured file-size ceilings for opencode.ts/chatHelpers.ts/chat.ts with a dated annotation; feature-flag catalog totals 79/Network 21). Focused suites 737/739 (the 2 reds, opencode-proxy-refusal-memory 'connection without configured accounts' and opencode-transient-rotation 'rotation lines carry correlationId', fail on the pure tip too); typecheck:core and open-sse typecheck clean; file-size green. Thank you @maxmad64bis!
…ouzapw#14585)

Keeps request-scoped Codex SSE errors/refusals out of account cooldowns and off other accounts. Maintainer rework: reconciled with the release tip (comboAttemptLoop keeps the tip's 429+retryAfter for all-skipped/all-inactive terminals using this PR's shared buildComboDiag(state, traceInvocationId, reason); roundRobinCombo keeps both requestScopedReplayKey and the persisted-cooldown skip, restoring the readCache import). Combo suites 279/280 then 27/27 after the import fix (combo-skipped-reset-timing green), typecheck:core and open-sse typecheck clean. Thank you @fouadSalkini!
…nd preference order (diegosouzapw#14754)

Maintainer rework: reconciled with release/v3.8.51 — locales rebuilt as the tip plus only this PR's keys (proxyRegistry.pool*, noAuthProvider.proxySetAside; bs also gets the 5 requestTimeline translations the tip was missing), 0 __MISSING__; NoAuthAccountCard keeps the tip's effective-egress shield (diegosouzapw#14796) and appends the set-aside note to its title; the new 'transport' set-aside kind from diegosouzapw#14802 maps to the proxy-unreachable label instead of the raw kind (new vitest case, red without the mapping). Tests: pool-visibility + refusal/transport suites 79/79, UI vitest 16/16; typecheck:core clean, open-sse typecheck OK, i18n ratio/keys/new-keys PASS, file-size OK. Thank you @maxmad64bis!
…gosouzapw#14756)

Maintainer rework: reconciled with release/v3.8.51 after diegosouzapw#14750/diegosouzapw#14795/diegosouzapw#14810/diegosouzapw#14659 — migration 192 now follows 190/191 with no gap, the call_logs INSERT carries has_content/usage_provenance + added_wait_ms/added_wait_cause + the optional resilience_actions column and the diegosouzapw#14474 id-collision retry, attempt logging keeps the fresh-UUID row key and reads the added wait late, opencode keeps both the served-account tracker and the park/throttle added-wait counters, the unused getAddedWaitPercentiles was dropped, and file-size-baseline.json was rebuilt from the tip with only this PR's own ceilings (opencode.ts, proxyFetch.ts, core.ts, RequestLoggerDetail.tsx) instead of rewinding unrelated entries. Tests: 133/133 focused (opencode-added-wait, applied-egress-key, egress-throttle, attempt-logging, call-log persistence/id-collision/provenance, resilience-actions); typecheck:core and check:open-sse-typecheck clean; check-file-size and check-migration-numbering OK. opencode-429-park-resume / opencode-429-pool-reselect fail identically on the pure release tip (inherited, not from this PR). Thank you @maxmad64bis!
…iegosouzapw#14752)

Maintainer rework: reconciled with release/v3.8.51 after diegosouzapw#14795/diegosouzapw#14810/diegosouzapw#14756 — migration 193 now follows 190/191/192 with no gap; AGENTS.md / llm.txt / README edits are out of the diff; selectorHelp ICU-escapes the selector=<name> placeholder; ENVIRONMENT.md keeps the tip's PROXY_SKIP_RECENTLY_FAILED default-on row plus this PR's SELECTOR_CONTROL_ALLOWLIST row. Tests: 142/142 focused (proxySubscription selector client/endpoint/functional/guard/help-ICU/schema/strip/trigger/warning, proxy-outcome-memory); typecheck:core and check:open-sse-typecheck clean; eslint clean on changed files; check-file-size and check-migration-numbering OK; no __MISSING__ markers. Thank you @maxmad64bis!
…osouzapw#14659 merge (diegosouzapw#14865)

Hotfix for a regression introduced by the diegosouzapw#14659 rework merge: restores the dropped reselectedProxy declaration in OpencodeExecutor (tip failed tsc TS2304 and threw ReferenceError on proxy-less opencode dispatch). opencode-429-pool-reselect 0/6 on the tip -> 6/6; opencode-proxy-refusal-memory 12/12; open-sse typecheck and file-size green.
…diegosouzapw#14657)

Adds PROXY_POOL_SHARED_EGRESS_ORDER (default off): for egress-bucketed providers, a pool member sharing a recently refused member's observed egress address ranks just below healthy members (order only, never excluded). Maintainer rework: reconciled with release/v3.8.51 (kept diegosouzapw#14755's maybeEmitPoolExhausted and the diegosouzapw#14688 default-on refusal memory; flag-off functional case pins PROXY_SKIP_RECENTLY_FAILED=false; registry/catalog totals 80 flags / Network 22 after diegosouzapw#14659). Focused proxy pool/rotation/refusal/flag suites 305/305; typecheck:core and open-sse typecheck clean; file-size green; env-doc-sync red only on the inherited tip TAVILY_BASE_URL (diegosouzapw#14442). Thank you @maxmad64bis!
…wave (diegosouzapw#14867)

Follow-up of the 2026-09-25 merge-batch wave (owner-approved): opencode suites reset the refusal memory between cases (19/19), TAVILY_BASE_URL documented, featureFlagProxyPoolSharedEgressOrderDescription translated in 65 locales, migration count 190 (count-only in AGENTS.md/llm.txt). env-doc-sync, docs-counts and i18n:check-keys green on the current tip.
…crosses the boundary (GHSA-9p9m-h9rj-rhhg) (diegosouzapw#14916)

Pre-request hooks ran in a vm context built from host objects, so hook code could reach the server realm. Hooks now run in a realm created from a null-prototype object; the request context goes in and the result, mutated context and log lines come back only as JSON strings; the host never awaits or calls anything from the realm. Hook contract unchanged except URL/URLSearchParams are no longer provided. Reported by @alienkeric. Remaining CI reds are the release-tip base-reds tracked in diegosouzapw#14866.
…der installs (GHSA-mh4f-3xj9-4gc4) (diegosouzapw#14994)

server.env (generated JWT_SECRET / STORAGE_ENCRYPTION_KEY / API_KEY_SECRET) was written by scripts/build/bootstrap-env.mjs and electron/main.js without an explicit mode (0644 in a 0755 dir under umask 022). Both writers now create the dir 0700 and the file 0600 and repair an existing world-readable file on every start. Follow-up to GHSA-2pg2-xm9r-8544. Reported by @peterbussch. Remaining CI reds are the release-tip base-reds tracked in diegosouzapw#14963.
…al callers (GHSA-jmq6-8j86-8xqj) (diegosouzapw#14995)

The connection test's local cline/qoder CLI probe (getCliRuntimeStatus -> sh -c 'command -v') is reached from three remotely-reachable routes (/api/providers/{id}/test, /api/providers/test-batch, POST /api/providers). The probe now runs only for loopback / private-LAN callers (getRequestPeerLocality, same trusted signals as isLoopbackRequest) and for the credential-health scheduler; remote callers get the upstream test without the local runtime diagnosis. Reported by @zer0d4y5. Remaining CI reds are the release-tip base-reds tracked in diegosouzapw#14963.
… Bosnian UI keys (diegosouzapw#14788)

* docs(i18n): refresh 129 drifted mirrors and sync 11 Bosnian UI keys

The base branch edited COMPRESSION_GUIDE, ENVIRONMENT, FREE_TIERS,
AUTO-COMBO and ROUTE_GUARD_TIERS without updating their translations.
This refreshes 129 of the 330 locale x doc pairs; every one passed the
new output guard and a structure/leak check against its source. The other
201 pairs are recorded as stale (non-blocking): the translation backend
became unusable mid-batch (Codex quota exhausted, cheaperinference
wallet empty, the auto routes returning 5xx), so they wait for the next
refresh.

bs.json: adds the 11 combo UI keys that a PR added to the other 65
locales only.

* docs(i18n): re-adopt target hashes after pre-commit formatting

* docs(i18n): sync 5 Bosnian request-timeline keys and refresh da DOCKER_GUIDE

* docs(i18n): re-adopt target hashes after pre-commit formatting

* docs(i18n): refresh da FEATURE_FLAGS, ENVIRONMENT and README after base merges

* docs(i18n): re-adopt target hashes after pre-commit formatting

* docs(i18n): refresh 540 drifted and stale mirrors across 66 locales

Re-translates every locale of README, AGENTBRIDGE, ENVIRONMENT,
FEATURE_FLAGS, FREE_TIERS, AUTO-COMBO, COMPRESSION_GUIDE, DOCKER_GUIDE and
ROUTE_GUARD_TIERS that drifted or was left stale. Every mirror passed the
output guard and a structure/leak check against its source.

* docs(i18n): re-adopt target hashes after pre-commit formatting
…iegosouzapw#15016)

* fix(i18n): use the canonical 提供者 for "provider" in zh-CN and zh-TW

Two recent strings used renderings the glossary retires:
- featureFlagProxyPoolSharedEgressOrderDescription (zh-CN 提供商, zh-TW 供應商)
- combos.advancedHelp.connectionAwareExpansion (zh-TW 供應商)

This left tests/unit/i18n-glossary-consistency-check.test.ts red (4 tests)
on the release tip. It passes 33/33 after the fix.

* chore(changelog): add fragment for diegosouzapw#15016
…iegosouzapw#14987)

Validated in the 2026-09-28 merge-batch: all 38 PRs of this wave boarded together on release/v3.8.51 (a58000c) — typecheck:core and open-sse typecheck clean, focused tests of the whole board 429/434 (the remaining reds belong to PRs held for rework or already fail on the tip), static gates green apart from file-size/docs-count reconciliation landed in the wave follow-up. Thank you @HouMinXi!
diegosouzapw#14983)

Validated in the 2026-09-28 merge-batch: all 38 PRs of this wave boarded together on release/v3.8.51 (a58000c) — typecheck:core and open-sse typecheck clean, focused tests of the whole board 429/434 (the remaining reds belong to PRs held for rework or already fail on the tip), static gates green apart from file-size/docs-count reconciliation landed in the wave follow-up. Thank you @HouMinXi!
…gosouzapw#14979)

Validated in the 2026-09-28 merge-batch: all 38 PRs of this wave boarded together on release/v3.8.51 (a58000c) — typecheck:core and open-sse typecheck clean, focused tests of the whole board 429/434 (the remaining reds belong to PRs held for rework or already fail on the tip), static gates green apart from file-size/docs-count reconciliation landed in the wave follow-up. Thank you @HouMinXi!
…overy (diegosouzapw#14915)

Validated in the 2026-09-28 merge-batch: all 38 PRs of this wave boarded together on release/v3.8.51 (a58000c) — typecheck:core and open-sse typecheck clean, focused tests of the whole board 429/434 (the remaining reds belong to PRs held for rework or already fail on the tip), static gates green apart from file-size/docs-count reconciliation landed in the wave follow-up. Thank you @HouMinXi!
…osouzapw#14869) (diegosouzapw#14913)

Validated in the 2026-09-28 merge-batch: all 38 PRs of this wave boarded together on release/v3.8.51 (a58000c) — typecheck:core and open-sse typecheck clean, focused tests of the whole board 429/434 (the remaining reds belong to PRs held for rework or already fail on the tip), static gates green apart from file-size/docs-count reconciliation landed in the wave follow-up. Thank you @HouMinXi!
…uzapw#14888) (diegosouzapw#14912)

Validated in the 2026-09-28 merge-batch: all 38 PRs of this wave boarded together on release/v3.8.51 (a58000c) — typecheck:core and open-sse typecheck clean, focused tests of the whole board 429/434 (the remaining reds belong to PRs held for rework or already fail on the tip), static gates green apart from file-size/docs-count reconciliation landed in the wave follow-up. Thank you @HouMinXi!
…iegosouzapw#14899)

Validated in the 2026-09-28 merge-batch: all 38 PRs of this wave boarded together on release/v3.8.51 (a58000c) — typecheck:core and open-sse typecheck clean, focused tests of the whole board 429/434 (the remaining reds belong to PRs held for rework or already fail on the tip), static gates green apart from file-size/docs-count reconciliation landed in the wave follow-up. Thank you @HouMinXi!
…quest proxy (diegosouzapw#14886)

Validated in the 2026-09-28 merge-batch: all 38 PRs of this wave boarded together on release/v3.8.51 (a58000c) — typecheck:core and open-sse typecheck clean, focused tests of the whole board 429/434 (the remaining reds belong to PRs held for rework or already fail on the tip), static gates green apart from file-size/docs-count reconciliation landed in the wave follow-up. Thank you @HouMinXi!
…uzapw#14744) (diegosouzapw#14849)

Validated in the 2026-09-28 merge-batch: all 38 PRs of this wave boarded together on release/v3.8.51 (a58000c) — typecheck:core and open-sse typecheck clean, focused tests of the whole board 429/434 (the remaining reds belong to PRs held for rework or already fail on the tip), static gates green apart from file-size/docs-count reconciliation landed in the wave follow-up. Thank you @HouMinXi!
…pw#14842)

Validated in the 2026-09-28 merge-batch: all 38 PRs of this wave boarded together on release/v3.8.51 (a58000c) — typecheck:core and open-sse typecheck clean, focused tests of the whole board 429/434 (the remaining reds belong to PRs held for rework or already fail on the tip), static gates green apart from file-size/docs-count reconciliation landed in the wave follow-up. Thank you @HouMinXi!
… exceeds the retry window (diegosouzapw#14959)

Validated in the 2026-09-28 merge-batch: all 38 PRs of this wave boarded together on release/v3.8.51 (a58000c) — typecheck:core and open-sse typecheck clean, focused tests of the whole board 429/434 (the remaining reds belong to PRs held for rework or already fail on the tip), static gates green apart from file-size/docs-count reconciliation landed in the wave follow-up. Thank you @shipsfromrio!
…zapw#14955)

Validated in the 2026-09-28 merge-batch: all 38 PRs of this wave boarded together on release/v3.8.51 (a58000c) — typecheck:core and open-sse typecheck clean, focused tests of the whole board 429/434 (the remaining reds belong to PRs held for rework or already fail on the tip), static gates green apart from file-size/docs-count reconciliation landed in the wave follow-up. Thank you @shipsfromrio!
…diegosouzapw#14954)

Validated in the 2026-09-28 merge-batch: all 38 PRs of this wave boarded together on release/v3.8.51 (a58000c) — typecheck:core and open-sse typecheck clean, focused tests of the whole board 429/434 (the remaining reds belong to PRs held for rework or already fail on the tip), static gates green apart from file-size/docs-count reconciliation landed in the wave follow-up. Thank you @shipsfromrio!
HouMinXi and others added 23 commits September 29, 2026 04:04
…ud/auth for non-manage keys (diegosouzapw#15048)

/api/cloud/auth keeps maskedApiKey/projectId for manage/admin keys only and honours allowedConnections. 4 failures on the tip, green with the change. Thank you @HouMinXi!
…shboard started (diegosouzapw#15042)

The Trae /authorize callback only saves a connection for a one-time state issued by the management-authenticated authorize-state endpoint, and the API host must be a trae.ai https origin. New test fails on the tip, green with the change; typecheck clean. Thank you @HouMinXi!
…orwarding headers (diegosouzapw#15043)

Default-password login and the /api/cli/connect lockout now use the stamped socket peer instead of forwarding headers. 6 failures on the tip, green with the change; login/audit suites green on the batch board. Thank you @HouMinXi!
… routes (diegosouzapw#15044)

OAuth login/connection routes now require management auth (manage-scoped keys, session, CLI token), and requireManagementAuth no longer inherits the public-path shortcut in the no-password window. 14 failures on the tip, green with the change; security-hardening integration test green. Thank you @HouMinXi!
…rusted proxy fronts it (diegosouzapw#15038)

Forwarding headers only count as a proxy signal from loopback/private/Cloudflare peers or OMNIROUTE_TRUSTED_PROXIES, and the IP filter judges a server-stamped client address. 16 failures on the tip, green with the change; ip-filter and peer-stamp suites green. Note for operators: a reverse proxy on a public address in front of an IP whitelist must now be listed in OMNIROUTE_TRUSTED_PROXIES. Thank you @HouMinXi!
…egosouzapw#15040)

The /v1/ws and Responses WebSocket relays now report the real client address and drop client-written forwarding headers. 10 failures on the tip, green with the change; responses-ws suites green on the batch board. Thank you @HouMinXi!
… a request (diegosouzapw#15047)

The API-port bridge now relays remote clients with X-Forwarded-For set to their connection address instead of looking like loopback. 15 failures on the tip, green with the change. Note: a private-LAN proxy in front of the API port keeps its chain only with OMNIROUTE_TRUST_PROXY=private. Thank you @HouMinXi!
…zapw#15033)

Antigravity now sends its finite JSON payload as a replayable string body instead of a one-shot duplex stream. Maintainer rework on top: moved the two new regressions to tests/unit/antigravity-replayable-request-body.test.ts (the original file crossed the 1200-line test cap) and renamed the now-unused stream parameter to _stream (eslint). The new tests fail 2/2 on the release tip and pass with the change; executor-antigravity suite 33/33; typecheck clean. Thank you @mdigitalbh81!
diegosouzapw#15050)

The OMNIROUTE_API_KEY/ROUTER_API_KEY passthrough key and the model-sync internal token are now compared in constant time. Maintainer rework on top: merged the current release tip and froze the +1-line import growth of src/sse/services/auth.ts in file-size-baseline.json. tests/unit/secret-compare-constant-time.test.ts fails 3 on the release tip and passes 5/5 with the change; typecheck and file-size gates clean. Thank you @HouMinXi!
…iegosouzapw#15036)

The Cursor exec_read decoder keeps ReadArgs offset/limit and the read bridge forwards them only to a read tool whose schema accepts them, so partial reads no longer loop on 'file unchanged'. Maintainer rework on top: merged the current release tip and froze the +31-line decoder growth of open-sse/utils/cursorAgentProtobuf.ts in file-size-baseline.json. New tests fail 6 on the release tip and pass with the change; cursor exec-router/bridge/protobuf suites 102/102; typecheck and file-size gates clean. Thank you @QuangBlue!
…apw#15066)

Maintainer rework: the <tool_call ...> opening-tag regex still scanned to end-of-text from every unterminated tag (20k tags ~14 s); it now stops at '<', with the hostile case added to the test (red ~17.7 s -> green). Typed the test without explicit any. Focused tests 61/61. Board-validated with the rest of the batch on release/v3.8.51: typecheck:core, check:open-sse-typecheck, check-file-size, eslint on changed files, i18n:check-keys all green. Thank you @HouMinXi!
…gosouzapw#15067)

Maintainer rework: typed the test's executor stand-in (no-explicit-any is an error under tests/). New test red 3/6 with production reverted, green 6/6; all 2,791 PROVIDER_MODELS ids pass the new check. Board-validated with the rest of the batch on release/v3.8.51: typecheck:core, check:open-sse-typecheck, check-file-size, eslint on changed files, i18n:check-keys all green. Thank you @HouMinXi!
…gosouzapw#15068)

Maintainer rework: typed the test's tool handlers (no-explicit-any under tests/). New test red 6/8 with production reverted, green 8/8; existing MCP memory/skill suites and integration memory-pipeline green; scopes unchanged, keyless stdio still honours the explicit owner. Board-validated with the rest of the batch on release/v3.8.51: typecheck:core, check:open-sse-typecheck, check-file-size, eslint on changed files, i18n:check-keys all green. Thank you @HouMinXi!
…how it is spelled (diegosouzapw#15064)

Maintainer rework: bounded the public-only lookup (request timeout, max 5 s, honours abort) and made a pinned request fail closed when the lookup fails instead of connecting unpinned (both new tests red on the previous head, green now); adapted the built-in HTTP skill test to the pinned path via a test-only override; dropped explicit any from the test; reconciled with the tip's createPinnedFetch fix. 40 related test files green. Board-validated with the rest of the batch on release/v3.8.51: typecheck:core, check:open-sse-typecheck, check-file-size, eslint on changed files, i18n:check-keys all green. Thank you @HouMinXi!
…quest (diegosouzapw#15060)

Maintainer rework: reconciled with diegosouzapw#15038/diegosouzapw#15040 on the tip — the wider forwarding-header set only sets the via-proxy marker from a peer that may be a proxy (hasProxyHopHeader && isTrustedProxyPeer). Loopback without headers stays local. 35 peer-stamp/route-guard/live-WS test files green. Board-validated with the rest of the batch on release/v3.8.51: typecheck:core, check:open-sse-typecheck, check-file-size, eslint on changed files, i18n:check-keys all green. Thank you @HouMinXi!
… combo allow-list (GHSA-7j4q-6gx6-pg77) (diegosouzapw#15072)

Built-in virtual routes (auto/*, qtSd/*) are not persisted combos, so the combo allow-list lookup treated them as "not a combo, allowed" and a combo-restricted key could reach every connected provider through them. They now fall back to their own name as the combo name and are denied unless the allow-list grants them (combo/* or an explicit entry); allowAutoCombos still applies on top. Reported and fixed by @aldoeliacim. Remaining CI reds reproduce on the pure release tip (diegosouzapw#15032).

Co-authored-by: Aldo <17973757+aldoeliacim@users.noreply.github.com>
…cket proxy (Hard Rule diegosouzapw#12) (diegosouzapw#15073)

responses-ws-proxy.mjs (shipped as dist/responses-ws-proxy.mjs) sent raw exception text to the client in the failed-upgrade 500 body (CodeQL diegosouzapw#1021/diegosouzapw#1022) and in the upstream-connect response.failed frame, which could carry the upstream proxy URL with credentials. Both now return a fixed message; details stay server-side. Remaining CI reds reproduce on the pure release tip (diegosouzapw#15032).
diegosouzapw#15063)

Maintainer rework: reconciled with the tip (login route imports: tip's loginPeer + this PR's mintDashboardSessionToken). New test red 2/7 with the production routes reverted, green 7/7; the caller's session survives its own password change; login/OIDC/settings/pipeline/session-token suites green; typecheck:core, open-sse typecheck, file-size, eslint and i18n keys green on the combined board. Thank you @HouMinXi!
…s too (diegosouzapw#15078)

Contract propagation for diegosouzapw#14293 (endpoint sections moved to references/endpoints.md). 2/2 with the fix; red on the tip without it.
…failing them (diegosouzapw#15074)

Fixes a regression from diegosouzapw#14737: the new Cursor stream driver failed every turn on the Connect end-of-stream JSON trailer (flag 0x02). New test 0/3 on the tip, 3/3 with the fix; all cursor-* suites 539/539; open-sse typecheck, eslint and file-size clean. Thank you @QuangBlue!
…souzapw#15077)

Release blocker: the tarball's postinstall and the WS proxy imported files missing from package.json files (diegosouzapw#12961, diegosouzapw#15040). New closure test 6/7 on the tip -> 7/7 with the fix. Thank you @maxmad64bis!
Brings the 12 main-only commits into the frozen release branch so the release
PR (diegosouzapw#11442) is no longer CONFLICTING. Most are main twins of fixes the release
branch already carries in a newer shape; resolved by proving the release side
contains them:
- diegosouzapw#12246 temp-dir retry: release uses cleanupTempDataDir / maxRetries (kept ours).
- diegosouzapw#11719 Bun image: release is already on oven/bun 1.4.2 (kept ours).
- diegosouzapw#12086 CVE-2025-68121 .trivyignore: dropped on release by diegosouzapw#12429 (wreq-js).
- diegosouzapw#13865 npm 11 / electron overrides: release pins are newer (kept ours);
  the optional transformers webpack-ignore change merged cleanly.
- streamTiming clock injection: release moved to a monotonic clock (kept ours).
Real main-only fix ported: diegosouzapw#13072 hoistLeadingTextSystemMessages, added to the
release's claudeSystemRole.ts (the call site in chatCore.ts merged cleanly).
The request-supplied subpath after /v1/responses was appended to the upstream
Codex URL verbatim, so an encoded slash or dot (..%2f) reached the upstream
as-is and could be read there as path traversal. Refuse subpaths carrying a
backslash, query/fragment delimiter, NUL, a percent-encoded dot, slash,
backslash, # ? or NUL, or a . / .. segment, and fall back to the plain
/responses endpoint.
@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Add OpenCode v2 plugin with a unified OmniRoute model catalog

✨ Enhancement 🧪 Tests 📝 Documentation ⚙️ Configuration changes 🕐 40+ Minutes

Grey Divider

AI Description

• Add a separate OpenCode v2 plugin exposing models, combos, and auto-combos in one catalog.
• Preserve catalog availability with staged refreshes and credential-scoped, last-known-good
 snapshots.
• Add installation guidance and tests for mapping, enrichment, authentication, caching, and v1
 parity.
Diagram

graph TD
  Host["OpenCode v2"] --> Plugin["Plugin hooks"] --> Catalog["Catalog mapping"] --> Picker["Model picker"]
  Gateway["OmniRoute gateway"] --> Sources["Models and metadata"] --> Catalog
  Sources --> Cache["Catalog snapshots"] --> Plugin
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Share mapping logic with the v1 plugin
  • ➕ Could reduce duplicated rules and future parity work.
  • ➖ Couples releases across different OpenCode host contracts and requires a shared-package boundary.

Recommendation: Keep a separate v2 package because the host contracts differ. Staged publication is appropriate: optional management endpoints should not block core models. Extract host-independent mapping rules later if maintaining parity becomes costly.

Files changed (41) +13439 / -7

Enhancement (19) +4382 / -0
server.jsExpose a root entry point for local installs +10/-0

Expose a root entry point for local installs

• Re-exports the built plugin from a path probed by OpenCode's directory loader.

@omniroute/opencode-plugin-v2/server.js

index.tsRegister v2 hooks and stage catalog refreshes +643/-0

Register v2 hooks and stage catalog refreshes

• Connects OpenCode hooks to authentication, snapshot loading, publication, and provider reloads. Publishes essential models before optional sources finish and retains last-known data on failures.

@omniroute/opencode-plugin-v2/src/index.ts

catalog.tsBuild the unified model catalog +899/-0

Build the unified model catalog

• Combines raw models, explicit combos, and auto-combos. Applies filtering, enrichment, alias deduplication, usability checks, and nested-combo resolution.

@omniroute/opencode-plugin-v2/src/catalog.ts

cache.tsPersist credential-scoped catalog snapshots +248/-0

Persist credential-scoped catalog snapshots

• Adds memory TTL handling and validated disk snapshots for warm starts and fallback. Bounds snapshot size and uses temporary-file writes.

@omniroute/opencode-plugin-v2/src/cache.ts

credentials.tsResolve inference credentials +122/-0

Resolve inference credentials

• Implements credential lookup and missing-key diagnostics before catalog requests.

@omniroute/opencode-plugin-v2/src/credentials.ts

compat.tsCheck the OpenCode host contract +28/-0

Check the OpenCode host contract

• Validates that the host supplies the APIs required by the v2 plugin.

@omniroute/opencode-plugin-v2/src/compat.ts

legacy-model.tsBridge intermediate model representations +81/-0

Bridge intermediate model representations

• Defines the model shape used by catalog mapping and host conversion.

@omniroute/opencode-plugin-v2/src/legacy-model.ts

enrichment-report.tsReport degraded metadata sources +41/-0

Report degraded metadata sources

• Adds source-specific diagnostics for failures in optional management endpoints.

@omniroute/opencode-plugin-v2/src/enrichment-report.ts

gemini-language.tsSanitize Gemini-bound tool schemas +43/-0

Sanitize Gemini-bound tool schemas

• Adds compatibility handling for schema keywords Gemini rejects.

@omniroute/opencode-plugin-v2/src/gemini-language.ts

enrich.tsFetch and apply catalog enrichment +606/-0

Fetch and apply catalog enrichment

• Maps names, pricing, provider attribution, and free-tier budgets onto catalog entries. Resolves canonical provider IDs through aliases.

@omniroute/opencode-plugin-v2/src/shared/enrich.ts

models-map.tsMap gateway models to OpenCode models +339/-0

Map gateway models to OpenCode models

• Converts raw gateway entries into publishable models and routing details.

@omniroute/opencode-plugin-v2/src/shared/models-map.ts

combos-map.tsMap configured combos +254/-0

Map configured combos

• Derives combo catalog entries and capabilities from constituent models.

@omniroute/opencode-plugin-v2/src/shared/combos-map.ts

auto-combos.tsMap generated combos +219/-0

Map generated combos

• Converts gateway auto-combos into catalog entries.

@omniroute/opencode-plugin-v2/src/shared/auto-combos.ts

fingerprint.tsFingerprint catalog changes +127/-0

Fingerprint catalog changes

• Computes identifiers used to avoid unnecessary provider reloads.

@omniroute/opencode-plugin-v2/src/shared/fingerprint.ts

gemini.tsAdd shared Gemini compatibility helpers +166/-0

Add shared Gemini compatibility helpers

• Provides reusable Gemini-specific schema handling.

@omniroute/opencode-plugin-v2/src/shared/gemini.ts

naming.tsStandardize model display names +295/-0

Standardize model display names

• Adds naming helpers for enriched models, provider tags, and free-tier presentation.

@omniroute/opencode-plugin-v2/src/shared/naming.ts

usable.tsFilter by provider usability +171/-0

Filter by provider usability

• Derives usable provider aliases and checks models and combos against provisioned connections.

@omniroute/opencode-plugin-v2/src/shared/usable.ts

logger.tsAdd plugin-scoped logging +81/-0

Add plugin-scoped logging

• Supplies configurable diagnostics for startup, source degradation, and refreshes.

@omniroute/opencode-plugin-v2/src/shared/logger.ts

index.tsExpose shared catalog helpers +9/-0

Expose shared catalog helpers

• Collects shared fetching, mapping, and formatting exports.

@omniroute/opencode-plugin-v2/src/shared/index.ts

Tests (11) +2996 / -0
catalog.test.tsTest unified catalog collection +342/-0

Test unified catalog collection

• Checks model publication and mapping across catalog sources.

@omniroute/opencode-plugin-v2/tests/catalog.test.ts

index.test.tsTest plugin initialization +303/-0

Test plugin initialization

• Exercises host-hook registration and publication.

@omniroute/opencode-plugin-v2/tests/index.test.ts

staged-refresh.test.tsTest essential-first publication +442/-0

Test essential-first publication

• Covers late optional data, reloads, and refresh ordering.

@omniroute/opencode-plugin-v2/tests/staged-refresh.test.ts

refresh-failopen.test.tsTest gateway failure fallback +196/-0

Test gateway failure fallback

• Checks retention of a usable last-known catalog.

@omniroute/opencode-plugin-v2/tests/refresh-failopen.test.ts

cache-ttl-snapshot.test.tsTest cache expiry and snapshots +381/-0

Test cache expiry and snapshots

• Covers TTL, warm starts, and cached catalog reuse.

@omniroute/opencode-plugin-v2/tests/cache-ttl-snapshot.test.ts

disk-snapshot-atomicity.test.tsTest safe snapshot replacement +251/-0

Test safe snapshot replacement

• Checks persistence when snapshots are written or replaced.

@omniroute/opencode-plugin-v2/tests/disk-snapshot-atomicity.test.ts

management-token.test.tsTest management authentication +253/-0

Test management authentication

• Checks separation and fallback between inference credentials and management tokens.

@omniroute/opencode-plugin-v2/tests/management-token.test.ts

parity.test.tsCheck v1 catalog parity +236/-0

Check v1 catalog parity

• Uses parity cases to detect differences in model presentation and mapping.

@omniroute/opencode-plugin-v2/tests/parity.test.ts

nested-combos.test.tsTest nested-combo resolution +248/-0

Test nested-combo resolution

• Exercises capability mapping and unresolved references.

@omniroute/opencode-plugin-v2/tests/nested-combos.test.ts

enrichment.test.tsTest metadata enrichment +136/-0

Test metadata enrichment

• Checks names, pricing, and provider attribution.

@omniroute/opencode-plugin-v2/tests/enrichment.test.ts

auto-combos.test.tsTest auto-combo publication +208/-0

Test auto-combo publication

• Checks conversion of generated combos into catalog entries.

@omniroute/opencode-plugin-v2/tests/auto-combos.test.ts

Documentation (4) +304 / -0
README.mdDocument package installation and credentials +141/-0

Document package installation and credentials

• Explains gateway sources, configuration, and the root entry point needed for file-based installs.

@omniroute/opencode-plugin-v2/README.md

RELEASE.mdDocument v2 release steps +9/-0

Document v2 release steps

• Adds package-specific release guidance.

@omniroute/opencode-plugin-v2/RELEASE.md

LICENSELicense the standalone plugin +21/-0

License the standalone plugin

• Adds the package's MIT license.

@omniroute/opencode-plugin-v2/LICENSE

OPENCODE-V2-PLUGIN.mdAdd the OpenCode v2 installation guide +133/-0

Add the OpenCode v2 installation guide

• Documents package selection, authentication, management tokens, catalog behavior, and options.

docs/guides/OPENCODE-V2-PLUGIN.md

Other (7) +5757 / -7
package.jsonDefine the separately published v2 package +71/-0

Define the separately published v2 package

• Declares OpenCode v2 compatibility, package exports, build and test scripts, and publish settings.

@omniroute/opencode-plugin-v2/package.json

package-lock.jsonLock v2 package dependencies +5471/-0

Lock v2 package dependencies

• Records resolved runtime and development dependencies.

@omniroute/opencode-plugin-v2/package-lock.json

options.tsValidate v2 configuration +140/-0

Validate v2 configuration

• Defines strict options, management-token precedence, endpoint timeouts, and actionable validation errors.

@omniroute/opencode-plugin-v2/src/options.ts

tsconfig.jsonConfigure v2 TypeScript compilation +24/-0

Configure v2 TypeScript compilation

• Adds TypeScript settings for the standalone package.

@omniroute/opencode-plugin-v2/tsconfig.json

tsup.config.tsConfigure the distributable build +16/-0

Configure the distributable build

• Defines bundling for the package entry point and declarations.

@omniroute/opencode-plugin-v2/tsup.config.ts

.gitignoreIgnore generated package files +4/-0

Ignore generated package files

• Excludes local build and installation output.

@omniroute/opencode-plugin-v2/.gitignore

opencode-plugin-ci.ymlTest and package both OpenCode plugin majors +31/-7

Test and package both OpenCode plugin majors

• Adds Node 22 and 24 test jobs for v2 and uploads its built artifact after tests pass.

.github/workflows/opencode-plugin-ci.yml

diegosouzapw and others added 6 commits September 29, 2026 11:43
… 4) (diegosouzapw#15109)

Release-captain base-red fix (v3.8.51 release PR diegosouzapw#11442, unit shards 3-4): one production defect (proxyLogger pulled into every settings→proxies import and queried the DB at import time; helper extracted to src/lib/proxyLogHost.ts) and seven contract propagations from diegosouzapw#14732, diegosouzapw#15044, diegosouzapw#15067, diegosouzapw#13548, diegosouzapw#14117×diegosouzapw#14844, diegosouzapw#12810. 74/74 across the seven files, 176/176 proxy-log neighbours, typecheck clean.
…ue drift, ai-attribution history (diegosouzapw#15104)

Release-captain gate fixes (v3.8.51 release PR diegosouzapw#11442), owner-approved: AI-attribution historical allowlist for 93 already-merged commits (range scan only) + dependabot[bot] exemption; the 8 UI strings whose English was rewritten this cycle translated in 31 locales; the 18 edited doc sources adopted (1188 mirrors kept as stale for a v3.8.52 retranslation round).
…p, command-code none, contract drift) (diegosouzapw#15111)

Release-captain base-red fix (v3.8.51 release PR diegosouzapw#11442, unit shards 7-8): two production defects (GPT-5.1+ sampling stripped by a static rule from diegosouzapw#14133; Command Code 'none' effort clamped although diegosouzapw#14692 routes Responses bodies that accept it) plus contract propagations; image combos restored to sequential priority per the maintainer's decision (the diegosouzapw#13852 image fan-out billed every leg on every request). Focused suites green, typecheck clean.
…tool-map side channel, audio id, CI cache key, stale contracts) (diegosouzapw#15112)

Release-captain base-red fix (v3.8.51 release PR diegosouzapw#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 (diegosouzapw#12961 cache key, diegosouzapw#14627, diegosouzapw#14156, diegosouzapw#14273, diegosouzapw#14959, diegosouzapw#15002). All twelve reds reproduced on the tip and pass; typecheck clean.
Keeps open-sse/executors/codex.ts under its frozen file-size ceiling (1600 > 1584);
no behavior change.
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.