Skip to content

feat(providers): Cursor PKCE login with Bearer quota, auto router, and empty-turn errors - #9909

Merged
diegosouzapw merged 356 commits into
cheaper-inference:release/v3.8.50from
yansigit:feat/cursor-pkce-login
Aug 21, 2026
Merged

diegosouzapw merged 356 commits into
cheaper-inference:release/v3.8.50from
yansigit:feat/cursor-pkce-login

Conversation

@yansigit

@yansigit yansigit commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Add deep-control PKCE Login with Cursor (start/poll/cancel + refresh) so Docker/headless installs work without IDE/cursor-agent import.
  • Provider Limits uses OpenCodex-style Bearer chain on api2.cursor.sh (GetCurrentPeriodUsage → /api/usage/summary → /auth/usage) with cookie dashboard as last fallback.
  • Always expose catalog auto plus OpenCodex router modes auto-cost / auto-balance / auto-intelligence (wire default + optimization ModelParameter); live-catalog reconciliation allowlists them.
  • Empty turns / mid-stream errors surface actionable 429/401/400/502 via classifier (quota vs size vs auth); benign NGHTTP2_CANCEL after tool suspend finishes cleanly.
  • Docker client version: env → local FS → disk-cached installer scrape (oakimov SWR) → pin.
  • Docs: docs/providers/CURSOR-DOCKER.md.

⚠️ base-red inherited: #9737 (release/v3.8.50 tip is red — do not chase inherited failures in this PR).

Related Issues

Validation

  • Change type: provider / oauth / UI / i18n / docs
  • Focused tests and category gates from the golden path
  • npm run lint (changed files + pre-commit lint-staged)
  • Reconciled with origin/release/v3.8.50; focused checks rerun afterward
  • Production-code changes include new/updated automated tests
  • SonarQube PR analysis — pending CI

Focused commands

node --import tsx/esm --test \
  tests/unit/cursor-usage-fetcher.test.ts \
  tests/unit/cursor-available-models.test.ts \
  tests/unit/cursor-errors-classify.test.ts \
  tests/unit/cursor-agent-cli-version.test.ts \
  tests/unit/cursor-login-pkce.test.ts \
  tests/unit/cursor-token-refresh-wiring.test.ts \
  tests/unit/cursor-model-effort-suffix-7289.test.ts \
  tests/unit/oauth-cursor-auto-import.test.ts \
  tests/unit/cursor-streaming.test.ts
npm run typecheck:core
node --import tsx/esm scripts/check/check-provider-consistency.ts
node scripts/check/check-fabricated-docs.mjs

Focused Cursor suites: pass. typecheck:core: pass. Provider-consistency: pass.

Tests Added Or Updated

  • tests/unit/cursor-login-pkce.test.ts (new)
  • tests/unit/cursor-token-refresh-wiring.test.ts (new)
  • tests/unit/cursor-available-models.test.ts (new)
  • tests/unit/cursor-errors-classify.test.ts (new)
  • tests/unit/cursor-usage-fetcher.test.ts (Bearer chain)
  • tests/unit/cursor-agent-cli-version.test.ts (installer cache)
  • tests/unit/cursor-model-effort-suffix-7289.test.ts / oauth-cursor-auto-import.test.ts

Coverage Notes

  • OAuth login/refresh, AvailableModels normalize, usage fetcher, empty-turn classifier, and CLI version resolution are covered by the unit tests above.
  • Live :20129 checks (Provider Limits UI, cu/auto, premium empty-turn wording) remain useful on a depleted Cursor account but are not merge blockers.

Reviewer Notes

Made with Cursor

@diegosouzapw

Copy link
Copy Markdown
Collaborator

Thanks @yansigit — sizable Cursor PKCE feature. Unit coverage is strong; the live OAuth/PKCE browser flow needs a VPS smoke before merge (held in the release-drain). I'll also do a pass on access/refresh token storage.

1 similar comment
@diegosouzapw

Copy link
Copy Markdown
Collaborator

Thanks @yansigit — sizable Cursor PKCE feature. Unit coverage is strong; the live OAuth/PKCE browser flow needs a VPS smoke before merge (held in the release-drain). I'll also do a pass on access/refresh token storage.

@diegosouzapw diegosouzapw added needs-vps PR requires Hard Rule #18 VPS smoke test on 192.168.0.15 before merge hold-vps PR verde, merge aguardando validação live (release-drain) labels Aug 10, 2026
thinh0704hcm added a commit to thinh0704hcm/OmniRoute that referenced this pull request Aug 17, 2026
@diegosouzapw diegosouzapw removed hold-vps PR verde, merge aguardando validação live (release-drain) needs-vps PR requires Hard Rule #18 VPS smoke test on 192.168.0.15 before merge labels Aug 17, 2026
@diegosouzapw

Copy link
Copy Markdown
Collaborator

Thanks for the thorough Cursor PKCE work — the focused suite (106 tests) and typecheck/provider-consistency all pass locally. Two concrete blockers before this can merge:

  1. src/shared/components/CursorAuthModal.tsx:53 — the rewritten isOpen effect now calls setTab/setError/setLoginUrl/setSessionId/setLoginPolling/setDockerHint synchronously in the effect body, which trips react-hooks/set-state-in-effect as a hard ESLint error (confirmed with npx eslint against this branch). The old exhaustive-deps suppression you removed is gone, so nothing masks this — npm run lint will fail CI. Please restructure the reset logic.

  2. pollCursorAuthOnce()'s catch block in src/lib/oauth/services/cursorLogin.ts returns the raw fetch err.message, which flows unsanitized into the JSON body returned by POST /api/oauth/cursor/login/poll. Per the repo's error-sanitization rule this should go through sanitizeErrorMessage() first.

Separately — since the PKCE handshake and the Bearer usage-chain only run against real cursor.com/api2.cursor.sh, this needs a live VPS smoke test (login start→poll→persist, then Provider Limits) before merge. Worth a quick check too that the model-catalog rewrite doesn't silently orphan any ids still referenced by existing combos.

