Commit Graph

5897 Commits

Author SHA1 Message Date
Will Gordon
f1fcdbfa6e fix(executors): route current Claude generations through Vertex partner endpoint (#8852)
* fix(executors): route current Claude generations through Vertex partner endpoint

PARTNER_MODELS pinned three Claude 3.x prefixes (claude-3-5-sonnet,
claude-3-opus, claude-3-haiku). Every newer Claude generation on Vertex
(claude-sonnet-4-6, claude-haiku-4-5, etc.) fell through to the
Google-publisher branch instead, producing an invalid
publishers/google/models/claude-... path.

Replace the pinned prefixes with a single generic "claude-" prefix:
any Claude model on Vertex is always an Anthropic partner model, never
a Google one, so this can't go stale again the way pinned version
strings did.

Fixes #1985

* docs: add changelog fragment for #8852
2026-07-28 11:49:52 -03:00
Diego Rodrigues de Sa e Souza
ed6a19e05b fix(ci): raise the git ls-files buffer in check:tracked-artifacts (#8844)
* fix(ci): raise the git ls-files buffer in check:tracked-artifacts

`execFileSync` defaults to a 1 MiB stdout buffer and throws ENOBUFS past it.
`git ls-files -s` on this repo is already at 1,042,494 bytes across 11,091
tracked files — 6,082 bytes from the ceiling. Any PR adding roughly sixty files
crosses it.

That matters more than a failing script: the check runs on pre-commit, so once
the listing crosses 1 MiB, committing breaks for everyone working the repo, not
just for the change that happened to cross it. It is not a hypothetical — the
private EE fork hit it this week when a sync landed ~214 translation files and
pushed the listing 504 bytes over; every commit there failed the hook until
this same fix landed.

Both call sites now share a GIT_LS_OPTS with a 64 MiB ceiling — far above any
plausible tree, rather than just above today's, since the listing only grows.

* docs(changelog): add fragment for #8844

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
2026-07-28 10:25:21 -03:00
Bob.Hou
7193b0a433 feat(oauth): support web client type for remote Google OAuth (#8845)
* feat(oauth): support web client type for remote Google OAuth

Google Desktop app OAuth clients require loopback redirect URIs per
policy, which breaks remote deployments where the browser cannot reach
127.0.0.1 on the server. Add ANTIGRAVITY_OAUTH_CLIENT_TYPE env var:
when set to 'web' and OMNIROUTE_PUBLIC_BASE_URL is configured, the
loopback redirect URI is upgraded to the public base URL. Default
behavior (desktop) is unchanged.

Enables remote deployments without SSH tunneling by registering a Web
application OAuth client in Google Cloud Console.

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

* docs(changelog): add fragment for #8845

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

---------

Signed-off-by: Minxi Hou <houminxi@gmail.com>
Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
2026-07-28 10:25:12 -03:00
Bob.Hou
38cee62d42 fix(antigravity): discover projectId during token refresh (#8842)
* fix(antigravity): discover projectId during token refresh

The initial OAuth exchange can fail to populate projectId via
loadCodeAssist (network timeout, account not yet onboarded). The
runtime transformRequest path already recovers via
ensureAntigravityProjectAssigned, but refreshCredentials did not --
after a token refresh the per-token memoization cache is invalidated
(new access token = new cache key), so every subsequent request
triggers a fresh loadCodeAssist round-trip that may fail again.

Add a best-effort ensureAntigravityProjectAssigned call in
refreshCredentials when projectId is empty, mirroring the pattern in
transformRequest. Persist the discovered id so it survives the next
refresh or restart.

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

* chore(quality): rebaseline antigravity.ts and test file-size

antigravity.ts grew from 1493 to 1528 lines (+35) with projectId
discovery in refreshCredentials. executor-antigravity.test.ts is a new
test file at 1098 lines (above cap 1000) with 4 new test cases.

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

* fix(antigravity): match ExecutorLog arity in the refresh discovery log

`ExecutorLog.info` (open-sse/executors/base.ts) is `(tag, message) => void` —
two parameters. The discovery log passed a third metadata object, which failed
typecheck:core with TS2554 on antigravity.ts:777. Bind the message to a local
and pass two arguments, matching the sibling warn on the catch branch. Kept to
two lines so the file stays at its frozen size; ran Prettier, which also wrapped
the pre-existing over-width `const msg` line below.

Also adds the changelog fragment for the fix.

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

---------

Signed-off-by: Minxi Hou <houminxi@gmail.com>
Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
2026-07-28 10:25:03 -03:00
Suman
2675b650b5 fix(cli): escape claude args and stop aborting on exit in omniroute launch (#8837)
* fix(cli): escape claude args and stop aborting on exit in omniroute launch

Windows launches go through spawn(..., { shell: true }), which joins argv
with plain spaces and no escaping (Node DEP0190). Any argument containing a
space was split, so `omniroute launch -p "two words"` reached claude as
`-p two` plus stray positionals, and the prompt was silently truncated.

Escape each argument for cmd.exe instead: CRT argv rules first (double the
backslashes preceding a quote, escape embedded quotes, wrap in quotes), then
cmd metacharacters caret-escaped twice. The second pass is required because
claude.cmd is an npm shim that re-parses %* on the way to node; with a single
pass arguments still truncated at the first `&` or `|`.

The command action also called process.exit() on any non-zero exit. That tore
the loop down while the exited child's inherited stdio handles were still
closing and aborted the process with a libuv assertion
(!(handle->flags & UV_HANDLE_CLOSING), src/win/async.c:94, exit 0xC0000409)
instead of returning claude's exit code. Set process.exitCode and let the
loop drain.

Extracts resolveClaudeSpawn() alongside the existing resolveCodexSpawn()
precedent so both the platform choice and the escaping are unit-testable.

Tests: tests/unit/cli/launch-windows-spawn-args.test.ts (new, 7 tests).
Four pure-function tests plus golden strings pin the exact encoding on every
platform; a Windows-only test round-trips argv through a real npm-style .cmd
shim and asserts embedded quotes, `&`, `|`, `%PATH%`, `^`, `!`, a trailing
backslash and an empty string all arrive byte-identical.

Note: bin/cli/commands/launch-codex.mjs carries the identical defect (same
shell:true concatenation, same process.exit) and is left unchanged here.

* docs(changelog): add fragment for #8837

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

---------

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
2026-07-28 10:24:54 -03:00
hppsc1215
85f1d78d11 fix(ghe-copilot): route OpenAI-native models via Responses API (#8835)
* fix(ghe-copilot): route OpenAI-native models via Responses API

- Add targetFormat: 'openai-responses' to gpt-5.4-mini, gpt-5.3-codex, gpt-5-mini,
  mai-code-1-flash, and oswe-vscode-prime in ghe-copilot registry.
- Register gpt-5.6-sol, gpt-5.6-terra, and gpt-5.6-luna with openai-responses targetFormat.
- Update GheCopilotExecutor.buildUrl() to route openai-responses and codex models to
  <gheUrl>/responses while keeping Claude and Gemini on /chat/completions.
- Add unit tests verifying targetFormat parity and buildUrl endpoint routing.

* docs(changelog): add fragment for #8835

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

---------

Co-authored-by: Alex <sefias_methue@hotmail.de>
Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
2026-07-28 10:24:45 -03:00
Tito Fauzan Putra
0809c73430 fix(providers): correct Codex GPT-5.6 context window (#8838)
* fix(providers): correct Codex GPT-5.6 context window (#7702)

* docs(changelog): add fragment for #8838

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

* test(providers): align the remaining GPT-5.6 context-window assertions

Three suites assert the pinned GPT_5_6_CODEX_CAPABILITIES contract through the
VS Code and provider-models routes, and still expected 372000. They only surface
in a full run, so the focused loop on this PR stayed green while `npm run test:unit`
failed with five `272000 !== 372000`.

The two conservative-merge cases in provider-models-route-codex keep testing what
they tested: live 999999 still exceeds the pinned value (pinned wins) and live
100000 is still below it (live wins). Only the pinned number and the comments
naming it move.

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

---------

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
2026-07-28 10:24:36 -03:00
Diego Rodrigues de Sa e Souza
3b515d90b3 fix(api): stop /v1/models rebuilding the catalog on almost every request (#8833)
* fix(api): stop /v1/models rebuilding the catalog on almost every request

The response cache added by #6408 memoized the serialized body for
`modelCatalogCacheTtlMs`, defaulted to 1500 ms. On a real install the builder
takes far longer than that: measured on the production VPS, ~49 s for a 1.3 MB /
2645-model catalog. Any two requests more than 1.5 s apart therefore both missed
the fresh window, and the second fell into stale-while-revalidate — which
rebuilds via `setTimeout(…, 0)` and, because the builder is overwhelmingly
synchronous under the single-threaded App Router, pins the event loop, so even
the "served immediately" stale body only reaches the client once the rebuild
finishes. Net effect: ~50 s on essentially every call.

Measured on the VPS (1 cold build + 5 sequential requests):

    cold = 48.93s
    req1 =  3.19s   ← the only hit
    req2 = 50.51s   req3 = 50.93s   req4 = 47.43s   req5 = 52.28s

Raise the default to 60 s. A short TTL is redundant with the invalidation this
cache already has: `invalidateDbCache()` bumps `modelCatalogCacheVersion` on
every settings/connections/combos/pricing write and
`dropCatalogCacheIfStateChanged()` drops the whole cache the moment it moves, so
post-write freshness never depended on the TTL. What the TTL governs is the
"nothing was written" case, where replaying a body built seconds ago is the
point of the cache. 60 s matches the ceiling the settings schema already allows
for the override, so the default can never exceed what an operator may configure.

The value that actually takes effect is the settings default, not the constant:
`catalog.ts` resolves `dbSettings.cache?.modelCatalogCacheTtlMs ??
CATALOG_CACHE_TTL_MS_DEFAULT`, and the `??` never falls through while a settings
default is declared. Raising only the constant is a silent no-op — which is how
the first attempt at this fix measured identical to no fix at all. All three
declarations are aligned and a test pins them together.

* docs(changelog): add fragment for #8833

---------

Co-authored-by: diegosouzapw <diegosouzapw@users.noreply.github.com>
2026-07-28 03:55:20 -03:00
Diego Rodrigues de Sa e Souza
aa85fa02bb feat(api): prompt-cache health summary endpoint and analytics tab (#8827)
Adds GET /api/usage/cache-health and a Cache Health tab under
/dashboard/analytics, both backed by a pure summary over the cache columns
already present in call_logs.

Motivated by a production diagnosis where the aggregate ratio was actively
misleading. The window read 24.8M cached tokens and wrote 9.5M — a
write/read of 0.385, which reads as merely mediocre. The actual shape was
very different: the median call wrote 848 tokens while 18% of the calls
carried 94% of every written token, and two models in the same window sat
at 0.13 (Sonnet) and 0.51 (Opus). Averaging hid all three facts.

So the summary reports what the average cannot: the distribution
(p50/p90/p99), the concentration (how few calls carry how much of the
write), and the per-model split. The heavy-write threshold is relative to
the window (10x the median, floored at 1024) because a cutoff tuned for
130k-token conversations reports nothing at all on 2k-token ones; 1024 is
the minimum Anthropic bills for cache creation, below which a write carries
no signal.

Calls that neither read nor wrote are counted separately from thrash — a
route that does not cache is an absence of caching, not a sick cache — and
only successful calls are summarized, since a 4xx/5xx never reached the
provider cache and would dilute the ratio.

Tests cover the summary (8) and the route (6, against a real SQLite so the
WHERE clause itself is exercised), including that an internal failure
answers 500 without leaking the stack trace, the SQL text or a table name.
2026-07-28 03:53:28 -03:00
Diego Rodrigues de Sa e Souza
c8f1d62de5 fix(dashboard): surface quota pool delete failures instead of failing silently (#8829)
The handler awaited the DELETE and never looked at the response:

    if (!confirm(t("removeConfirm"))) return;
    await fetch(`/api/quota/pools/${id}`, { method: "DELETE" });
    await mutate();

When the request failed — a 401 from an expired session, a 500, a dropped
connection — the page revalidated, the card stayed exactly where it was, and
nothing was shown. From the operator's side the click was indistinguishable
from a misclick, so the natural reaction is to click again. The page had no
error surface at all, unlike the sibling flow in the API manager which already
checked res.ok and rendered the message.

Adds a dismissible alert above the header, fed by both failure paths: a
non-ok response (appending the API's message when it sends one) and a thrown
request, which has no response to read. The alert clears on the next attempt,
so a transient failure does not leave a stale error on screen.

Reported against two boxes on 2026-07-28. The delete itself was working there
— the audit log shows the pools were removed — which is exactly what this
change makes visible either way.

Tests (vitest/jsdom, 5): non-ok response, thrown request, success path stays
quiet and still revalidates, a dismissed confirmation issues no DELETE at all,
and an earlier error clears once a later delete succeeds.
2026-07-28 03:35:28 -03:00
Diego Rodrigues de Sa e Souza
18c1a2b43e docs: restore the Polish API_REFERENCE removed by #8823 (#8831)
* docs: restore the Polish API_REFERENCE removed by #8823

`docs/reference/API_REFERENCE.md` lists Polish in its language index, but the
target file no longer exists: #8823 ("replace outdated Polish docs with
translation from latest English") deleted
`docs/i18n/pl/docs/reference/API_REFERENCE.md` without writing a replacement.
Every other one of the 31 linked languages still has the file.

That leaves `check:doc-links` red — and because the Docs Gates job only runs on
pull requests, the branch itself reports green while carrying the break. The
first PR opened afterwards is what surfaces it, and it blocks every PR until
fixed.

Restored from `71c5a7592^`. The content is the pre-#8823 translation, which is
by definition outdated relative to the English source — but an outdated
translation behind a working link is strictly better than a 404, and the next
translation pass will overwrite it. `check:doc-links` now passes (813 internal
links, 0 broken).

The same commit also removed four other Polish files
(`cloudflare-zero-trust-guide.md`, `features/context-relay.md`,
`reference/CLI-TOOLS.md`, `reference/ENVIRONMENT.md`). None of them is linked
from a scanned index, so none breaks the gate; they are noted here so a later
translation pass can decide whether they should come back too.

* chore(quality): freeze the pre-existing exhaustive-deps error in ProviderAccountRoutingCard

Second half of unbreaking the base. `lint:json --max-warnings 0` fails on
`ProviderAccountRoutingCard.tsx:87` — `save` calls `load()` but declares only
`[providerKey]`. Same root cause as the broken doc link in the previous commit:
the Lint job is skipped on pushes to `release/**`, so the branch reports green
while carrying the violation, and every PR inherits it.

Frozen rather than fixed: adding `load` to the dependency array changes when
the callback is recreated in a settings component, and that belongs in a change
that can verify the card's behaviour. The entry keeps the gate meaningful for
genuinely new warnings instead of leaving it red for everyone.

With this and the restored Polish file, both gates pass on the base again.
2026-07-28 03:06:35 -03:00
rinseaid
d30f484089 fix(reasoning): sanitize streamed K3 think tags (#8821)
* fix(reasoning): sanitize streamed K3 think tags

* refactor(stream): move think-tag helpers into thinkTagParser leaf module

open-sse/utils/stream.ts is frozen at 2887 lines with zero headroom, and the
kimi-coding-apikey think-tag handling added here pushed it to 2951. The parsing
half of that work has no SSE dependency, so it moves to thinkTagParser.ts (an
unfrozen leaf): the open-tag lookahead predicate and the end-of-stream flush
delta assembly. stream.ts keeps only the SSE envelope - enqueue, payload
collection, logging.

Getting back under the frozen ceiling needed more than just the PR's own
added lines, since even the fully self-gating helpers still cost a handful of
call-site lines stream.ts has zero room for. Along the way this also
deduplicates a synthetic chat-completion-chunk literal that was copy-pasted
three times in stream.ts (textual tool-call flush, think-tag flush, terminal
finish_reason synthesis) into one buildSyntheticChatChunk() in
streamHelpers.ts - pure DRY, no behavior change.

Behavior is unchanged: same tests, same counts.

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

---------

Co-authored-by: rinseaid <rinseaid@rinseaid.net>
Co-authored-by: diegosouzapw <diegosouzapw@users.noreply.github.com>
Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
2026-07-28 01:27:20 -03:00
backryun
2f5e1858bb fix(types): normalize Kie transcription results (#8824) 2026-07-28 00:45:25 -03:00
Adrian Rogala
71c5a7592f docs: replace outdated Polish docs with translation from latest English (#8823) 2026-07-28 00:45:21 -03:00
MumuTW
b4776a2d84 test(quality): fail loudly when a source-scanning guard is negative-only (#8619)
* test(quality): fail loudly when a source-scanning guard is negative-only

A negative guard — assert.doesNotMatch(src, /x/) or src.includes(x) === false —
passes against an empty string. Once the code it guards is extracted into another
file the parent no longer contains the string, so the assertion keeps passing while
protecting nothing. The regression coverage is deleted with no test turning red,
which is exactly the failure mode the god-file decomposition campaign (#8617) is
about to trigger 90-odd times.

Adds tests/unit/source-scanner-guards.test.ts: a hard gate (no baseline, no
allowlist) requiring every test variable bound to project source to carry at least
one positive anchor. Classification runs on logical statements with strings, regexes
and comments blanked out, so a guard wrapped across lines cannot slip past — that
folding is what exposed 3 of the 7 violations.

Fixes all 7 violations across 6 files with one stable top-level export anchor each.
Two were security scope guards held only by multi-line negative assertions: the SSRF
guards on /api/sync/initialize (#323) and the proxy-bypass guards on chatHelpers.ts
and chatCore.ts (#3226) — the latter anchored on handleChatCore precisely because
that file is a decomposition target.

Adds tests/_helpers/readSrc.ts, a repo-root-relative reader that throws on a missing
or empty file instead of returning "".

Refs #8617

* docs(changelog): number the fragment for #8619

* chore(skills): sync cli-backup-sync SKILL.md with catalog

Same tip fix as #8657 so Merge integrity is green without waiting for
that PR to land. Regenerated via generate-agent-skills --apply.
2026-07-27 23:57:30 -03:00
backryun
67d13dc7a8 fix(types): declare idempotency input contracts (#8820) 2026-07-27 23:57:26 -03:00
backryun
6133939acd fix(types): align web fallback contracts (#8819) 2026-07-27 23:57:23 -03:00
backryun
07ce14cd82 fix(types): type memory skills injection logger (#8816) 2026-07-27 23:57:19 -03:00
diegosouzapw
d6c06932ec chore(quality): re-pin ceilings after merge-train 3 + register CF-1010 test
file-size: apiKeys.ts 1518 -> 1529 (#8805, treating cx/* and codex/* as
equivalent provider prefixes in API-key model permissions) and chatCore.ts
5006 -> 5020 (#8806, passing the real response payload into plugin onResponse
hooks instead of a hardcoded {status:200}). Both extend existing call sites
rather than adding a branch.

stryker: account-fallback-cf1010-no-retry-8775.test.ts covers
accountFallback.ts but was missing from tap.testFiles, which would redden
Fast Quality Gates on every subsequent PR.
2026-07-27 22:39:06 -03:00
Diego Rodrigues de Sa e Souza
b53bd968a2 fix(security): remove quadratic trailing-slash trim in Alibaba endpoint normalization (#8333)
CodeQL js/polynomial-redos (alerts #765/#766).

`/\/+$/` has no left anchor, so the engine retries the match at every
start offset and each attempt re-walks the whole slash run before failing
`$` — O(n^2) on a connection baseUrl made of many slashes. Measured on
the vulnerable code: 10k slashes = 102ms, 30k = 968ms, 60k = 4028ms
(clean quadratic); through the public resolvers the same input took 22s.

providerSpecificData.baseUrl is operator-supplied config and reaches the
trim via isFamilyPresetUrl() -> normalizeEndpoint() and both resolve*Url()
helpers, so the input is reachable.

Replaces all three identical occurrences with a linear charCodeAt scan.
CodeQL only flagged two of them; the third (resolveAlibabaProviderModelsUrl)
carries the same defect and is fixed here rather than left behind.

Behavior is unchanged: every trailing slash is still removed (not a bounded
subset), interior slashes are preserved, and an all-slash string still
collapses to empty — asserted by the new behavior test.
2026-07-27 22:38:30 -03:00
Alex
53f8284842 fix(api): enforce image generation API key auth (#8306)
* fix(api): enforce image generation API key auth

* fix(api): align image route auth guard with clientApiPolicy

The route-level guard added for image generation was stricter than the
authz middleware that already fronts /api/v1/* (src/proxy.ts →
clientApiPolicy), so requests the pipeline admits were 401'd by the
handler:

- A cookie-authenticated dashboard session was rejected under
  REQUIRE_API_KEY=true. The dashboard Media page
  (dashboard/cache/media) and the Playground call these routes with a
  session and no Bearer — the same mismatch already fixed for
  /api/playground/presets.
- A presented invalid key was rejected even with REQUIRE_API_KEY=false,
  where clientApiPolicy (#2257) and the sibling /v1/embeddings and
  /v1/web/fetch routes degrade a stale CLI key to anonymous instead.

Extract the shared guard into shared/utils/clientApiRouteAuth so both
image routes (and future /v1 handlers) mirror the middleware contract
instead of re-deriving it, and drop the now-dead auth imports.

Also switch the call-log attribution fallback back to `||`: with `??`,
an empty-string apiKeyId/apiKeyName would be persisted verbatim and
would block the request-scoped context, which the previous
`entry.apiKeyId || null` never did.

Tests: cover the dashboard-session and keyless-mode-invalid-key
branches, and split the auth/attribution cases into
image-generation-route-auth.test.ts to stay under the 800-line
new-test-file cap.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>

---------

Co-authored-by: alexey.nazarov@softmg.ru <alexey.nazarov@softmg.ru>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 22:38:25 -03:00
Alan Vb
6a59c09460 feat(search): add Firecrawl search provider (#8814)
* feat(search): add Firecrawl search provider

Introduce the Firecrawl search provider to handle `POST /v1/search` requests via Firecrawl /v2/search API (sources: web|news)

* chore(changelog): add changelog on firecrawl search provider support

* fix(search): satisfy file-size freeze and APIKEY count

---------

Co-authored-by: allanvb <allanvb@users.noreply.github.com>
2026-07-27 22:38:21 -03:00
NBN-N3
ff168ab086 fix(electron): use NEXT_DIST_DIR when stripping stale native modules (#8794)
removeNativeModules() was called with a hardcoded ".next" path while the actual
distDir is NEXT_DIST_DIR (".build/next" by default). Because the function
early-returns when the directory does not exist, the cleanup silently no-opped
and the plain-Node-ABI better-sqlite3 copy produced by `next build` survived
into the packaged app.

At runtime the standalone server runs under ELECTRON_RUN_AS_NODE, so it needs
the Electron ABI (148 for electron 43). Loading the ABI-137 copy fails with
ERR_DLOPEN_FAILED, the app falls back to the sql.js WASM driver, the connection
is closed and retried in a loop, WASM memory is never reclaimed and the process
OOMs -> HTTP 500 on every route.

Also adds assertNoStaleHashedNatives() so a wrong baseDir fails the build
instead of silently shipping a broken installer. This has regressed at least
twice (#1497 with ABI 127 vs 145, #7082/#7681 with 137 vs 148).

Refs #7082, #7681, #1497, #8792. Supersedes the abandoned #7123.
2026-07-27 22:38:18 -03:00
backryun
0bd7ceadf8 fix(types): declare virtual chaos combo config (#8815) 2026-07-27 22:38:15 -03:00
backryun
5f5f4a589d fix(types): preserve compression detail config shapes (#8812) 2026-07-27 22:38:11 -03:00
backryun
1b1e962221 fix(types): preserve reasoning policy record shapes (#8811) 2026-07-27 22:38:08 -03:00
backryun
308d3f06e2 fix(types): export web executor types from their module (#8810) 2026-07-27 22:38:04 -03:00
Prudhvi Vuda
253856544f fix(sse): pass real response payload into plugin onResponse hooks (#8806)
Plugins always received a hard-coded {status:200} stub, so hooks like
request-logger never saw response bodies. Forward translated JSON for
non-streaming completions and a streamed flag for SSE without reading
the body twice.

Closes #8711
2026-07-27 22:38:01 -03:00
Prudhvi Vuda
3e6684ebb4 fix(api): treat cx/* and codex/* as equivalent API-key model permissions (#8805)
Dashboard Codex restrictions use the public cx/ alias while /v1/responses
normalizes bare Codex IDs to codex/, so allow/block checks falsely 403'd.
Expand permission candidates via the provider registry alias map.

Closes #8803
2026-07-27 22:37:58 -03:00
AmirHossein Rezaei
0e14d66a71 fix(dashboard): restore Usage Model Breakdown column sorting (#8769) (#8802)
Extract ModelTable from the charts god-file and drive header clicks through a
single sort state object plus a pure sorter so column toggles reliably reorder
rows. Compute missing share pct from summary totals when the analytics API
omits it.
2026-07-27 22:37:55 -03:00
AmirHossein Rezaei
4d66dd113f fix(resilience): honor Cloudflare 1010 retryable:false — skip COOLDOWN_RETRY (#8775) (#8800)
Api-key 403 bodies with Cloudflare error 1010 / browser_signature_banned /
retryable:false were treated as short AUTH_ERROR cooldowns, so the chat loop
waited ~21–33s before falling through. Return cooldownMs:0 so the tier fails
fast without permanently banning the account.
2026-07-27 22:37:51 -03:00
AmirHossein Rezaei
1c2143182c fix(combo): fail open when strict context filter empties unknown-only pools (#8786) (#8798)
Strict contextFilterMode excluded every target whose context limit was missing
from the capability catalog, so otherwise-executable combos returned 404
no_executable_targets. Restore unknown-context targets when no known-good
survivor remains, surface context_requirements_exhausted from targetResolution,
and keep the empty-pool payload in pinRecovery after the #8592 split.
2026-07-27 22:37:48 -03:00
diegosouzapw
1bb3093dcf chore(quality): re-pin chatCore.ts ceiling after merge-train 2
#8595 (compact Responses multi-turn images before the context hard-reject)
grows open-sse/handlers/chatCore.ts 4955 -> 5006. The growth is irreducible at
the existing compaction chokepoint: a last-resort retry against the concrete
token budget plus the estimateFinalInputTokens helper, both wired into the
pre-existing call site rather than a new branch. Covered by
tests/unit/8560-responses-image-compaction.test.ts.
2026-07-27 21:13:20 -03:00
Rouzbeh†
8e5dc0de1b fix(client): rewrite absolute fetch/EventSource paths under basePath (#8515)
* fix(client): rewrite absolute fetch/EventSource paths under basePath

Absolute browser calls like fetch("/api/...") and new EventSource("/api/...")
do not honor Next.js basePath, so subpath deploys (OMNIROUTE_BASE_PATH) break
dashboard health checks, settings APIs, and SSE unless a reverse proxy rewrites
the domain root.

- Add withBasePath / getDeployBasePath helpers
- Install ref-counted fetch + EventSource rewrite when basePath is set
  (same pattern as installDashboardCsrfFetch)
- Mount BasePathNetworkProvider at the root so login works too
- Mirror OMNIROUTE_BASE_PATH to NEXT_PUBLIC_OMNIROUTE_BASE_PATH for the client
- Document in .env.example; unit tests for rewrite rules

* docs(changelog): add fragment for #8515 basePath client fetch

* test(client): move basePath tests into a scanned dir and fix no-op call-shape asserts

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

* fix(client-sweep-8515): restore CHANGELOG #8471 bullet and fix basePath TS2322

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

---------

Co-authored-by: rqzbeh <rqzbeh@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>
Co-authored-by: diegosouzapw <diegosouzapw@users.noreply.github.com>
2026-07-27 21:12:49 -03:00
NOXX - Commiter
530221aa0f fix(hyperagent): sticky thread for agentic tool loops (Claude Code) (#8470)
* fix(hyperagent): sticky thread for agentic tool loops (Claude Code)

Follow-up to #7994. When a reverse-conversion proxy rewrites assistant
text between turns (Intent+JSON -> native tool_calls -> re-serialized tool
text), conversationFingerprint(prefix) no longer matches the key stored
after turn 1, so HyperAgent created a new thread and multi-turn tool
results appeared as a cold start.

- Key sticky sessions by root user task (normalize pin wrappers)
- Flatten Anthropic tool_use / tool_result for lastUserText + fingerprints
- Regression tests for mutated-assistant tool loops

Tests: tests/unit/executor-hyperagent.test.ts (19/19)

* chore(quality): rebaseline for PR #8470 own-growth (hyperagent sticky thread)

open-sse/executors/hyperagent.ts grows 937->1026 lines and gains one new
cognitive/cyclomatic-complexity violation (extractMessageText, from the
new Anthropic tool_use/tool_result flattening branches) on top of
inherited base-tip drift already present on origin/release/v3.8.49
(file-size was already at cap; cognitive-complexity 951->956 and
cyclomatic-complexity 2130->2169 drift predates this PR). Rebaselined
file-size to 1026, cognitiveComplexity to 957, complexity count to 2170,
matching measured values on the merged tree.

Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com>

---------

Co-authored-by: ikelvingo <im.kelvinwong@gmail.com>
Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com>
Co-authored-by: diegosouzapw <diegosouzapw@users.noreply.github.com>
Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
2026-07-27 21:12:45 -03:00
Wilson
f8487648c8 fix(resilience): keep resource 404s from cooling models (#8756) 2026-07-27 21:12:42 -03:00
Austin Liu
2bd5076800 fix(resilience): add UND_ERR_SOCKET to PROXY_UNREACHABLE_ERROR_CODES (#8788) (#8795)
Fixes #8788.

- Added 'UND_ERR_SOCKET' to PROXY_UNREACHABLE_ERROR_CODES so socket disconnects and mid-stream closes are properly tagged with code PROXY_UNREACHABLE and errorCode proxy_unreachable.
- Ensured tagProxyUnreachable normalizes error code to PROXY_UNREACHABLE and errorCode to proxy_unreachable when catching socket-level disconnects.
- Added test in proxyfetch-undici-retry.test.ts verifying UND_ERR_SOCKET classification.
2026-07-27 21:12:38 -03:00
dependabot[bot]
aecf2fccef deps: bump the production group across 1 directory with 18 updates (#8793)
Bumps the production group with 18 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [@aws-sdk/client-bedrock-runtime](https://github.com/aws/aws-sdk-js-v3/tree/HEAD/clients/client-bedrock-runtime) | `3.1091.0` | `3.1096.0` |
| [@lobehub/icons](https://github.com/lobehub/lobe-icons) | `5.14.0` | `5.15.0` |
| [@modelcontextprotocol/sdk](https://github.com/modelcontextprotocol/typescript-sdk) | `1.29.0` | `1.30.0` |
| [@toon-format/toon](https://github.com/toon-format/toon) | `2.3.1` | `4.1.0` |
| [fumadocs-core](https://github.com/fuma-nama/fumadocs) | `16.11.5` | `16.13.0` |
| [fumadocs-ui](https://github.com/fuma-nama/fumadocs) | `16.11.5` | `16.13.0` |
| [jose](https://github.com/panva/jose) | `6.2.3` | `6.2.4` |
| [lucide-react](https://github.com/lucide-icons/lucide/tree/HEAD/packages/lucide-react) | `1.25.0` | `1.27.0` |
| [material-symbols](https://github.com/marella/material-symbols/tree/HEAD/material-symbols) | `0.45.8` | `0.45.9` |
| [next](https://github.com/vercel/next.js) | `16.2.11` | `16.2.12` |
| [next-intl](https://github.com/amannn/next-intl) | `4.13.3` | `4.13.4` |
| [playwright](https://github.com/microsoft/playwright) | `1.61.1` | `1.62.0` |
| [react](https://github.com/react/react/tree/HEAD/packages/react) | `19.2.7` | `19.2.8` |
| [react-dom](https://github.com/react/react/tree/HEAD/packages/react-dom) | `19.2.7` | `19.2.8` |
| [react-is](https://github.com/react/react/tree/HEAD/packages/react-is) | `19.2.7` | `19.2.8` |
| [recharts](https://github.com/recharts/recharts) | `3.10.0` | `3.10.1` |
| [smol-toml](https://github.com/squirrelchat/smol-toml) | `1.7.0` | `1.7.1` |
| [undici](https://github.com/nodejs/undici) | `8.8.0` | `8.9.0` |



Updates `@aws-sdk/client-bedrock-runtime` from 3.1091.0 to 3.1096.0
- [Release notes](https://github.com/aws/aws-sdk-js-v3/releases)
- [Changelog](https://github.com/aws/aws-sdk-js-v3/blob/main/clients/client-bedrock-runtime/CHANGELOG.md)
- [Commits](https://github.com/aws/aws-sdk-js-v3/commits/v3.1096.0/clients/client-bedrock-runtime)

Updates `@lobehub/icons` from 5.14.0 to 5.15.0
- [Release notes](https://github.com/lobehub/lobe-icons/releases)
- [Changelog](https://github.com/lobehub/lobe-icons/blob/master/CHANGELOG.md)
- [Commits](https://github.com/lobehub/lobe-icons/compare/v5.14.0...v5.15.0)

Updates `@modelcontextprotocol/sdk` from 1.29.0 to 1.30.0
- [Release notes](https://github.com/modelcontextprotocol/typescript-sdk/releases)
- [Commits](https://github.com/modelcontextprotocol/typescript-sdk/compare/v1.29.0...1.30.0)

Updates `@toon-format/toon` from 2.3.1 to 4.1.0
- [Release notes](https://github.com/toon-format/toon/releases)
- [Commits](https://github.com/toon-format/toon/compare/v2.3.1...v4.1.0)

Updates `fumadocs-core` from 16.11.5 to 16.13.0
- [Release notes](https://github.com/fuma-nama/fumadocs/releases)
- [Commits](https://github.com/fuma-nama/fumadocs/compare/fumadocs@16.11.5...fumadocs@16.13.0)

Updates `fumadocs-ui` from 16.11.5 to 16.13.0
- [Release notes](https://github.com/fuma-nama/fumadocs/releases)
- [Commits](https://github.com/fuma-nama/fumadocs/compare/fumadocs@16.11.5...fumadocs@16.13.0)

Updates `jose` from 6.2.3 to 6.2.4
- [Release notes](https://github.com/panva/jose/releases)
- [Changelog](https://github.com/panva/jose/blob/main/CHANGELOG.md)
- [Commits](https://github.com/panva/jose/compare/v6.2.3...v6.2.4)

Updates `lucide-react` from 1.25.0 to 1.27.0
- [Release notes](https://github.com/lucide-icons/lucide/releases)
- [Commits](https://github.com/lucide-icons/lucide/commits/1.27.0/packages/lucide-react)

Updates `material-symbols` from 0.45.8 to 0.45.9
- [Release notes](https://github.com/marella/material-symbols/releases)
- [Commits](https://github.com/marella/material-symbols/commits/v0.45.9/material-symbols)

Updates `next` from 16.2.11 to 16.2.12
- [Release notes](https://github.com/vercel/next.js/releases)
- [Commits](https://github.com/vercel/next.js/compare/v16.2.11...v16.2.12)

Updates `next-intl` from 4.13.3 to 4.13.4
- [Release notes](https://github.com/amannn/next-intl/releases)
- [Changelog](https://github.com/amannn/next-intl/blob/main/CHANGELOG.md)
- [Commits](https://github.com/amannn/next-intl/compare/v4.13.3...v4.13.4)

Updates `playwright` from 1.61.1 to 1.62.0
- [Release notes](https://github.com/microsoft/playwright/releases)
- [Commits](https://github.com/microsoft/playwright/compare/v1.61.1...v1.62.0)

Updates `react` from 19.2.7 to 19.2.8
- [Release notes](https://github.com/react/react/releases)
- [Changelog](https://github.com/react/react/blob/main/CHANGELOG.md)
- [Commits](https://github.com/react/react/commits/v19.2.8/packages/react)

Updates `react-dom` from 19.2.7 to 19.2.8
- [Release notes](https://github.com/react/react/releases)
- [Changelog](https://github.com/react/react/blob/main/CHANGELOG.md)
- [Commits](https://github.com/react/react/commits/v19.2.8/packages/react-dom)

Updates `react-is` from 19.2.7 to 19.2.8
- [Release notes](https://github.com/react/react/releases)
- [Changelog](https://github.com/react/react/blob/main/CHANGELOG.md)
- [Commits](https://github.com/react/react/commits/v19.2.8/packages/react-is)

Updates `recharts` from 3.10.0 to 3.10.1
- [Release notes](https://github.com/recharts/recharts/releases)
- [Changelog](https://github.com/recharts/recharts/blob/main/CHANGELOG.md)
- [Commits](https://github.com/recharts/recharts/compare/v3.10.0...v3.10.1)

Updates `smol-toml` from 1.7.0 to 1.7.1
- [Release notes](https://github.com/squirrelchat/smol-toml/releases)
- [Commits](https://github.com/squirrelchat/smol-toml/compare/v1.7.0...v1.7.1)

Updates `undici` from 8.8.0 to 8.9.0
- [Release notes](https://github.com/nodejs/undici/releases)
- [Commits](https://github.com/nodejs/undici/compare/v8.8.0...v8.9.0)

---
updated-dependencies:
- dependency-name: "@aws-sdk/client-bedrock-runtime"
  dependency-version: 3.1096.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production
- dependency-name: "@lobehub/icons"
  dependency-version: 5.15.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production
- dependency-name: "@modelcontextprotocol/sdk"
  dependency-version: 1.30.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production
- dependency-name: "@toon-format/toon"
  dependency-version: 4.1.0
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: production
- dependency-name: fumadocs-core
  dependency-version: 16.13.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production
- dependency-name: fumadocs-ui
  dependency-version: 16.13.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production
- dependency-name: jose
  dependency-version: 6.2.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production
- dependency-name: lucide-react
  dependency-version: 1.27.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production
- dependency-name: material-symbols
  dependency-version: 0.45.9
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production
- dependency-name: next
  dependency-version: 16.2.12
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production
- dependency-name: next-intl
  dependency-version: 4.13.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production
- dependency-name: playwright
  dependency-version: 1.62.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production
- dependency-name: react
  dependency-version: 19.2.8
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production
- dependency-name: react-dom
  dependency-version: 19.2.8
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production
- dependency-name: react-is
  dependency-version: 19.2.8
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production
- dependency-name: recharts
  dependency-version: 3.10.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production
- dependency-name: smol-toml
  dependency-version: 1.7.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production
- dependency-name: undici
  dependency-version: 8.9.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-27 21:12:34 -03:00
dependabot[bot]
6afec74f26 deps: bump electron from 43.1.1 to 43.2.0 in /electron (#8782)
Bumps [electron](https://github.com/electron/electron) from 43.1.1 to 43.2.0.
- [Release notes](https://github.com/electron/electron/releases)
- [Commits](https://github.com/electron/electron/compare/v43.1.1...v43.2.0)

---
updated-dependencies:
- dependency-name: electron
  dependency-version: 43.2.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-27 21:12:30 -03:00
Prudhvi Vuda
f3a78d68e8 fix(sse): compact Responses multi-turn images before context hard-reject (#8560) (#8595)
* fix(sse): compact Responses multi-turn images before context hard-reject (#8560)

Codex Desktop sessions near the 372k input cap were rejected on the second
inline image because compressContext no-op'd on Responses input[] and never
pruned older vision turns. Adapt via bodyAdapter, prune older images while
keeping the latest, and run last-resort compaction before the budget check.

* docs(env): document CONTEXT_KEEP_LATEST_IMAGES for #8560

Keep check:env-doc-sync green after the context image-pruning override.

---------

Co-authored-by: diegosouzapw <diegosouzapw@users.noreply.github.com>
Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
2026-07-27 21:12:27 -03:00
diegosouzapw
352d48fedc test(pack): list ensureAndroidCacheDir.mjs in the pack-artifact fixture
#8593 registered bin/cli/utils/ensureAndroidCacheDir.mjs in
PACK_ARTIFACT_REQUIRED_PATHS so the Android/Termux cache module cannot silently
drop out of the npm tarball. Three test files read that list; only
pack-artifact-entrypoint-closures.test.ts derives it dynamically. This one
hardcodes the expected set, so it went red on a correct change.

Adding the entry here, not relaxing the assertion — a hardcoded fixture is what
makes an accidental REMOVAL from the required-paths list loud, which is the
whole point of the guard.
2026-07-27 21:12:14 -03:00
MumuTW
0eeb8f45c0 refactor(sse): extract combo target resolution into combo/targetResolution.ts (#8592)
* refactor(sse): extract combo dispatch prelude into combo/dispatchPrelude.ts

Pure move, no behaviour change. First of ~7 PRs decomposing the combo.ts
god-file (#3501).

handleComboChat evaluates a series of dispatch branches before it ever
reaches target resolution or the sequential attempt loop. None of them
iterate targets in priority order or need the failover/retry/credential
gate machinery that follows, so they move to a leaf:

  - context-cache pin routing (Fix #679), including the
    pinIsDurablyUnhealthy / isPinnedModelDurablyUnhealthy health gate
  - fusion panel dispatch + the #6455 misconfiguration warn
  - pipeline chaining
  - nested combo-ref execute-mode runtime-unit dispatch

Only the chaos and round-robin hand-offs stay inline (11 and 13 lines);
extracting those would be pure indirection.

open-sse/services/combo.ts 3642 -> 3341 (-301)
open-sse/services/combo/dispatchPrelude.ts: 619 (under the 800 cap)

Each helper keeps the fall-through protocol the inline blocks had: return
a Response to OWN the request, return null to fall through. A flipped
null/Response would silently bypass the whole combo strategy, so the new
tests pin both directions for every branch.

combo.ts re-exports pinIsDurablyUnhealthy so combo-pin-health-gate.test.ts
keeps resolving. The leaf takes handleComboChat as a `runCombo` parameter
instead of importing it, so combo/ keeps zero back-edges into combo.ts.

Complexity-neutral: the first cut added +3 violations (two
max-lines-per-function, one complexity) inside the new leaf, so
evaluatePinnedResponse, orderRuntimeUnits, recordRuntimeUnitStickySuccess
and buildBaseOptions were split out. check:complexity now measures 2169
and check:cognitive-complexity 956 — identical to the pristine base.

* test(sse): close the dispatch-prelude coverage holes found by mutation testing

An adversarial mutation audit of the suite added in the previous commit
found it guarded the fall-through protocol well but asserted almost
nothing about what the helpers do once they OWN the request. 5 of 12
seeded mutations survived. Worst case: deleting the pinned-model
dispatch call outright left all 12 tests green.

Three holes, now closed (8 tests -> 20):

Hole A — the honored-pin path had zero coverage. Both existing pin tests
DROP the pin, so the dispatch, the 200-but-empty quality gate, the
[408, 429, 500, 502, 503, 504] failover list and the catch(pinErr)
branch were unguarded — exactly the logic the 2026-06-21 / 2026-06-22
incident comments call load-bearing. Adds five tests over a seeded
healthy provider connection so the pin is actually honored.

Hole B — orderRuntimeUnits was only ever driven with `priority`, which
is a no-op through it. Four of five strategy branches could be deleted
with nothing failing. Adds round-robin rotation and weighted sticky
ordering tests.

Hole C — recordRuntimeUnitStickySuccess never did anything under test:
both its guards need weighted/round-robin, so an early return changed
nothing. Covered by the new sticky-batch test.

Verified by re-running the mutations rather than assuming: all 7 that
previously survived (delete-pin-dispatch, serve-despite-failed-quality,
never-fail-over-on-transient, rr-counter-not-advanced, rotation-removed,
weighted-sticky-skipped, sticky-recording-no-op) are now killed.

The first sticky-batch test I wrote was itself vacuous — asserting "same
unit twice" holds equally when the recording helper is stubbed out, since
nothing advances the counter either. It now asserts the batch runs out
and rotation resumes on the third dispatch, which is what actually
distinguishes the two.

Also restores API_KEY_SECRET in test.after; it was set at module load and
never put back, inconsistent with the DATA_DIR handling beside it.

* fix(ci): teach known-symbols gate the relocated fusion/pipeline dispatch

The combo sub-check of check:known-symbols asserts every canonical routing
strategy has a real dispatch branch. It scanned a hardcoded file list and
matched only `strategy === "..."`, so the prelude extraction tripped it twice:

  [combo] 2 estratégia(s) canônica(s) sem branch de despacho em combo.ts:
      ✗ fusion
      ✗ pipeline

Both branches are still wired — they just moved to combo/dispatchPrelude.ts and
took the early-return guard form `if (strategy !== "fusion") return null;` that
extracting a branch into a `tryXDispatch()` leaf naturally produces.

Two changes, both extending existing precedent (the list already carries the
Block J leaves for the same reason):

- register combo/dispatchPrelude.ts in comboDispatchFiles
- widen the extractor to `strategy [!=]== "..."` so the inverted guard counts

Loose `==`/`!=` stay rejected, and no `handledNotCanonical` fallout: the gate
now reports 20 canonical strategies, all 20 via despacho.

* chore(ci): register combo-dispatch-prelude test in stryker tap.testFiles

check:mutation-test-coverage --strict failed once the known-symbols fix let
Fast Quality Gates advance to it:

  ✗ 2 covering unit test(s) across 2 module(s) are missing from
    stryker.conf.json tap.testFiles
      open-sse/services/combo/comboStructure.ts
      open-sse/services/combo/rrState.ts

The new tests/unit/combo-dispatch-prelude.test.ts exercises both modules, and
both are already in stryker's mutate list, so without the registration its
mutant kills would not have counted toward the nightly mutation gate.

Note (unchanged, still out of scope): combo/dispatchPrelude.ts itself is not in
stryker's `mutate` list. Adding it would widen the nightly mutation surface,
which is a separate call from fixing this drift.

* docs(changelog): add fragment for #8582 combo dispatch prelude

* refactor(sse): extract combo target resolution into combo/targetResolution.ts

Pure move, no behaviour change. Lifts the target-resolution stage of
handleComboChat — everything between the dispatch prelude and the attempt
loop — into a new leaf, open-sse/services/combo/targetResolution.ts.

Moved verbatim: provider-wildcard expansion, weighted step-group resolution
+ sticky-weighted eligibility, request-tag routing, the known-context-overflow
early return, the smart/pipeline-enabled auto dispatch, auto-strategy
ordering, per-strategy ordering, cache-strategy affinity, session stickiness,
eval scores, request-compatibility + context-requirement filters, task-aware
reordering, prompt-cache affinity, and the priority-strategy pre-screen.

The three early exits become an { earlyResponse } result so the host decides
to return them (same pattern as resolveAutoStrategyOrder). The values the
attempt loop still reads — orderedTargets, stickyWeightedLimit,
getWeightedStepKeyForTarget, the session-stickiness result and preScreenMap —
are returned instead of closed over. Loop config (maxRetries, retryDelayMs,
fallbackDelayMs, maxSetRetries, setRetryDelayMs) stays in combo.ts.

buildAutoCandidates is dependency-injected because it lives in combo.ts, so
the leaf keeps zero back-edges into its host.

combo.ts 3640 -> 3321 lines; new leaf 484 lines (under the 800 cap).
Part of the #3501 god-file decomposition campaign.

* refactor(sse): split targetResolution into stage helpers, ratchet combo.ts file-size baseline

Follow-up to the target-resolution extraction: the moved region landed as one
311-line function, which converted inline code inside the (already-violating)
handleComboChat into a NEW separately-counted violating function — check:complexity
2169 -> 2171 and check:cognitive-complexity 956 -> 957.

Split resolveComboTargetPipeline along its natural stage boundaries into 14
helpers (wildcard expansion, weighted eviction/eligibility/sticky-key/selection,
step-key mapper, context-overflow response, pool-size log, smart-pipeline dispatch
and its fall-through logger, strategy ordering, continuity filters, task-aware
ordering, prompt-cache enablement/first-target protection/affinity stage). Each
stage takes the previous stage's output and returns the next; still a pure move.

The leaf now contributes ZERO complexity, max-lines-per-function and
cognitive-complexity violations. Both ratchets are back at base 4053e2314 values:
check:complexity 2169, check:cognitive-complexity 956. (Both still print RED
against their frozen ceilings 2130/951 — pre-existing base-red per #8580.)

Also ratchets ONLY the open-sse/services/combo.ts entry in
config/quality/file-size-baseline.json from 3642 to 3322, with a justification
note in the file's existing style. No sweep of unrelated entries.

* chore: stack targetResolution on dispatchPrelude tip, rebank + skills

Rebased onto refactor/combo-dispatch-prelude. Keep both leaves in
check-known-symbols. Regenerate file-size baseline; sync agent skills.

* fix(sse): restore #8494 capability fail-closed after targetResolution extract

Stacking targetResolution onto the dispatchPrelude tip dropped the #8488/#8494
compatFilterFailOpen wiring: hard capability filters emptied the pool into a
generic 404 no_executable_targets, and fail-open never re-admitted the pool.

Restore describeCapabilityFilterExhaustion earlyResponse in
applyContinuityFilters and the matching round-robin path, then rebank the
file-size baseline for tip growth the incomplete prior rebank missed.

* fix(sse): realign model-lockout cooldown options with the post-#8254 type

This branch predates #8254, which renamed the recordModelLockoutFailure option
`exactCooldownVerified` -> `exactCooldownIsUpstreamReset` and changed combo.ts's
predicate from `lockoutHintVerified` (#8393's `lockoutHintMs > 0`) to
`lockoutHintMs > mlSettings.baseCooldownMs`. Rebasing onto the current tip brought
the renamed type without updating these two call sites, so typecheck:core failed
with TS2353 at both.

Restores the base expression verbatim rather than re-wiring `lockoutHintVerified`
under the new name. The base predicate is the correct one: selectLockoutCooldownMs
returns the parsed hint ONLY when `lockoutHintMs > baseCooldownMs`, and otherwise
returns 0 or a synthetic baseCooldownMs — so `lockoutHintMs > 0` would mark a
synthetic cooldown as an upstream reset and let it bypass the #7940 maxCooldownMs
cap, which is the bug #8254 fixed.

---------

Co-authored-by: MumuTW <johnsxn.us@gmail.com>
Co-authored-by: diegosouzapw <diegosouzapw@users.noreply.github.com>
Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
2026-07-27 19:13:31 -03:00
diegosouzapw
cd7a492984 chore(quality): re-pin file-size ceilings after merge-train 1H + fix stryker drift
file-size: nine frozen entries could not absorb the combined result of the
31-PR train. Two distinct causes, kept apart in the baseline note on purpose:

  (1) GENUINE irreducible growth at existing chokepoints —
      providerLimits/auth (#8632), rateLimitManager (#8616),
      models-catalog-route.test (#8610).
  (2) COLLISION with #8585, which banked shrinks measured on the pre-train
      release tip while 30 sibling PRs in the SAME train grew those files
      again — chat/accountFallback (#8628), chatCore (#8613),
      videoGeneration (#8581), imageGeneration.

Ceilings re-pinned to the post-merge tip. #8612 (also in this train) automates
shrink-banking so this self-inflicted drift stops recurring.

stryker: three covering unit tests were missing from tap.testFiles —
isLocalStreamLifecycleError-abort-shape (circuitBreaker.ts, a shared base-red
that was reddening Fast Quality Gates on every open PR),
noauth-autocombo-lockout-7623 (accountFallback.ts) and
kimi-quota-reset-recovery (auth.ts), the latter two landed with this train.
2026-07-27 19:13:07 -03:00
MumuTW
cf66338948 chore(skills): regenerate cli-backup-sync SKILL.md to match catalog (#8657)
* chore(skills): regenerate cli-backup-sync SKILL.md to match catalog

check:agent-skills-sync was failing on release/v3.8.49 tip because the
generated SKILL.md still documented backup-status flags the catalog no
longer exposes. Re-run generate-agent-skills --apply (9-line delete only).

* docs(changelog): add fragment for #8657 agent-skills sync
2026-07-27 19:08:26 -03:00
Austin Liu
44c03926bd fix(autoCombo): handle missing model_capabilities table in taskFitness (#8603) (#8650)
Check if model_capabilities table exists in SQLite before running SELECT query in loadModelCapabilities to prevent SQL error when the table has not been created yet.

Co-authored-by: Austin Liu <austinliu@Austins-MacBook-Air-3.local>
2026-07-27 19:08:22 -03:00
backryun
1055d14025 refactor(sse): peel the bare Response off handleChatCore's union in responsesHandler (#8647)
`handleChatCore` returns a union that includes a bare `Response` alongside the
`{ success, response, … }` envelopes — early returns that never build one.
responsesHandler read `result.success` / `result.response` straight off it, so
three diagnostics fired on the `Response` arm, which has neither.

Added an `instanceof Response` guard before the envelope checks. The outcome is
unchanged: a bare Response already fell through `!result.success` (undefined,
so truthy under `!`) and was returned as-is; it is now returned one branch
earlier, explicitly.

208 -> 205, zero new, on a line-number-agnostic diff of the full tsc error set.

The other two diagnostics in this file are left alone on purpose. Declaring
convertResponsesApiFormat's return type fixes them, but immediately surfaces the
next masked error at the handleChatCore call — `onStreamFailure` is declared
required in that parameter object while responsesHandler has always omitted it
in production. Fixing that means touching chatCore's signature, which belongs
with the chatCore work rather than a three-line guard. Measured 5 fixed / 1 new
and reverted, keeping the zero-new invariant.

No new tests: the guard adds a branch that returns the same value the fall-
through already returned, and the bare-Response path is exercised by the 400
tests in the responses suites, all passing.

Co-authored-by: backryun <busan011@ormbiz.co.kr>
2026-07-27 19:08:19 -03:00
backryun
6601ae3a8d fix(middleware): declare withInjectionGuard's context parameter optional (#8644)
All five diagnostics in src/lib/batches/dispatch.ts are the same:

  Type '(request: any, context: any) => Promise<any>' is not assignable to
  type 'BatchRouteHandler'.
    Target signature provides too few arguments. Expected 2 or more, but got 1.

`withInjectionGuard()` returns `guardedHandler(request, context: any)` with the
second parameter required, so every route it wraps advertises arity 2. The batch
dispatcher's `BatchRouteHandler` is `(request: Request) => …`, and TS rejects
assigning a function that needs an argument the caller will never supply.

The declaration was wrong about its own runtime. `dispatch.ts:48` already calls
`handler(request)` with one argument, and has been doing so in production;
`context` is only forwarded to the inner handler, where routes that do not read
it get `undefined`. Marking it `context?: any` states what was already true.

208 -> 203, zero new, on a line-number-agnostic diff of the full tsc error set.
One character; nothing executable changed.

No test added: the one-argument call path is the existing behaviour and is
already covered — embeddings-auth.test.ts and embeddings-route-apikeymeta-6929
call `POST(req)` directly through withInjectionGuard, which is exactly the arity
this now permits. 370/370 across the 43 injection-guard / batch / embeddings /
moderation suites; typecheck:core, eslint and check:file-size clean.

Co-authored-by: backryun <busan011@ormbiz.co.kr>
2026-07-27 19:08:15 -03:00
Austin Liu
5cf3d1fc9c fix(windows): request shell when spawning bare qoder binary name on Windows (fixes #8590) (#8633)
* fix(windows): request shell when spawning bare qoder binary name on Windows (fixes #8590)

Post Node CVE-2024-27980, spawn('qodercli', [], { shell: false }) fails with ENOENT on Windows when command is a bare binary name without extension. Enabling shell mode for bare command names allows cmd.exe to resolve .cmd / .bat wrappers from PATH.

* fix(windows): ensure windowsHide: true on open-sse qoder/devin child spawns

---------

Co-authored-by: Austin Liu <austinliu@Austins-MacBook-Air-3.local>
2026-07-27 19:08:12 -03:00
glaze
01c0f8a7dd fix(providers): recover Kimi after quota reset (#8632)
* fix(providers): recover Kimi after quota reset

* docs: add Kimi quota recovery changelog
2026-07-27 19:08:09 -03:00
Éder Costa
9108955323 fix(claude): classify native subscription quota 429 (#8628)
Co-authored-by: Escalada Online <aescaladaonline@gmail.com>
Co-authored-by: diegosouzapw <diegosouzapw@users.noreply.github.com>
2026-07-27 19:08:05 -03:00