Commit Graph

3859 Commits

Author SHA1 Message Date
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
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
6c93e74f26 fix(quality): base-red pair — stryker tap registration + turn-pin suites aligned to the window gate (#12255)
* chore(quality): register native-codex-turn-pin tests in stryker tap.testFiles

The mutation-test-coverage gate (--strict) fails on the release tip: the two
native-codex-turn-pin suites (#10379 merge wave) cover open-sse turn-pin code
and src/shared/utils/circuitBreaker.ts but were not listed in
stryker.conf.json tap.testFiles, so their mutant kills would not count. Adds
both files; the gate now passes clean (4728 test files scanned, no drift).

* style: prettier pass on stryker.conf.json

* test(sse): align turn-pin suites to the provider-cooldown window gate

The two native-codex-turn-pin suites landed via the #10379 merge wave after
PR #12247 forked, so #12247's green CI never saw them: they set up 'provider
in global cooldown' with a single recordProviderCooldown call, the pre-#12247
contract. Since the window gate, a provider only counts as cooling after
providerFailureThreshold failures inside the window — the setup now loops to
the profile threshold (same alignment the tracker's own legacy suite got in
Sibling sweep: all 7 suites touching recordProviderCooldown pass (60/60).
2026-09-01 03:18:59 -03:00
Diego Rodrigues de Sa e Souza
2e17161ea2 feat(sse): wire the PROVIDER_PROFILES window gate into the global provider cooldown (#12247)
providerFailureThreshold / providerFailureWindowMs / providerCooldownMs shipped
in PROVIDER_PROFILES with no runtime consumer (2026-08-31 docs audit, P0.1).
Provider-level entries in providerCooldownTracker now honor them: the whole
provider only counts as cooling after providerFailureThreshold failures inside
providerFailureWindowMs, then cools for providerCooldownMs. Connection-level
entries keep the pre-existing exponential backoff, and the layer stays opt-in
(PROVIDER_COOLDOWN_ENABLED, default off) — default behavior is unchanged.

TDD: tests/unit/provider-cooldown-window-gate.test.ts written first (4 red on
the old behavior), then the wiring; legacy tracker suite aligned to the new
contract (23/23 green). Docs: AGENTS.md breaker section + RESILIENCE_GUIDE
opt-in layer subsection; executors soft-drift refresh (104 -> 106).
2026-09-01 01:57:17 -03:00
Diego Rodrigues de Sa e Souza
4bcd8cee99 fix(combo): always clear the loop-safety timer, not just on the happy path (#11804) (#12245)
dispatchWithCooldownRetry arms a loop-safety timer (setTimeout, 10 minutes by
default) on every setTry iteration, so a combo that never produces a terminal
response still answers with a 504 instead of hanging. The only clearTimeout in
the whole file sat inside the `if (anySuccess)` branch — the comment said so
verbatim: "clear the safety timer on the happy path".

Every error exit therefore returned the response to the client while leaving a
600s timer pending, its closure retaining orderedTargets and the exhausted
provider/connection sets: all_targets_skipped, all_accounts_inactive, the
aggregated-status return, the final fallback, and the global-timeout branch.
The timer is also re-armed per setTry iteration with no clear in between.

Field evidence from the issue: two requests that failed quality validation
returned 502 to the client immediately, and "Combo loop safety timeout ...
force-terminating" was logged for both exactly 600 seconds later — the leaked
timers firing long after the requests were gone.

Fixed structurally rather than by sprinkling clearTimeout across the five
return sites: the handle is hoisted to function scope and released in a
finally, so a future `return` added to this function cannot silently
reintroduce the leak. The 504 backstop itself is unchanged.

Note the timer already called .unref(), so it never held the event loop open —
this is a memory-retention leak, not a hang.
2026-09-01 01:08:09 -03:00
Diego Rodrigues de Sa e Souza
a249a9dc28 docs(api): document every implemented route in openapi.yaml (276 → 692 paths) (#12212)
* docs(api): document every implemented route in openapi.yaml (276 -> 692 paths)

Follow-up nº 3 of the 2026-08-31 docs audit: 416 implemented routes had no
OpenAPI entry (gamification, radar, skills, webhooks, mcp, a2a, tunnels,
version-manager and plugins were absent entirely). Adds a minimal, honest
entry for each — real methods parsed from every route.ts's exports, a group
tag and a neutral path-derived summary; no invented semantics. Rich schemas
remain hand-curated in the existing entries.

Generated by scripts/ad-hoc/gen-openapi-missing-paths.mjs, which enumerates
routes with the same lib check:api-docs-refs uses — the spec now covers
692/692 real routes and the gate verifies every spec path has a real route.

* docs(api): security tiers on generated paths, regenerated API skills, size baseline

The first CI round caught three real contract gaps in the generated coverage:

- Generated operations on LOCAL_ONLY routes now carry x-loopback-only (and
  x-always-protected for ALWAYS_PROTECTED_API_PATHS), resolved through the real
  src/server/authz/routeGuard.ts at generation time. The
  openapi-security-tiers guard now also accepts LOCAL_ONLY_API_PATTERNS —
  param-shaped routes (/api/providers/{id}/login) are classified by regex in
  the runtime and were invisible to the prefix-only check.
- The API agent skills are generated FROM the spec: 18 SKILL.md files
  regenerated via generate-agent-skills --apply so the generator stays 46/46.
- src/app/docs/lib/openapi.generated.ts grew with the spec (171 -> 1347 lines,
  emitted by gen-openapi-module): frozen in file-size-baseline.json with a
  _rebaseline justification — shrink by slimming the spec, never by editing
  the generated module.
2026-09-01 00:53:25 -03:00
Davide Baraldo
e7a65d28db fix(oauth): keep Claude personal and Team organizations apart (#12222)
* fix(oauth): keep Claude personal and Team organizations apart

One Anthropic identity reaches its personal workspace and every Team
organization it belongs to with the same email AND the same accountUUID,
each with its own tokens, plan and rate limits. The OAuth dedup matched on
email alone for every provider except Codex, so authenticating the second
organization overwrote the first connection instead of adding one: only the
most recent organization stayed usable. organizationUUID is the field that
separates them (cliUserID cannot be used, it changes on every login).

Disambiguate on organizationUUID, mirroring how Codex uses
workspaceId/chatgptUserId (#7737):

- findExistingOAuthConnectionMatch routes claude through a new
  isSameClaudeAccount helper, so a login only merges into an existing row
  when the organization agrees;
- isMatchingOauthIdentity gains organizationUUID as a third optional
  disambiguator, compared strictly two-sided;
- createProviderConnection passes the incoming organizationUUID, closing the
  same hole on the create path.

Rows stored before Claude returned organizationUUID keep the bare-email
match, so re-authenticating an existing connection still updates it in place
instead of forking a duplicate. No behaviour change for other providers.

* docs(oauth): changelog fragment for #12222
2026-09-01 00:51:38 -03:00
Bob.Hou
05490304bf fix(oauth): mark empty Antigravity projectId as degraded and clear stale errors (#11284) (#12205)
* fix(oauth): mark empty Antigravity projectId as degraded (#11284)

The #11284 gate only fired when projectDiscoveryOutcome was set. Paste
credentials, persistOAuthConnection, and agy CLI import could persist
projectId="" as testStatus=active, so the dashboard showed Connected
while fetchAvailableModels returned 403.

Degrade on empty projectId itself. Keep the refresh token stored so
request-time bootstrap can still self-heal.

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

* fix(oauth): clear stale degrade fields and bind CLI imports to builtin client

persistOAuthConnection left errorCode/lastError on the row when a later
connect discovered a Cloud Code projectId. agy CLI import also kept a
leftover custom: oauthClient marker from dashboard OAuth, so the next
refresh hit the operator web client instead of the public desktop client.

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

* fix(oauth): null error fields on healthy create paths too

Update already cleared errorCode/lastError* when a projectId appeared.
Create payloads still omitted the keys; match the update shape so a
fresh row cannot keep a leftover degrade marker.

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

* test(oauth): pin healthy create/upsert nulling of degrade fields

Forge flagged create payloads omitting errorCode/lastError* when a
projectId is present. Production already writes explicit nulls; the
reader strips them via cleanNulls, so pin both the payload shape and
the upsert path that must overwrite a leftover degrade marker.

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

* fix(oauth): type create payload from AntigravityDegradedProjectState

The persistence helper duplicated a subset of the degrade type and
dropped warning. Align the parameter so the HTTP-only warning field
cannot drift from the exported type.

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

* fix(oauth): persist degrade status through a single override helper

OAuth exchange/poll-callback spread the whole degrade object, which
wrote warning into the SQLite row and left healthy updates as {}.
Centralize testStatus/errorCode/lastError* so they always win over a
spread tokenData payload.

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

---------

Signed-off-by: Minxi Hou <houminxi@gmail.com>
2026-09-01 00:51:18 -03:00
backryun
dc6daf27b6 fix(dev): silence webpack runtime module warnings (#12228)
Co-authored-by: backryun <backryun@daonlab.local>
2026-09-01 00:50:58 -03:00
backryun
8d388912a7 feat(providers): refresh vendored ChatGPT Web connector to v4.0.7 (#12181)
Refresh the existing MIT-licensed miuuyy/codex-chatgpt-web vendor snapshot and its OmniRoute integration as one reviewable change.

Co-authored-by: backryun <backryun@daonlab.local>
2026-09-01 00:50:15 -03:00
killer30001000
debb82bdd7 feat(usage): add Kilo Code balance and Kilo Pass quotas (#12178)
* feat(usage): add Kilo Code balance and Kilo Pass quotas

* feat(usage): add Kilo Pass dashboard meter

* test(usage): cover Kilo Code quota integration

* docs(usage): document Kilo API endpoint override
2026-09-01 00:49:54 -03:00
b3nw
c49ee53bc1 feat(providers): add RPD to rate limit overrides (#12147) 2026-09-01 00:49:33 -03:00
Rafa Martins
0e5e195519 build: add contributor fast profile (#12192) 2026-09-01 00:48:57 -03:00
Paulo Oliveira
3383adbbd1 perf(sse): defer cloneLogPayload until after SSE collector cap check (#12243)
* perf(sse): defer cloneLogPayload until after SSE collector cap check

Dropped SSE events no longer pay the structuredClone cost. The clone now
runs only for events that survive the maxEvents/maxBytes cap, eliminating
~9,800 wasted deep clones per streaming response (65-71% faster push).

Reducer snapshot isolation restored:
- OpenAI reducer stores first-chunk primitives instead of a chunk reference
- Responses reducer snapshots only needed fields, deep-cloning nested output/metadata
- getEvents() keeps defensive-copy semantics via cloneLogPayload

* chore: add changelog fragment for #12241
2026-09-01 00:47:34 -03:00
mdigitalbh81
a4b4bca2ee fix(combo): stop retries when pinned Codex model is unavailable (#12240) 2026-09-01 00:47:29 -03:00
Syed Raheemuddin
18dd83cd87 fix(sse): sort injected tools deterministically for prompt caching (#12234) 2026-09-01 00:47:24 -03:00
Syed Raheemuddin
ae37413aff fix(resilience): isolate local host execution errors from provider circuit breakers (#12233)
Local process execution failures (ENOENT spawn errors, binary missing, EPIPE, exit codes) were incorrectly treated as upstream provider failures, opening provider circuit breakers and cooling down valid connections. Added `isLocalExecutionError` guard to skip circuit breaker trips and connection disables when local host execution fails.
2026-09-01 00:47:21 -03:00
Syed Raheemuddin
26eeead268 fix(memory): honest probe-driven FTS5 keyword status + memory_id rowid sync (#12231)
* fix(memory): honest probe-driven FTS5 keyword status + memory_id rowid sync

The "no such module: fts5" complaint on FTS5-less runtime builds (sql.js/WASM
under a global install) was masked by a hardcoded keyword.available=true in
engineStatus and an unsanitized FTS5 MATCH path. Address root cause:

- engineStatus(): probe runtime via supportsFts5(db) instead of hardcoding
  available=true; keywordEngineStatus() reports the true backend (FTS5 vs
  none) with a reason. Schema, OpenAPI, dashboard chip updated to match.
- store.ts: sync memory_id to the SQLite rowid on insert (+ self-heal legacy
  NULL rows). Migration 023 keys the FTS5 external-content trigger off
  memory_id, but plain INSERT left it NULL so the JOIN returned 0 rows —
  keyword/hybrid search silently returned nothing on FTS5-capable builds.
- retrieval.ts: apply sanitizeFts5Query() to the preview MATCH path.

Tests updated/added across memory-engine-status, memory-retrieve-preview,
memory-schemas-roundtrip, memory-store, and the integration engine-status
test (dropping the hardcoded "always available" assertion). 66 unit tests
pass; lint and typecheck clean.

* fix(memory): sanitize FTS5 queries for memory retrieval

Prevent SQLite FTS5 syntax errors by sanitizing query terms and replacing FTS control operators with double-quoted tokens.
2026-09-01 00:47:16 -03:00
Dizzle
1b64372316 test(free-tier): counting vs deciding regimes (#12226)
Declare the two answers to "is it free?": counting may use the
Radar-overlaid catalog, deciding reads only the shipped FREE_MODEL_BUDGETS
plus :free suffix / zero pricing / grantsFreeAccess. No production behavior
change. A static-import guard discovers every non-client consumer of
freeModels.ts and asserts none reaches getRadarCatalog / getRadarCache,
mirroring client-bundle-no-server-only-10692 on the server arc.

Co-authored-by: Max <maxmad64@gmail.com>
2026-09-01 00:47:12 -03:00
KeelTrace
ba200b8d2b fix(chat-admission): clarify local 503 source (#12223) 2026-09-01 00:47:07 -03:00
Bob.Hou
2bd3023e09 fix(combos): prioritize SQLite row id over inner JSON id and notify delete errors (#12213)
When a combo is duplicated or imported, its inner data JSON blob may
retain a stale id from the template. withRowId previously kept the inner
string id instead of prioritizing the database primary key (row.id),
causing GET /api/combos to return mismatched ids and breaking subsequent
DELETE / PUT operations with 404.

Also add an error notification branch to handleDelete in the combos page
so failed delete requests surface actionable feedback instead of failing
silently.

Signed-off-by: Minxi Hou <houminxi@gmail.com>
2026-09-01 00:46:54 -03:00
Abhishek Sharma
d0529c0365 fix(providers): gate the Codex auto-ping usage read on the shared quota throttle (#12209)
Every Codex quota read goes through throttleQuotaFetch() — the #6009/#6058
gate that spaces genuine upstream calls so many accounts behind one IP do not
fire in the same second, which is the pattern documented to have got a Codex
OAuth token revoked. The auto-ping scheduler called getCodexUsage() directly,
so the one Codex path that runs unattended every 60s per connection was the
one skipping the mitigation written for Codex.

The tick walks connections sequentially but without spacing, so N enabled
connections still produce N upstream usage requests within a few hundred ms.

Gate the read on the same throttle, injected through deps like every other
effect in this module. Placed after the skip checks so a connection filtered
out by the circuit breaker, a cooldown or the failure cache does not consume
a slot and delay the connections that do reach the network.

This does not change the polling cadence. Codex sets pingWhenResetAtSlides
because its resetAt slides forward while the window is idle, so the per-tick
re-fetch is deliberate and is left alone.

Closes #11904
2026-09-01 00:46:50 -03:00
Vadim Zhyvylo
50a6f7e325 fix(translator): keep system content parts as Responses instructions (#12207)
The leading system message was read as `typeof content === "string" ?
content : ""`, so a Chat-Completions content-part array — valid for
`system`, and what every prompt-caching client sends — collapsed the
whole system prompt into an empty `instructions`. Upstream accepted the
request and reported a normal prompt_tokens count, so the model answered
with no instructions at all and nothing in the response said so.

Mid-conversation system turns already handled the array shape (#7056);
only the first one did not. Reuses buildResponsesTextParts() and joins
the text parts, since `instructions` is a string rather than a part array.

Co-authored-by: Vadim Zhyvylo <zhyvylo@involve.software>
2026-09-01 00:46:46 -03:00
Diego Rodrigues de Sa e Souza
63e4afa321 feat(dashboard): orchestration canvas — unified model + snapshot hook (part 1/2) (#12156)
Modelo puro do Orchestration Canvas (tipos, 3 mappers, mergeSnapshot com dedupe/staleness/cap, projeções flow+overview) + hook de polling com gatilho WS. Ciclo completo: 9 tasks TDD com review por task, review final whole-branch + fixes verificados 6/6, refactor de complexity re-validado (comportamento preservado). CI: 18 pass. Testes: 31 node:test + 2 vitest. Parte 2/2 (UI /dashboard/orchestration) na sequência.
2026-08-31 14:42:20 -03:00
Rahil Mavani
73db936f98 fix(api): keep registry width and type on embedding models (#11761)
* fix(api): keep registry width and type on embedding models

* docs: changelog fragment for embedding registry fix

* test(api): cover embedding width and type merge

Exercises /v1/models rather than the registry in isolation: a synced
model colliding with an embeddingRegistry entry must keep the width the
registry states, and a synced model the registry names must be typed as
an embedding model.

Fails on catalog.ts before e7fbb62 (2 failures), passes after.

Refs #11759
2026-08-31 14:16:41 -03:00
backryun
4b5266d3f8 fix(dev): isolate batch dispatch from instrumentation (#12081)
Co-authored-by: backryun <backryun@daonlab.local>
2026-08-31 14:14:02 -03:00
backryun
f8b01c966e fix(dev): make logging resources HMR-singleton (#12079)
Co-authored-by: backryun <backryun@daonlab.local>
2026-08-31 14:13:53 -03:00
backryun
e12fb110f9 [URGENT] fix(dev): reduce instrumentation executor fan-out (phase 3) (#12078)
* fix(dev): reduce instrumentation executor fan-out

* fix(ci): reduce credential refresh complexity

---------

Co-authored-by: backryun <backryun@daonlab.local>
2026-08-31 14:13:46 -03:00
backryun
2fbd0f5c25 fix(dev): isolate root layout settings reads (#12076)
Co-authored-by: backryun <backryun@daonlab.local>
2026-08-31 14:13:37 -03:00
Jacob Stoner
18c71b91dc feat(auto-combo): add weighted score router strategy (#12155)
Add a direct low-level mode for users who require explicit control over provider selection. score selects the highest configured weighted score directly while reusing the existing exploration rate.

Exact ties preserve configured candidate order. rules and all other strategies remain unchanged.
2026-08-31 14:10:50 -03:00
MSiva
9392bd55c2 fix(translator): preserve falsy primitive values in Gemini and Antigravity function response results (#12191) 2026-08-31 14:10:44 -03:00
Alvin T. Veroy
668beed5b8 fix(sse): absorb AbortError/request_signal_aborted in the client-abort crash guard (#12165)
OmniRoute's SSE teardown aborts in-flight legs with
`Error [AbortError]: request_signal_aborted` on client disconnects
(open-sse/utils/streamHandler.ts getClientAbortReason), and fetch/DOM
cancellation surfaces as AbortError with an abort-flavoured message.
isClientAbortError() only matched message 'aborted'/'Aborted' plus errno
codes, so these shapes fell through shouldSwallowUncaught() and were
re-thrown from the process-level uncaughtException/unhandledRejection
handlers — killing the whole server on a routine client disconnect
(observed as repeated exit-code-7 crashes with
'uncaughtException: Error [AbortError]: request_signal_aborted').

Match AbortError by name when the message is abort-flavoured; genuine
errors that merely mention 'abort' (e.g. TypeError) still crash loudly.

Tests: new unit cases for the SSE/DOM AbortError shapes, a child-process
regression proving the process survives both benign emissions with the
production no-logger install shape, and a child-process test proving
genuine errors keep crash semantics.
2026-08-31 14:10:33 -03:00
Bob.Hou
298ad0fd64 fix(translator): strip plaintext reasoning content for opaque responses backends (#12128) (#12171)
Signed-off-by: Minxi Hou <houminxi@gmail.com>
2026-08-31 14:10:26 -03:00
Diego Rodrigues de Sa e Souza
6706c382d8 docs(audit): align every published number with the code and harden check:docs-counts (#12200)
Fase 2 da auditoria código×docs 2026-08-31: ~90 divergências corrigidas em README/llm.txt(+42 espelhos)/SVGs/AGENTS.md/25+ docs; correções semânticas (breaker 8/12/2 + DEGRADED, webhooks sem eventos fantasma, ROUTE_GUARD_TIERS completo, API_REFERENCE sem fantasmas, reasoning 200); gate check:docs-counts endurecido (versão em prosa, patterns anti-evasão, superfície +llm.txt/mcp-server/omni-mcp/tier-flow) +5 testes; fonte do gerador de agent-skills corrigida (107/32 → 110/33) e mesma família varrida do Copilot prompt, 39 locales, skills/README, CONTRIBUTING e 2 guias.
2026-08-31 13:39:21 -03:00
Diego Rodrigues de Sa e Souza
b7a0c54139 chore(lint): batch 5 of #12146 — combos, endpoint, provider-stats, api-manager and costs react-hooks violations resolved (#12174)
* chore(lint): batch 5 of #12146 — resolve the react-hooks compiler violations in combos, endpoint, provider-stats, api-manager and costs

40 violations across 14 files, all real refactors (no eslint-disable, no new
suppressions; the areas' react-hooks entries are deleted from the freeze):

- set-state-in-effect (30): fetch-on-mount effects moved behind an async
  continuation (usePools, usePoolUsage, useApiKeyUsageLimits, Notion/Obsidian
  source cards, A2A/MCP dashboards, ComboControlCenterClient, provider-stats,
  combos modal loaders, ApiManager initial load); prop/state sync converted to
  state adjustment during render with prev tracking (ApiKeyUsageLimitCard,
  PoolWizard dimensions/reset/group snap, combos sortMethod, builder reset,
  builder stage guard, single-provider default, stale intelligent selection);
  localStorage reads became lazy useState initializers (combos usage guide).
- immutability / TDZ (8): effects that scheduled fetchers declared below them
  moved after the declarations (EndpointPageClient, ApiManagerPageClient,
  combos mount load); fetchData relocated below the per-key fetchers it calls.
- static-components (7): provider-stats SortIcon hoisted to module level.
- preserve-manual-memoization (2): ApiManager blockedModels dep destructured to
  a local; provider-scope derivation memoized so downstream memos see a stable
  dependency.

Validation: eslint (CI command with suppressions, --max-warnings 0) clean on the
14 files; dashboard typecheck within baseline; mutation gate no drift; area
tests 208/208 (node) + 29/29 (vitest).

Refs #12146

* chore(lint): batch 5 follow-up — hoist the render-adjustment predicates so the new-code complexity gates stay flat

The render adjustments added one cyclomatic branch to combos/page.tsx and one
cognitive point to PoolWizard (caught by the new-code gate on the committed
work); the compound conditions now live in pure module-level predicates.

* chore(lint): batch 5 follow-up 2 — PoolWizard render adjustments live in two small hooks

One consolidated hook tripped max-lines-per-function (>80) and the cognitive
budget; the dimensions and open/close adjustments now live in two focused hooks
with a shared WizardSetters type, and the group snap stays inline (one branch).
complexityNewCode=0, cognitiveComplexityNewCode=0.

* test(quota): repoint the two PoolWizard structural pins at the render-adjustment hook

quota-edit-opens-wizard anchored the pre-fill block on the old '} else if (editPool)'
effect literal and quota-pool-wizard-edit expected a bare 'if (editPool)' that only
existed there; both now anchor on the batch-5 structure (submit still branches via
if (!editPool)).
2026-08-31 03:10:49 -03:00
Diego Rodrigues de Sa e Souza
7f49b342b5 chore(lint): batch 0 of #12146 — type the call-log-cap sqlite rows instead of 45 as-any casts (#12157)
Typed CallLogRow / PayloadEnvelope views over the raw rows and payload envelopes;
(assert as any).equal back to assert.equal. Suppression entry for the file removed —
the gate now watches it for real. eslint (CI command) clean, suite 15/15.

Refs #12146
2026-08-30 20:35:33 -03:00
小妍儿 ✨
897c3f8c9d fix(cli): register alias resolver hooks in-thread on modern runtimes (#12073) (#12083)
Validado em worktree combinada com typecheck limpo, testes focados verdes, gates de file-size/complexity/cognitive-complexity/cycles OK. Obrigado!
2026-08-30 19:40:14 -03:00
Wahid Sadik
4e4522c285 fix(sse): strip type:'custom' from Claude tools on agentrouter dispatch (#12126)
Validado em worktree combinada com typecheck limpo, testes focados verdes, gates de file-size/complexity/cognitive-complexity/cycles OK. Obrigado!
2026-08-30 19:39:50 -03:00
quiterunner-commits
9aa7c2459a fix(sse): stop advertising video providers the dispatcher cannot run (#12131)
Validado em worktree combinada com typecheck limpo, testes focados verdes, gates de file-size/complexity/cognitive-complexity/cycles OK. Obrigado!
2026-08-30 19:39:46 -03:00
Abhishek Divekar
8a1d9bf910 feat(resilience): default the credential health check sweep to 60 minutes (#12138)
Validado em worktree combinada com typecheck limpo, testes focados verdes, gates de file-size/complexity/cognitive-complexity/cycles OK. Obrigado!
2026-08-30 19:39:42 -03:00
Bob.Hou
ececf91e9e fix(search): treat HTTP 432 and plan limit errors as transient cooldown (#12139)
Validado em worktree combinada com typecheck limpo, testes focados verdes, gates de file-size/complexity/cognitive-complexity/cycles OK. Obrigado!
2026-08-30 19:39:36 -03:00
quiterunner-commits
43f2b2c288 fix(sse): refuse an AI Horde queue that cannot fit the request budget (#12143)
Validado em worktree combinada com typecheck limpo, testes focados verdes, gates de file-size/complexity/cognitive-complexity/cycles OK. Obrigado!
2026-08-30 19:39:33 -03:00