adevwithpurpose and others added 18 commits August 18, 2026 11:55
Two independently-merged PRs (cheaper-inference#10263 agentic-conversation-tracking-v4
and cheaper-inference#10362 exclusive-managed-session-leases) each picked migration
slot 155 against different base states, landing a real collision on
release/v3.8.50 (155_agentic_conversations.sql vs
155_exclusive_connection_leases.sql; cheaper-inference#10263 also claimed 156 via
156_conversation_turn_nodes.sql). Renumbered cheaper-inference#10362's migration to the
next free slot (157) and updated its own regression test
(exclusive-connection-leases.test.ts) that asserted the literal
filename/slot. No retroactive guard needed: CREATE TABLE IF NOT
EXISTS is idempotent under either number.

Confirmed via check-migration-numbering.mjs (154 migrations, 0
duplicates) and the full exclusive-connection-leases test suite
(11/11 pass).
…d port (cheaper-inference#10517)

* fix(deps): bump nanoid, dompurify for 2 new Dependabot alerts (cheaper-inference#189, cheaper-inference#190)

Bumps: nanoid ^3.3.17 (was transitive, now overridden), dompurify ^3.4.13
(with monaco-editor scoped override). Closes Dependabot cheaper-inference#189, cheaper-inference#190.

Remaining cheaper-inference#182-cheaper-inference#188 (js-yaml + mermaid) already closed by cheaper-inference#9651 merge —
awaiting Dependabot re-scan.

npm audit → 0 vulnerabilities.

* fix(repo): harden .gitignore to also ignore a _tasks symlink (/_tasks)

_tasks is a SEPARATE nested git repo (gitignored). The pattern _tasks/ (trailing
slash) ignores only a directory, not a SYMLINK named _tasks. A self-referential
_tasks symlink can slip in via git add -A and, once pulled, checkout materializes
it over the real _tasks repo (destroying plans/specs/hands-off). Anchored /_tasks
ignores the symlink too, preventing re-capture.

* Hide health-check excluded models from /v1/models catalog (cheaper-inference#10026)

Mirror the request-time exclusion rule (provider_specific_data.excludedModels)
in the unified catalog builder: a model is hidden when its provider has
connections but none of them is eligible for it. Applied across the
PROVIDER_MODELS, synced, custom, alias-backed, and managed-fallback loops
so ghost models no longer appear as available.

Co-authored-by: ritheshcn25 <ritheshcn25@users.noreply.github.com>

* fix(models): memoize getModelsDevPricing (event loop / healthz) (cheaper-inference#10055)

* fix(models): memoize getModelsDevPricing for /v1/models catalog

resolveCatalogPricing called getModelsDevPricing once per model while
building GET /v1/models. Each call re-scanned models_dev_pricing and
JSON.parsed every row (~10k SQL scans + multi-GB parse work), pegging
the event loop so even /healthz timed out (cheaper-inference#9685, cheaper-inference#10052).

Memoize the parsed map until saveModelsDevPricing / clearModelsDevPricing
and add a unit test for invalidation.

Signed-off-by: Ravi Tharuma <RaviTharuma@users.noreply.github.com>

* fix(db): invalidate modelsDevPricing cache on DB reset (cheaper-inference#10055)

Copilot review fixes:
1. Register invalidateModelsDevPricingCache() with DB state reset system
   so resetDbInstance() clears the process-local memo, preventing stale
   pricing data from surviving across DB reset/restore operations.
2. Add test assertion verifying DB reset bypasses the memo (Copilot cheaper-inference#10055).

The process-local memo at modelsDevSync.ts:204 caches getModelsDevPricing()
results until saveModelsDevPricing()/clearModelsDevPricing() to avoid
re-scanning all pricing rows on every /v1/models request. Without this hook,
backup restore and test DB resets would serve stale cached data from the
previous connection.

Tests: npm run test:unit:serial -- tests/unit/modelsDevSync-extended.test.ts

---------

Signed-off-by: Ravi Tharuma <RaviTharuma@users.noreply.github.com>
Co-authored-by: Ravi Tharuma <RaviTharuma@users.noreply.github.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>

* fix(oauth): route Zed hosted sign-in callback back to the dashboard port

Zed's native-app sign-in always redirects the browser to the loopback port
sent as native_app_port (hardcoded default 58443), where nothing listens:
the browser shows "site can't be reached" and the login looks broken even
though the token is in the URL. The manual paste fallback was broken too -
handleManualSubmit requires a ?code= param that Zed's callback
(user_id + access_token) never carries, so the flow could never complete.

- zed-hosted: derive native_app_port from the dashboard's own loopback
  port so the redirect lands back on OmniRoute; remote/LAN origins keep
  the old default port and the paste flow
- app root: forward ?user_id=...&access_token=... to the /callback relay
  instead of dropping the query string on the /dashboard redirect
- /callback relay: recognize the Zed payload (no code param) and relay the
  full URL as the exchange payload; allow postMessage to both loopback
  spellings (localhost/127.0.0.1) of the same port
- OAuthModal: zed-hosted popup auto-completes on true localhost; the
  manual paste path passes the full URL through to the exchange instead
  of erroring with "No authorization code found"
- manual input panel: zed-hosted-specific placeholder and hint
- tests: extend the postMessage scope guard with the loopback same-port
  trusted origins

* changelog: fragment for cheaper-inference#10517

* fix(oauth): derive Zed native_app_port from server config, not browser scheme/port

resolveDashboardLoopbackPort() previously re-derived the dashboard's loopback
port from the browser-supplied redirectUri (window.location.port ||
protocol === "https:" ? "443" : "80"), which produced http://127.0.0.1:443/
native-app redirects when the dashboard was reached over HTTPS on its
implicit default port (e.g. behind a local TLS-terminating reverse proxy) -
a scheme/port mismatch, since Zed's own redirect is always plain http and
nothing serves plain HTTP on 443 in that scenario.

This code runs server-side (in the OAuth authorize API route), so once the
redirect URI's hostname is confirmed loopback it now uses the OmniRoute
process's own authoritative listening port via getRuntimePorts()
(OMNIROUTE_PORT/PORT/DASHBOARD_PORT) instead of re-deriving it from the
browser-observed scheme/port. Non-loopback (remote/LAN) redirect URIs still
return null and fall back to the manual paste flow.

Adds tests/unit/zed-hosted-loopback-port-derivation.test.ts (8 cases)
covering the port-derivation logic directly, including the HTTPS-default-port
mismatch scenario that motivated this fix, env-var precedence, IPv6 loopback,
non-loopback/remote fallback, and buildAuthUrl's native_app_port wiring.

Also rebaselines config/quality/file-size-baseline.json for OAuthModal.tsx's
own growth from this PR's earlier commit (1134->1149 gate units) - legitimate
zed-hosted callback wiring at the existing provider-switch chokepoint, not
extractable without a broader modal decomposition (tracked in cheaper-inference#3501).

The live Zed OAuth handshake itself (root -> /callback -> OAuthModal exchange
against the real zed.dev endpoint) still needs a documented VPS smoke test
per Hard Rule cheaper-inference#18; this fix covers the TDD-able port-derivation logic that
motivated the change.

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>

---------

Signed-off-by: Ravi Tharuma <RaviTharuma@users.noreply.github.com>
Co-authored-by: diegosouzapw <diegosouzapw@users.noreply.github.com>
Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouza.pw@outlook.com>
Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com>
Co-authored-by: ritheshcn25 <rithesh.chandran@snb.ca>
Co-authored-by: ritheshcn25 <ritheshcn25@users.noreply.github.com>
Co-authored-by: Ravi Tharuma <25951435+RaviTharuma@users.noreply.github.com>
Co-authored-by: Ravi Tharuma <RaviTharuma@users.noreply.github.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
…em metadata (cheaper-inference#10655)

`assertValidEngine()` valida id, apply, compress, getConfigSchema e
validateConfig — não exige `metadata`. Uma engine registrada sem esse campo é,
portanto, um registro legal. Mas `canRunAtCompressionStage` lia
`engine.metadata.executionStages` sem guarda, então essa engine legal derrubava o
pipeline inteiro com `TypeError: Cannot read properties of undefined` em vez de
falhar aberto, que é o contrato da compressão.

Metadata ausente é o mesmo caso de "não declarou executionStages" e passa a cair
no mesmo fallback documentado: só pre-translation.

Isso destravava também `tests/unit/compression/pipeline-circuit-breaker.test.ts`,
que registra uma engine de teste sem metadata e vinha 8/9 na base — agora 9/9. O
teste novo torna o contrato explícito, em vez de deixá-lo dependendo de uma
reprodução incidental noutro arquivo.

Co-authored-by: Xiangzhe <bakryun0718@proton.me>
Co-authored-by: adevwithpurpose <adevwithpurpose@users.noreply.github.com>
…time registry (R0.3) (cheaper-inference#10633)

* test(sse): golden characterization of the executor map before the R0.3 registry refactor

Freezes the 137-entry provider-id → executor mapping (class, provider
identity, backing PROVIDERS config), the no-shared-instances invariant,
and the getExecutor() dispatch rules (memoized DefaultExecutor fallback,
cloud-agent guard cheaper-inference#6699, search-provider guard cheaper-inference#10274) as stable JSON
snapshots. The upcoming ExecutorRegistry must keep both snapshots
byte-identical.

* refactor(sse): route executor lookup through ExecutorRegistry (R0.3)

Adds open-sse/executors/registry.ts (Map-based registry mirroring
translator/registry.ts): the built-in table in executors/index.ts stays
declarative, every entry is registered at module load, and
getExecutor()/hasSpecializedExecutor() resolve through the registry.
DefaultExecutor fallback, its memoization, and the cloud-agent (cheaper-inference#6699) /
search-provider (cheaper-inference#10274) guards are unchanged.

Also fixes a latent lookup leak: the old object-literal lookup treated
Object.prototype names (constructor, toString, ...) as specialized
executors; the Map registry resolves them to the DefaultExecutor
fallback like any unknown provider.

Parity proof: executor-map golden (137 entries, byte-identical
before/after), check:known-symbols green, 1018 tests across the 65
executor test files green. Docs: OPEN_SSE_ARCHITECTURE factory section
corrected (it claimed generation from providerRegistry).

Refs cheaper-inference#3501

* test(executors): regenerate ExecutorRegistry golden snapshots after release sync

release/v3.8.50 sunset mimocode and added cloudflare-playground + jina-search
since this PR's snapshots were captured; refresh the golden fixtures to match.

---------

Co-authored-by: Xiangzhe <bakryun0718@proton.me>
Co-authored-by: Markus Hartung <mail@hartmark.se>
Co-authored-by: adevwithpurpose <adevwithpurpose@users.noreply.github.com>
…lidade com evidência (cheaper-inference#10647)

* feat(compression): target-wire OmniGlyph stage and transport fidelity gate

Roda o OmniGlyph depois da tradução para o wire real do provedor, em vez do
corpo de origem. Um cliente OpenAI roteado para Claude deixava de comprimir com
skip:source_format_not_claude porque o corpo ainda estava em formato OpenAI
quando a engine era avaliada.

- dispatch nativo por wire: Anthropic Messages, OpenAI Chat Completions e
  OpenAI Responses (input[] preservado, sem achatar para messages[]);
- estágio target-wire pós-translateRequest, com guarda contra dupla compressão
  no caminho Claude→OpenAI;
- preserveSystemPrompt do OmniRoute mapeado para compressSystem: false;
- imageTransportPolicy: fidelidade de bytes/dimensões separada de supportsVision;
  só Anthropic/Claude tem recibo byte-preserving, o resto é fail-closed;
- contagem de tokens de data URL PNG no wire OpenAI (marcador ;base64,);
- README e i18n en/pt-BR com claims escopados ao caminho medido.

* feat(compression): adota omniglyph 1.4.0 e tira o gate de modelo da env do host

O 1.4.0 introduziu escopos de segurança e passou a resolvê-los dentro de
isOmniGlyphSupportedModel() lendo process.env.OMNIGLYPH_PROFILE. Somado ao
OMNIGLYPH_MODELS que já existia, duas variáveis do ambiente do host decidiam em
silêncio o gate de TODO request do OmniRoute: passthrough desligaria a engine
inteira e OMNIGLYPH_MODELS admitiria modelos sem recibo medido, enquanto a UI
segue prometendo "Claude Fable 5 na rota direta medida".

O adapter passa a usar isOmniGlyphSupportedModelForScope() com escopo explícito
e fixa o escopo mais restrito como teto: a env só pode ESTREITAR a allowlist,
nunca alargar. Os dois wires compartilham a mesma lista no pacote desde o
1.4.0, então uma checagem cobre Anthropic e GPT.

- omniglyph ^1.3.1 -> ^1.4.0 (lock em 1.4.0);
- testes de regressão para os dois caminhos de sequestro por env;
- teste de contrato dos exports novos (escopo, perfis, accounting).

O 1.4.0 também traz, sem mudança de código aqui: correção do glyph K que era
lido como H, remoção do backtracking polinomial no secret-guard, overrides do
pnpm em pnpm-workspace.yaml e as transitivas vulneráveis resolvidas.

* feat(compression): expõe os perfis semânticos do omniglyph nos três wires

O 1.4.0 trouxe perfis nomeados (coding-safe, balanced, aggressive,
passthrough), mas só transformAnthropicMessages() os resolve sozinho: os
transformadores OpenAI recebem TransformOptions cru e ignorariam o campo. Um
perfil escolhido pelo operador valeria no wire Claude e sumiria no OpenAI. O
adapter passa a mesclar o perfil com mergeCompressionProfileOptions() antes de
chamar Chat Completions e Responses.

O default segue aggressive — a política que os recibos publicados mediram.
Medido nesta base: com coding-safe/balanced, uma sessão sem histórico acumulado
para em below_min_chars e a engine não faz nada, porque os dois fixam
minCompressChars no máximo e desligam system/tools/tool-results. Como a engine é
opt-in, um default assim entregaria "ligado, 0% de ganho".

O perfil é TETO, não piso: mergeCompressionProfileOptions não deixa um override
do chamador reabrir uma lane lossy que o perfil fechou. Coberto por teste, por
ser contra-intuitivo.

Também fecha um caminho em que o OmniRoute violaria a própria política: o wire
OpenAI do pacote não tem compressSystem — honra apenas compressTools,
gptHistory, minCompressChars e reflow, e sempre troca a instrução por um
ponteiro para a imagem. Com preserveSystemPrompt ligado, imagear assim queimaria
o prefixo quente que a decisão cache-aware está protegendo, sem nada no corpo
devolvido denunciando. A engine agora pula com
skip:system_preservation_unsupported_on_wire.

* feat(compression): contabilidade física do omniglyph com grau de evidência

O adapter descartava o TransformInfo inteiro, então a UI mostrava um número de
economia sem dizer de onde ele vinha — contagem do provider, estimativa ou só
diferença de bytes. O 1.4.0 expõe normalizeAccounting(), que classifica essa
evidência e resolve a semântica de cache por família: Anthropic reporta input,
cache-create e cache-read em buckets DISJUNTOS, enquanto OpenAI e xAI reportam
cached como SUBCONJUNTO do input. Somar à mão dá double-count silencioso.

O novo omniglyphTelemetry.ts não filtra por denylist — MONTA um objeto novo,
campo a campo, só com número e enum. TransformInfo mistura contadores
inofensivos com material que não pode ser persistido: bytes PNG,
imageSourceText(s), recoverable[].text, os sha8 de system/CLAUDE.md/primeira
mensagem, nomes de tags observadas e o bloco env (cwd, branch, versões). Copiar
o objeto inteiro transformaria telemetria de compressão em vazamento de prompt.
O teste de negação prova que segredo, caminho do operador, texto do system e
base64 não aparecem, e varre a allowlist exigindo que toda string seja de um
enum conhecido.

- provider threaded do chatCore e do bridge Codex WS até a engine; ausente vira
  `unknown`, que faz o upstream recusar adivinhar buckets de cache;
- contabilidade propagada para o engineBreakdown do passo (o agregado do
  pipeline soma todas as engines e não serviria);
- skip não emite contabilidade: zeros ali seriam indistinguíveis de "a engine
  nem rodou".

* feat(compression): perfil do omniglyph configurável, persistido e documentado

Fecha o caminho do operador: o perfil já existia no adapter, mas só como
default de código. Agora atravessa schema Zod, normalizador do banco, API de
settings e a página dedicada do engine.

- OmniglyphConfig tipado + omniglyphConfigSchema (z.enum dos quatro perfis);
- normalizeOmniglyphConfig: nome desconhecido vindo do storage cai para o
  default em vez de virar "roda com a política padrão";
- seletor na página do engine, com PATCH próprio — o perfil vive fora do mapa
  `engines`, e mandá-lo junto reescreveria o mapa inteiro (o store persiste o
  mapa como uma linha JSON só);
- i18n en/pt-BR descrevendo o custo medido de cada perfil, não só o nome;
- README e COMPRESSION_ENGINES.md com a regra do teto e o motivo de o default
  não ser o perfil mais seguro.

Corrige de passagem um teste-irmão que ninguém via: o gate de transporte na UI
deixou de dizer "direct Anthropic" quando os wires OpenAI nativos entraram, mas
tests/unit/ui/omniglyphContextPage.test.tsx continuou afirmando a cópia antiga.
O arquivo inteiro estava excluído do vitest.config.ts como "cheaper-inference#8618 pre-existing
failure", então a quebra passou silenciosa. Com a asserção alinhada o arquivo
fecha 3/3, e a exclusão sai — o próprio comentário mandava removê-la quando
corrigida.

A doc não nomeia OMNIGLYPH_MODELS: o gate de docs fabricadas está certo em
apontar que o OmniRoute nunca lê essa env — quem lê é o pacote.

* fix(i18n): paridade do locale vi com as chaves novas do perfil do omniglyph

`tests/unit/i18n-vi-completeness.test.ts` exige paridade ESTRITA de chaves entre
en e vi — diferente do ratchet `i18n:check-ui-coverage`, que passa com 80%. As 11
chaves do seletor de perfil entraram só em en e pt-BR, e o gate de cobertura
seguiu verde, então a quebra só apareceu na matriz completa do CI.

---------

Co-authored-by: Xiangzhe <bakryun0718@proton.me>
Co-authored-by: adevwithpurpose <adevwithpurpose@users.noreply.github.com>
A single orphaned "<<<<<<< HEAD" line (no matching =======/>>>>>>>
pair) leaked into release/v3.8.50 via PR cheaper-inference#10039's merge-conflict
resolution during this session's serial-merge sweep. Repo-wide sweep
confirms no other stray markers exist. Table structure verified
intact before/after removal.
…heaper-inference#10537)

* feat(providers): refresh Gemini Flash catalogs and pricing

* fix(providers): refresh gemini-web Flash catalog

* chore(providers): eliminate Gemini 3.5/3.6 Flash models

* feat(providers): refresh Perplexity Web model mappings

* feat(providers): refresh PromptQL and Notion catalogs

* feat(providers): refresh KIE TinyCMS and Conol catalogs

* feat(providers): refresh OpenCode Zen catalog

* chore(providers): finish Gemini Flash cleanup

* chore(providers): retire Google Imagen 4
- ENVIRONMENT.md: CLI_ALLOW_CONFIG_WRITES default is true (matches cliRuntime),
  CLI_QODER_BIN default is qodercli (also in .env.example), CLI_GEMINI_BIN is
  server-side detection only (omniroute run resolves from PATH)
- CLI-TOOLS.md: catalog counts 26 code / 8 agents (adds the missing zcode row),
  setup targets without auto-discovery list Qwen (not Gemini; Gemini is
  launch-only), hostSetupCommand only for the six tools with a host recipe,
  global env block uses GOOGLE_GEMINI_BASE_URL at the root, mention
  omniroute run as the generic launcher
- CLI-INTEGRATIONS.md: manifest aliases, per-target --model wiring (openai/ and
  omniroute/ prefixes, qwen hard-requires --model), run exit-code contract,
  gemini child-env scrub notes
- CODEX-CLI-CONFIGURATION.md: document omniroute configure codex / run codex
- SETUP_GUIDE.md + QUICK-START.md: surface the generic omniroute run launcher
- ENVIRONMENT.md: disambiguate OMNIROUTE_SMOKE_API_KEY (canary) from the
  OMNIROUTE_SMOKE_* CLI smoke-harness variables
… bins, stale front-matter

Counts (check:docs-counts STRICT, 8 drifts → 0):
- 153 → 154 migrations in README.md, AGENTS.md and llm.txt (+ its 42 i18n mirrors,
  which must stay byte-identical to the root file)
- 340 → 341 providers in README.md, AGENTS.md, llm.txt, the package.json description
  and the 4 SVG diagrams; PROVIDER_REFERENCE.md regenerated via gen:provider-reference
  (the new entry is the cloudflare-playground no-auth provider)

Undocumented environment variables:
- 13 CLI_*_BIN vars that exist in cliRuntime but were in neither ENVIRONMENT.md nor
  .env.example (kilo, opencode, hermes, forge, jcode, deepseek-tui, codewhale, smelt,
  pi, crush, omp, letta, windsurf — windsurf ships no default command)
- OMNIROUTE_DEBUG and OMNIROUTE_HEALTHCHECK_PATH, read in code but absent from
  .env.example
- CLI_CURSOR_BIN documents both fallbacks (agent, then cursor)

Front-matter: bump the seven CLI/reference docs this effort touched from the stale
3.8.40/2026-06-28 stamp to the current release.

Out of scope but blocking check:docs-all, fixed with evidence: ENVIRONMENT.md and
.env.example still documented the Adobe Firefly CDP Chrome runtime removed in cheaper-inference#9255.
Seven of its variables are read nowhere in the codebase and its source file no longer
exists; surviving vars are repointed at adobeFireflyBrowserLogin.ts and CHROME_PATH at
its real readers. The two Gemini CLI auth vars the run launcher scrubs from the child
env are added to the fabricated-docs external-tool allowlist, next to the existing
CODEX_HOME/COPILOT_PROVIDER_BASE_URL entries.

npm run check:docs-all now exits 0 for the first time on this base.
…ss all 42 locales

The translated CLI docs predated the relay-like CLI work: every locale still shipped
the legacy Codex `config.yaml` quickstart (dropped from the English source when the
generator moved to TOML), none mentioned the `omniroute run` launcher or the Gemini
target, and CLI-INTEGRATIONS.md existed only in Polish.

Regenerated through the project pipeline (npm run i18n:run) for the two guides the
CLI effort changed:
- docs/i18n/*/docs/reference/CLI-TOOLS.md — 42 locales updated; the obsolete YAML
  quickstart is gone from all of them (the remaining config.yaml mentions mirror the
  English legacy note and Continue's own config)
- docs/i18n/*/docs/guides/CLI-INTEGRATIONS.md — 42 locales, 41 of them new files

ENVIRONMENT.md is deliberately not included: at ~26 chunks per locale it exceeds the
pipeline's 60s per-chunk timeout and fails after retries. It needs a raised
OMNIROUTE_TRANSLATION_TIMEOUT_MS, which is a separate maintenance run.

Verified: check:docs-all exits 0, doc-links reports no broken internal links, and
spot-checks confirm technical identifiers, front-matter and language bars survive
translation intact.
…rence#10674) (cheaper-inference#10691)

`open-sse/services/combo.ts` imported "../../src/lib/localDb.js" — a .js suffix
on a module that only exists as .ts. Turbopack resolved it by accident until the
dependency-tree change in cheaper-inference#10647; after that the instrumentation hook died at boot
with MODULE_NOT_FOUND, breaking `npm run dev` and the production build (60
consecutive red `Build App` runs on release/v3.8.50).

Fixes the same latent pattern in src/lib/usage/usageLedger.ts, which survived only
because it is an `import type` and is erased before resolution.

Adds a guard rejecting relative .js specifiers across open-sse/ and src/. Package
specifiers are untouched: publishing ESM as .js is legitimate there (e.g.
@modelcontextprotocol/sdk), and only first-party relative imports are first-party
TypeScript.

Closes cheaper-inference#10674

Co-authored-by: Xiangzhe <bakryun0718@proton.me>
…-inference#10692) (cheaper-inference#10695)

* fix(sse): import localDb through its real .ts extension (cheaper-inference#10674)

`open-sse/services/combo.ts` imported "../../src/lib/localDb.js" — a .js suffix
on a module that only exists as .ts. Turbopack resolved it by accident until the
dependency-tree change in cheaper-inference#10647; after that the instrumentation hook died at boot
with MODULE_NOT_FOUND, breaking `npm run dev` and the production build (60
consecutive red `Build App` runs on release/v3.8.50).

Fixes the same latent pattern in src/lib/usage/usageLedger.ts, which survived only
because it is an `import type` and is erased before resolution.

Adds a guard rejecting relative .js specifiers across open-sse/ and src/. Package
specifiers are untouched: publishing ESM as .js is legitimate there (e.g.
@modelcontextprotocol/sdk), and only first-party relative imports are first-party
TypeScript.

Closes cheaper-inference#10674

* fix(config): keep the SQLite driver out of the client bundle (cheaper-inference#10692)

The `aihorde` entry in IMAGE_PROVIDERS imported its live-catalog service directly.
IMAGE_PROVIDERS is reachable from "use client" dashboard pages — they read its KEYS
to derive which providers support which media kind — so that import dragged
aihordeImageCatalog → safeOutboundFetch → proxyFetch → featureFlags → db/core →
sqljsAdapter into the browser graph. The build then tried to bundle fs/net/tls for
the browser and failed with 28 Module not found errors, leaving `Build App` red for
60 consecutive runs and no artifact buildable from the branch.

A dynamic import() does not fix this: the bundler still has to make the module
browser-loadable. The dependency is inverted instead — the registry entry knows only
a pure registration module, and the server-only service registers itself on import,
which every server path needing live models already does. With nothing registered the
getter yields [], exactly what the live catalog returned before its first poll.

Validated by a full `npm run build:release`: 0 Module not found, artifact produced.

Closes cheaper-inference#10692

---------

Co-authored-by: Xiangzhe <bakryun0718@proton.me>
…le (cheaper-inference#10698)

`check:agent-skills-sync` fails on the release tip (22b89a2) with no local
changes: the generator reports omni-inference and cli-resilience as stale.

The `quota status` / `quota preview` / `quota ensure` subcommands were added to
the CLI catalog without re-running the generator, so the committed SKILL.md
files no longer match it. Output of `generate-agent-skills.mjs --apply`,
additive only — no hand edits.

Co-authored-by: Xiangzhe <bakryun0718@proton.me>
…me one (cheaper-inference#10697)

* docs(guides): DASHBOARD_ALLOW_EMBED is build-time, not a runtime flag

The VS Code guide told operators to "start OmniRoute with
DASHBOARD_ALLOW_EMBED=vscode". Next.js compiles headers() into the route
manifest, so next.config.mjs reads the variable while the bundle is built —
exporting it in front of an already-built server does nothing, which is the
exact trap anyone on `npm install -g omniroute` or the Docker image falls into.

Documents the build-time nature, the working from-source recipe, and which
install paths can enable it at all. ENVIRONMENT.md and .env.example already
said build-time; this aligns the how-to with them and with the extension's own
fallback message.

* docs(changelog): announce the VS Code Copilot Chat integration

The release notes only mentioned OmniCopilot in passing, inside the DASHBOARD_ALLOW_EMBED bullet — a reader would never learn the extension exists. Adds the fragment that says it plainly, with both store links.

---------

Co-authored-by: Xiangzhe <bakryun0718@proton.me>
… code (cheaper-inference#10699)

`npm install -g <tarball>` on the .17 gateway writes the whole package and then fails
renaming the old tree into its staging directory (ENOTEMPTY, exit 217). The canary read
that non-zero exit as "install failed", aborted before the restart, and discarded npm's
stderr through execFileSync throwing — so on 2026-08-18 the deploy stopped half-done
twice, each time leaving new files on disk under an old running process, with no clue in
the log.

The exit code is not trustworthy in either direction: the 2026-08-14 outage installed a
package built from the wrong branch and exited 0. classifyInstallOutcome() therefore
decides on the BUILD_SHA read back from the installed package, and fails closed when it
is absent or does not match — a zero exit with the wrong artifact is still a failure.

npm reuses the same staging directory name, so the orphan blocks the next install with
the same error; orphanStagingDirFromStderr() surfaces the exact path. It is not removed
automatically — that is an rm -rf under /usr/lib, not something a deploy script should
decide on its own.

Refs cheaper-inference#10429

Co-authored-by: Xiangzhe <bakryun0718@proton.me>
…y point (cheaper-inference#10692) (cheaper-inference#10700)

The guard shipped with cheaper-inference#10695 watched two hand-picked modules. It now walks the static
import graph from all 753 "use client" files in src/ (plus the two originally pinned
entries), so the invariant is verified across the repo instead of where someone
remembered to look. Full sweep runs in ~750ms.

Two exclusions make that practical:

- `import type` is not an edge — TypeScript erases it before the bundler sees it.
  Counting type imports turns 3 real findings into 29; a guard that cries wolf gets
  switched off.
- Dynamic `import()` is still not followed. It does not break a bundle edge (that was
  tried for cheaper-inference#10692 and failed) but it does move the module into a chunk the browser
  fetches on demand, which is a legitimate boundary.

The widened sweep immediately found what the narrow one could not: five value-form
imports of `db/batches` / `db/files` across three files under dashboard/batch, each
reaching db/core → the SQLite driver. All five bind only interfaces (BatchRecord,
FileRecord) used in type position, so the compiler was eliding them and the build stayed
green — the same latent shape as cheaper-inference#10692 before cheaper-inference#10647 removed the toolchain's tolerance.
Marking them `import type` makes the elision explicit instead of incidental.

Refs cheaper-inference#10692

Co-authored-by: Xiangzhe <bakryun0718@proton.me>
Co-authored-by: Cursor <cursoragent@cursor.com>
arminanton and others added 23 commits August 21, 2026 04:23
…stays CJS (cheaper-inference#10936)

Validado no worktree combinado: typecheck:core, changelog-integrity, complexity, cognitive-complexity, file-size, lint e teste focado (colocate-standalone-esm-scope) todos verdes. Fix real, correção de regressão introduzida por cheaper-inference#10836 (server.js CommonJS quebrando com type:module reintroduzido). CI vermelho é o base-red já rastreado em cheaper-inference#9985. Obrigado!
…cards (cheaper-inference#10939)

Validado no worktree combinado: typecheck:core, changelog-integrity, complexity, cognitive-complexity, file-size, lint todos verdes. Fix de UX real e bem documentado (cards de Account Split mostravam UUID cru em vez de email/nome da conta). CI vermelho é o base-red já rastreado em cheaper-inference#9985. Obrigado!
…letions, 1.5/2.0/2.5-flash) (cheaper-inference#10942)

Validado no worktree combinado: typecheck:core, changelog-integrity, complexity, cognitive-complexity, file-size, lint e teste focado (agnes-provider, 11/11) todos verdes. Atualização de catálogo/dados alinhada à documentação oficial vigente. CI vermelho é o base-red já rastreado em cheaper-inference#9985. Obrigado!
…ted 429 (cheaper-inference#10920)

Validado no worktree combinado: typecheck:core, changelog-integrity, complexity, cognitive-complexity, file-size, lint e testes focados (egress-ip-lock-10880, egress-lock-allowlist-10880, proxy-logs-egress-lookup-10880) todos verdes. Otimização de resiliência bem fundamentada (cooldown de conexões compartilhando IP de egress após 429 do allowlist). CI vermelho é o base-red já rastreado em cheaper-inference#9985. Obrigado!
… and 401 recovery (cheaper-inference#10944)

Validado no worktree combinado: typecheck:core, changelog-integrity, complexity, cognitive-complexity, file-size, lint e 52 testes focados (kimi-jwt, kimi-credentials-extract, kimi-token-refresh, kimi-web-401-retry, provider-refresh-token-route, token-health-check-kimi) todos verdes. Implementação sólida e bem testada de ciclo de vida de token para Kimi Web. CI vermelho é o base-red já rastreado em cheaper-inference#9985. Obrigado!
…ed factory + wrappers (reopen) (cheaper-inference#10910)

Validado no worktree combinado: typecheck:core (confirma que TODOS os símbolos exportados foram preservados — TlsClientHangError, TlsClientUnavailableError, looksLikeSse, isCloudflareChallenge continuam re-exportados em cada wrapper), changelog-integrity, complexity, cognitive-complexity, file-size, lint e testes focados via vitest (chatgptTlsClient, grokTlsClient) + node:test (chatgpt-web-handoff-resume, lmarena-provider, claude-web-live-alignment, chatgpt-web, claude-web-slow-first-byte, grok-web-cloudflare-classification, grok-web) todos verdes. Refactor de consolidação bem executado: -3379 linhas líquidas, zero mudança de comportamento, 6 clientes TLS quase idênticos viram uma factory + wrappers finos. CI vermelho é o base-red já rastreado em cheaper-inference#9985. Obrigado!
…er-inference#10888)

Validado no worktree combinado: typecheck:core, changelog-integrity, file-size, lint todos verdes. Correção real dos 3 alertas CodeQL (HMAC em vez de hash bruto, URL parsing em vez de substring, dismiss documentado). CI vermelho é o base-red já rastreado em cheaper-inference#9985.
…r-inference#10727) (cheaper-inference#10916)

Validado no worktree combinado: typecheck:core, changelog-integrity, file-size, lint e 2/2 testes focados passando. Diagnóstico bem investigado do timeout WS do Meta AI (readyState exposto no erro). CI vermelho é o base-red já rastreado em cheaper-inference#9985.
cheaper-inference#7592) (cheaper-inference#10921)

Validado no worktree combinado: typecheck:core, changelog-integrity, file-size, lint e 7/7 testes focados passando. Investigação completa com verificação de ancestralidade via merge-base antes de fechar a issue original. CI vermelho é o base-red já rastreado em cheaper-inference#9985.
…ode_modules (cheaper-inference#7346) (cheaper-inference#10924)

Validado no worktree combinado: typecheck:core, changelog-integrity, file-size, lint e teste focado passando. Root cause bem documentado (distDir customizado gera dois node_modules externalizados). CI vermelho é o base-red já rastreado em cheaper-inference#9985.
…per-inference#5483 regression) (cheaper-inference#10946)

Validado no worktree combinado: typecheck:core, changelog-integrity, complexity, cognitive-complexity, file-size, lint e testes focados todos verdes. Regressão real corrigida (apiType=chat agora é honrado em vez de forçado para /responses). CI vermelho é o base-red já rastreado em cheaper-inference#9985.
… on (cheaper-inference#10945) (cheaper-inference#10951)

Validado no worktree combinado: mesmos gates + testes focados verdes. Bug real e bem reproduzido (least-used nunca gravava lastUsedAt, sempre a mesma conexão escolhida). CI vermelho é o base-red já rastreado em cheaper-inference#9985.
Validado no worktree combinado: mesmos gates + teste focado verde. Preserva effort_tiers declarados pelo provider (Kimi k3) em vez de substituir pela lista canônica genérica. CI vermelho é o base-red já rastreado em cheaper-inference#9985.
… effort immediately (cheaper-inference#10957)

Validado no worktree combinado: mesmos gates + testes focados verdes. Fix bem medido (context window real vs anunciado divergindo por até 24h para modelos sincronizados fora do ciclo). CI vermelho é o base-red já rastreado em cheaper-inference#9985.
… 404ing (cheaper-inference#10947) (cheaper-inference#10958)

Validado no worktree combinado: mesmos gates + teste focado verde. Root cause medido na release publicada v3.8.49 (nome de artefato NSIS com espaço vs. hífen no manifest). CI vermelho é o base-red já rastreado em cheaper-inference#9985.
…inference#10926)

Validado no worktree combinado: mesmos gates + testes focados verdes. Extensão opt-in bem desenhada sobre cheaper-inference#10909 (dimensão de uso real via call_logs). CI vermelho é o base-red já rastreado em cheaper-inference#9985.
…heaper-inference#10855)

Tirado de Draft e validado no worktree combinado: mesmos gates verdes (mudança de UI/i18n sem cobertura automatizada dedicada, mas de baixo risco — só warnings e ocultação condicional de UI). Fix de UX real (cheaper-inference#10794 — 401 confuso ao pular senha no onboarding). CI vermelho é o base-red já rastreado em cheaper-inference#9985.
cheaper-inference#10948)

Validado no worktree combinado: mesmos gates + 36 testes focados verdes. Feature bem documentada e testada (tool calling completo para copilot-m365-web via SignalR, incluindo keepalives e detecção de erro silencioso). CI vermelho é o base-red já rastreado em cheaper-inference#9985.
)

Validado no worktree combinado: typecheck:core, changelog-integrity, complexity, cognitive-complexity, file-size, lint e teste focado (vps-compose) todos verdes. Bundle Docker aditivo, seguro-por-padrão (loopback, secrets obrigatórios, imagem pinada), bem documentado. CI vermelho é o base-red já rastreado em cheaper-inference#9985.
…est) (cheaper-inference#9985) (cheaper-inference#10778)

Reconciliado com a release e revalidado: typecheck:core, check:dead-code (410 real vs 416 na baseline resolvida — a PR mede corretamente sua própria melhoria), lint (adicionei 1 entrada de suppression para GrokBuildToolCard.tsx, arquivo mergeado depois que esta branch nasceu, 2 violações novas de react-hooks/set-state-in-effect não capturadas pela contagem original), complexity, cognitive-complexity, file-size, changelog-integrity e 11/11 testes do tieredRotation todos verdes. Drena 3 dos 8 hard failures do cheaper-inference#9985. Obrigado!
@diegosouzapw
diegosouzapw merged commit c130f2a into cheaper-inference:release/v3.8.50 Aug 21, 2026
0 of 3 checks passed
muhamadgalihsaputra pushed a commit to niyatna/NiyatnaRoute that referenced this pull request Sep 27, 2026
…d empty-turn errors (cheaper-inference#9909)

⭐5 — Cursor PKCE login com Bearer quota, auto router e empty-turn errors. Feature completa e testada (11 arquivos de teste, 133 testes focados, todos verdes).

**Validação (worktree combinado `.claude/worktrees/fix-9909`, board sobre `origin/release/v3.8.50`):**
- 3 conflitos reais resolvidos: `config/quality/eslint-suppressions.json` (aditivo), `open-sse/config/providers/registry/cursor/index.ts` (dedup de 208 entradas de catálogo, 0 IDs duplicados verificado), `open-sse/executors/cursor.ts` (imports aditivos).
- `npm run typecheck:core`: limpo.
- `check-changelog-integrity`, `check-file-size`, `check-complexity` (2615/2774), `check-cognitive-complexity` (1175/1223), `check-dead-code` (410/416): todos OK.
- `check-public-creds`: 1 entrada obsoleta pré-existente na allowlist (`copilot-m365-web.ts:330`), já presente no tip da release — não é desta PR.
- `npm run lint`: 0 errors (5 warnings pré-existentes).
- Testes focados (`cursor-agent-cli-version`, `cursor-available-models`, `cursor-catalog-combo-compat`, `cursor-errors-classify`, `cursor-login-pkce`, `cursor-model-effort-suffix-7289`, `cursor-streaming`, `cursor-token-extractor`, `cursor-token-refresh-wiring`, `cursor-usage-fetcher`, `empty-stream-no-content-8649`): 133/133 verdes.
- Corrigido durante a validação: 1 teste novo da própria PR (`cursor-model-effort-suffix-7289.test.ts`, "splits effort off legacy grok- ids") colidia com `CURSOR_MODEL_ALIASES` já mesclado na release (mapeia `grok-4.5-high` → `cursor-grok-4.5-high` antes do fallback legado rodar); ajustado para usar um id não-aliasado (`grok-3-high`) que de fato exercita o fallback — commit `68b58ed`.

Obrigado pela contribuição, @yansigit — feature robusta com boa cobertura de testes.
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.