Commit Graph

4 Commits

Author SHA1 Message Date
Diego Rodrigues de Sa e Souza
36abd86929 fix(ci): clear the 08-08 base-red layers — dead-code, prod crash in chat.ts, Responses payload regression, born-red stdio test, gate drifts (#9757)
* fix(ci): drop unused RadarReferrals type export — dead-code ratchet back to 227 baseline

The radar referral-links feature (#9697) exported the inferred type
RadarReferrals from feedSchema.ts but nothing imports it (the singular
RadarReferral is the consumed type). knip counts it as a new dead export,
pushing the dead-code ratchet to 228 > 227 and failing Fast Quality Gates
on every PR born after the merge. RadarReferralsSchema itself stays — it
is used by RadarFeedSchema.

Refs #9737

* fix(ci): clear the 08-08 base-red layer — prod crash in chat.ts, Responses API payload regression, born-red stdio test, gate drifts

Six independent base-reds from the 08-07 evening merge batch, each verified
against the pure release/v3.8.50 tip:

- src/sse/handlers/chat.ts: #9467's squash carried a refactor hunk that
  renamed the all-rate-limited breaker guard to an UNDEFINED variable
  (isAllRateLimited) — a production ReferenceError on the all-accounts-429
  path (chat.ts is outside typecheck:core scope, so only tests caught it).
  Restore credentials?.allRateLimited. Guard: chat-rate-limit-body-lock (2/2),
  also un-breaks batch_api and chat-combo-live-test.
- open-sse/utils/stream.ts: #9315 switched providerPayload summaries to the
  accumulated responseBody, but in passthrough paths that body is synthesized
  in chat-completion shape — Responses API lost its `response` object in the
  dashboard payload. Keep the events-derived summary for OPENAI_RESPONSES
  only. Guard: stream-utils + stream-collector-9315 suites (51/51).
- tests/unit/mcp-stdio-json-purity.test.ts: born red — the full CLI chain
  takes ~10s (2x tsx import + DB init) and the test slept a fixed 4s. Poll
  for the first stdout line with a 60s deadline instead.
- tests/unit/plugins-route-error-sanitization.test.ts: register #9445's new
  marketplace/install route in PLUGIN_ROUTES (route already sanitizes) (33/33).
- tests/unit/provider-models-route-codex.test.ts: realign pinned GPT-5.6
  input limit to #9432's deliberate 272000→922000 bump (7/7).
- lint: fix 11 no-explicit-any errors in repro-9630 + specialty-9293 tests,
  prune 1 orphaned suppression, allowlist the opencode-ai devDependency
  (#8869, publisher-verified), and reword a doc line the fabricated-docs
  gate misread as an env var.

Gates re-verified locally: lint:json --max-warnings 0 exit 0, dead-code 227,
typecheck:core clean, check:deps OK, check:fabricated-docs OK.

Refs #9737

* fix(ci): clear the third 08-08 base-red layer — invalid ru rule pack, stale event pin, orphaned UI repro test, pack/mutation/file-size drifts

Follow-up to the previous layer: the serial fast-gates chain unmasked one
more stratum after file-size/dead-code went green, all verified against the
merged release/v3.8.50 tip:

- compression rules ru/ultra.json (#9581): two rules shipped
  minIntensity "notes", which is not a valid CavemanIntensity
  (lite|full|ultra) — loading ANY language pack list threw and killed the
  rtk-loader suite. Mapped both to "ultra" (they are the most aggressive
  punctuation/case rules, matching the en pack tiers). 2/2.
- plugins-welcome-banner-e2e: #9668 added the onStreamComplete builtin
  event (real emission path via runOnStreamCompleteHooks) and missed this
  pinned-list sibling. 35/35.
- tests/unit/free-pool-frontend-repro (#9046): landed as .tsx with
  node:test semantics — no runner collects tests/unit/*.tsx, so it NEVER
  ran (test-discovery NEW-orphan). It contains zero JSX; renamed to .test.ts
  so the unit runner's existing glob collects it. 5/5 (first real run).
- pack-policy: allow + require bin/mcpStdioConsoleGuard.mjs (#9281) — it is
  preloaded via node --import by bin/mcp-server.mjs, so a published artifact
  without it crashes 'omniroute --mcp' at startup.
- stryker.conf.json: add 5 covering unit tests from the batch (#8779/#9204/
  #9330/#9630/openrouter-passthrough) to tap.testFiles (--strict drift).
- file-size-baseline: consolidate the base-drift rebaseline for the 12
  files grown by the 08-06..08-08 batches (#9616's entries never reached the
  base; measured on this branch's tree — this PR's own source edits add zero
  lines to any frozen file).

Local battery: file-size/deps/test-discovery/mutation/pack-policy/dead-code/
duplication/docs-all/secrets/vuln/workflows ratchets all exit 0; full lint
gate --max-warnings 0 exit 0.

Refs #9737

* fix(types): clear the 3 uncovered open-sse-typecheck regressions + realign combo skip-code siblings

Fourth base-red layer unmasked by the serial gates. The other 4 typecheck
regressions (codex.ts, kiro.ts, tierResolver.test.ts, translator/index.ts)
already have dedicated open [TS7] PRs (#9748/#9753/#9742/#9747) — not
duplicated here. This commit covers only what no open PR owns:

- devin-agentic/serializer.ts TS2367: drop the dead 'role === "system"'
  branch — the guard above already narrows role to user|assistant (system
  throws unsupported_role). Devin suites 104/104.
- raycast.ts TS2416: the buildHeaders 'override' never matched the base
  signature (2nd param is the signed payload string, not the stream
  boolean) — renamed to a private buildRaycastRequestHeaders helper so a
  polymorphic buildHeaders(credentials, true) call can never bind here.
- modelMetadataRegistry.ts TS2352: PricingByProvider → nested-record cast
  now goes through unknown (shape is runtime-guarded by findInsensitive).
- combo-routing-engine.test.ts: realign 2 pre-dispatch-skip expectations to
  #9630's deliberate ALL_TARGETS_SKIPPED contract (87/87).

Refs #9737

* fix(ci): clear the fifth 08-08 base-red layer — reasoning-placeholder contract sweep, GPT-5.6 limits sweep, vi key parity

The 08-08 merges (#9610 reasoning replay, #9432 GPT-5.6 limits, #9630 combo
skip codes, #9336 provider key links) each changed a contract and left
sibling tests pinning the old one. Full grep sweep per contract, not just
the shard that happened to go red:

- reasoning placeholder (#9573/#9610): the fix DELIBERATELY removed
  NON_ANTHROPIC_THINKING_PLACEHOLDER injection on cache miss — the model
  echoed the placeholder as its own reasoning (empty stop) and re-poisoned
  cache + client history; DeepSeek's 400 is specific to an EMPTY STRING, not
  an absent field. Realigned reasoning-cache (2 cases, renamed to describe
  omission) + tool-request-sanitization (1 case + dead import). 60/60.
- GPT-5.6 Codex limits (#9432, 272000 -> 1050000 ctx / 922000 input):
  realigned vscode-token-routes-gpt56 (2) + vscode-token-routes (3). 43/43
  together with t23-t24.
- combo skip codes (#9630): t23-t24-fallback-resilience T24 now expects
  ALL_TARGETS_SKIPPED like the combo-routing-engine siblings.
- vi.json key parity: #9336 added providers.getApiKey/getApiKeyDescription
  to en.json without syncing vi (the only locale with a parity gate).
  Translated both; providers block reordered to match en key order. 5/5.
- pack-artifact-policy.test.ts: sibling of this PR's own required-paths
  change (bin/mcpStdioConsoleGuard.mjs). 10/10.
- combo-routing-engine.test.ts: dropped the 6 comment lines added in the
  previous commit so the frozen test file-size stays at its baseline (the
  rationale lives in that commit message, not the test body).

Gates: file-size, test-discovery, mutation-test-coverage, pack-policy,
open-sse-typecheck, dead-code all exit 0.

Refs #9737

* fix(translator): keep the reasoning_content placeholder for Xiaomi MiMo — #9610 traded one live 400 for another

The xiaomi-mimo replay test (9router#1321) went red on the base after #9610
removed the NON_ANTHROPIC_THINKING_PLACEHOLDER injection globally. That test
is NOT stale — it guards a documented upstream 400 ('Param Incorrect: The
reasoning_content in the thinking mode must be passed back to the API'), so
realigning it would have masked a reintroduced production bug.

Two real bugs conflict here:
- #9573: forwarding the placeholder makes the model continue its chain of
  thought FROM that text (echo -> empty stop) and re-poisons cache/history.
- 9router#1321/#1337: omitting reasoning_content on a plain replay turn makes
  Xiaomi MiMo reject the request outright.

#9610's evidence for omitting is provider-specific — it verified that
deepseek-v4-flash accepts an ABSENT field. It does not extend to MiMo. So the
omission stays for every provider #9610 covered, and the placeholder survives
the cache miss only for xiaomi-mimo (new requiresReasoningContentPresence
predicate next to isReasoningOnlyReplayTarget). The echo that comes back is
still stripped on the way in by isInternalReasoningPlaceholder(), so #9573's
cache/history poisoning stays fixed for MiMo too.

Both contracts now hold simultaneously: xiaomi-mimo replay + reasoning-cache +
tool-request-sanitization 61/61; placeholder-strip/responses/translator/combo
regression sweep 168/168. Gates: file-size, open-sse-typecheck, dead-code,
mutation-test-coverage exit 0; typecheck:core clean.

A live check on the VPS (Hard Rule #18 path 2) is the only way to confirm the
DeepSeek half of #9610's empirical claim; flagging it in the PR rather than
widening this fix on speculation.

Refs #9737

* test(translator): pin the reasoning-placeholder provider scope so neither half of the conflict can silently re-break

#9610 removed the placeholder globally on the strength of ONE provider's
observed behavior (deepseek-v4-flash accepting an absent reasoning_content),
which re-opened the MiMo 400 (9router#1321). The previous commit scoped the
placeholder to xiaomi-mimo; this pins BOTH directions in one test so the next
global edit fails loudly instead of trading the bugs again:

- xiaomi-mimo plain replay turn, cache miss -> reasoning_content present
  (narrowing the scope away from MiMo re-opens 9router#1321)
- deepseek plain replay turn, cache miss -> reasoning_content absent
  (widening it back to DeepSeek re-opens the #9573 echo bug)

Guard verified by mutation: forcing requiresReasoningContentPresence() to
return true makes the DeepSeek half fail (1 pass / 1 fail), and the file was
restored from the pre-probe copy before committing.

Also checked kimi-coding/kimi-coding-apikey, the other strict-contract entries
in REASONING_REPLAY_PROVIDERS: their originating PR (#7673) fixes capture and
replay of REAL reasoning and documents no 400 on an absent field, so they stay
out of the placeholder scope — evidence-scoped, not speculatively widened.

Reasoning suites together: 87/87. Gates: file-size, test-discovery,
mutation-test-coverage, dead-code exit 0; eslint clean.

Refs #9737

---------

Co-authored-by: diegosouzapw <diegosouzapw@users.noreply.github.com>
2026-08-08 09:08:45 -03:00
Diego Rodrigues de Sa e Souza
8fac6bcd48 feat(.50): completa itens restantes — G13, G14, gap34, docs, R0.2 (#9126)
* feat(ci): G0 — quality rail (PR→release/**) ganha ratchets+segurança do trilho A

O refactor de god-files do trilho 3.8.50→3.9.0 acontece em PRs→release/**, e esse
trilho pulava o motor de ratchet, o CodeQL ratchet e todos os scanners de segurança
— exatamente onde a rede era necessária (5 das 13 causas da reconciliação de 07-24
eram regressões reais shipadas por CI verde por-PR).

Modo enxuto, jobs EXISTENTES (a .51 consolida lanes; nenhum job novo):

- lint-guard: quality:collect + ratchet --allow-missing + require-tighten +
  check:codeql-ratchet. O job já escreve .artifacts/eslint-results.json, então o
  motor entra a custo ZERO de ESLint (um inventário, dois consumidores). Coverage
  ausente degrada gracioso (--allow-missing); autoridade de coverage segue no
  trilho A. + permissions security-events:read para o CodeQL ratchet.
- fast-gates: check:cycles, check:lockfile, duplication, dead-code, type-coverage,
  compression-budget + install endurecido dos scanners (gh release download,
  zizmor PINADO 1.25.2 = mesmo auditor do ci.yml) + secrets/vuln/workflows/
  openapi-breaking com --ratchet (self-skip sem binário; só regressão medida bloqueia).
- Fora de propósito: bundle-size (self-skip sem build → configuração morta) e o
  run de coverage (fast-unit já roda a suíte cheia).

Runners intocados: guard tests/unit/vps-runner-variable-scope.test.ts verde;
teste novo tests/unit/quality-rail-gate-membership.test.ts pina a MEMBERSHIP dos
gates no trilho B (red antes da edição, green depois).

Validação no tip puro (nenhum base-red fabricado para a fila de PRs abertos):
cycles OK · lockfile OK · duplication 4.26% (base 5.72%) · dead-code 226 (base
227) · type-coverage 94.13% (base 92.17%) · compression OK · secrets 0 (base 0) ·
vuln 5 (base 10) · codeql 0 (base 0) · oasdiff 0 (base 0) · zizmor 178 (base 190)
· actionlint exit 0 no arquivo editado · quality-ratchet 56 métricas OK +
require-tighten OK com --allow-missing.

Refs #8084

* feat(.50): G13 golden-set, G14 import boundaries, gap34 deterministic, docs sync, R0.2 dead hooks

Integra os itens restantes da 3.8.50:

- G13: golden-set determinístico para combo.ts e chatCore.ts via seams públicas
- G14: no-restricted-imports para localDb barrel fora de src/lib/db/ e executors em src/app/
- Gap34: teste determinístico de timeout DuckDuckGo sem rede real
- Docs: golden path de contribuição + sincronização de números canônicos
- R0.2: remoção dos 7 hooks mortos do BUILTIN_EVENTS + UI marketplace ajustada

* fix(r0.2): remove marketplace tab remnants from plugins page — fixes dashboard typecheck regression

* chore(r0.2): remove pluginWorker.ts, signing.ts, sandbox.ts — zero importers confirmed

* fix(docs): remove OMNIROUTE_PLUGINS_ALLOW_EXEC reference — env var removed with pluginWorker.ts in R0.2

* fix(env): remove dead OMNIROUTE_PLUGINS_ALLOW_EXEC from .env.example — consumer removed in R0.2

* fix(test): update sidebar-visibility assertion for R0.2 marketplace removal

---------

Co-authored-by: diegosouzapw <diegosouzapw@users.noreply.github.com>
2026-08-01 10:44:23 -03:00
Diego Rodrigues de Sa e Souza
8169b97d84 Release v3.8.18 (#3482)
* chore(release): open v3.8.18 development cycle

* fix(catalog): stop Codex CLI model-catalog refresh from erroring (#3481)

Codex's model-catalog refresh (codex_models_manager) does
GET /v1/models?client_version=<v> and decodes a JSON object with a
TOP-LEVEL `models` array. OmniRoute answers in the OpenAI-standard
`{object,data}` shape, so codex fails with "missing field `models`"
and logs "failed to refresh available models" on every startup.

Detect codex clients via the `originator` / `user-agent` = `codex_*`
headers they send and add an EMPTY top-level `models: []` so the decode
succeeds. Non-codex OpenAI clients keep the byte-identical `{object,data}`
response.

The array is intentionally empty: codex replaces its built-in per-model
agent prompt (`base_instructions`, ~21k chars) with whatever a populated
entry carries for the selected model, so emitting our catalog would drop
the agent prompt to nothing and break codex's agent behaviour (verified
empirically against codex 0.137). An empty list keeps codex on its
built-in model info — same inference as before, minus the error.

Validated end-to-end with the real handler against codex 0.137:
"failed to refresh available models" → 0 occurrences, instructions
preserved (built-in Codex agent prompt, not empty).

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* chore: ignore quality reports and local prompt artifacts

Add generated quality gate reports, metrics files, and local setup prompt
artifacts to .gitignore to prevent committing environment-specific or
temporary files.

* fix(provider): detect Responses API format when body has `input` but … (#3490)

Integrated into release/v3.8.18

* fix(sse): normalize numeric provider ids to strings (#3451)

Integrated into release/v3.8.18

* feat(browserPool): resolve Playwright proxy from proxy_registry DB (#3492)

Integrated into release/v3.8.18

* fix(theoldllm): generate X-Request-Token server-side, drop Playwright (#3491)

Integrated into release/v3.8.18

* feat(plugins): add lifecycle hooks and theme-manager plugin (#3473)

Integrated into release/v3.8.18

* fix(combo): parallel pre-screen + circuit-breaker fast-exit for priority combos (#3169)

Integrated into release/v3.8.18

* feat(ui): unifi active and finished requests into single view #1422 (#3401)

Integrated into release/v3.8.18

* docs(changelog): record #3401, #3473, #3492, #3490, #3451, #3491, #3169 under v3.8.18

* feat(docs): add doc accuracy gate + refresh AGENTS.md counts (#3510)

Integrated into release/v3.8.18

* fix(sse): drop empty-choices chunks without usage instead of injecting retry text (#3513)

PR #3422 ('allow OpenAI usage-only empty choices chunks') reintroduced the
assistant-content injection '[OmniRoute] Upstream returned an empty response.
Please retry.' for empty `choices: []` chunks that carry no valid usage. Clients
(Goose/opencode) feed that text back as a turn and spin in a retry loop -- the
exact regression #3400 had fixed by dropping the chunk.

Restore the drop behavior for the no-usage case while preserving #3422's
standards-compliant forwarding of usage-only `include_usage` final chunks.
Realign the mislabeled stream-utils test (it asserted the injection) and add a
dedicated regression guard.

Reported-by: @mochizzan
Refs: #3502, #3388, #3400, #3422

* fix(authz): fall back to URL token when Authorization isn't a usable Bearer (#3504)

Integrated into release/v3.8.18

* fix(playground): authenticate via session, test key policy by id (#3503)

Integrated into release/v3.8.18

* docs(changelog): record #3510, #3504, #3503 under v3.8.18

* fix: llama base url normalization (#3519)

* docs(changelog): reconcile v3.8.18 — add #3519, #3513, #3435-repair, gitignore chore (full commit↔changelog coverage)

* fix(opencode-plugin): bound regex quantifiers in normaliseFreeLabel (polynomial-ReDoS)

CodeQL js/polynomial-redos: unbounded \s* before an anchored \s*$ allowed
O(n²) backtracking on attacker-influenced display names. Bounded to {0,8}/{1,8}
(ample for any real label spacing). Plugin builds + 254 tests green.

* fix(types): restore clean typecheck:core for v3.8.18 release gate

- getPendingRequests() typed to real shape (was widened to object) → fixes
  unknown 'count' in the unified-requests view (#3401)
- streamChunks log payload cast to its declared type (callLogs.ts)
- preScreenTargets aligned to canonical IsModelAvailable signature (#3169),
  Promise.resolve-normalized so .catch never hits a bare boolean

All 5 gates green: lint(0 err) + typecheck:core + cycles + docs-all + unit + vitest(146).

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Andrey Borodulin <borodulin@gmail.com>
Co-authored-by: Dmitrii Safronov <zimniy@cyberbrain.cc>
Co-authored-by: Paijo <14921983+oyi77@users.noreply.github.com>
Co-authored-by: PizzaV <103120356+pizzav-xyz@users.noreply.github.com>
Co-authored-by: Markus Hartung <mail@hartmark.se>
Co-authored-by: Felipe Almeman <4226997+zhiru@users.noreply.github.com>
2026-06-09 15:56:24 -03:00
Paijo
9e7f3cad10 feat(plugins): plugin hook wiring + comprehensive test suite + welcome-banner example (#3045)
Follow-up to the plugins framework (#3041): wires the plugin hooks end-to-end and adds full test coverage.

- Wires `onRequest` / `onResponse` / `onError` hooks into the chat pipeline (`chatCore` now imports from the unified `hooks` registry).
- Loads active plugins on server startup (`pluginManager.loadAll()` in `server-init`) so they survive restarts.
- Ships a `welcome-banner` example plugin (`examples/plugins/`) + test fixture.
- Adds a comprehensive plugin test suite (manifest, db, config, hooks, manager lifecycle, loader IPC, permissions, scanner, welcome-banner e2e).

Integration fixes applied during review:
- `manager.install` now removes an orphaned `destDir` (DB row gone but files left on disk) before the atomic rename, guarded by path containment — it previously failed with `ENOTEMPTY` (a regression surfaced by the new lifecycle test).
- `plugins-db` / `plugins-manager-lifecycle` tests now initialize the DB via the real migration `076` (`getDbInstance`) rather than relying on ambient state, so a missing/renumbered migration fails loudly instead of being masked.

348/348 plugin tests pass; typecheck / cycles clean.

Co-authored-by: oyi77 <14921983+oyi77@users.noreply.github.com>
2026-06-01 16:20:11 -03:00