Commit Graph

8130 Commits

Author SHA1 Message Date
WebPerson
24b784e9bb [Performance] Enable React Compiler for automatic memoization (#11783)
* feat(ui): enable React Compiler (#67)

Enable reactCompiler: true in next.config.mjs (Next 16 + React 19.2.8).
This automates memoization at build time, removing manual useCallback/useMemo
debt (591 + 283 instances respectively) and preventing stale-closure bugs.

Test results (pre-existing failures unchanged):
  vitest UI: 282/295 files pass (13 fail = missing router/ReactFlow mocks)
  vitest:    1805/1857 tests pass (52 fail = same pre-existing mock issues)
  node:test: api/services/db all pass (except platform-specific
             serviceSupervisorSpawnError — Windows spawn("ls") issue)

No new failures introduced by the compiler transform.
Optional cleanup: remove now-redundant useCallback/useMemo in hot components.

* fix(build): add babel-plugin-react-compiler peer dependency (#67)

React Compiler (reactCompiler: true in next.config.mjs) requires
babel-plugin-react-compiler as an explicit peer dependency — Next.js
declares it as optional ("*") and does not auto-install it.

Installed babel-plugin-react-compiler@1.0.0 as a devDependency.
Resolves correctly from both the project root and the next package
context (Turbopack resolution path).

* fix(ci): allowlist babel-plugin-react-compiler for React Compiler

The React Compiler peer is a real npm package (facebook/react, MIT) required
by Next 16 `reactCompiler: true`. Adding it to the anti-slopsquat allowlist
unblocks check:deps and the 6A.8 unit-test gate.

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

* fix(ci): drop unused collectSSE helper that trips ESLint

The helper was leftover from #12151 and fails the absolute
lint:json --max-warnings 0 gate on every PR that includes it.

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

---------

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
Co-authored-by: WebPerson <jonlwheat2-gif@users.noreply.github.com>
2026-09-02 00:10:59 -03:00
ragnar-claude
96824288f5 feat(compression): make proactive context-compression threshold a live setting (#11564)
* feat(compression): make proactive context-compression threshold a live setting

The proactive compression trigger ratio was a hardcoded COMPRESSION_THRESHOLD =
0.7 in chatCore. Operators could not move compression relative to a client's own
compaction point (e.g. Codex Desktop self-compacts at ~0.85 of its window, so
the 0.7 proxy threshold always preempts the client's compaction with the
proxy's lossier one — see #8932 for what that produced before 3.8.50).

New: key_value namespace 'compression', key 'proactiveConfig',
{"thresholdRatio": 0.7}. Clamped [0.1, 0.99], 30s TTL cache, ipFilter
persistence pattern (#6131), synchronous read stays in the hot path. Default
unchanged; missing/invalid rows fall back to 0.7.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* test(compression): cover the live proactive-compression threshold (read, validity bounds, fallback, TTL)

Locks in getProactiveCompressionRatio() (src/lib/db/compression.ts), the
key_value-backed replacement for chatCore's hardcoded 0.7:

  - shipped default 0.7 when no compression/proactiveConfig row exists
  - 30s TTL cache: a fresh DB write stays invisible until the TTL lapses
    (clock mocked via node:test mock timers, Date API — the module keeps
    its cache private with no reset hook)
  - valid override read from key_value, boundary values 0.1/0.99 included
  - out-of-range ratios fall back to the DEFAULT (a validity window, not
    clamping to the nearest bound — matching the shipped comment)
  - broken JSON / non-numeric thresholdRatio: 0.7, without throwing

Guard verified by mutation: switching the window to clamping fails the
out-of-range case.

---------

Co-authored-by: root-cli (Hermes ops) <info@livewellwith.us>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com>
2026-09-02 00:02:45 -03:00
Deftera
678e6077e4 fix(kiro): do not permanently ban on 'User is not authorized to make this call' (#11809)
* fix(kiro): do not permanently ban on 'User is not authorized to make this call'

* test(kiro): regression cover the 403 'User is not authorized' non-ban classification

---------

Co-authored-by: Deftera186 <Deftera186@users.noreply.github.com>
Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com>
2026-09-02 00:02:31 -03:00
Mr White
5a0a131bc7 feat(usage): devin-cli agentic quota + openrouter credits in Provider Limits (#12256)
* feat(usage): devin-cli agentic quota + openrouter credits in Provider Limits

Two provider families with live quota APIs were missing from the Provider
Limits dashboard because their list entries were absent:

- devin-cli: new usage leaf querying the Codeium seat-management Connect API
  (exa.seat_management_pb.SeatManagementService/GetUserStatus, protobuf over
  POST with the raw `Basic <token>-<token>` auth header the CLI itself uses).
  Surfaces the plan name plus daily/weekly agentic quota percentages with
  reset timestamps from the GetUserStatus plan_status payload, via a minimal
  hand-rolled protobuf encoder/reader (no proto dependency warranted for two
  fixed messages).
- openrouter: the /key + /credits quota fetcher (#6842) was already wired
  into the dispatcher but gated out of the bulk sync — add it to
  USAGE_SUPPORTED_PROVIDERS and PROVIDER_LIMITS_APIKEY_PROVIDERS so key
  limits and account credits actually surface.

* fix(build): externalize tiktoken so tiktoken_bg.wasm resolves at runtime

The vendored ChatGPT Web connector v4.0.7 (#12181) imports tiktoken
(get_encoding) at module level. tiktoken's node build reads
tiktoken_bg.wasm via a __dirname-relative fs.readFileSync during import;
when Next bundles the package the wasm asset is not traced into the server
chunk, and page-data collection for every route reaching the tokenizer
(e.g. /api/providers/[id]/chatgpt-web-codex-doctor) aborts with
"Missing tiktoken_bg.wasm" — breaking the whole standalone build.

Externalize it like the other runtime-resolved native/wasm packages
(sql.js, sqlite-vec, better-sqlite3): the require stays at runtime, where
node_modules/tiktoken/tiktoken_bg.wasm resolves normally.

* fix(openrouter): /credits balance survives a /key failure

OpenRouter is credit-based, not subscription-based: the authoritative
remaining-credits signal is GET /api/v1/credits (total_credits -
total_usage, the documented "get remaining credits" endpoint), while the
/key limit fields are optional per-key caps that most accounts never set.

fetchOpenrouterQuota previously treated /key as mandatory — any /key
failure (429 rate limit, transient error, unexpected shape) discarded the
whole payload and the Usage dashboard showed "OpenRouter (usage endpoint
unreachable)" even though /credits was reachable. Now:

- /key unavailable + /credits OK → credits-only quota (creditBalance =
  total_credits - total_usage) instead of null
- /key 401/403 alone no longer means an invalid token; only a double
  auth-rejection (both endpoints) does
- null is returned only when both endpoints fail, and the dashboard label
  reflects that ("credits endpoint unreachable")

* fix(openrouter): render AI Credits as a USD credit count in Provider Limits

The Provider Limits card's dollar renderer only activates on
isCredits/creditCount rows (QuotaCardExpanded), but openrouter went through
parseGeneric — which drops `currency` and never sets those flags — so the
credits balance rendered as a meaningless "100% left" (the unlimited-credits
row is always 100%) instead of the actual credit count.

Route openrouter's `credits` quota through buildCreditsQuota() like the
DeepSeek/AgentRouter credits rows: label "AI Credits", dollar-formatted
balance. Free-tier request windows keep the generic percentage treatment.

* fix(usage): document DEVIN_SEAT_API_URL and split quota parsers

Keep fetchOpenrouterQuota and decodeProtoFields under the complexity
ratchets, and add the seat-management URL to the env/docs contract.

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

* test(usage): drop duplicated GLM quota-ordering test in provider-limits-ui

* test(usage): drop stale openrouter ACCEPTED_DIVERGENCE

OpenRouter is now in both USAGE_FETCHER_PROVIDERS and
USAGE_SUPPORTED_PROVIDERS, so the recorded aggregator divergence
is no longer real. Add the changelog fragment.

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

---------

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com>
2026-09-02 00:02:15 -03:00
Andrew B.
c702a27eda perf(compression): OOM mitigations for large payload hashing, memoization, and token estimation (#7847) (#11844)
* perf(compression): memory and OOM mitigations for large payload hashing and token estimation

* fix(compression): implement getMemoStats observability for result memo (#7847)

Adds the missing memo observability layer referenced by
tests/unit/compression/oom-memo-memory.test.ts and the monitoring API:

- resultMemo.ts: lifetime hit/miss counters + bounded time-ordered ring
  buffer (10k entries, ~90KB) powering 1m/5m/15m/1h hit-rate windows;
  getMemoStats() reports size/capacity/hits/misses/hitRate + windows.
- memoLookup() tags served results with stats.memoHit = true.
- clearMemoStore() also resets counters and the ring.
- compression/index.ts re-exports getMemoStats for the monitoring route.
- types.ts: optional memoHit field on CompressionStats.
- New GET /api/monitoring/compression route exposing the stats snapshot
  (lightweight, no DB) for operators to track cache-hit efficiency.

* fix(compression): align memo contract with upstream #11727 — return caller object, reset lookup counter in clearMemoStore

* fix(compression): restore unwrapEventEnvelope in stream payload collector summaries

The OOM-mitigation commit accidentally replaced unwrapEventEnvelope(evt.data)
with asRecord(evt.data) in the summary builders and live push, breaking
translate-mode {event, data} envelope unwrapping (clientPayload type detection)
and failing 2 stream-payload-collector tests. Restored upstream semantics;
kept the jsonLength OOM optimization as the only delta in this file.

* refactor(compression): break down writeValue and writeEncodedString to pass complexity ratchets

Refactors jsonSha256 internal helpers (writeValue, writeEncodedString)
into small, single-responsibility sub-functions under the complexity
threshold (max cyclomatic 15, max cognitive 15). Preserves exact
JSON.stringify parity, circular reference guards on both arrays and
plain objects, and escape behavior (all 530 relevant tests pass).

* test(compression): make oom-memo heap assertion robust without expose-gc

The CI unit-test shard runner does not pass --expose-gc, so global.gc is
undefined and heapUsed can still momentarily hold GC-pending transients
(observed 53.4 MiB after a 3MiB body). Gate the retained-heap assertion
on forced collection being available (3 forced cycles for array buffers)
instead of skipping it silently, and keep it fully active when
--expose-gc is present.

* fix(compression): restore worker-pool offload path in runCompressionAsync

The OOM-mitigation refactor dropped the isCompressionWorkerEligible /
runCompressionInWorker dispatch at the top of runCompressionAsync, silently
removing the base's worker-thread offload for eligible large payloads.
Restore the block exactly as on release/v3.8.51, ahead of the result-memo
path, keeping the memoization and hashing improvements intact.

* docs(api): document GET /api/monitoring/compression and log route errors via pino

Add the new monitoring endpoint to docs/openapi.yaml following the
neighboring System entries, and replace the route's console.error with
the repo-standard pino logger.

* fix(skills): regenerate omni-resilience and add changelog fragment

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

---------

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
Co-authored-by: Andrian Balanescu <AndrianBalanescu@users.noreply.github.com>
Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com>
2026-09-02 00:01:59 -03:00
Jacob Stoner
cf53b9220f feat(combos): add universal handoff feature flag (#12167)
* feat(combos): add universal handoff feature flag

Add a default-enabled runtime flag that lets operators disable universal context handoffs globally without changing existing combo configuration or requiring a restart.

* fix(i18n): seed the universal-handoff flag description key across locales

---------

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
2026-09-02 00:01:42 -03:00
Diego Rodrigues de Sa e Souza
d2a027a156 feat(dashboard): orchestration canvas fase 2 — quick wins + hardening (2.3/2.4/2.8/2.11/2.12, #12270, #12271) (#12393)
* feat(dashboard): orchestration pure snapshot filter (search + chips)

* feat(dashboard): orchestration collapse-by-source in flow projection

* refactor(dashboard): orchestration model polish (#12271 model items)

* feat(dashboard): theme-aware orchestration status tokens (2.12)

* feat(dashboard): particle StatusEdge for orchestration canvas (2.11)

* feat(dashboard): orchestration search/filter chips + collapse in URL (2.3+2.4)

* feat(dashboard): drawer trace copy + error/a11y hardening (2.8, #12270)

* chore(dashboard): orchestration UI hardening (#12270)

* fix(dashboard): type-safe source read in AgentsTab collapse handler

* fix(dashboard): pass staleSince into the sourceStale message

* test(dashboard): orchestration missing coverage + fase2 i18n (#12271)

* refactor(dashboard): split orchestration page/toolbar/drawer helpers under complexity ratchet

---------

Co-authored-by: Markus Hartung <diegosouzapw@users.noreply.github.com>
2026-09-01 23:13:47 -03:00
Diego Rodrigues de Sa e Souza
17a53d2eb9 feat(quality): complete test:scoped — --full map rebuild, stdin selection, CI loader parity (#8084 D1) (#12353)
- test:scoped:full (documented in the script header since #9143 but never wired) rebuilds
  config/quality/test-impact-map.json and then selects.
- select-impacted-tests.mjs gains --stdin so --staged selects from the index; the git-diff
  path only ever saw commits, so staged-only runs silently fell back to the heuristic.
- Loader parity with npm run test:unit / quality.yml TIA step (#6787): tests/unit/dashboard/**
  under --import tsx (CJS transform), tests/unit/serial/** at --test-concurrency=1, the rest
  under tsx/esm. The single tsx/esm invocation false-redded every dashboard test the map
  selected ("Unexpected token 'export'").
- CONTRIBUTING.md → Running Tests documents the three modes and the fail-safe exit 1.

Refs #8084
2026-09-01 23:01:40 -03:00
Diego Rodrigues de Sa e Souza
7f25d67d07 feat(radar): explain access rules before opt-in (#12342)
* feat(radar): explain access rules before opt-in

* docs(radar): pin D32 evidence boundary

* chore(changelog): number Radar D32 fragment

* fix(radar): clarify single-use account scope

* fix(radar): use a reliable activation icon

* fix(radar): complete Vietnamese D32 copy

* fix(radar): mark stale D32 locale copy
2026-09-01 20:20:50 -03:00
Diego Rodrigues de Sa e Souza
1586476183 fix(release): drain the 2026-09-01 base-red window — passthrough usage regression + radarPage i18n keys (#12327)
* fix(sse): forward the upstream's real trailing usage in passthrough; estimate only at flush

#12151 injected estimated usage into the finish chunk and dropped the real
trailing usage block that genuine OpenAI upstreams send afterwards — metered
clients got estimates instead of real token counts. The estimate now leaves
via a canonical usage-only chunk at flush, only when the upstream stayed
silent; a real trailing block is forwarded verbatim and wins. The tool_calls
finish_reason normalization now materializes its own rewrite (it piggybacked
on the removed finish-time rewrite), and the dead collectSSE helper goes with
it (subsumes #12324).

* fix(i18n): seed the radarPage limits/training keys the #12320 UI already consumes

RadarCatalogTable.tsx references radarPage.colLimits / trainsOnPrompts /
trainsOnPromptsHelp but #12320 never added them to en.json, so the EN
fallback could not resolve the __MISSING__ markers across 42 locales.
Real translations for pt-BR and vi; the rest resolve via the EN fallback.

* fix(sse): carry the chat stream id into flush-time synthetic chunks

The estimated usage-only chunk emitted at flush used passthroughResponsesId,
which is only ever set on the Responses path — on the chat path the synthetic
chunk shipped id: null, breaking the string-id invariant pinned by
stream-numeric-ids.test.ts. Track the upstream chat-completion id in the
passthrough loop and reuse it (falling back to the Responses id, then a
generated one). Sibling sweep: 74 files importing utils/stream — 603/603.
2026-09-01 16:17:48 -03:00
Dizzle
c9f9b6274e feat(providers): expose usage-supported in provider plugin manifest (#12214)
The provider plugin manifest already exposed usage-fetch (40 providers, #11903); this publishes the second capability, usage-supported, so integrators can tell without reading TypeScript whether the server usage routes accept a provider. #11903 closed #11722 after shipping only half of it and said so at the time — this is the follow-up it promised.

The two scopes genuinely differ and the docs now say how: usage-fetch resolves on id or alias (the dispatcher accepts both), usage-supported on id alone, because the runtime guard does a plain USAGE_SUPPORTED_PROVIDERS.includes(providerId) with no alias resolution. 42 providers carry both tags, 4 carry only usage-fetch (opencode, opencode-zen, openrouter, xai) and 3 only usage-supported (adobe-firefly, firefly, xiaomi-mimo-token-plan) — 7 measured differences, so neither tag implies the other. No list mutation, no new route, schemaVersion stays 1.

USAGE_SUPPORTED_PROVIDERS moved out of src/shared/constants/providers.ts into an import-free leaf at open-sse/services/usage/supportedProviders.ts, keeping the manifest's import graph light — the same move fetcherProviders.ts got in #11903, landed on the correct side of the workspace boundary.

Base note: the branch forked 46 commits before kilocode joined the list, so a wholesale take of its providers.ts would have silently dropped that id. Verified against the current release tip before merging — both sides hold the same 46 ids, nothing lost.

Verified on the current tip: typecheck:core clean, check:cycles OK across 417 files (the import-free-leaf claim holds), and 63/63 focused tests across provider-plugin-manifest, usage-fetcher-registration-coverage, adobe-firefly and agentrouter-quota-dashboard-rendering.

Thanks @maxmad64bis — and for finishing the half of #11722 that was left open rather than letting it sit.
2026-09-01 16:02:46 -03:00
Diego Rodrigues de Sa e Souza
8ef3447950 chore(deps): freeze onnxruntime-node and eslint-plugin-react-hooks out of dependabot groups (#12329)
onnxruntime-node only ever moves paired with @huggingface/transformers (already
frozen, #9962/#4050) — a solo bump breaks the single-copy ABI contract test and
reds every production-group PR. eslint-plugin-react-hooks stays pinned to 7.0.1
by a contract test until the 7.1.1 rule set is adopted in its own PR (the
#12146 migration completed today, so that adoption is now unblocked).
2026-09-01 15:54:04 -03:00
NightStalker-87
a784b42060 fix: resolve compression worker file using runtime anchors instead of… (#12183)
* fix: resolve compression worker file using runtime anchors instead of import.meta.url

Replace workerUrl() function that used import.meta.url with resolveWorkerFile()
function that uses runtime anchors (process.cwd() and process.argv[1]) to locate
the worker file. This fixes webpack module resolution in Next.js standalone
bundles where import.meta.url is replaced with a stub pointing to build machine
path.

Also update Dockerfile to copy required worker-related scripts and adjust
npm install flags for better compatibility.

* fix(docker): restore base Dockerfile — keep npm ci --ignore-scripts supply-chain guard

Revert every Dockerfile change from this branch back to release/v3.8.51:
the branch dropped --ignore-scripts (reopening install-time script
execution for all transitive deps), swapped the reproducible npm ci for
npm install, invoked the nonexistent 'npm approve-scripts' command, and
broke the better-sqlite3 smoke test with a stray space in ':memory: '.
The worker-file fix does not need any Dockerfile change.

* test(compression): export runtime-anchor helpers and cover worker-file resolution

firstAncestorWith's doc already claimed 'exported for tests' without the
export; export it together with resolveWorkerFile and add unit coverage
for the runtime-anchor resolution: cwd anchor, dirname(argv[1]) anchor,
bounded walk-up (8-level cap boundary), prod-first .js-over-.ts ordering,
dev .ts fallback and the fail-open cwd fallback when nothing exists.
Fixtures live in mkdtemp sandboxes only — the repo tree is never touched.

---------

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
2026-09-01 15:37:48 -03:00
Dizzle
a86b9019a8 feat(auto-combo): declare observed reliability as a scoring factor (#12317)
The weight table said stability accounts for "low latency stdDev / error rate". Grep errorRate in scoring.ts and you find it declared on ProviderCandidate and read nowhere — while combo.ts pulls 24 hours of usage history behind a ten-sample floor, falls back to real-time metrics, and hands every candidate an errorRate the scorer ignores. Two candidates, one failing 1% of calls and one failing 99%, scored identically at 0.459486.

This declares reliability as a sixteenth factor: 1 - failureRate, using the same formula, field precedence and rate-bounding speedRanking.ts already applies, so a corrupt reading means "nothing observed" rather than "fails every call". It ships at weight 0, leaving the ranking unchanged to the digit — the honest default, since which weight this deserves is a product call backed by traffic the author does not have. Two declared-but-silent factors already ship (cacheAffinity, resetWindowAffinity), so the pattern is not new. The stability row now describes what that factor actually computes: latency variance.

The rest is the mechanical 15 → 16 across nineteen documents and the forty-two llm.txt mirrors — sourced from check:docs-counts rather than a grep, the first real use of the gate #12316 extended.

Protected-surface note: this PR touches AGENTS.md, llm.txt and its 42 mirrors, and skills/omni-combos-routing/SKILL.md. Every changed line in those 45 files is a digit substitution and nothing else — masking all digits makes the removed and added lines identical, with no sentence added, removed or reworded. Reviewed and approved on that basis before merging.

Verified on the author's rebased head: check:docs-counts green (the gate that now enforces the count this PR moves), typecheck:core clean, and 71/71 focused tests across scoring-reliability-factor, combo-scoring-weights-schema-coverage, check-docs-counts-sync, lkgp-enabled-context, intelligent-routing-options and the combo-matrix auto integration suite.

Thanks @maxmad64bis — shipping the factor at weight 0 and saying plainly that the weight is someone else's call is the right way to land this.
2026-09-01 15:36:39 -03:00
Patryk Kopyciński
438db55c46 feat(providers): manual "Clear cooldown" action in the cooling panel (#12224)
* feat(providers): manual "Clear cooldown" action in the cooling panel

The persisted 429 cooldown (provider_connections.rate_limited_until) is
OmniRoute's local lesson, not upstream truth. When a quota has already
refreshed upstream (daily/weekly reset, provider-side fix), the only
automatic clear paths — Test-button success or Edit-modal key
re-validation — still require an upstream round-trip, so the user waits
out a bench that is already stale.

Adds a per-row "Clear cooldown" button to CoolingConnectionsPanel that
PUTs rateLimitedUntil: null (the route applies backoff reset defaults),
optimistically drops the bench, and refetches. The next request becomes
the real test of the key.

- useProviderConnections: handleClearCooldown + clearingCooldownId
  (in-flight guard mirrors the retestingId pattern)
- CoolingConnectionsPanel: optional onClearCooldown/clearingCooldownId
  props; button hidden for id-less rows, disabled per-row while clearing
- ProviderDetailPageClient: wires the new handler through
- i18n: en.json keys (clearCooldown, cooldownCleared,
  failedClearCooldown, ...) with providerText fallbacks

Tests: CoolingConnectionsPanel.test.tsx — click fires handler with the
row id, disabled + silent while in flight, per-row independence,
read-only when handler omitted, no button without connection id,
renders nothing when empty. Pre-existing
tests/unit/ui/CoolingConnectionsPanel.test.tsx stays green.

* fix(dashboard): dedupe clear-cooldown i18n keys and extract the row button

The providers namespace already carried an (orphaned) clearCooldown /
cooldownCleared / failedClearCooldown key trio, so the new feature keys
re-declared them as duplicate JSON keys ~1200 lines apart. JSON.parse is
last-wins, which silently shadowed the older values and broke ICU
placeholder parity in every locale (EN lost {model} while all 42
translations still carry it). Rename the feature's five keys to a
connection-scoped family instead:

  clearConnectionCooldown / clearConnectionCooldownInProgress /
  clearConnectionCooldownTitle / connectionCooldownCleared /
  failedClearConnectionCooldown

Also extract the per-row action into ClearCooldownButton so the panel
body stays inside the max-lines-per-function ratchet (was 83/80).

* feat(dashboard): mirror the clear-cooldown keys into all 42 locales

Adds the five connection-cooldown keys to every non-EN catalog with the
English value as the runtime fallback (fill-missing-from-en semantics),
and real translations for pt-BR and vi so their strict parity suites
stay meaningful:

  pt-BR: Limpar cooldown / Limpando… / Cooldown limpo — a conexão voltou
         ao roteamento / Falha ao limpar cooldown
  vi:    Xóa thời gian chờ / Đang xóa… / Đã xóa thời gian chờ — kết nối
         đã tham gia lại định tuyến / Không thể xóa thời gian chờ

---------

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
2026-09-01 12:08:31 -03:00
Bob.Hou
6dd82b77de fix(guardrails): pass providerId to getResolvedModelCapabilities in checkComboVision (#12112) (#12169)
* fix(guardrails): pass providerId to getResolvedModelCapabilities in checkComboVision (#12112)

Signed-off-by: Minxi Hou <houminxi@gmail.com>

* chore(quality): register combo-vision providerId test in the stryker tap set

---------

Signed-off-by: Minxi Hou <houminxi@gmail.com>
Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
2026-09-01 12:08:04 -03:00
Rafa Martins
17792ce0ad Update README.md (#12194)
Atualizaçao link global do Grupo 2

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
2026-09-01 12:07:42 -03:00
Nguyen Thanh Dat
5ff6513ca5 fix(combos): send null to clear an agent feature instead of omitting it (#12177)
* fix(combos): send null to clear an agent feature instead of omitting it

PUT /api/combos/[id] merges its body over the stored record, so an omitted
field means "leave unchanged". The combos editor deleted a cleared agent
feature from the payload, so unchecking context cache protection -- or
emptying the system message or the tool filter -- never persisted: the old
value survived the merge and the editor reopened with the toggle still on.

updateCombo already deletes any key explicitly set to null, which is how
description and context_length are cleared in the same save handler. Use the
same shape for the three agent fields, and make them nullable in
updateComboSchema so the null survives validation.

The clearing logic moves into comboAgentFeatures.ts so it can be tested
directly, matching comboQuotaOnlyFallback.ts next to it.

Fixes #12158

* chore(changelog): point the fragment at the real PR number
2026-09-01 12:07:22 -03:00
Syed Raheemuddin
8fc6834372 fix(system): propagate abort signal to stream reader in HTTP version checks (#12232)
When fetching version metadata from npm registry or GitHub APIs, if the remote connection stalls during stream reading, the 10-second AbortController timer aborts the request signal but the underlying body reader stream was not listening to the abort signal. This caused readBoundedJson reader.read() loop to hang until external socket close.

Now readBoundedJson listens to AbortSignal abort events, triggers reader.cancel(), and releases locks immediately on abort.
2026-09-01 12:07:02 -03:00
Dizzle
ad4b67d631 feat(radar): show the rate limits and training disclosure the feed already sends (#12320)
The Radar feed reports per-model rate limits and whether a provider says it may train on your prompts. Both fields are on RadarMergedEntry and the catalog table rendered neither — grep them in RadarCatalogTable.tsx and the only hits were the type declaration. The training flag is the one that stings: freeModelCatalog.ts documents it as "Surfaced in the UI", a promise the UI did not keep, and thirteen catalog entries carry it today.

Adds a Rate limits column and a badge in the ToS cell when a provider discloses training. No new data, no request, no API change.

Two judgement calls worth keeping: a limit of zero renders as 0/min rather than formatTokens' "rate-only" (right for a monthly budget, nonsense for a ceiling where zero is a real and alarming fact), and the badge condition is === true, since an absent training statement is not a guarantee.

Validation note — read before trusting the green: this PR's own suite (tests/unit/dashboard/radar-catalog-table-limits-training.test.tsx, 14 cases) could NOT be run locally. Vitest fails to resolve react18-json-view, which is declared in package.json and package-lock.json but is not present in this machine's node_modules; two pre-existing .test.tsx files in the same directory fail identically, so the cause is environmental and not this PR. The blocking test-vitest CI job runs npm ci and will execute it.

What was verified locally, in a combined batch worktree with all 11 PRs of this batch: 174/174 node-runner focused tests, typecheck:core clean, complexity 2706/3218, cognitive-complexity 1221/1437, check:cycles and check:docs-counts green, plus both CI i18n gates for the 42-locale pass — check-ui-keys-coverage PASS (all 42 locales at or above 65%) and check-ui-value-drift PASS against the release tip.

Thanks @maxmad64bis.
2026-09-01 11:58:50 -03:00
Dizzle
5253b93b89 feat(rankings): order free providers by measured reliability (#12218)
Free Provider Rankings sorted by the top model's Arena score, so a provider stayed first even if it failed every call — the reliability column from #11546 already showed what each one actually served, but ordering ignored it.

Adds an opt-in ?sortBy=reliability (API) and a "Most reliable first" toggle (page) sharing one comparator in freeProviderRankingsUsage.ts: measured providers first by successRate desc with ELO on ties, then unmeasured in their incoming order. The default is unchanged and locked by tests. successRate is null below MIN_USAGE_REQUESTS = 5 (existing, never zero), and ordering runs before slice(0, limit) so limit counts in the requested order. The toggle composes with the existing sortTypeFirst/groupByType grouping — a stable sort keeps reliability order within each group.

Opt-in is the right default here: the page is for discovery, including providers never called.

13 tests across three files (6 new for the comparator in isolation, plus filter and route coverage including unknown sortBy → 400 and the default path staying off call_logs).

Verified in a combined batch worktree with all 11 PRs of this batch: 174/174 focused tests, typecheck:core clean, complexity 2706/3218, cognitive-complexity 1221/1437, check:cycles and check:docs-counts green. The 42-locale i18n pass was checked with the CI gates: check-ui-keys-coverage PASS (all 42 locales at or above 65%) and check-ui-value-drift PASS against the release tip.

Thanks @maxmad64bis.
2026-09-01 11:58:15 -03:00
Dizzle
51587084ca fix(docs): the free-tier catalog ships no per-row confidence tag (#12318)
docs/reference/FREE_TIERS.md said its numbers were "gathered by web research (confidence tagged per row)". No entry carries one: grep -c confidence on the catalog returns 0, the type does not declare the field, and the API serves nothing of the sort. A reader looking for "how much can I trust this figure" was pointed at a per-row signal that never existed.

Replaced with the two counts the data actually supports — 7 of 446 entries carry hardStopGuaranteed (the field with the strictest sourcing rule in the repo: set only when the provider's own terms document that exceeding the free allowance refuses the request, source in a comment, never defaulted to true) and 13 carry a prompt-training disclosure. check:docs-counts reads both from the catalog at runtime, as required claims, so a reworded or deleted sentence fails rather than passing as "no claim in this file".

The PR deliberately does not add a confidence field — curating one is a product call, and it says so instead of inventing it.

Reconciled on merge: #12316 landed the gate extension underneath, so scripts/check/check-docs-counts-sync.mjs and its test took the tip's side plus this PR's own required-claim additions. Verified afterwards: check:docs-counts green, 48/48 across check-docs-counts-sync and free-catalog-no-confidence-field.

Thanks @maxmad64bis — checking the gate against a number it should reject (7 swapped for 99) is the right way to prove a gate works.
2026-09-01 11:57:54 -03:00
Nads
bdf218387b fix(resilience): per-model 402 on a passthrough gateway no longer terminalizes the whole connection (#12266)
* fix(resilience): per-model 402 on a passthrough gateway no longer terminalizes the whole connection

402 variant of #3027. Passthrough/gateway providers that multiplex many
models behind one credential (kilo-gateway, ollama-cloud, etc.) can 402
on a single PAID model while free models on the same key remain
perfectly usable. Previously any 402 unconditionally set the connection
to a terminal `credits_exhausted` status, which is never auto-recovered
without an operator reset — taking out every remaining model on that
provider, amplified further inside combo routing (measured: one 402
removed 9 of 14 fallback targets in a real combo, dropping success rate
from 98.3% to 74.2% on a fixed load test per the issue report).

Root cause (matches the issue's own analysis):
1. resolveTerminalConnectionStatus() returned "credits_exhausted" for
   ANY status === 402, with no per-model/passthrough check.
2. The generic per-model lockout gate (404/429/>=500) excluded 402.
3. The #3027 403-branch is gated on `!terminalStatus` — since (1) already
   resolves a terminal status for any 402 before that branch runs, simply
   adding 402 to its condition alone would not have fired.

Fix:
- resolveTerminalConnectionStatus() now takes isPerModelQuotaProvider and
  skips the connection-wide terminal path for a bare `status === 402`
  when true, letting it fall through to the per-model lockout branch
  instead. An explicit result.creditsExhausted (a provider's own
  classification, independent of HTTP status) is untouched and remains
  unconditionally terminal.
- Extended the existing #3027 per-model lockout branch to also handle
  402 (reason "credits" vs "forbidden" for 403), reusing the same
  cooldown/lockout machinery and log format.
- Single-credential (non-passthrough) providers are unaffected:
  isPerModelQuotaProvider is false there, so a 402 still terminalizes
  the connection as before — that behavior is deliberate for prepaid
  API keys (#5239 / #10616).

Also checked the issue's 4th root cause (terminal statuses never
auto-recovering) against the current codebase: connectionRecovery.ts
already has a 30-minute credits_exhausted reprobe
(isCreditsExhaustedReprobeCandidate) that the issue's report — filed
against v3.8.49 — didn't account for. The other two files it names
(rateLimit.ts's clearStaleCrashCooldowns, tokenHealthCheck.ts's
OAuth-refresh skip) legitimately exclude credits_exhausted for
unrelated reasons and are not bugs. Moot regardless: this fix prevents
credits_exhausted from being set at all for the passthrough case, so no
recovery wait is needed in the first place.

Tests: tests/unit/auth-passthrough-per-model-402-12242.test.ts, modeled
on the existing #3027 precedent test (real DB-backed integration test
via auth.markAccountUnavailable). Covers: paid-model-only lockout with
free model unaffected, a subsequent free-model request succeeding after
a sibling paid-model 402, single-credential 402 still fully terminal,
and no connection-wide backoff escalation on repeated 402s.

Verified:
- node --import tsx/esm --test tests/unit/auth-passthrough-per-model-402-12242.test.ts: 4/4 pass
- All related pre-existing tests (auth-ollama-cloud-per-model-403-3027,
  auth-terminal-status, openrouter-free-model-credits-exhausted,
  vertex-passthrough-model-lockout, 10347-embed-402-cooldown): 27/27
  pass, no regressions
- npm run typecheck:core: 0 errors
- npm run check:cycles: no cycles
- eslint (auth.ts + new test file, with project suppressions): 0 errors

Fixes #12242

* chore(quality): register 402 per-model test in stryker tap and de-ratchet auth.ts

- stryker.conf.json: add tests/unit/auth-passthrough-per-model-402-12242.test.ts
  to tap.testFiles in its alphabetical slot
- auth.ts: extract the #12242 connection-wide 402 decision into the pure helper
  isConnectionWideCreditsExhausted() so resolveTerminalConnectionStatus stays
  within the cyclomatic ratchet (file back to the base's 11 violations)

---------

Co-authored-by: OmniRoute Dev <dev@local>
Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
2026-09-01 11:52:21 -03:00
Dizzle
3b82d85081 docs(auto-combo): complete the mode pack table and gate what it claims (#12316)
docs/routing/AUTO-COMBO.md documented four mode packs; six ship. It printed 0.14 where modePacks.ts says 0.1333. And nothing was watching: four documents stated a scoring-factor count and check:docs-counts covered none of them. Wiring them up turned the gate red on seven real drifts — ARCHITECTURE.md and REPOSITORY_MAP.md at "9-factor" (code: 15) and "4 mode packs" (code: 6), RESILIENCE_GUIDE.md and SKILL.md at 13, AUTO-COMBO-GUIDE.md at both 5 and 13. ARCHITECTURE.md did not merely have the wrong number: it named nine factors that are not the engine's, and its four "mode packs" were the auto/* request prefixes.

A product fact fell out of writing the table: no pack sets quality, and applying a pack replaces the weight map wholesale (weights = pack in engine.ts, not a merge), so quality carries 0.03 by default and normalizes to 0 under any pack — pick a mode pack and the observed-quality signal stops voting. Documented, not changed.

The gate reads pack names from the module through the tsx subprocess that already reads every other code-derived count, matching the three spellings the docs actually use; on the reference document a missing claim now fails rather than passing. The dashboard was behind too (four of six packs offered); the count is dropped from the strategy label rather than corrected, since nothing reads selector labels and a right-today number goes stale unnoticed.

Verified in a combined batch worktree: 174/174 focused tests across all 11 PRs of this batch, typecheck:core clean, and check:docs-counts green with the four newly-wired documents.

Thanks @maxmad64bis — finding the quality-under-a-pack behaviour while writing a docs table is the kind of thing a table is for.
2026-09-01 11:50:57 -03:00
Dizzle
d19572fb95 fix(combo): expose every scoring weight the engine actually uses (#12314)
Two of the fifteen factors calculateScore applies could not be set by anyone. scoringWeightsSchema is a plain z.object, so zod strips what it does not name: PUT a combo with connectionDensity and you get a 200 back with nothing saved, and normalizeScoringWeights then reads the gap as a deliberate zero — switching off anti-concentration and the quality signal. DEFAULT_INTELLIGENT_WEIGHTS, the dashboard's own copy, missed the same two and every non-zero value differed from the engine's; summing to 1.05, validateWeights rejected them outright.

This adds the two keys to both lists and takes the dashboard defaults from DEFAULT_WEIGHTS. The scorer is not touched.

One behaviour change, and it is the point: a combo whose stored weights omitted the two keys was running with them at zero and the other thirteen renormalized upward. It now uses the engine's distribution (quota 0.1549 → 0.1429, health 0.1740 → 0.1605) and a test pins those numbers.

Left alone and documented rather than widened: rounded percentages now total 101% (six factors at 4.76% each render as 5%), and five stale .default() values in the schema that only bite when a config omits the key.

Verified in a combined batch worktree: 174/174 focused tests across all 11 PRs of this batch, typecheck:core clean, complexity 2706/3218, cognitive-complexity 1221/1437, check:cycles and check:docs-counts green.

Thanks @maxmad64bis — the red-before-green note (7 of 8 failing, and naming the one that passes on purpose) is exactly the evidence that makes a behaviour change reviewable.
2026-09-01 11:50:33 -03:00
Dizzle
5e6c9a92dc fix(sse): estimate usage in passthrough stream even with include_usage (#12151)
A passthrough stream could end with no usage even though the client asked for it via stream_options: {include_usage: true}, so providers that do not meter always showed 0 tokens. The fix estimates usage at the finish marker when the upstream stays silent (flagged estimated: true) and drops any duplicate trailing usage chunk so the client never sees two.

open-sse/utils/stream.ts:1982,1749 · open-sse/utils/usageTracking.ts:651,664

Six cases: the predicate (finish without usage but with content, trailing valid, empty response, tool-only) plus two SSE harness cases through createSSEStream passthrough.

Note on base: this branch forked 442 commits back and carried a base-red marker for #12109, which is now closed — the release tip has no open base-red issue. It merged cleanly against the current tip regardless.

Verified in a combined batch worktree: 174/174 focused tests across all 11 PRs of this batch (this PR's stream-passthrough-usage-estimation suite included), typecheck:core clean, check:cycles and check:docs-counts green.

Thanks @maxmad64bis.
2026-09-01 11:50:15 -03:00
Dizzle
2f33f2c20d feat(routing): report why the zero-cost guard excluded a candidate (#12319)
With freeAccessPolicy: "strict" the read-only candidate listing silently dropped rows, so an operator could not tell "no free allowance left" from "the quota fetcher is broken" — in a listing whose own module header promises a candidate the routing path would skip "is never dropped". #9133 settled the same question for the resilience filter via a skip opt-out; the zero-cost guard never got one.

It gets it now: the guard is disabled for the inspector build exactly as the resilience filter already is, and every candidate carries freeAccessExclusion — null when satisfied, otherwise one of seven named reasons. The last three (exhausted, state-unknown, no-connection) are the point: they used to look identical because the row just disappeared. STRICT_ZERO_COST.md documents what each asks the operator to do.

Dispatch is untouched and a test pins that. The three existing guard suites were not modified — their 31 cases are the net and still pass. excludeTosAvoid still drops candidates without a reason; documented as a separate question rather than widened into here.

Verified in a combined batch worktree: 174/174 focused tests across all 11 PRs of this batch, typecheck:core clean, complexity 2706/3218, cognitive-complexity 1221/1437, check:cycles and check:docs-counts green.

Thanks @maxmad64bis — the reason table and the honest note about the order change (freshness before status) made this easy to review.
2026-09-01 11:49:56 -03:00
Dizzle
33bdc386bc fix(free-tier): never serve a Radar overlay older than the shipped catalog (#12215)
GET /api/free-tier/summary could answer from a Radar overlay built 2026-08-02 while the release ships a catalog curated 2026-08-30 (FREE_CATALOG_CURATED_AT) — totals computed from older data, still tagged catalogSource: radar-overlay. The route now refuses any overlay built before the shipped catalog and falls back to that catalog through the operator's local state.

Tightens #11550 using the generatedAt persisted by #11435.

Verified in a combined batch worktree: 174/174 focused tests across all 11 PRs of this batch (this PR's free-tier-summary-radar-overlay suite included), typecheck:core clean, check-file-size, check-changelog-integrity, check:cycles and check:docs-counts green.

Thanks @maxmad64bis.
2026-09-01 11:49:49 -03:00
Dizzle
78a0e4b109 fix(usage): declare the Adobe Firefly usage fetcher the dispatcher already calls (#12321)
usage/fetcherProviders.ts exists, in its own words, "so the registration list can't drift from the dispatcher's switch statement". It drifted: #8006 added adobe-firefly and firefly to the dispatcher and to USAGE_SUPPORTED_PROVIDERS but not to this list, so the connection UI advertised usage support while the provider-plugin manifest, genericQuotaFetcher and the free-access quota cache all reported no fetcher — for two ids getUsageForProvider would happily serve.

Declaring them is what makes the balance actually get fetched (registerGenericQuotaFetchers wires a generic fetcher per declared id, and resolveFreeAccessState stops returning early), which the PR states plainly rather than burying as a side effect.

The test turns the docstring's prose invariant into enforcement: it reads the dispatcher's cases from source and compares both directions, and records each accepted difference against USAGE_SUPPORTED_PROVIDERS with a reason plus a staleness check, so the next drift can't hide among them. xiaomi-mimo-token-plan is left flagged as a real gap rather than widening the PR.

Verified in a combined batch worktree: 174/174 focused tests across all 11 PRs of this batch, typecheck:core clean, complexity 2706/3218, cognitive-complexity 1221/1437, check:cycles and check:docs-counts green.

Thanks @maxmad64bis.
2026-09-01 11:49:29 -03:00
Diego Rodrigues de Sa e Souza
accdfa9f33 fix(usage): console-aware Token Plan guidance + subscription hint on bailian 401 (#12288)
* fix(usage): console-aware Token Plan guidance and subscription hint on bailian 401

The personal Token Plan is sold through two consoles with different portals,
gateway hosts and login tickets. Two operator-facing messages ignored the split:

- The quota guidance always said 'get the cookie at home.qwencloud.com', even
  for connections served by the Alibaba Model Studio console — following it
  verbatim produces a cookie the gateway rejects (console mismatch →
  BailianGateway.Login.NotLogined). The guidance now derives the console from
  the provider via resolveConsoleSite, matching what the fetcher will do with
  the pasted cookie.
- Key validation mapped upstream 401 to a bare 'Invalid API key'. An expired
  Token Plan subscription produces the exact same upstream 401 (observed live
  2026-09-01: subscription ended 08-23, the working key started failing), so
  the message now names the subscription as a cause worth checking.

* test(providers): align the remaining bailian 401/403 message pins to prefix match

search-provider-validation.test.ts pinned the exact 'Invalid API key' string for
the bailian validator; the message now also names an expired Token Plan
subscription. Same property asserted (401/403 => invalid), prefix match.
2026-09-01 11:40:40 -03:00
Diego Rodrigues de Sa e Souza
9d04995950 chore(quality): baseline-headroom skips generated and vendored files in the fileSize worst-file signal (#12291)
The nightly headroom monitor flagged fileSize 🟡 permanently because the worst
frozen file was src/app/docs/lib/openapi.generated.ts — frozen at its emitter's
exact output size in #12212, i.e. ~0% headroom BY CONSTRUCTION (growth is
policed by conscious re-freezes, never by editing the module). Same class:
open-sse/vendor/** (upstream code nobody slims by hand).

isMonitorExemptFile() excludes .generated. modules and vendor/ paths from the
monitor's worst/near-cap accounting only — check:file-size itself still
enforces both. The fileSize row now points at the worst HUMAN-EDITABLE frozen
file (currently tests/integration/skills-pipeline.test.ts at 4.5%, a true
early warning: its baseline note already requires a split rationale for any
further growth).

Refs #12149
2026-09-01 11:25:34 -03:00
Diego Rodrigues de Sa e Souza
abbbca216d feat(sse): full language parity for output styles (es/de/fr/it/ru/zh + autoDetect) (#12289)
* feat(sse): add it/ru/zh caveman output instructions

* fix(sse): expose the dormant terse-prose translations through the catalog

* feat(sse): translate less-code to es/de/fr/it/ru/zh

* feat(sse): translate ponytail to es/de/fr/it/ru/zh

* feat(sse): translate i-have-adhd to es/de/fr/it/ru/zh

* test(sse): anchor wave-2 output-style translations to their own language

* feat(dashboard): offer every output-style language in the default-language selector

* feat(sse): let autoDetect pick the output-style instruction language

* docs(compression): consolidate the output-style tables and record full language parity
2026-09-01 11:13:26 -03:00
backryun
fe5f4b0ef9 chore(quality): remove unreachable code and restore test discovery (#11950) 2026-09-01 11:06:04 -03:00
backryun
c818655b5a chore(deps): refresh runtimes and adopt ESLint 10 (#11259)
Co-authored-by: backryun <backryun@daonlab.local>
2026-09-01 11:05:45 -03:00
Bob.Hou
9629d78ece fix(translator): strip neutral tool_choice when tools absent in Responses-to-Chat (#12141) (#12166)
Signed-off-by: Minxi Hou <houminxi@gmail.com>
2026-09-01 11:02:35 -03:00
brick30llc-ctrl
0ff164701d fix(sse): trust finish_reason over reasoning-ratio heuristic in response quality validation (#12262)
* fix(sse): trust finish_reason:length/max_tokens over the reasoning-ratio heuristic in response quality validation

A truncated response with empty content and reasoning_content present was
only rejected by validateResponseQuality() when reasoning consumed >=90%
of completion_tokens. A response truncated at a lower ratio (e.g. 63%)
passed through as "valid" even though the caller received no usable
content and finish_reason was explicitly "length" (or the alternate
"max_tokens" naming some providers use) -- an unambiguous truncation
signal the validator wasn't reading. Reproduced live against
nvidia/nemotron-3-super-120b-a12b: content:null, finish_reason:length,
reasoning_tokens 645/1024 (63%).

Trust finish_reason directly when it's reported, falling back to the
existing token-ratio heuristic only when it isn't. Does not affect the
deliberate-tiny-probe case (e.g. max_tokens:1 connectivity pings) --
those never produce reasoning_content, so the branch this change is in
doesn't run for them.

* docs(changelog): add fragment for #12262

---------

Co-authored-by: brick30llc-ctrl <admin@brick30.com>
2026-09-01 11:02:05 -03:00
backryun
f301d34ce5 fix(dev): qualify Turbopack runtime boundaries (#12258)
Co-authored-by: backryun <backryun@daonlab.local>
2026-09-01 11:01:56 -03:00
Rafa Martins
d920e6495a build: omit the standalone output target for contributor builds (#12204)
Completes the contributor profile: #12198 stopped OmniRoute from assembling the standalone bundle, but Next was still asked to emit one. Making output: "standalone" conditional on OMNIROUTE_BUILD_PROFILE=contributor removes the standalone tracing pass itself, which is where the remaining time went.

Default builds are unaffected — the flag is read from the env at config load and is false everywhere except the contributor profile, so tests/unit/next-config.test.ts still observes output === "standalone" (18/18 green across contributor-build-script, next-config and build-profile-stubs).

Reconciled on merge: CONTRIBUTING.md and scripts/build/backendOnlyPages.mjs already carried this stack's earlier steps on the tip, so both took the tip's side; only the next.config.mjs conditional and its test are this step's delta.

Thanks @rafacpti23 for splitting this into four reviewable steps — it made the whole stack easy to reason about.
2026-09-01 09:38:44 -03:00
Rafa Martins
7f008ed09a build: stub the instrumentation entrypoints in the contributor profile (#12203)
Contributor builds only need compilation to type-check; pulling src/instrumentation.ts drags the whole startup graph (DB boot, model-catalog warm, quota fetchers) into the build. stubContributorInstrumentation() swaps both entrypoints for no-ops before next build and hands them to the existing restoreDashboardPages() path afterwards, reusing the same {file, original} shape and the SIGINT/SIGTERM handlers already registered in that block.

Reconciled on merge: the stub originally wrote 'export async function register() {}' into both files, but src/instrumentation-node.ts exports registerNodejs() (register lives in src/instrumentation.ts). Harmless in practice — instrumentation.ts is its only importer and is stubbed at the same time — but the stub misstated the file's contract, so it now emits the right symbol per file and the test asserts it.

Verified: contributor-build-script 3/3 green.

Thanks @rafacpti23.
2026-09-01 09:37:32 -03:00
Rafa Martins
e3c440e804 build: skip standalone packaging in the contributor profile (#12198)
Makes the contributor profile actually fast: with OMNIROUTE_BUILD_PROFILE=contributor the build stops after next build instead of copying docs/ and running assembleStandalone, which is the expensive half and produces an artifact contributors never ship. Adds isContributorBuild() next to the existing isBackendOnlyBuild() and documents the compile-only contract in CONTRIBUTING.md.

Reconciled on merge: CONTRIBUTING.md's new paragraph was inside the ```bash fence and would have rendered as shell — moved below the closing fence. package.json auto-merged against the tip.

Verified: contributor-build-script + backend-only-smoke-workflows 10/10 green.

Thanks @rafacpti23.
2026-09-01 09:36:08 -03:00
Rafa Martins
d898d1a913 build: force the webpack bundler in the contributor build profile (#12197)
The contributor profile added in #12192 inherited the default Turbopack bundler. Turbopack's native allocator is the documented OOM risk on memory-constrained machines (scripts/build/build-next-isolated.mjs:201-202), and OMNIROUTE_USE_TURBOPACK=0 is the escape hatch the same script already honours at line 139 — the repo's own nightly-compat workflow pins it to "0" for exactly this reason. Setting it on build:contributor makes the fast profile usable on the machines it targets.

Reconciled on merge: the branch was 51 commits behind and #12192 had already landed the script line, so only the env flag is new; the tip's dependency block was kept verbatim rather than taking the stale package.json wholesale.

Verified: tests/unit/build/contributor-build-script.test.mjs 1/1 green.

Thanks @rafacpti23.
2026-09-01 09:35:03 -03:00
Diego Rodrigues de Sa e Souza
ad54249c54 fix(security): strip Qwen/Alibaba console-session cookies from provider API responses (#12287)
The Token Plan console cookie is a browser credential for the operator's
cloud-console account — same class as the ollama/opencode cookies that
sanitizeProviderSpecificDataForResponse already strips — but the four
qwen/alibaba fields (qwenCloudCookie, qwenCloudSecToken, alibabaConsoleCookie,
alibabaConsoleSecToken) were missing from the strip list, so GET /api/providers
returned the operator's console session in the clear to any dashboard session.

The edit modal depended on that leak: it initialized the cookie fields from the
round-tripped response. It now starts them empty, matching the ollama pattern —
the quota-scraping assign skips empty fields and the PUT handler's partial merge
preserves keys the payload does not carry, so 'leave blank to keep the stored
cookie' (already what the field hints promise) holds for real.

Found in the 2026-09-01 audit of the Token Plan quota feature.
2026-09-01 09:22:56 -03:00
Diego Rodrigues de Sa e Souza
eeba382049 fix(combo): bound the pre-dispatch unavailable skip so a stale label cannot dark a pool (#12168) (#12285)
getPersistedConnectionCooldownSkipReason() returned a skip for ANY connection
whose testStatus was `unavailable`, with no elapsed-cooldown check:

    if (status === "unavailable") return `Skipping ...`;

That is the raw-label anti-pattern AGENTS.md warns about ("check whether code
is reading raw state instead of using getStatus()/canExecute()") — the
resilience layers are meant to recover lazily. The sibling helper directly
above it, getConnectionStatusQuotaCutoffReason(), does require
hasFutureRateLimitUntil() before treating `unavailable` as blocking.

Its stated justification — "Lazy recovery is unaffected: clearAccountError()
resets the status on first success" — does not hold on this path. This gate
runs BEFORE dispatch, so it prevents the very successful request that would
call clearAccountError(). And a row whose rateLimitedUntil is absent cannot be
rescued by the out-of-band recovery job either, because hasElapsedCooldown()
there requires a timestamp to be present.

Net effect reported in #12168: an entire combo pool answering
ALL_TARGETS_SKIPPED with recordedAttempts === 0 — zero upstream attempts, no
path back to healthy.

The original intent (do not burst into a connection AUTH just retired, before
the timestamp lands) is preserved, but bounded: the bare label is honoured only
while lastErrorAt is inside a grace window, mirroring ERROR_LABEL_GRACE_MS in
src/lib/quota/connectionRecovery.ts so the two never disagree about whether a
label is still meaningful. Past the window the request goes through, and one
real attempt either succeeds (clearing the status) or re-arms the cooldown with
a fresh timestamp.

Regression introduced by #11360, shipped in v3.8.50.

Two assertions in repro-combo-persisted-cooldown-preskip.test.ts encoded the
buggy behavior as intended ("skips an unavailable connection whose cooldown
already expired") and are realigned to the corrected contract, plus a case for
the orphan state (unavailable with no timestamps at all).
2026-09-01 09:22:43 -03:00
Diego Rodrigues de Sa e Souza
412298b624 chore(proxy): purge the legacy 1proxy residue — sync/rotator modules, dead DB exports, dead settings tab and flag, docs (#12091) (#12290)
The 1proxy marketplace integration was decommissioned in v3.8.4; the code
survived only through the localDb barrel, deleted in #12055. Everything below
had zero consumers (grep-proven across src/, open-sse/, bin/, electron/,
scripts/ and tests/):

- src/lib/oneproxySync.ts and src/lib/oneproxyRotator.ts deleted.
- src/lib/db/oneproxy.ts: upsertOneproxyProxy, getOneproxyProxyById,
  getOneproxyProxyForRotation and markOneproxyProxyFailed removed (their only
  consumers were the two deleted modules); listOneproxyProxies and the record
  interface stay — open-sse/utils/proxyFallback.ts still uses them.
- src/shared/validation/oneproxySchemas.ts and the unmounted
  settings/components/OneproxyTab.tsx deleted (no importer anywhere; the live
  UI is the FreePool* tabs over /api/settings/free-proxies).
- ONEPROXY_ENABLED feature flag removed (readerless since oneproxySync died —
  the toggle no longer controlled anything); flag-count contract test aligned
  54 → 53.
- Docs: PROXY_GUIDE (component rows, env rows, the three omniroute/
  oneproxyRotator snippet sections), CODEBASE_DOCUMENTATION, REPOSITORY_MAP,
  ENVIRONMENT (ONEPROXY_* rows), FEATURE_FLAGS, .env.example — canonical +
  pl/zh-CN/zh-TW mirrors.
- The 308 compat redirects under /api/settings/oneproxy/ stay (deliberate API
  compat), as do the live free-proxy provider and proxy_registry rows.

check:dead-code drops 424 → 417 (baseline kept at the velocity-phase 500 —
banking shrinks is paused until v4.0, headroom grows to 16.6%).
check:docs-all, check:env-doc-sync, typecheck:core and the 8 free-proxy/
proxy-fallback test files are green.

Closes #12091
2026-09-01 09:22:26 -03:00
Benson K B
f5e70950d3 chore(ci): point the circular-deps gate at dpdm's real JS entrypoint (#11615)
`node_modules/.bin/dpdm` is an npm shell wrapper, so `node <that path>` crashed with `SyntaxError: missing ) after argument list` and the advisory circular-deps gate in ci.yml (job quality-extended) reported an error instead of a result on every run. Pointing DPDM_BIN at `node_modules/dpdm/lib/bin/dpdm.js` restores it: the gate now completes and reports circularDeps=154 (exit 0).

Scope reduced during merge — the branch was 522 commits behind and carried three base-drift files that were reconciled back to the release tip: open-sse/services/combo.ts (reverted routing code + a @/lib/localDb barrel import, Hard Rule #2), config/quality/eslint-suppressions.json (dropped ~45% of the frozen suppressions), and tests/unit/cli-env-inline-comment-10100.test.ts (replaced a working module import with new Function() source scraping, Hard Rule #3). Rationale documented in the PR discussion.

Verified: check:circular-deps crashes on the pure tip and completes on the merged branch; tests/unit/cli-env-inline-comment-10100.test.ts 5/5 green against the restored version.

Thanks @benzntech for catching the dpdm breakage.
2026-09-01 07:37:06 -03:00
Diego Rodrigues de Sa e Souza
073b98462d feat(dashboard): orchestration canvas — /dashboard/orchestration with Agents/Routing/Overview tabs (part 2/2) (#12261)
UI completa do Orchestration Canvas sobre o modelo da parte 1: página /dashboard/orchestration com abas em URL (Agents=grafo vivo via FlowCanvas, Routing=ComboLiveStudio intocado, Overview=contadores+kanban com totais reais sob cap), drawer de detalhe com approve/cancel (unwrap por fonte verificado contra as rotas reais, erros client-safe, prUrl https-only), i18n com traduções REAIS em 43 locales, entrada no sidebar. Ciclo SDD: 9 tasks TDD com review por task (Task 15 com fix round: Critical A2A unwrap + rewrite de lint + guard XSS), review final whole-branch (Ready to merge, 0 Critical/Important, refactor de complexity provado behavior-preserving), 3 fixes de CI validados RED→GREEN. CI: tudo verde. Crédito do conceito visual: design da PR #11815.
2026-09-01 05:12:54 -03:00
Diego Rodrigues de Sa e Souza
30a26d9dcb docs(agents): protected-surface merge rule — operator approval for agent-instruction files (#12253)
PR #11770 (2026-09-01) added a CLAUDE.md section instructing every AI agent to
clone and execute a third-party setup script; a merge campaign swept it into
the release branch with no human risk review (reverted in #12249). Review
focus now carries the rule: PRs touching CLAUDE.md / AGENTS.md / GEMINI.md /
llm.txt / skills SKILL.md files are HOLD until explicit per-PR operator
approval — CI validates code, not instruction-surface intent.
2026-09-01 03:40:33 -03:00
Diego Rodrigues de Sa e Souza
d53da4fdc8 chore(quality): dedupe tap.testFiles entries added by racing base-red fixes (#12265)
Dedupe trivial de 1 arquivo; gate mutation --strict verde local no conteúdo pós-merge (363 entradas únicas, 0 duplicatas).
2026-09-01 03:34:13 -03:00
Diego Rodrigues de Sa e Souza
3c8b553811 chore(quality): register native-codex turn-pin tests in stryker tap.testFiles (#12263)
Gate check:mutation-test-coverage --strict red→verde local (registro dos 2 testes turn-pin no tap.testFiles, drift da mesma classe do #12170). O único check vermelho desta PR (Unit shard 4/4) é o base-red dos próprios testes turn-pin desalinhados pelo #12247 — corrigido pela #12259, mergeada na sequência. Reds circulares: cada PR só está vermelha no item que a outra corrige.
2026-09-01 03:29:48 -03:00
Diego Rodrigues de Sa e Souza
aa2aec5e59 docs: Chaos Mode setup guide + weighted strategy semantics (#12250)
Gap surfaced by OmniCopilot#16: the Chaos Mode dashboard page, its per-key
chaosModeEnabled permission and both dispatch endpoints had no setup doc at
all (only the auto/chaos table line existed), and AUTO-COMBO.md never stated
that weighted is a proportional draw where zero-weight steps are never drawn.
New docs/guides/CHAOS-MODE.md (registered in meta.json + docs/README.md) and
a 'weighted semantics' subsection under the strategy table, both written from
the code (chaosConfig.ts, chaosExecutor.ts, both routes, targetSorters.ts,
targetResolution.ts). check:docs-all exits 0.
2026-09-01 03:25:47 -03:00