⭐5 — freeAccessPolicy "strict" opt-in (default off): verifica candidatos de auto-combo contra estado de quota ao vivo + segurança econômica por conexão antes do dispatch (fail-closed — estado desconhecido/stale/incompleto é excluído). Zero mudança de comportamento com o default "off". 82 testes focados, eslint/prettier/typecheck limpos, docs em docs/routing/STRICT_ZERO_COST.md.
⭐5 — Provider x-search de primeira classe (SuperGrok/xAI x_search) em POST /v1/search e MCP omniroute_x_search. Fallback de credenciais xai-oauth→xao→xai; distinto de web search e do X Developer MCP.
Reconciliado com o release tip (que já incluía #10981 "skip catalog-default SearXNG" deste mesmo lote): merge trouxe 5 conflitos reais de contagem gerada (llm.txt/README.md/AGENTS.md/PROVIDER_REFERENCE.md/SVGs/46 mirrors i18n, todos verificados como bump puro 347→348, sem perda de conteúdo do HEAD) + 1 conflito real de mergeable=CONFLICTING.
Durante a validação, os 3 testes novos de SearXNG expuseram um bug real de interação com #10981: `isUnconfiguredLoopbackSearchProvider()` checava o baseUrl ESTÁTICO do catálogo em vez do baseUrl efetivo (após override de `provider_options.baseUrl` ou `providerSpecificData.baseUrl` da conexão), então QUALQUER request a searxng-search — mesmo com override customizado — era rejeitado como se fosse o default não-configurado. Corrigido em `open-sse/handlers/search.ts` (resolve o baseUrl efetivo via `resolveSearchBaseUrl()` antes do skip-check, tanto para o provider primário quanto o alternate). Um teste do próprio #10988 que assumia o comportamento pré-#10981 (default localhost:8888 sempre atendido) foi atualizado para refletir o comportamento já mesclado e intencional (503 quando não configurado).
Validação completa: typecheck limpo, 70/70 testes unit (search-route/search-registry/x-search-provider/searxng-loopback-default), 24/24 vitest MCP, 14/14 integration (search-providers-catalog), lint limpo nos arquivos tocados, docs-counts-sync OK (2 drifts soft pré-existentes, não relacionados), gates estáticos (file-size/complexity/cognitive/dead-code/changelog) todos OK.
⭐5 — Fix(#10967,#10966): combo diag exhausted_connection truncava o UUID por hardcode de provider="unknown"; recovery hint de quota caía em "retry" genérico. TDD RED→GREEN, 149 testes-irmãos verdes. UNSTABLE é o base-red inherited #9985.
⭐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.
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 #9985.
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 #9985.
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 #9985. Obrigado!
Reconciliado com a release (drift em typecheck-baseline.json/glm.ts/fetchTimeout.ts/stryker.conf.json — a tip já simplificou essas funções, mantida a versão da tip) e corrigi o `no-explicit-any` no teste novo (cast tipado, mesmo padrão já usado em outros testes do repo). Validado: lint limpo, teste focado passando. Fix real e bem documentado (claude-*/gemini-*/gemma-* sem provider ativo agora retorna 404 model_not_found em vez de 401 enganoso). CI vermelho é o base-red já rastreado em #9985. Obrigado!
Validado + reconciliado: 86/86 testes focados (combo-disable-session-stickiness, base-executor-sanitize-effort, command-code-executor) passando. Incluí o rebaseline do file-size (commandCode.ts 1023→1038, crescimento legítimo deste PR) diretamente no branch — evitando o erro que cometi antes (rebaseline só na worktree local, nunca chegando ao branch real). Correção real de bug com repro ao vivo documentada. CI vermelho é o base-red já rastreado em #9985. Obrigado!
Reconciliado com a release (conflito aditivo em targetTimeoutRunner.ts — combina o warning G3 já mergeado com a resolução de effectiveTimeoutMs deste PR) e revalidado: 41/41 testes focados passando (upstream-timeout-connection-tier, combo-target-timeout-runner, provider-specific-data-schema). CI vermelho é o base-red já rastreado em #9985. Obrigado!
Validado no worktree combinado do lote: typecheck:core, lint, gates de qualidade (file-size rebaselineado com justificativa — crescimento legítimo em modelCapabilities.ts/commandCode.ts) e os 97+9 testes focados (vision-bridge, command-code vision, model-select-field-catalog-vision) todos verdes. Duas correções reais (#10808/#10809) bem documentadas. CI vermelho neste PR é o base-red já rastreado em #9985. Obrigado!
Validado no worktree combinado do lote: typecheck:core, lint, gates de qualidade e os novos testes gcf-numeric-domain/gcf-count-mismatch verdes (mais os já existentes do codec GCF). Fix de losslessness bem documentado e cirúrgico. CI vermelho neste PR é o base-red já rastreado em #9985. Obrigado!
Two fixes: (1) createDisconnectAwareStream now distinguishes graceful max_tokens truncation (partial content already reached the client, upstream closes without a terminal marker → clean stop, no error) from a real empty-content failure (still surfaces the 502). Fixes#7699, keeps #8649 intact. (2) liteEngine's compressToolResults now requires an explicit boolean before overriding step config, instead of letting a malformed value leak through the `??` chain.
Validated in an isolated worktree boarded onto origin/release/v3.8.50 (0 conflicts, 4 files):
- 68/68 tests pass (silent-sse-close-7699, compression/lite, empty-stream-no-content-8649, stream-handler).
- check-file-size, check-changelog-integrity: OK.
- typecheck:core: clean.
- check-complexity / check-cognitive-complexity: OK, both under baseline.
Note: the empty-content Claude error message text changed from "Upstream stream ended without a terminal marker" to "Provider returned empty content" (matches the OpenAI/Responses branch wording) — intentional, documented in the PR.
Co-authored-by: minhlongs <minhlongs@users.noreply.github.com>
Adds a snapshot-generation button to each Auto-Combo catalog card: computeSnapshotWeights() scores candidates (taskFit/stability/tierPriority/costInv) at combo-creation time instead of the previous hardcoded weight:1, and the new POST /api/combos/duplicate endpoint materializes any auto/* template into a persistent, editable static combo with normalized weights. Closes#10231.
Validated in an isolated worktree boarded onto origin/release/v3.8.50 (0 conflicts, 51 files):
- 19/19 focused tests pass (snapshot-weights, combos-duplicate-route, combos-duplicate-resolution-audit) — covers auth gate (401/403), input validation (400/422), success shape, weight normalization, naming/dedup, and error-response sanitization (no stack traces).
- check-file-size, check-changelog-integrity: OK.
- typecheck:core: clean.
- check-complexity / check-cognitive-complexity: OK, both under baseline.
Co-authored-by: swingtempo <swingtempo@users.noreply.github.com>
Obrigado — feature substancial e bem estruturada: separa qualidade operacional (comportamento de wire: 4xx/5xx, 429, respostas malformadas, stream interrompido) de qualidade semântica (só setada por avaliadores externos, nunca inferida do sucesso HTTP), com confidence/sample-awareness para não deixar poucos sucessos de sorte dominarem o ranking. Instrumentação de streaming (TTFT/ITL) threaded até RoutingEvent, endpoint de explicabilidade, e teste E2E determinístico cobrindo degradação→recuperação→blip.
Validação (worktree própria a partir de origin/release/v3.8.50, merge limpo, 0 conflitos):
- typecheck:core limpo, complexity/cognitive-complexity dentro do baseline
- 59/59 testes passando (mlx-provider, routing-adaptive-e2e, routing-events(-concurrency), routing-otel, routing-quality, routing-scoring-quality, stream-timing, auto-combo-scoring-clamp)
Obrigado — follow-up bem feito do #10424: um 422 GCP_PROJECT_REQUIRED (BYOP — a conta Google precisa trazer seu próprio GCP Project) é específico da CONTA, não do provider inteiro, então o fast-fail anterior falhava mesmo quando uma conta antigravity irmã saudável poderia atender o request. Agora rotaciona automaticamente para a conta irmã (excluindo a conta BYOP por 24h) e só surfaça o erro acionável quando não há irmã disponível. Estado de rotação rastreado separado de maxAttempts, então falhas normais do antigravity nunca ganham uma segunda chance (sem dispatch duplo).
Validação (worktree própria a partir de origin/release/v3.8.50, merge limpo — auto-merge em chatCore.ts, 0 conflitos reais):
- typecheck:core limpo, complexity/cognitive-complexity dentro do baseline
- tests/unit/antigravity-byop-account-rotation.test.ts + error-classifier.test.ts — 36/36 passando
Obrigado — bug de produção real e muito bem raiz-causado: resolveConversationId travava a request path por 10-130s em históricos longos de agente (medido em produção: p50 12.6s / max 130.2s em requests com ≥200 mensagens), por re-hashear o texto completo de cada turno a cada passo do walk de reconexão (O(starts × anchors × walkLength) HMACs síncronos).
Fix cirúrgico: memoiza o hash de cada turno por request + budget de passos compartilhado entre candidatos (degrada como no-match, nunca como attach não verificado ou latência ilimitada). Resultado medido: 17.2s → 0.3s no repro.
Validação (worktree combinado a partir de origin/release/v3.8.50, 0 conflitos):
- typecheck:core limpo, complexity/cognitive-complexity dentro do baseline
- tests/unit/conversationTracker-reconnect-7847.test.ts (novo) — cobre o cap de budget, degradação com budget zero, e guarda de regressão de wall-clock (falha em 17.2s pré-fix)
- 17 testes de semântica pré-existentes + conversationTurnContent (5) — passando sem alteração
Obrigado — ganho de performance real e bem medido: substitui loops char-a-char por RegExp nativa do V8 em cleanupArtifacts/normalizeMessageWhitespace, 53-296x mais rápido nos payloads testados (830KB: 109ms→1.6ms; 3.36MB: 296ms→5.4ms), com paridade byte-a-byte confirmada.
Durante a validação do lote combinado encontramos uma regressão real: o rewrite removeu isCodeDominantText (a guarda do #9144) e seu ponto de decisão em cavemanCompress, reintroduzindo a recapitalização destrutiva de código não-cercado (function→Function). Identificado por tests/unit/compression/caveman-file-reference-9144.test.ts, que passa no tip puro do release e falhava após este PR. Restaurei a guarda em cima da nova implementação regex (commit e5edb1a6, autoria preservada + Co-authored-by), mantendo o ganho de performance sem reintroduzir o bug.
Validação final (worktree combinado a partir de origin/release/v3.8.50):
- typecheck:core limpo, complexity/cognitive-complexity dentro do baseline
- tests/unit/compression/lite.test.ts + caveman-*.test.ts — 99/99 passando (incluindo o #9144 restaurado)
Obrigado — um PDF de ~1MB enviado como file/document base64 (OpenAI ou Claude) era medido caractere-a-caractere, estimando 350.022 tokens (o mesmo documento pelo path Gemini inlineData já estimava 1.209). Corrige a inconsistência reconhecendo os shapes que faltavam, sem introduzir constante nova.
Validação (worktree combinado a partir de origin/release/v3.8.50, 0 conflitos):
- typecheck:core limpo, complexity/cognitive-complexity dentro do baseline
- tests/unit/10840-file-token-context.test.ts — 5/5 passando
- Suítes de contexto relacionadas — 59/59 (5 arquivos) passando
Merged — locally validated (11/11 focused tests across both new test files, typecheck:core clean after a 1-char fix pushed to this branch: ComboLogger.error is optional in combo/types.ts so the defensive race-catch needed log.error?.(...) — TS2722 otherwise). Solid production diagnosis (47 unhandledRejections traced to the orphaned race loser). Thanks!
Merged — locally validated together with related stanleytejakusuma PRs (typecheck:core clean, complexity/cognitive/file-size/changelog gates green, focused tests passing). Great incident writeup and clean fix. Thanks!
Merged — locally validated together with related cryptiklemur PRs (typecheck:core clean, complexity/cognitive/file-size/changelog gates green, focused tests passing). Real bug, clean fix, great regression test. Thanks!
Merged — locally validated (fusion-vision-panel-3378 test green, file-size/changelog gates green) after resolving base-drift against #10842/#10838 (both landed just before).
Merged — validated together with a batch of related RaviTharuma PRs in one combined worktree (typecheck:core clean, complexity/file-size/changelog gates green, focused tests passing). Thanks for the contribution!
Merged — validated together with a batch of related maxmad64bis PRs in one combined worktree (typecheck:core clean, complexity/cognitive-complexity/file-size/changelog gates green, focused tests passing). Thanks for the contribution!
Merged — per owner decision on RFC #10141: gateway token estimates (chars/4) become advisory-only for combo routing/context checks, never a hard pre-dispatch 400. Locally validated (31/31 focused tests across capability filtering, target resolution, and the #8841 repro), file-size/changelog gates clean, merges conflict-free against the current release tip. Thanks for the well-scoped fix and for flagging this as an RFC first!
Merged — locally validated (30/30 focused tests: chatcore-codex-account-pool, codex-account-cooldown-write, codex-account-pool, providers-route-codex-account-pool, resilience-explain-codex-account, sse-auth-codex-account-pool; typecheck:core clean; file-size/complexity/cognitive-complexity/changelog gates all green). Merges clean against the current release tip with zero conflicts. Great refactor — extracting persistCodexQuotaState out of chatCore.ts into a proper codexAccount/ module with virtual quota pool isolation is a solid improvement. Thanks!
Merged via merge-train (release/v3.8.50, batch1 2026-08-20) — static gates (typecheck/file-size/complexity/cognitive/changelog) green on the combined tree; test:unit reds observed in the boarded run were verified pre-existing on the pure release tip (unrelated flake), not caused by this PR. Thanks for the contribution!
Merged via merge-train (release/v3.8.50, batch1 2026-08-20) — static gates (typecheck/file-size/complexity/cognitive/changelog) green on the combined tree; test:unit reds observed in the boarded run were verified pre-existing on the pure release tip (unrelated flake), not caused by this PR. Thanks for the contribution!
Merged via merge-train (release/v3.8.50, batch1 2026-08-20) — static gates (typecheck/file-size/complexity/cognitive/changelog) green on the combined tree; test:unit reds observed in the boarded run were verified pre-existing on the pure release tip (unrelated flake), not caused by this PR. Thanks for the contribution!
Merged via merge-train (release/v3.8.50, batch1 2026-08-20) — static gates (typecheck/file-size/complexity/cognitive/changelog) green on the combined tree; test:unit reds observed in the boarded run were verified pre-existing on the pure release tip (unrelated flake), not caused by this PR. Thanks for the contribution!
Merged via merge-train (release/v3.8.50, batch1 2026-08-20) — static gates (typecheck/file-size/complexity/cognitive/changelog) green on the combined tree; test:unit reds observed in the boarded run were verified pre-existing on the pure release tip (unrelated flake), not caused by this PR. Thanks for the contribution!
Merged via merge-train (release/v3.8.50, batch1 2026-08-20) — static gates (typecheck/file-size/complexity/cognitive/changelog) green on the combined tree; test:unit reds observed in the boarded run were verified pre-existing on the pure release tip (unrelated flake), not caused by this PR. Thanks for the contribution!
* fix(security): zero out open CodeQL code-scanning alerts
- src/mitm/handlers/antigravity.ts: fix broken \s regex escape in a
template-string RegExp (unrecognized escape silently dropped the
backslash, breaking the whitespace match) — also clears the two
useless-regexp-character-escape alerts.
- open-sse/executors/gemini-web.ts: replace the unbounded polynomial
regex in isMissingBrowserExecutable() with plain substring checks.
- src/shared/middleware/chatBodyAdmission.ts, open-sse/services/
conversationTracker.ts, src/app/api/v1/models/catalogCache.ts:
annotate the sha256 fingerprint hashes (admission-budget key,
conversation identity, catalog memo key — none are password/
credential hashes) with codeql[js/insufficient-password-hash]
suppressions; the existing suppression comments in
chatBodyAdmission.ts were on the wrong line and CodeQL never
picked them up.
- tests/unit/qwen-token-plan-console-site.test.ts, tests/unit/
cloudflare-playground-provider.test.ts: replace raw
string.includes(hostname) assertions with new URL(...).hostname
equality/endsWith checks, closing the incomplete-url-substring-
sanitization alerts without weakening what the tests verify.
* fix(security): correct codeql suppression comment syntax
The prior codeql[rule-id] trailing comments mixed in extra text after
the rule id, and CodeQL's PR-diff check re-flagged all three fingerprint
sha256 calls as new js/insufficient-password-hash alerts. Use the bare
`// codeql[js/insufficient-password-hash]` suppression comment on the
flagged line, with the justification moved to a plain comment on the
line above.
* fix(security): switch fingerprint hashes from sha256 to HMAC-SHA256
The prior codeql[js/insufficient-password-hash] suppression comments
were not honored by the PR-diff CodeQL check, which kept flagging the
three fingerprint call sites (admission-budget bucket key, conversation
identity, catalog memo-map key) as new alerts.
Switch createHash("sha256") to createHmac("sha256", <fixed context
label>) at all three sites: a keyed, domain-separated digest is the
semantically correct construction for a fingerprint anyway (it no
longer collides with an attacker-supplied unkeyed digest of the same
input), and it does not match the insufficient-password-hash sink
pattern.
* chore(ci): retrigger CodeQL after dismissing pre-existing fingerprint-hash alerts
Empty commit to force a fresh default-setup CodeQL scan now that
alerts #827/#833/#834/#837 are dismissed as false positives (see PR
description) — the prior scan predates the dismissal.
---------
Co-authored-by: Markus Hartung <mail@hartmark.se>
validateResponseQuality's streaming-SSE peek only flagged an OpenAI-shape
stream as invalid when it closed WITHOUT ever reaching finish_reason/[DONE]
(#7285 truncation guard). A stream that DOES reach finish_reason: "stop"
but never carries any real content, reasoning, or tool_calls in any chunk
fell through as valid, exactly reproducing the reported content:null /
completion_tokens:0 HTTP 200 for cmd/meta/muse-spark-1.2-contributor.
Add a sibling failover branch for the terminated-but-empty case, mirroring
the existing truncation branch. Tool-calls-only streams are unaffected —
they already short-circuit through the earlier content-detection branch.
Co-authored-by: Markus Hartung <mail@hartmark.se>
* fix(sse): import localDb through its real .ts extension (#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 #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#10674
* fix(config): keep the SQLite driver out of the client bundle (#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#10692
---------
Co-authored-by: Xiangzhe <bakryun0718@proton.me>
`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 #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#10674
Co-authored-by: Xiangzhe <bakryun0718@proton.me>
* 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 "#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>
`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>