mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-12 18:22:48 +03:00
35f5a00a673d68b63e1c6d4843dd0956c8eaf164
14 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
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> |
||
|
|
e7f6b1d130 |
feat(radar): flag-gated signed free-model catalog overlay (#9515)
* feat(dashboard): add RADAR_ENABLED flag (default off) * feat(db): radar feed cache + settings with encrypted supporter key * feat(radar): signed feed sync with pinned key and version floor - feedSchema.ts: Zod v4 schema mirroring the server feed format (discriminated union on budget.kind, enum constraints, etc.) - pinnedKeys.ts: Ed25519 SPKI-DER pinned key + env override for forks - verify.ts: signature verification over exact wire bytes, never throws - sync.ts: full download/verify/validate/cache pipeline with injectable deps, feature-flag gate, opt-in gate, version floor (numeric compare), and sanitized error reasons (no stack traces) - 40 tests covering: contract hash, key handling, sig verification, schema validation, version compare, all sync paths (disabled, opt_out, invalid_signature, invalid_schema, stale, updated, error), auth header injection, and cache-untouched assertions for every failure mode * feat(radar): read-time overlay merge rules over the free catalog Pure function applyFeed() merges the cached Radar feed over the static baseline catalog at read time, honoring 4 rules: 1. Feed never overwrites a local override field. 2. enabled:false disables the entry with disabledBy:"radar" provenance. 3. User-added entry NOT in the feed survives untouched. 4. User deletion tombstone prevents feed resurrection. getRadarCatalog() accessor in index.ts: flag off / no cache / corrupt payload all fall back to baseline. Valid cache applies the overlay and returns feed metadata (version, tier, fetchedAt). TDD: 19 tests (4 rules + dedup + origin + accessor flag/cache/corrupt/ valid/bad-feed + baselineToMergedEntries converter). * feat(dashboard): radar catalog and guided setup screens - API routes: GET /api/radar/catalog, POST /api/radar/sync, POST /api/radar/settings - All gated on RADAR_ENABLED flag (404 when off) - Error responses via buildErrorBody(), never raw stack/message - Settings never echoes clear supporter key (masked omr_****<last4>) - Sync delegates to syncRadar() server-side, never proxies feed URL - Dashboard pages: - /dashboard/radar: 4 states (flag off, opt-in pending, empty, populated) - /dashboard/radar/setup?provider=X: guided setup with steps, key URL, test connection - Uses existing Card component and next-intl patterns - Sidebar: radar entry in costs group with icon - i18n: pt-BR and en keys for radarPage and radarSetupPage namespaces - Tests: - radar-api-routes.test.ts: 11 tests (flag-off 404, flag-on shape, error sanitization) - radar-page-state.test.ts: 5 tests (pure state logic) - All 90 radar tests pass (including prior 74) * docs(radar): module doc and flag-off inertia test Add docs/frameworks/RADAR.md covering the flag gate, the separate data-sync opt-in and privacy promise, the Ed25519 signature/pinned-key security model, tiers, the read-time overlay merge rules, and the self-hosting env vars — plus index entries in CLAUDE.md/AGENTS.md/docs/README.md/REPOSITORY_MAP.md. Document RADAR_FEED_URL and RADAR_FEED_PUBKEY in .env.example and docs/reference/ENVIRONMENT.md to satisfy check:env-doc-sync, which was failing on this branch since the sync.ts commit added the reads. Add tests/unit/radar-inertia.test.ts as the single canonical place asserting the "RADAR_ENABLED off => zero behavioral delta" claim end to end: the three /api/radar/* routes 404, the flag resolves to the definition default with no override, getRadarCatalog() returns exactly the baseline without touching the cache, and computeFreeModelTotals() keeps its pinned values with the Radar module imported alongside it. * fix(db): renumber radar migration to 135 after collision with 134 The base branch introduced 134_proxy_logs_egress_ip while this branch carried 134_radar_cache_settings; the migration runner rejects duplicate numeric prefixes. This migration has never been applied to a real database (the PR is unmerged), so no retroactive isSchemaAlreadyApplied guard is needed. * i18n(radar): translate radar catalog and setup strings to all locales The UI-coverage ratchet measures (present - placeholder) / total_en, so the __MISSING__ sentinels that i18n:sync-ui writes do not count as covered — only real translations restore the metric. Scoped to this PR's namespaces (radarPage, radarSetupPage, sidebar.radar*) instead of a bulk sync, which would have pulled ~978 unrelated pending keys into this diff. Placeholders and code identifiers verified preserved across all 1682 strings. * fix(radar): trust the served-tier header instead of the signed body field The signed feed body always carries tier:"live" by design (one signed artifact per version — rewriting the field server-side per request would break the exact-bytes Ed25519 signature). The server now returns the tier ACTUALLY served via the x-omniroute-feed-tier response header, so free users on a delayed community snapshot no longer see "Ao vivo (tempo real)" in the UI. sync.ts now reads and validates that header (falling back to the body's tier only when the header is absent or holds an unrecognized value) and stores the served tier in the cache; index.ts already surfaces cache.tier to the UI unchanged. * test(combo): shorten an assert message that exceeded the line limit The assertion added by #9507 was 104 chars, so prettier reformatted it into five lines on the next commit that touched the file, pushing it past its frozen size (3449) and failing check:file-size. The message is shortened (the issue reference stays in the comment directly above); the assertion itself is unchanged, and the file is back to 3448 lines and prettier-clean. * i18n(radar): use the canonical zh-TW glossary terms The machine translation produced retired renderings the glossary gate blocks: 供應商 for provider (canonical 提供者) and 文檔 for documentation (canonical 文件). Fixed across the 11 affected radar strings; tests/unit/i18n-glossary-consistency-check.test.ts is back to 17/17. * fix(radar): point the default feed URL at the domain that exists radar.omniroute.dev was a placeholder for a domain that was never registered, so an out-of-the-box sync would fail DNS resolution for every user. The live feed is served from radar.omniroute.online (the subdomain the design always specified), now behind Cloudflare TLS. Forks still override it via RADAR_FEED_URL. --------- Co-authored-by: diegosouzapw <diegosouzapw@users.noreply.github.com> |
||
|
|
9099df4484 |
feat(models): unify token limit overrides (#8908)
Validated in local merge-train (devbox-vm-06-dev002) @ combined-tip (FAST gates — only pre-existing audit.test.ts flake). |
||
|
|
ee4cd0d795 |
fix(api): consult LiteLLM pricing_synced layer in resolveCatalogPricing so deployed models absent from models.dev and defaults get pricing in /v1/models (#9364)
Closes #9364 |
||
|
|
e50f2329dc |
fix(lib): memoize catalog pricing/capability lookups to fix cold /v1/models freeze (#8697) (#8987)
Root cause: a cold GET /v1/models catalog rebuild froze the entire server 41-54s. node --prof profiling found a systemic missing-memoization pattern — a per-model function rescanning a static or synced data structure with Object.entries()/ Object.keys() (or hitting SQLite) on every call instead of once per rebuild. Fixed 6 instances of the same pattern, found by iteratively re-profiling the full catalog sweep after each fix (plus a whitebox review pass) until no further hotspot of this shape remained: 1. getModelsDevPricing() (modelsDevSync.ts) — re-ran a synchronous SQLite query and re-JSON.parse'd ~180 blobs on every call (up to ~6091x instead of once per request). Memoized via the existing modelCatalogCacheVersion invalidation signal (same pattern as getCachedRawProviderConnections/getCachedProviderNodes in db/readCache.ts). Dominant cost of the original 41-54s freeze. 2. findInsensitive() (modelMetadataRegistry.ts, resolveCatalogPricing) — rebuilt a full Object.entries() scan on every case-insensitive lookup miss, twice per model. Replaced with a lowercase-key index built once per distinct pricing object and cached by identity (WeakMap). Warns once at index-build time on a case-insensitive key collision instead of silently discarding the second value. 3. getSyncedCapability() (modelsDevSync.ts) — ran a per-model SQLite SELECT on cold cache instead of self-warming the whole-table cache; no caller in the /v1/models build path ever primed it, so a cold rebuild ran one SQLite round-trip per model per call site. Now self-warms via the existing bulk getSyncedCapabilities() on first miss. Measured as the dominant remaining cost after fixes 1-2 (~70% of a full catalog sweep). 4. getCanonicalModelSpecId() (shared/constants/modelSpecs.ts) — up to 3 separate linear scans over the static MODEL_SPECS table per call (exact ci, alias ci, prefix). Replaced with a lazy, lowercase-key index built once (MODEL_SPECS never changes at runtime); prefix-match iteration order preserved exactly so resolution outcomes are unchanged. 5. getStaticSpecCanonicalModelId() (modelCapabilities.ts) — duplicated the same exact+alias scan as (4) in a second, separate rescan. Now reuses the shared index via a new exported helper (findModelSpecIdByExactOrAlias) instead of maintaining a second cache over the same static table. reverseModelsDevProviders() (modelCapabilities.ts) — rescanned Object.entries(MODELS_DEV_PROVIDER_MAP) (also static) on every call; memoized by provider key. Result is frozen (readonly) since it is now shared across calls instead of freshly allocated each time. 6. resolveModelAlias() (shared/constants/modelSpecs.ts) — rescanned Object.entries(MODEL_SPECS) unconditionally once per model (verified 1:1 call ratio, no short-circuit). Case-sensitive exact match (Array.includes(), no .toLowerCase()) — uses a dedicated exact-match index, deliberately not the case-insensitive alias index from fix 4/5 (would silently broaden matches). Measured on a 1940-pair real-catalog sample (static PROVIDER_MODELS registry): cold sweep 828ms -> 356ms after fixes 3-5 on top of 1-2, extrapolating to roughly 1s on the real ~6091-model catalog, down from the original 41-54s freeze. Complementary to the stale-serve fix in #8801 (upstream) — neither alone eliminates the freeze. Tests: call-count regression guards for every fix (DB prepare / Object.entries / Object.keys call counts staying constant instead of scaling with iteration count), plus correctness coverage for case-insensitive/case-sensitive resolution. All pre-existing consumer suites re-verified passing (96 tests total across 19 files). Co-authored-by: diegosouzapw <diegosouzapw@users.noreply.github.com> |
||
|
|
455906c181 | fix(reasoning): forward Ollama Cloud thinking (#9290) | ||
|
|
295189d4a8 |
fix(models): stop inventing chat capabilities for specialty surfaces (#8016) (#8022)
Co-authored-by: RaviTharuma <ravitharuma@users.noreply.github.com> Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> |
||
|
|
0f6e440dfe |
fix(models): attach models.dev pricing to GET /v1/models entries (#8018) (#8025)
Co-authored-by: RaviTharuma <ravitharuma@users.noreply.github.com> Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> |
||
|
|
67a0b99240 |
feat(providers): add GPT-5.6 model family (#6862)
* feat(providers): add GPT-5.6 model family * fix(chatgpt-web): resume temporary chat handoffs * fix(codex): auto-merge discovery, filter denylist, revalidate on lifecycle Restore live/GitHub auto-merge for Codex catalogs, drop models via explicit denylist (GPT-5.4 family), and run scrub+live re-sync once on first-start, app upgrade, or setup completion. Success log: kill deprecated models complete. * fix(codex): preserve live catalog reconciliation Expose remote-only Codex models without dropping user custom entries, and complete lifecycle revalidation only after a successful internal sync. Keep credentialed self-fetches pinned to the active dashboard listener. --------- Co-authored-by: backryun <backryun@daonlab.local> |
||
|
|
62b1bc9d05 |
feat(api): standardize effort + thinking request params (#6241) (#6398)
standardize effort + thinking request params (#6241) (net +1/-0, tests OK). Integrated into release/v3.8.46. |
||
|
|
1bda6c15dc |
Release v3.8.44 (#5925)
* fix(install): add pnpm-workspace.yaml allowBuilds + pnpm.json for pnpm 11+ pnpm 11 introduced ERR_PNPM_IGNORED_BUILDS for native addon packages. Without explicit allowBuilds approval, these packages silently skip build scripts and OmniRoute fails to start with missing native modules. Changes: - pnpm-workspace.yaml: Set allowBuilds=true for all 13 native addon packages (@parcel/watcher, @swc/core, better-sqlite3, core-js, esbuild, keytar, koffi, libxmljs2, onnxruntime-node, protobufjs, sharp, tls-client-node, unrs-resolver) - pnpm.json: Migrate onlyBuiltDependencies from package.json (deprecated field) to the new pnpm.json config file per pnpm 11 spec. Tested on: pnpm 11.9.0, Node 24, Windows 11. Fixes: pnpm install ERR_PNPM_IGNORED_BUILDS on fresh clone with pnpm 11. * chore(release): open v3.8.44 development cycle * test(security): parse Kimi Web URL host instead of substring match (CodeQL #689) (#5928) Alert js/incomplete-url-substring-sanitization: the Kimi Web executor test asserted result.url.includes("www.kimi.com"), which a hostile host like www.kimi.com.evil.net would also satisfy. Parse the URL and assert on the exact hostname (new URL(result.url).hostname === "www.kimi.com"), which is both a stronger check and clears the CodeQL warning. * refactor(translator): extract thinking-budget fitting from openai-to-claude (#5932) Extract the thinking-budget fitting cluster (fitThinkingToMaxTokens + private safeCapMaxOutputTokens + MIN_* constants) verbatim into the pure leaf openai-to-claude/thinkingBudget.ts. Host re-exports fitThinkingToMaxTokens so external importers keep working and imports it back for internal use. Host 822 -> 738 LOC (under the 800 cap). No behavior change: byte-identical bodies, public export set unchanged. Adds a split-guard test; all consumer tests stay green (translator-openai-to-claude, strip-empty, minimax-m3, passthrough). * chore(release): pipeline hardening — test-masking pre-flight gate + contributors/uncovered helpers (#5926) * chore(ci): add test-masking PR-context gate to release-green pre-flight Reproduce check:test-masking (vs origin/main) inside validate-release-green so non-allowlisted net-assert reductions surface in the local pre-flight instead of in a ~40-min CI layer on the release PR. run() now merges a per-gate opts.env so GITHUB_BASE_REF reaches the child. HARD gate; skipped under --quick. Context: v3.8.43 release cost 3 CI round-trips for PR-context gates (test-masking, file-size, pr-evidence) that check:release-green did not reproduce locally. * chore(release): add contributors generator + uncovered-commit reconciliation helpers - scripts/release/gen-contributors.mjs: reproducible `### 🙌 Contributors` table for a CHANGELOG version (parenthetical-group parser → accurate per-PR attribution, noise-handle denylist). v3.8.43 shipped without the section (a real miss) because it was hand-built. npm run release:contributors <version> [--inject]. - scripts/release/list-uncovered-commits.mjs: lists commits since the last tag with no CHANGELOG bullet (v3.8.43 had 123/176 uncovered at reconciliation start). Advisory, maintainer-side. npm run release:uncovered. - 20 unit tests (parenthetical attribution, noise exclusion, idempotent injection, coverage window). * chore(quality): absorb web-cookie-providers-new file-size drift from #5928 (base-red on release/v3.8.44) * refactor(translator): split openai-responses request translator into pure leaves (#5940) Extract the shared pure primitives and the chat->Responses direction out of the 894-line openai-responses.ts request translator: - openai-responses/helpers.ts: pure primitives (toRecord/toString/clampCallId/ normalizeVerbosity/etc + markers/regexes/JsonRecord), zero host imports - openai-responses/toResponses.ts: openaiToOpenAIResponsesRequest (chat->Responses), imports the helpers leaf Host keeps openaiResponsesToOpenAIRequest (Responses->chat, imported by production) plus both register() directions, and re-exports openaiToOpenAIResponsesRequest so external importers (tests) keep working. Host 894 -> 529 LOC (under the 800 cap). Verbatim bodies (multiset check: leaf A 54/54, leaf B 294 lines, fn1 intact), public export set unchanged, leaves never import the host (no cycle). Adds a split-guard test; all consumer tests stay green (responses-translation-fixes 37, verbosity 4, reasoning-effort 4, orphaned-tool-filter 8, empty-tool-name-loop 8, headroom-responses-format 3). * chore(ci): pr-evidence FAIL output tells you to push (body edit does not re-run the gate) (#5944) ci.yml ignores the 'edited' event, so adding the Evidence block to the PR body after a push does not re-run check:pr-evidence — you need another commit. The FAIL report now says so, at the exact place someone sees the red check. + 5 unit tests (classification + hint-on-fail / no-hint-on-pass). Decided against a separate edited-triggered workflow: pr-evidence is not a required check (no ruleset gates it; release PRs merge UNSTABLE, not BLOCKED), so the gap is cosmetic and the generate-release skill already puts Evidence in the body before the first push. * fix(providers): Perplexity Web emits real tool_calls in streaming mode (mirror chatgpt-web toolMode) (#5927) (#5937) Perplexity Web (Pro/Max) only converted <tool>{...}</tool> text into OpenAI tool_calls for non-streaming requests (hasTools && !stream). Streaming requests -- the default for agentic coding clients -- got the raw <tool> text as plain delta.content and never emitted a tool_calls SSE delta, so clients could not execute tools. Reuses the provider-agnostic buildToolModeResponse()/ toolCompletionToSseStream() helpers already shipped for chatgpt-web (#5240): when tools are requested, buffer the full completion and convert it into either a JSON completion or a terminal SSE replay carrying delta.tool_calls + finish_reason: tool_calls, regardless of the caller's stream flag. Extended buildToolModeResponse()'s idSeed to be caller-supplied (default 'cgpt', perplexity-web passes 'pplx') so tool_call ids stay provider-specific without duplicating the helper. Non-tool streaming is unchanged (still lives token-by-token via buildStreamingResponse). * fix(discovery): resolve duplicate /v1 paths and redirect aborts (#5904) Integrated into release/v3.8.44. Thanks @hamsa0x7 for diagnosing the doubled /v1 discovery path and the REDIRECT_BLOCKED probe-loop abort (#5899). De-scoped to the discovery fix (the #5903 session-affinity work is handled by #5943) and added Rule #18 regression guards. * docs(changelog): record #5926 + #5944 (release-pipeline hardening) under v3.8.44 Maintenance (#5952) * docs(claude): add Hard Rule #22 — cross-session safety (git stash + in-flight PRs) (#5955) Integrated into release/v3.8.44 — Hard Rule #22 (cross-session safety). * refactor(translator): extract pure helpers from response/openai-responses (#5949) Extract the 5 stateless helpers (normalizeToolName, stripEmptyOptionalToolArgs, normalizeOutputIndex, normalizeUpstreamFailure, extractResponsesReasoningSummaryText) verbatim into the pure leaf openai-responses/pureHelpers.ts (no stream state, no host import). Host imports them back and re-exports normalizeUpstreamFailure for external importers (tests). Host 1091 -> 1001 LOC. The stateful streaming core stays in the host (out of scope). Byte-identical bodies (multiset 73/73), no cycle. Adds a split-guard; consumer tests stay green (responses-translation-fixes 37, combo-param-validation-fallback-4519 5). * docs(compression): document upstream sync policy for RTK/Caveman engines (#5830) (#5948) Integrated into release/v3.8.44 — docs-only upstream sync policy for RTK/Caveman engines (closes #5830). All 7 checks green. * fix(sse): strip ANSI/VT100 codes from gemini-cli stream frames (#5934) Integrated into release/v3.8.44 — ReDoS-safe ANSI/VT100 strip for gemini-cli stream frames (port of upstream #2273, thanks @anki1kr). PR test green (5/5), file-size gate OK. * fix(translator): strict Anthropic content-block compliance in antigravity→openai request (#5935) Integrated into release/v3.8.44 — strict Anthropic content-block compliance in antigravity→openai (port upstream #2296). PR test green (9/9). UNSTABLE red is the pre-existing environmental setup-claude base-red (opencode-plugin dist not built in fast-path), not a regression from this PR. * fix(mcp): auto-recover stale streamable HTTP sessions on initialize (#5957) Integrated into release/v3.8.44 — MCP stale streamable-HTTP session auto-recovery (thanks @Chewji9875). * fix(providers): validate v0 Platform API keys via chats endpoint (#5954) Integrated into release/v3.8.44 — v0-vercel Platform API key validation (thanks @vittoroliveira-dev). * fix(api): relax provider-scoped chat completion validation (#5907) Integrated into release/v3.8.44 — relaxed provider-scoped chat validation + regression test (thanks @nickwizard). * fix(providers): strip /v1 unconditionally to avoid /v1/v1/models fetch error (#5899) (#5920) Integrated into release/v3.8.44 — unconditional /v1 strip in both models-discovery paths + regression test (thanks @anki1kr). * fix(resilience): per-window is_exhausted + honor quota-exhaustion preflight for priority combos (#5923) (#5941) Integrated into release/v3.8.44. * fix(resilience): honor active codex session affinity over per-request reset-aware re-scoring (#5903) (#5943) Integrated into release/v3.8.44. * fix(thinking): only inject redacted_thinking replay block when tool_use present and thinking enabled (#5945) (#5953) Integrated into release/v3.8.44. * feat(providers): add ClinePass API-key provider (#5942) Integrated into release/v3.8.44 — ClinePass API-key (BYOK) provider (port upstream 9router#2304, co-authored @adentdk). Validated locally: 16 clinepass tests green; fixed the APIKEY count 158→159 + translate-path golden snapshot (clinepass is a genuine new provider). Remaining UNSTABLE red is the pre-existing environmental setup-claude base-red (opencode-plugin dist not built in fast-path). Supersedes stub #5541. * feat(api): add /v1/ocr endpoint (Mistral OCR) + Mistral moderation (#5950) Integrated into release/v3.8.44 — /v1/ocr endpoint (Mistral OCR) + Mistral moderation (port upstream 9router#2064, co-authored @waguriagentic). Validated locally: 14 ocr-route tests + moderation/servicekind/endpoint-category suites green (CORS→Zod→handler + no-stack-leak assertion). Reds are inherited DRIFT only: cognitive-complexity ratchet (none from OCR files — pre-existing cycle drift, rebaselined at release) + environmental setup-claude base-red. * fix(codex): convert chat json schema to responses text format (#5933) Integrated into release/v3.8.44 — converts Chat Completions json_schema response_format → Responses API text.format on the Codex path, and preserves existing text.format through verbosity normalization. Base redirected main→release; the openai-responses.ts split that landed this cycle was reconciled by re-applying the delta onto openai-responses/toResponses.ts. Validated locally: 48 translator-openai-responses-req + 8 codex-verbosity tests green. Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com> * feat(providers): add Claude Sonnet 5 support across the model pipeline (#5833) Integrated into release/v3.8.44 — wires claude-sonnet-5 end-to-end (registries, modelSpecs, pricing ×3, cost, Sonnet-family fallback, 1M-ctx, static models). Reconciled the add/add overlap with the already-merged #5796 (kept the PR's superset test with the family-fallback assertion). Validated locally: kiro-sonnet-5 + catalog + pricing/modelSpecs/fallback suites all green. Thanks @ggiak! Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com> * feat(relay): gate bifrost auto routing by provider manifest (#5870) Integrated into release/v3.8.44 — gates Bifrost auto-routing by the provider plugin manifest (only manifest-eligible providers reach the sidecar; ineligible/unknown fall back to the TS path with explicit reasons). Superset of #5869 (carries the full manifest + registry + docs). Resolved an integration-test conflict in favor of the release (which already subsumes this PR's readiness/removeDirWithRetry improvements). Validated locally: 4 provider-plugin-manifest + 11 relay-routing-backend tests green. Thanks @KooshaPari! Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com> * refactor(translator): extract pure message helpers from openai-to-kiro (852→751) (#5947) * refactor(translator): extract pure message helpers from openai-to-kiro Extract the pure tool/message helpers (parseToolInput, normalizeKiroToolSchema, serializeToolResultContent) verbatim into the leaf openai-to-kiro/messageHelpers.ts. The host imports them back for convertMessages. They were module-private, so the public export set is unchanged (no re-export needed). Host 852 -> 751 LOC. Byte-identical bodies (multiset 99/99), leaf has zero imports (no cycle). Adds a split-guard; consumer tests stay green (translator-openai-to-kiro 33, translator-ai-sdk-image-parts 3). * chore: re-trigger CI (stuck runner on 2/2 shard) * refactor(executors): extract pure prompt + composer helpers from cursor (#5960) Extract two pure clusters from the cursor executor into sibling leaves: - cursor/prompt.ts: isRecordLike + toolChoiceDirectiveLine + buildCursorOutputConstraints - cursor/composer.ts: composer thinking-as-content decoding (isComposerModel, visibleComposerContentFromThinking, composerReasoningRemainder + markers) Host imports both back for internal use and re-exports the 3 composer helpers for external importers (tests). Host 1576 -> 1451 LOC. Byte-identical bodies (verbatim multiset prompt 65/65, composer 32/32), leaves have zero imports (no cycle). Adds a split-guard; consumer tests stay green (cursor-composer-thinking, cursor-streaming, cursor-agent-tool-calls, translator-openai-to-cursor, cursor-agent-system-prompt). * refactor(executors): extract pure SSE-collect parsing from antigravity (#5962) Extract the pure SSE-payload -> collected-stream parser (AntigravityCollectedStream, stripZeroWidth, parseAntigravityTextualToolCall, addAntigravityTextualToolCall, processAntigravitySSEPayload/Text, flushAntigravitySSEText) verbatim into the leaf antigravity/sseCollect.ts. Host imports the helpers it uses and re-exports processAntigravitySSEPayload for external importers (tests). Host 1812 -> 1671 LOC. Byte-identical bodies (verbatim multiset 135/135), leaf does not import the host (no cycle). Credit/quota state, auth, and HTTP dispatch untouched. Adds a split-guard; consumer tests stay green (executor-agy 8, executor-antigravity 26, antigravity-sse-collect-socket-release, copilot-agent-antigravity-parity 6). * refactor(executors): extract pure model maps + resolvers from chatgpt-web (#5967) Extract the static model maps (MODEL_MAP, MODEL_FORCED_EFFORT, THINKING_CAPABLE_SLUGS) and the pure thinking-effort resolvers (isThinkingCapableModel, normalizeThinkingEffort, resolveThinkingEffort, ResolvedChatGptModel, resolveChatGptModel) verbatim into the pure leaf chatgpt-web/models.ts. Host imports the two resolvers it uses back. Host 3205 -> 3076 LOC. Byte-identical bodies (verbatim multiset 120/120), leaf has zero imports (no cycle). Auth/PoW/session/HTTP dispatch and all module caches untouched. Adds a split-guard; consumer tests stay green (chatgpt-web 86, chatgpt-web-tools-5240 4, chatgpt-web-sha3-boringssl-5531 5). * refactor(executors): decompose grok-web into pure tool/markup leaves (#5994) Extract the pure OpenAI<->Grok tool-translation, native-tool mapping, markup cleanup, and NDJSON stream types out of the 1872-line grok-web executor into 4 sibling leaves: - grok-web/types.ts: GrokStreamResponse/GrokStreamEvent (stream types) - grok-web/tool-bridge.ts: OpenAI<->Grok tool translation + registry + classifiers - grok-web/native-tools.ts: native-tool selection/scoring + native->OpenAI mapping - grok-web/text-cleanup.ts: Grok markup stripping + GrokMarkupFilter Layered, acyclic: types <- tool-bridge <- native-tools; text-cleanup <- types; host imports the leaves. All symbols module-private (no host re-export). Host 1872 -> 887 LOC. Byte-identical bodies (verbatim per-leaf), no cycle, all new leaves <= 800 cap (tool-bridge split at line 753 to stay under). Auth/cookie/TLS/HTTP dispatch untouched. Adds a split-guard; consumer tests stay green (grok-web 62, grok-cli-oauth 15, grok-cli-strip-params 2). * refactor(executors): extract pure quota parsing from codex (#5999) Extract the pure Codex quota-snapshot parsing + reset/cooldown scheduling (CodexQuotaSnapshot, parseCodexQuotaHeaders, getCodexResetTime, getCodexDualWindowCooldownMs) verbatim into the leaf codex/quota.ts. Host re-exports the 4 symbols so handlers/chatCore/codexQuota.ts + tests keep resolving. Host 1539 -> 1427 LOC. Byte-identical bodies (verbatim 98/98), leaf has zero imports (only Date, no cycle). WS transport, auth, HTTP dispatch untouched. Adds a split-guard; consumer tests stay green (executor-codex 40, codex-quota-fetcher 7, chatcore-codex-quota 5). * refactor(executors): extract pure stream formatters from deepseek-web (#6000) Extract the pure content/citation formatters (isThinkingModel, isSearchModel, cleanDeepSeekToken, formatStreamContent, DeepSeekSearchResult, appendSearchCitations) verbatim into the leaf deepseek-web/stream-format.ts. Host imports the 5 it uses back into transformSSE/collectSSEContent (cleanDeepSeekToken stays internal to the leaf). Host 1147 -> 1108 LOC. Byte-identical bodies (verbatim 34/34), leaf has zero imports (no cycle), all module-private (no re-export). PoW/auth/token-cache/HTTP dispatch untouched. Adds a split-guard; consumer tests stay green (deepseek-web 35, deepseek-web-rolling-window-2942 5, deepseek-web-tools-execute 3). * refactor(api): add validatedJsonBody helper (salvage #5075) (#5931) Fuses JSON body parsing + Zod validation into a single call that returns either type-narrowed data or a ready-to-return 400 NextResponse with the standard error envelope. Salvaged as the Tier 1 portable helper from the closed refactor PR #5075; the bulk route migration is intentionally not ported. Adds a focused 6-case regression test. Co-authored-by: KooshaPari <KooshaPari@users.noreply.github.com> * feat(qoder): drive PAT auth via qodercli, add dashboard quota, fix connection display (#5816) Integrated into release/v3.8.44 — Qoder PAT auth via qodercli binary + dashboard quota + dual-auth connection fix. Thanks @AgentKiller45 (co-author @judy459)! Validated locally (release-green on its own merits): lint 0, typecheck:core 0, 104 qoder/usage/UI tests green, file-size gate OK (owner-approved qoderCli.ts baseline-freeze 666→989), env-doc-sync fixed (documented QODER_CLI_CONFIG_DIR). The 2 remaining CI reds are INHERITED base-reds, not caused by this PR: (1) LEDGER-4 minimax-m3 supportsVision (minimax-m3 base + cline-pass/minimax-m3 from the already-merged #5942); (2) mutation-test-coverage missing 3 tests in stryker.conf (#5903/#5942/#5923). Both cleaned up separately. Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com> * fix(providers): minimax-m3 supportsVision (LEDGER-4) + stryker tap.testFiles drift (#6012) Release-green cleanup — clears LEDGER-4 minimax-m3 supportsVision + stryker tap.testFiles drift base-reds. Validated locally. * fix(registry): flag cline-pass/minimax-m3 as multimodal (supportsVision) (#6003) The cline-pass provider's minimax-m3 entry was missing supportsVision, breaking the LEDGER-4 registry-consistency test (all minimax-m3 entries must set supportsVision to match lite.ts — minimax-m3 is multimodal). Every other minimax-m3 registry entry (trae, bazaarlink, cline, ollama-cloud, ...) already sets it. This was a base-red on release/v3.8.44 inherited by every open PR. Validated by the existing failing-then-passing guard tests/unit/review-reviews-v3814-fixes.test.ts (LEDGER-4). * refactor(executors): extract pure payload construction from claude-web (#6006) Extract the pure Claude-web payload types + transforms + default tools/style (ClaudeWebRequestPayload, ClaudeWebStreamChunk, DEFAULT_CLAUDE_MODEL, generateMessageUUIDs, getDefaultTools, getDefaultPersonalizedStyle, transformToClaude, transformFromClaude) verbatim into the leaf claude-web/payload.ts. Host imports the 3 it uses back (ClaudeWebRequestPayload type + the two transforms). Host 1056 -> 835 LOC. Byte-identical bodies (verbatim 149/149), leaf imports only randomUUID (no host import, no cycle), all module-private (no re-export). Cookie/auth/ Turnstile/TLS/HTTP dispatch untouched. Adds a split-guard; consumer tests stay green (claude-web 13, claude-web-auto-refresh 6). * refactor(executors): extract pure upstream-header helpers from base (#6008) Extract the pure upstream-header helpers (mergeUpstreamExtraHeaders, getCustomUserAgent, setUserAgentHeader, applyConfiguredUserAgent, isOpenAICompatibleEndpoint, stripStainlessHeadersForOpenAICompat) verbatim into the leaf base/headers.ts. base.ts is imported by ~18 executors, so the host re-exports all 6 to keep those import paths intact; it also imports the 4 it uses internally in the BaseExecutor class. The trivial JsonRecord type alias is redefined locally in the leaf to avoid a base<->leaf cycle. Host 1539 -> 1451 LOC. Byte-identical bodies (verbatim 78/78), leaf does not import the host (no cycle). typecheck:core validates all base importers still resolve via the re-export. Adds a split-guard; consumer tests stay green (executor-base-utils 22, executor-default-base 49, executor-strip-stainless-openai-compat 6, plus executor sanity via typecheck). * refactor(executors): extract pure wire protocol from perplexity-web (#6014) Extract the pure Perplexity wire protocol (consts, SSE stream types, SSE parsing, OpenAI<->Perplexity message translation, request/query builders, content extraction, sseChunk) verbatim into the leaf perplexity-web/protocol.ts. Host imports back the 10 symbols it uses; everything module-private (no re-export). Session cache, TLS fetch, auth, and the executor class stay in the host. Host 1028 -> 534 LOC. Byte-identical bodies (verbatim), leaf imports only randomUUID (no host import, no cycle). Adds a split-guard; consumer tests stay green (perplexity-web 26, streaming-tools-5927 2, tls-client 6, key-validation-models 2). * refactor(executors): extract pure URL normalizers from default (#6015) Extract the pure per-provider chat-URL normalizers (normalizeBailianMessagesUrl, normalizeDataRobotChatUrl, normalizeAzureAiChatUrl, normalizeWatsonxChatUrl, normalizeOciChatUrl, normalizeSapChatUrl, normalizeXiaomiMimoChatUrl, normalizeOpenAIChatUrl, getOpenRouterConnectionPreset) verbatim into the leaf default/urlNormalizers.ts. Host imports them back into buildUrl/transformRequest; the now-dead build*ChatUrl/normalizeBaseUrl imports move to the leaf. All module-private (no re-export). Host 864 -> 815 LOC (shrunk below its frozen baseline). Byte-identical bodies (verbatim 45/45), leaf does not import the host (no cycle). buildHeaders/execute/auth untouched. Adds a split-guard; consumer tests stay green (executor-default-base 49, anthropic-compatible-bearer 3, strip-client-metadata 3). * feat(webfetch): support self-hosted FireCrawl instances (#5793) Integrated into release/v3.8.44 — self-hosted FireCrawl support (FIRECRAWL_BASE_URL/FIRECRAWL_TIMEOUT_MS). Re-cut clean onto the release tip (branch was fossilized from a pre-v3.8.40 snapshot). Validated: 4 firecrawl tests green, env-doc-sync + docs-sync pass. UNSTABLE red is the inherited environmental setup-claude base-red. * feat(xai): register XaiExecutor with reasoning-effort suffix parsing (#5800) Integrated into release/v3.8.44 — XaiExecutor with reasoning-effort suffix parsing. Re-cut clean onto the release tip (branch was fossilized). Validated: 6 xai-executor tests green, provider-consistency OK, typecheck:core 0 errors, env-doc-sync in sync. UNSTABLE red is the inherited environmental setup-claude base-red. * feat(discovery): Phase 2 — reporter, /api/discovery/* routes (strict loopback-only) + dashboard UI (#5939) * feat(discovery): Phase 2 reporter — discoveryResults DB module + service wiring Adds src/lib/db/discoveryResults.ts (CRUD over the discovery_results table from migration 074) and wires the opt-in discovery service to persist and read findings through it: persistDiscoveryResult / getDiscoveryResults / getDiscoveryResultById / markVerified / deleteDiscoveryResult, with (provider, method, endpoint) upsert de-duplication. Re-exported from localDb. The service stays opt-in / default-off. The /api/discovery/* routes and the dashboard UI tab are intentionally deferred to Phase 2b — they need the local-only enforcement model (Hard Rules #15/#17 territory) decided first. TDD: tests/unit/db/discovery-results.test.ts (8 cases, DB + service delegation), isolated DATA_DIR with resetDbInstance cleanup. * feat(discovery): Phase 2b — /api/discovery/* routes (strict loopback-only) Adds the discovery HTTP surface on top of the reporter DB module: GET /api/discovery/results list findings (optional ?providerId) GET /api/discovery/results/:id one finding (404 if absent) DELETE /api/discovery/results/:id delete a finding POST /api/discovery/scan scan a provider + persist findings POST /api/discovery/verify/:id mark a finding verified Authorization: strict loopback-only. "/api/discovery/" is added to LOCAL_ONLY_API_PREFIXES so the central authz pipeline (proxy.ts → runAuthzPipeline → managementPolicy) rejects non-loopback callers with a 403 LOCAL_ONLY before any handler runs. It is deliberately NOT in LOCAL_ONLY_MANAGE_SCOPE_BYPASS_PREFIXES — no remote manage-scope bypass — because POST /scan issues outbound probes to provider endpoints (SSRF-adjacent) and must never be tunnel-reachable. Handlers also call requireManagementAuth (defense in depth) and return sanitized errors via createErrorResponse. Tests: - tests/unit/authz/discovery-routes-local-only.test.ts (8) — security guard: isLocalOnlyPath true + not manage-scope-bypassable for all four paths. - tests/unit/api/discovery-routes.test.ts (6) — handler integration over an isolated DATA_DIR: list/filter, by-id 200/404/400, scan persist + 400 on empty/malformed body, verify 200/404, delete 200/404, no stack-trace leak. * feat(discovery): Phase 2c — dashboard UI tab (Tools → Discovery) Adds the /dashboard/discovery page (DiscoveryPageClient) that consumes the Phase 2b /api/discovery/* routes: scan a provider, list findings, verify or delete them. Registered in the sidebar under the Tools group (icon travel_explore) and given a "discovery" i18n namespace + sidebar keys in en.json (other locales fall back to en via next-intl until synced — the locale files are in a pre-existing coverage deficit unrelated to this change). Registers the UI test path in vitest.config.ts (advisory ui suite). Tests: src/app/(dashboard)/dashboard/discovery/__tests__/DiscoveryPageClient.test.tsx (3 cases: loads+renders results, empty state, fetches /api/discovery/results on mount; stable useTranslations mock to avoid the fetch-loop). NOTE: the ui vitest suite cannot run in this workspace — @testing-library/dom (a @testing-library/ react peer dep) is absent from node_modules, which fails ALL existing ui tests equally; the test runs in CI. Component verified locally via typecheck + lint. * test(discovery): register discovery-routes-local-only in stryker tap.testFiles The mutation-test-coverage gate (--strict) flags any unit test covering a mutated module that isn't listed in stryker.conf.json tap.testFiles. This PR's tests/unit/authz/discovery-routes-local-only.test.ts covers src/server/authz/ routeGuard.ts (a mutated module, which this PR edits by adding the /api/discovery/ local-only prefix), so it must be registered for its mutant kills to count. No behavior change. * refactor(discovery): split DiscoveryPageClient to satisfy max-lines-per-function The complexity ratchet (max-lines-per-function: 80) flagged the single 184-line DiscoveryPageClient function (+1 over baseline). Extract the data layer into two hooks (useDiscoveryResults for list/loading/feedback, useDiscoveryActions for scan/verify/delete), a shared callApi helper, and two presentational sub-components (DiscoveryScanForm, DiscoveryResultCard). Every function is now under the 80-line ceiling; complexity gate back to baseline 1995. No behavior change — same exported component, same endpoints, same props. * test(sidebar): include discovery in omni-proxy item-order snapshot Adding the Discovery item to the Tools group (this PR's sidebar entry) extends the ordered omni-proxy section list. Update the exact-match deepEqual snapshot in sidebar-visibility.test.ts to include "discovery" in its position (after traffic-inspector). The assertion stays exact — this reflects the intentional new item, it does not weaken the check. * docs(changelog): restore release bullets eaten by merge auto-resolve; re-add discovery bullet additively * chore(quality): bump testFrozen for translator-openai-responses-req.test.ts (1097 -> 1172) Base-red inherited from #5933, which grew the test file to 1171 lines (Hard Rule #18 regression tests) without adjusting the frozen cap. The release tip itself fails check:file-size; this unblocks every PR into release/v3.8.44. File untouched by this PR. * chore(quality): restore stryker tap.testFiles entries eaten by merge auto-resolve The merge of origin/release/v3.8.44 silently dropped the 3 entries added on the release side (#5903, clinepass, #5923). Took the release version verbatim and re-added only this PR's entry (discovery-routes-local-only) in alphabetical order. check:mutation-test-coverage green locally. * chore(quality): reconcile inherited v3.8.44 merge-burst drift + include discovery in tools-group order test - complexity 1995->2003 and cognitive 856->859: both measure IDENTICAL on the pristine release tip ( |
||
|
|
ee24eb52d4 |
Release v3.8.33 (#4515)
Release v3.8.33 — full CHANGELOG in the PR body. Blocking gates green (Build, Lint, Unit Tests 8/8, Package Artifact, Quality Gates, Quality Ratchet, Docs Sync, PR Test Policy, test-vitest). Admin-merged over a Node 26 future-compat timer flake (1/4) + an E2E UI flake (3/9) — both verified non-deterministic; full test:unit validated locally (16936 pass). |
||
|
|
a25d5f1ef6 |
Release v3.8.13 (#3327)
* chore(release): open v3.8.13 development cycle Bump 3.8.12 → 3.8.13 across package.json, lockfile, electron/, open-sse/, and docs/reference/openapi.yaml; add the [3.8.13] cycle placeholder to the root CHANGELOG and the 41 i18n mirrors. Integration branch for the v3.8.13 cycle — fixes/features land here via per-issue PRs and it merges to main at release time. * fix(ci): skip auto-deploy when VPS host is unreachable from the runner (#3299) Integrated into release/v3.8.13 * fix(dev): auto-rebuild better-sqlite3 on Node ABI mismatch at dev startup (#3301) Integrated into release/v3.8.13 * feat(api): accept path-scoped API keys on client API routes (#3300) Integrated into release/v3.8.13 * fix(sse): harden against empty responses causing Copilot Chat failures (#3297) Integrated into release/v3.8.13 * fix(api): remove Completions.me rickroll provider (discussion #3293) (#3302) Integrated into release/v3.8.13 * fix(opencode-provider): extract contextLength from live model catalog (#3298) Integrated into release/v3.8.13 * feat(web-cookie): self-service login infrastructure + auto-refresh daemon (#3292) Integrated into release/v3.8.13 * docs(changelog): record the v3.8.13 PRs merged this round (#3292/#3300/#3297/#3298/#3301/#3302/#3299) * fix(auth): harden URL token extraction — drop query-string fallback, gate to client routes (security follow-up to #3300) (#3309) Security follow-up to #3300 — integrated into release/v3.8.13 * docs: rename resolve-issues → review-issues skill references * fix(dashboard): keep no-auth providers visible under 'Show configured only' (#3290) (#3312) no-auth providers (opencode, duckduckgo-web, theoldllm, veoaifree-web) never create a DB connection row so stats.total stays 0, which the configured-only filter treated as 'unconfigured' and hid them — even though they are always usable and appear unconditionally in /v1/models. filterConfiguredProviderEntries now treats displayAuthType === 'no-auth' as configured. Co-authored-by: uniQta <uniQta@users.noreply.github.com> * fix(cli): resolve update paths relative to script + recursive backup (#3295) (#3313) omniroute update always failed on a global install: - getCurrentVersion() read package.json from process.cwd(), which on a global npm/brew install is the user's working dir, not the package root → null → 'Could not determine current version'. - createBackup() resolved bin/ from cwd too, and passed the 'cli' directory to copyFileSync → EISDIR, swallowed by the catch → 'Failed to create backup'. Both now resolve package.json/bin relative to the script via import.meta.url, and the backup uses cpSync({recursive:true}) so the cli/ directory is copied. Co-authored-by: uniQta <uniQta@users.noreply.github.com> * fix(theoldllm): read upstream body once to avoid [502] body-already-read (#3296) (#3314) On the cached-token path the executor never enters the refresh branch, so the same upstream Response was read with .text() twice (token-rejection check + final body). A Response body is single-use, so the second read threw 'Body is unusable: Body has already been read', caught and surfaced as [502]. Read the body once into finalBody and only re-read after a token-rejection refetch. Co-authored-by: onizukashonan14-png <onizukashonan14-png@users.noreply.github.com> * fix(sse): strip leaked internal tool envelopes from streaming output (#3311) Integrated into release/v3.8.13 * fix(sse): expose Claude + Gemini budget tiers in the antigravity catalog (#3184) (#3303) Integrated into release/v3.8.13 (#3184) * fix(catalog): compute combo context_length from known targets only (#3304) Integrated into release/v3.8.13 — live contextLength + known-targets combo context (#3298 follow-up) * chore(i18n): add message keys for proxy UI + vscode/ollama endpoint (#3307) Integrated into release/v3.8.13 — i18n message keys for proxy UI + vscode/ollama * feat(dashboard): i18n the proxy settings UI (#3310) Integrated into release/v3.8.13 — i18n the proxy settings UI * feat(api): model catalog enrichment + MCP model-catalog tools (#3306) Integrated into release/v3.8.13 — model catalog enrichment + MCP model-catalog tools, reconciled with #3309 URL-token hardening * test(catalog): align Antigravity preview-alias test with #3303 budget tiers #3303 added the Gemini `-high`/`-low` budget tiers to ANTIGRAVITY_PUBLIC_MODELS (user-callable on the Antigravity OAuth backend, verified via #3184), but did not update the catalog-route test that asserted `antigravity/gemini-3.1-pro-high` must NOT be exposed. The assertion now reflects the intended behavior — the client-visible budget alias IS surfaced — while keeping the legacy `gemini-claude-*` alias keys unexposed. Caught running the full catalog suite on the merged release HEAD (the #3303 round only ran the antigravity-aliases and usage-hardening files). * docs(changelog): record the 6 PRs merged this review round into v3.8.13 #3306/#3307/#3310 (New Features — VS Code split: catalog+MCP, i18n keys, proxy UI i18n), #3311/#3303/#3304 (Bug Fixes — SSE envelope sanitizer, antigravity budget tiers, combo known-targets context_length). * chore(release): finalize v3.8.13 changelog and cleanup Finalize the v3.8.13 changelog with release date, maintenance notes, and contributor credits. Update MCP docs to reference the correct tool inventory diagram, exclude nested .claude worktrees from ESLint scans, and tighten a response sanitizer type guard. * fix(dashboard): refresh connections after provider auth import (#3320) Integrated into release/v3.8.13 — refresh connections after provider auth import * fix(codex): strip client-only params on native /responses passthrough (#3317) (#3325) A /v1/responses request against the built-in codex/ provider does an openai-responses -> openai-responses passthrough (CodexExecutor.transformRequest returns the body early for _nativeCodexPassthrough). It forwarded client-only fields verbatim and the Codex upstream rejected them with 400 Unsupported parameter: prompt_cache_retention / safety_identifier / user — breaking Factory Droid (which injects all three). The chat-completions path already strips these (base.ts #1884, openai-responses translator #2770) but the passthrough skips translation. Strip the three fields in the shared block before the passthrough return; user is removed unconditionally since Codex /responses always rejects it. Co-authored-by: tycronk20 <tycronk20@users.noreply.github.com> * fix(dashboard): normalize agent-bridge /state response to stop page crash (#3318) (#3326) The Agent Bridge page seeded a well-shaped initialData default then replaced it wholesale with the raw /api/tools/agent-bridge/state response. The route returns { server, agents } but the UI reads { serverState, agentStates, bypassPatterns, mappings }, so serverState became undefined and AgentBridgeServerCard crashed on serverState.running — surfaced as the full-page 'Internal Server Error' boundary (client render error, not a real 5xx). Add a shared normalizeAgentBridgeState() that maps the route shape into the page contract (server.running/certExists -> serverState) and always returns safe defaults (never undefined serverState). Wired into both the SSR loader (page.tsx) and the polling hook. The legacy 'agents' entry shape differs from AgentStateEntry so it is not coerced; full route<->page contract reconciliation (port, upstreamCa, bypassPatterns, mappings, agentStates) is a follow-up. Co-authored-by: tycronk20 <tycronk20@users.noreply.github.com> * docs: VS Code/Ollama endpoints + env & i18n tooling (#3319) Integrated into release/v3.8.13 — VS Code/Ollama docs + env & i18n tooling * feat(provider): test-all endpoint, rate-limit overrides, visibility f… (#3267) Integrated into release/v3.8.13 — provider test-all endpoint, rate-limit overrides, model visibility * feat: auto-combo optimization, playground model dropdown, only-configured toggle (#3322) Integrated into release/v3.8.13 — auto-combo candidate expansion + playground dropdown + only-configured toggle * feat(api): VS Code Copilot Ollama-compatible BYOK endpoint (#3316) Integrated into release/v3.8.13 — VS Code Copilot Ollama-compatible BYOK endpoint (reconciled with #3306/#3309 auth hardening) * chore(release): document #3320 in the v3.8.13 changelog + contributor credits --------- Co-authored-by: Felipe Almeman <4226997+zhiru@users.noreply.github.com> Co-authored-by: Wilson <pedbookmed@gmail.com> Co-authored-by: Hernan Javier Ardila Sanchez <hjasgr@gmail.com> Co-authored-by: Paijo <14921983+oyi77@users.noreply.github.com> Co-authored-by: uniQta <uniQta@users.noreply.github.com> Co-authored-by: onizukashonan14-png <onizukashonan14-png@users.noreply.github.com> Co-authored-by: tycronk20 <tycronk20@users.noreply.github.com> Co-authored-by: Vinayrnani <vinayrnani@gmail.com> |
||
|
|
4ae488b25b |
feat(runtime): add hot-reloadable guardrails and model diagnostics
Introduce a runtime settings layer that hydrates persisted config at startup and reapplies aliases, payload rules, cache behavior, CLI compatibility, usage tuning, and related switches when settings change or SQLite updates. Replace the legacy prompt injection middleware path with a guardrail registry that supports prompt injection detection, PII masking, disabled guardrail overrides, and post-call response handling across the chat pipeline. Add a metadata registry for model catalog and alias resolution so catalog endpoints return enriched capabilities plus diagnostic headers and typed alias errors instead of ad hoc responses. Convert unsupported built-in web_search tools into an OmniRoute fallback tool, execute them through builtin skills, and preserve Responses API function call output with sanitized usage fields. Centralize provider header fingerprints for GitHub, Cursor, Qwen, Qoder, Kiro, and Antigravity, and migrate management passwords from env or plaintext storage into persisted bcrypt hashes during startup and login. |