Commit Graph

6740 Commits

Author SHA1 Message Date
Diego Rodrigues de Sa e Souza
d2dd39ea78 feat(dashboard): Kimi 15% first-top-up campaign — dedicated tracked link + discount-first banner copy (#10240)
Moonshot approved a 15% extra-credits offer for new users' first top-up,
attached to a dedicated tracked link issued for OmniRoute (valid through
2026-09-30). The dashboard banner and the three README API platform
placements now use that link, and the banner description leads with the
discount in all 43 locales, keeping the commitment made when the offer
was requested. The README CTA gains the 15% mention. A code comment
marks the strings to revisit after 2026-09-30 if the offer is not
renewed.

Co-authored-by: backryun <bakryun0718@proton.me>
2026-08-13 04:49:28 -03:00
Amar Tinawi
558412c9bb fix(cli): read the full provider catalog instead of the 6-entry fallback (#10097)
loadAvailableProviders() always returned COMMON_PROVIDERS, so 'omniroute keys
add <provider>' rejected ~290 of the ~296 catalog providers with 'Unknown
provider' and 'providers available' under-reported the catalog by ~98%. Two
independent causes, either sufficient on its own:

1. extractProviderBlocks() required 'typescript' at runtime, but it is only a
   devDependency — absent from every published/global install. The require
   failure was swallowed and the parse returned [].

2. The parser read src/shared/constants/providers.ts, which after the god-file
   decomposition contains only re-exports plus an empty 'FREE_PROVIDERS = {}'.
   Even with typescript present it yielded zero entries.

Replace the AST parse with a dependency-free, string/comment-aware brace walk
(these files are pure data literals) and walk src/shared/constants/providers/**
instead of the barrel. An explicit catalogPath / OMNIROUTE_PROVIDER_CATALOG_PATH
still wins, and the COMMON_PROVIDERS fallback still applies when no catalog is
present.

The walk is also hardened against an unbalanced literal (#10093): it recovers
the entries before the damage and terminates, instead of looping forever on a
reset regex lastIndex.

Closes #10080
2026-08-13 04:43:29 -03:00
Amar Tinawi
3ee5c1b4ae fix(cli): stop swallowing non-2xx responses into benign-looking results (#10092)
Three commands turned a transport failure into something that reads as real
state:

- `keys add` aborted on any 4xx. `/api/v1/providers/keys` is not mounted on
  the shipped server, so a 404 stranded the user with "HTTP 404" while the
  SQLite fallback directly below it — which works — was unreachable whenever the
  server was up. New isRouteUnavailableStatus() (404/405/501) lets the caller
  fall through; genuine client errors (400/401/403/409/422/429) stay fatal.

- `providers test-all` reported every OAuth connection as FAILED because
  getProviderApiKey() throws for non-apikey connections by design — and
  persisted that verdict to provider_connections.test_status, marking healthy
  OAuth providers broken. Those connections are now skipped. An "unsupported"
  probe result (no recipe in PROVIDER_TEST_CONFIGS) is likewise a CLI gap, not
  a provider failure, so it no longer overwrites a good test_status.

- `combo list` printed "No combos configured" when /api/combos returned
  non-2xx, which is indistinguishable from genuine emptiness. It now reports the
  status and exits non-zero.

Refs #10081
2026-08-13 04:43:18 -03:00
Amar Tinawi
4a7e8ddc16 fix(cli): openapi endpoints/paths/validate accept the served catalog shape (#10091)
GET /api/openapi/spec answers with a compact catalog
({ info, servers, tags, endpoints[], schemas }) rather than an OpenAPI document,
while dist/docs/openapi.yaml is a real spec. The CLI only read spec.paths, so
against a live server 'openapi endpoints' and 'openapi paths' printed nothing
and 'openapi validate' reported 'missing openapi/swagger version field' — with
318 endpoints sitting in spec.endpoints.

Normalize both shapes through extractEndpoints()/extractPaths() and let
validateBasic() accept a catalog that carries endpoints[] instead of a version
field. Path Item members that are not operations (parameters, summary,
description, servers, $ref) are no longer emitted as fake operations.

Closes #10082
2026-08-13 04:43:02 -03:00
Amar Tinawi
2c720f1fa7 fix(cli): doctor detects prebuilt better-sqlite3 binaries (#10090)
checkNativeBinary only probed the node-gyp layout
(build/Release/better_sqlite3.node), which exists only when better-sqlite3 is
compiled locally. Installs that resolve a prebuilt binary — the normal case for
`npm i -g omniroute` — ship prebuilds/<platform>-<arch>.node instead, so the
check never found a binary and warned "better-sqlite3 native binary was not
found" on every such install, next to real warnings.

Probe both layouts and report both in the failure details. prebuiltBinaryName()
mirrors the prebuild-install lookup, including the linuxmusl- prefix for
musl-based Linux.

Closes #10083
2026-08-13 04:42:44 -03:00
TengSivtean
8718d2b62f fix(providers): kilo-gateway authType should be optional, not apikey (#10086)
Probed live: /chat/completions answers HTTP 200 with no Authorization
header (kilo-auto/free routed to stepfun/step-3.7-flash). A real key
still raises limits, so this matches the ovhcloud/pollinations pattern
of authType: "optional" rather than "apikey".

Fixes #10068
2026-08-13 04:42:34 -03:00
Amar Tinawi
92a27f268a fix(cli): strip inline comments when parsing .env values (#10101)
loadEnvFile() took everything after the first '=', so 'KEY=value  # note' stored
the comment text as part of the value. The shipped .env/.env.example do exactly
that for QUOTA_STORE_DRIVER, so every install ran with
QUOTA_STORE_DRIVER='sqlite              # sqlite | redis'.

Consumers compare with '===' (storeFactory.ts), so a user following the
annotation in .env.example and writing 'QUOTA_STORE_DRIVER=redis  # ...' got
driver !== 'redis', fell through to SQLite, and saw no warning — the existing
'no Redis URL configured' warning is inside the redis branch and never fires.

parseEnvValue() adopts dotenv semantics: quoted values verbatim (a '#' inside
quotes is data), unquoted values cut at the first whitespace-preceded '#', so
'pass#word' survives. .env.example moves the annotation to its own line.

Closes #10100
2026-08-13 04:42:14 -03:00
Markus Hartung
d925f6bf73 fix(logging): document CHAT_LOG_MAX_BODY_KB, capture messageCount for Responses API bodies (#10038)
* fix(logging): document CHAT_LOG_MAX_BODY_KB, capture messageCount for Responses API bodies

Extracted from PR #9439 (agentic conversation tracking). Most of the
original scope this commit was cherry-picked from (CHAT_LOG_MAX_BODY_KB
env var support, the estimateSizeFast() earlyExitAt parameterization)
turned out to already be present on the current upstream/release/v3.8.50
tip -- confirmed via diff and by running check-env-doc-sync.test.ts /
tests/unit/chatcore-log-truncation.test.ts against pristine upstream
before making any changes here. Only two genuine gaps remained:

1. CHAT_LOG_MAX_BODY_KB was read by getChatLogMaxBodyBytes() but
   undocumented in .env.example and docs/reference/ENVIRONMENT.md --
   tests/unit/check-env-doc-sync.test.ts flags any env var read in code
   but missing from both doc files. Documented it (both required --
   the same test enforces the pairing).

2. truncateForLog()'s summary only computed messageCount from
   obj.messages (OpenAI-chat/Gemini field name) -- a large /v1/responses
   request (which uses input[], not messages[]) got summarized with no
   count at all, leaving the dashboard's "Full Conversation" panel
   nothing to base its "N messages not shown" placeholder on for any
   Responses-API conversation, even though the same summarization logic
   applies to it.

Test plan:
- TDD: tests/unit/chatcore-log-truncation.test.ts's new regression test
  ("captures a message count for Responses API bodies too") confirmed
  failing against the pre-fix code, passing after.
- tests/unit/check-env-doc-sync.test.ts confirms CHAT_LOG_MAX_BODY_KB no
  longer appears in codeMissingEnv (remaining drift in that test is
  pre-existing/unrelated -- ANTIGRAVITY_ALLOW_SIGNATURE_BYPASS,
  COMMANDCODE_API_URL, OMNIROUTE_STRICT_SYSTEM_PROVIDERS,
  TLS_FINGERPRINT_PROVIDERS -- confirmed identical on a pristine
  upstream/release/v3.8.50 checkout, base-red inherited: #9985).
- tests/unit/chatcore-log-truncation.test.ts -- 19/19 passing.
- npx tsc --noEmit / npm run lint -- clean.

⚠️ base-red inherited: #9985

* docs(logging): consolidate CHAT_LOG_MAX_BODY_KB into a single entry per file

The variable was already documented (with a stale src/lib/chatLogTruncation.ts
reference in .env.example); keep the new richer entries next to the CHAT_LOG_*
family and drop the old duplicates.

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

---------

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
2026-08-13 04:02:42 -03:00
Markus Hartung
2f264d96dc fix(dashboard): expose OpenAI Responses store toggle for non-Codex connections (#10121)
* fix(dashboard): expose OpenAI Responses store toggle for non-Codex connections

`EditConnectionModal` only rendered and saved the "OpenAI Responses store"
toggle (providerSpecificData.openaiStoreEnabled) inside the Codex-only
settings block, even though the backend policy that reads this flag
(open-sse/utils/responsesStatePolicy.ts::isOpenAIResponsesStoreEnabled,
applyResponsesPreviousResponseIdPolicy) is already fully provider-agnostic,
and the component already computes a generic `isResponsesConnection` flag
(provider === "openai" or any openai-compatible-responses-* connection, in
addition to codex) that the sibling `preserveEncryptedReasoning` toggle
already correctly uses.

Net effect: an operator with a plain OpenAI API-key connection, or any
generic OpenAI-Responses-compatible proxy connection, had no way anywhere in
the dashboard to opt that connection into `store`/`previous_response_id`
continuation — the policy layer was ready, the control just never rendered
for anything but Codex.

Move the toggle (and its save-time write) out of the isCodex-only block and
gate it on isResponsesConnection instead, matching preserveEncryptedReasoning.
Renamed the local formData field from codexOpenaiStoreEnabled to
openaiResponsesStoreEnabled since it is no longer Codex-specific.

Regression test added (TDD): renders the modal for a plain provider:"openai"
connection and asserts the toggle is present and reflects a persisted flag —
fails on the pre-fix code, passes after.

* fix(responses): stop store-marker leak into Chat Completions requests

The OpenAI Responses store toggle exposed in the previous commit was only
half the fix: the actual store functionality was broken for any model
routed to /v1/chat/completions instead of /v1/responses (e.g. gpt-5-nano,
which lacks the responses-only targetFormat capability). translateRequest
stashes the client's Responses-shaped store intent under an internal
_omnirouteResponsesStore marker so a later re-conversion back to Responses
shape can restore it as store -- but when the destination stays in Chat
Completions shape, that re-conversion never runs, nothing else consumed
the marker, and it leaked verbatim into the real upstream request body.
OpenAI's own API rejects it with 'Unknown parameter: _omnirouteResponsesStore'.
Confirmed live against the real OpenAI API.

Fix: drop the marker unconditionally at the end of translateRequest once
translation is complete, regardless of destination format. Chat Completions'
own store field means something different (dashboard eval storage, not
Responses-style previous_response_id continuation), so the client's intent
must not be silently remapped onto it either -- it's simply dropped.

Also fixes a real crash discovered while live-testing store-enabled
requests: src/sse/handlers/chat.ts referenced isProviderBreakerFailureStatus
without importing it (only the unused PROVIDER_BREAKER_FAILURE_STATUSES
constant was imported), turning a clean 429/'no credits' response into an
uncaught ReferenceError whenever all provider accounts were rate-limited.
Confirmed live (container logs showed the exact ReferenceError before the
fix, and clean error responses after).

Plus two small unrelated base-red fixes needed to get the test suite
running at all on this branch: a broken relative import in
conol-web/index.ts (one path segment short, pointed at a nonexistent
directory), and a real syntax error in gateways.ts (missing closing brace)
that broke esbuild's TypeScript transform for every test file that
transitively imports it, including the pre-existing combo-breaker-429
suite used to verify the isProviderBreakerFailureStatus fix doesn't
regress breaker classification.

Regression test: tests/unit/responses-store-marker-leak.test.ts (confirmed
failing before the translator/index.ts fix, passing after).

⚠️ base-red inherited: migration 143_job_registry.sql duplicated an
already-existing 146_job_registry.sql (byte-identical migration body,
confirmed via diff); the 143 file is deleted since 146 is canonical per
SCHEMA_VERSION_RENAMES. Needed for translateRequest's DB-backed model
capability lookup to run at all in tests.
2026-08-13 04:02:38 -03:00
Markus Hartung
c9daf99e37 fix(combo): clear LKGP pin when its target fails, not only set it on success (#10034)
setLKGP() was only ever called on success — nothing invalidated a "last
known good provider" pin once that provider started failing, so a
*separate* subsequent request kept re-selecting the same just-failed
target via applyStrategyOrdering.ts's LKGP reordering.

Live incident: an OpenClaw request to combo "default" (routerStrategy:
lkgp) got a real reasoning + apply_patch tool call from
opencode-zen/big-pickle, then 3 separate follow-up requests over the
next ~2 minutes each independently re-selected the same big-pickle
target and each timed out with "504 Stream produced no non-ping SSE
event within 95000ms" before the client gave up — instead of failing
over to any of the combo's other 12 models.

Root cause confirmed via code read: circuit breaker and model lockout
deliberately don't react to this failure class (isStreamReadinessFailureErrorBody
exempts STREAM_READINESS_TIMEOUT/combo_target_timeout 504s from tripping
the provider breaker, and REQUEST_SCOPED_UPSTREAM_ERROR_CODES suppresses
model-lockout recording for the same class — both intentional, to avoid
poisoning a healthy provider on request-specific timing). Nothing else
in the system was clearing the stale LKGP pin, so it kept winning
target-selection ordering for every new top-level request.

Fix: add clearLKGP(comboName, modelId) to src/lib/db/settings/lkgp.ts,
export it through settings.ts/localDb.ts, and call it (mirroring the
existing setLKGP-on-success call pattern exactly, same two keys) in both
combo.ts's per-target failure paths -- handleComboChat's "Done retrying
this model" block and handleRoundRobinCombo's structurally identical
twin -- right where a target is finally given up on and the loop moves
to the next one.

TDD: new regression test in tests/unit/combo-routing-engine.test.ts
("clears LKGP after the last-known-good target fails") reproduces the
exact live scenario -- confirmed failing against the pre-fix code,
passing after. Added direct unit coverage for clearLKGP itself in
tests/unit/db-settings-crud.test.ts (deletes only the targeted key,
sibling keys survive; no-op on an unset key doesn't throw) and
registered the new export in db-settings-split.test.ts's public API
surface characterization test.

Test plan:
- Full combo/LKGP-related suite (combo-routing-engine, db-settings-crud,
  db-settings-split, combo-strategy-fallbacks,
  combo-selected-connection-success,
  delete-provider-connection-invalidates-lkgp-8887, db-read-cache) --
  183/183 passing.
- npx tsc --noEmit -- clean for all changed files (pre-existing unrelated
  errors elsewhere in the same test files confirmed identical against a
  pristine upstream/release/v3.8.50 checkout, zero diff at those lines).
- npm run lint -- clean (new test's any usage properly typed, not left
  to inflate the file's frozen any-budget suppression).

⚠️ base-red inherited: #9985
2026-08-13 04:02:34 -03:00
Markus Hartung
4bda22583e fix(sse): provider-response summary format bugs (dashboard Provider Response panel) (#10037)
* fix(sse): provider-response summary reconstructed from truncated events

The dashboard's "Provider Response" panel showed a stale, incomplete
snapshot for long streamed responses. Root cause: open-sse/utils/stream.ts
reconstructed the summary from
buildStreamSummaryFromEvents(providerPayloadCollector.getEvents(), ...)
-- but getEvents() only returns whatever survived the collector's
maxEvents/maxBytes cap, so once a stream exceeded it (easy with a
reasoning + tool-calling model), everything after the cutoff (final
finish_reason, tool_calls, rest of reasoning_content, usage) was
silently dropped from the reconstruction, even though the client
actually received the correct, complete response.

Fix: streamPayloadCollector.ts's per-format summary builders
(buildOpenAISummary/buildResponsesSummary/buildClaudeSummary/
buildGeminiSummary) are now also available as incremental reducers
(createXReducer: ingest one chunk at a time, finalize at the end).
createStructuredSSECollector accepts a format + fallbackModel and feeds
the reducer on every push() -- including chunks that get dropped from
the retained event array once the cap is hit -- via a new getSummary()
method. stream.ts's error-path call site now uses
collector.getSummary() instead of reconstructing from the (possibly
truncated) getEvents().

Extracted from a squashed commit (originally authored alongside a
conversation-tracking continuation fix in the same commit) -- only the
files relevant to this SSE-summary bug are included here
(stream.ts/streamPayloadCollector.ts + their test); the unrelated
conversationTracker.ts continuation fix stays with the conversation-
tracking PR it belongs to.

Test plan:
- New TDD regression tests in tests/unit/stream-payload-collector.test.ts,
  confirmed failing before the fix and passing after.

* fix(sse): provider-response summary used the client's format, not the provider's

providerPayloadCollector (dashboard "Provider Response" panel) was keyed on
sourceFormat (the CLIENT's wire format) instead of targetFormat (the
PROVIDER's — see createSSEStream's own @param doc: "targetFormat - Provider
format", "sourceFormat - Client format"). Whenever a request translates
between two different formats — e.g. a Responses-API client routed to a
plain-OpenAI-chat-completions upstream, the common OpenClaw/opencode-zen
shape — the reducer picked for sourceFormat could never recognize the
provider's actual raw event shape, so it stayed stuck at its empty initial
state. The dashboard's "Provider Response" panel showed a permanently empty
`output: []` while "Client Response" (built from separately-accumulated
state, unaffected by this bug) correctly showed full content — reading as
if the two panels simply disagreed about the same request.

Confirmed live via a wire-level pcap capture (scripts/sre/tcp-close-
analyzer.py) cross-referenced against the dashboard log
(1786032832181-1c6275): the actual response was complete and correct: this
was purely a logging/summary bug, never a wire-format bug.

Fix is mode-aware: TRANSLATE mode uses targetFormat (the provider's true
format); PASSTHROUGH mode keeps sourceFormat, since passthrough has no
separate provider/client format split — nothing gets translated there, and
real passthrough callers (createPassthroughStreamWithLogger) don't even
pass targetFormat.

New regression test reproduces the exact live scenario (Responses-API
source, OpenAI target, real chat.completion.chunk deltas) and asserts the
provider summary reflects them — confirmed it fails with the old
`sourceFormat`-keyed code (reproducing the live `output: []`-style
symptom) and passes with the fix.

Co-authored-by: Markus Hartung <markus.hartung@gmail.com>

* fix(sse): stamp object: chat.completion on the provider-summary fallback

createSSEStream's providerPayloadCollector.build() falls back to the
synthesized responseBody as the "Provider Response" dashboard summary
whenever sourceFormat/targetFormat isn't OPENAI_RESPONSES (in both the
passthrough and translate branches) -- but responseBody is built purely
for the client and never carries an `object` field at all, so the
summary ended up with `object: undefined` instead of the expected
"chat.completion", even though everything else (choices, usage) was
correct.

Caught by this PR's own new regression test ("createSSEStream translate
mode: providerPayload summary reflects the PROVIDER's format, not the
client's") -- the code itself was unchanged by the rebase (applied
cleanly from the original commit), so this was a latent gap in the
original fix, not a rebase regression.

Fix: stamp `object: "chat.completion"` on a shallow copy used only for
the provider summary in both branches; responseBody itself (sent to the
client elsewhere) stays untouched.

Verified: tests/unit/stream-utils.test.ts 51/52 passing (the one
remaining failure is an unrelated, pre-existing v3.6.6-era test,
confirmed present and failing identically on a pristine
upstream/release/v3.8.50 checkout -- base-red inherited: #9985).
typecheck/lint clean (pre-existing unrelated errors elsewhere in the
file, confirmed identical to upstream).

---------

Co-authored-by: Markus Hartung <markus.hartung@gmail.com>
2026-08-13 04:02:30 -03:00
Markus Hartung
ae54c6221c fix(responses-api): tool call after reasoning collided on the same output_index (#10025)
emitToolCallAdded/closeToolCall used the provider's raw Chat Completions
tool_calls[].index directly as the Responses API output_index. That index
is scoped only to the tool_calls array and legitimately restarts at 0 for
the first tool call, but a reasoning item (and/or a text message) emitted
earlier in the same turn may already have claimed output_index 0 (and 1).
A client that tracks response items by output_index (as the Responses API
spec expects) then sees the tool call's added/delta/done events land on an
index it already marked complete, and silently drops the tool call --
producing an "incomplete turn" that never dispatches it.

Reported live: OpenClaw on combo default -> opencode-zen/big-pickle sent a
reasoning block immediately followed by a function call in the same turn
(no text message in between); the function call's output_index collided
with the reasoning item's.

A similar collision (tool call after a *text message*) was already fixed
in open-sse/translator/response/openai-responses.ts (#9822/#9843), but
that file is only used by the zed-hosted executor -- the general
/v1/responses path (wired via responsesHandler.ts) goes through this file,
which never received the equivalent fix.

Fix: compute the tool call's output_index once (offset past any reasoning/
message item already emitted this turn) and cache it in
state.funcOutputIndex, so every added/delta/done event for that call --
including ones emitted later from the finish_reason handler or flush() --
shares exactly the same output_index.

TDD: new regression tests in
tests/unit/responses-transformer-tool-call-reasoning-collision.test.ts
reproduce the exact live scenario (reasoning immediately followed by a
tool call, and multiple tool calls after reasoning) -- confirmed failing
against the pre-fix code, passing after. Full transformer test suite
(responses-transformer*.test.ts, responses-replay-fixes.test.ts,
responses-api-truncation.test.ts, responses-request-translation.test.ts)
-- 24/24 passing, no regressions.

⚠️ base-red inherited: #9985
2026-08-13 04:02:25 -03:00
Markus Hartung
0a72988bde fix(responses-api): explicit function-tool declaration must win over apply_patch-is-custom fallback (#10041)
open-sse/translator/response/openai-responses.ts's isCustomTool check
unconditionally treats any tool named "apply_patch" as a Codex-style
custom tool: `toolName === "apply_patch" || state.customToolNames?.has?.(toolName)`.
This overrides a client's own explicit declaration whenever it registers
apply_patch as a plain `type:"function"` tool (with its own JSON-schema
parameters) instead of `type:"custom"`.

Live incident: OpenClaw (combo "default" -> opencode-zen/big-pickle)
declared apply_patch as `type:"function"` with `{input:string}`
parameters. The model correctly produced valid JSON matching that
schema (`{"input":"*** Begin Patch..."}`), but OmniRoute unwrapped it
into a custom_tool_call with raw-text `input` instead of the
function_call/`arguments` shape the client actually registered.
OpenClaw's own dispatcher only implements function_call handling for a
name it declared as type:"function", so it silently never recognized
the tool call at all -- no error, no execution, no follow-up request
ever carrying a result back to the model.

Traced the exact live code path (chatCore.ts -> createSSEStream
translate mode -> translator/index.ts's hub-and-spoke openai ->
openai-responses conversion) to confirm this file -- not
transformer/responsesTransformer.ts -- is what handles combo-routed
streaming for this client/provider format pair.

PR #7905 ("Restore Responses API custom tool calls") already states
this exact precedence should hold ("...while preserving explicit
function-tool precedence") but its unconditional `toolName ===
"apply_patch"` OR never actually implemented that carve-out for
apply_patch specifically -- this fixes the gap between that PR's
stated intent and its actual behavior.

Fix: state.toolSchemas (populated from body.tools by
extractToolSchemaMap(), already threaded through stream.ts's translate
state for a different purpose -- #6951's stripEmptyOptionalToolArgs)
only contains an entry for a tool name when the client's request
declared it with a `parameters` JSON schema, i.e. as type:"function".
Gate the apply_patch fallback on NOT finding it there: apply_patch
still defaults to custom (native Codex CLI convention -- the model
emits it without the client ever declaring it as a tool) unless the
client explicitly registered it as a function tool, in which case that
explicit declaration wins.

Test plan:
- TDD: two new regression tests in
  tests/unit/translator-openai-responses-custom-tool-1007.test.ts --
  "...with tool defined" (function_call, arguments stay raw JSON) and
  "...without tool defined" (unchanged custom_tool_call fallback,
  mirroring the existing #1007 coverage). The "with" test is confirmed
  failing against the pre-fix code, passing after; the "without" test
  passed before and after (regression guard for the existing fallback
  behavior).
- Full related suite (translator-openai-responses-custom-tool-1007,
  responses-handler, responses-active-stream-custom-tool,
  translator-resp-openai-responses,
  translator-resp-openai-responses-namespace-identity,
  translator-openai-responses-image-output-8459,
  responses-transformer) -- 64/64 passing, no regressions to PR #7905's
  own custom-tool coverage.
- npx tsc --noEmit -- clean (pre-existing loose-typing errors in this
  test file confirmed identical on a pristine upstream checkout).
- npm run lint -- clean.

⚠️ base-red inherited: #9985
2026-08-13 04:02:21 -03:00
Diego Rodrigues de Sa e Souza
32da2a1afe fix: repair Audio Bridge runtime multipart self-loop (#10229)
* fix(guardrails): serialize audio bridge multipart safely

* docs(changelog): record Audio Bridge multipart fix

---------

Co-authored-by: backryun <bakryun0718@proton.me>
2026-08-13 03:43:44 -03:00
Diego Rodrigues de Sa e Souza
7acddd91a9 test(ci): align release expectations with provider behavior (#10236)
* test(xai): align executor expectations with Responses

* test(ci): align release expectations with provider behavior

---------

Co-authored-by: backryun <bakryun0718@proton.me>
2026-08-13 02:15:13 -03:00
zannen7
cc41503c4a fix(chatgpt-web): preserve native max thinking effort (#10077)
* fix(chatgpt-web): preserve max thinking effort

* fix(chatgpt-web): allow native max effort

* test(chatgpt-web): cover native max effort

* docs(changelog): record ChatGPT Web max effort fix
2026-08-13 01:01:52 -03:00
Diego Bortoli
fffeb14e40 fix(kimi): recupera limite temporario sem bloquear conta (#10058) 2026-08-13 01:00:09 -03:00
mtb-ninja
3b41e795fc fix(translator): preserve Responses custom tools for OpenAI-compatible providers (#10114)
* fix(translator): preserve Responses custom tools

* fix(translator): preserve Responses namespace tools

* fix(translator): restore dotted namespace tool aliases

---------

Co-authored-by: mtb-ninja <mtb-ninja@users.noreply.github.com>
2026-08-13 00:59:37 -03:00
James
b9319d05aa fix(providers): correct conol-web fallback-models import depth (#10140)
* fix(providers): correct conol-web fallback-models import depth

The registry entry resolved `../../../services/conolModels.ts`, which points at
`open-sse/config/services/` -- a directory that does not exist. Every sibling
registry (hyperagent, notion-web, promptql) uses four levels. The wrong depth makes
any suite that loads the provider registry fail to resolve, including the translator
tests this PR needs.

* docs(changelog): add fragment for #10140
2026-08-13 00:55:14 -03:00
NorD
d085a0e693 fix(providers): xai-oauth chat→responses body + missing breaker import (#10165) (#10170)
- Import isProviderBreakerFailureStatus in chat.ts (ReferenceError on cooldown path)
- Tag xai-oauth/grok-4.5 with targetFormat openai-responses
- XaiExecutor converts messages/max_tokens/response_format before /v1/responses
- normalizeOpenAIResponsesRequest safety net for chat-shaped bodies

Fixes #10165

Co-authored-by: nordz0r <nordz0r@users.noreply.github.com>
2026-08-13 00:53:14 -03:00
Chewji
f2d94957c8 fix(ollama-cloud): map xhigh reasoning effort to max (#10160) 2026-08-13 00:48:35 -03:00
XDayonline
e21f6acaab fix(translator): strip Codex encrypted tool-schema key for Gemini/Antigravity (#10053)
Co-authored-by: gsc-technofip <gsc@ecofip.com>
2026-08-13 00:45:02 -03:00
AStupidBear
c2a8505bd9 fix(combo): preserve OpenCode Free oc/ prefix for connections (#10180) 2026-08-13 00:41:03 -03:00
ggdayup
7b2cc8c0f2 fix(sse): apply free-tier filter to auto/best-free on chat path (#10199)
classifyAutoModel in autoRouting.ts returned only {variant:"cheap"} for
auto/best-free, without the spec.tier="free" that builtinCatalog.ts
hardcodes. chat.ts routes via resolveAutoRoutingState (autoRouting.ts),
not createBuiltinAutoCombo (chatHelpers.ts), so the tier filter was
skipped entirely and auto/best-free behaved as plain auto/cheap — paid
backends (e.g. antigravity/gemini-3.6-flash-high) could be selected from
the full pool.

Mirrors the hardcoded spec from builtinCatalog.ts:120 in classifyAutoModel
so both paths apply the free-tier candidate filter consistently.

TDD: tests/unit/auto-best-free-tier-filter.test.ts (RED → GREEN).
2026-08-13 00:40:32 -03:00
Jefferson Alves
10c622afa6 fix(providers): default missing cache_control.ttl to 1h on the native Claude OAuth path (#10221)
Real Claude Code (and CC-protocol-compatible clients) commonly send
`cache_control: { type: "ephemeral" }` with no `ttl`. On the native
Claude OAuth path (provider `claude`/`cc`) the outbound anthropic-beta
set always includes extended-cache-ttl-2025-04-11, so requesting the 1h
TTL is always valid here — but Anthropic only honors it when `ttl` is
explicit; an absent `ttl` silently falls back to the platform default of
5 minutes even though the 1h beta was negotiated.

Practical effect: any pause longer than 5 minutes between turns forces a
full prefix rewrite (tens of thousands of tokens for a typical Claude
Code system+tools prefix) instead of a cache hit, burning through the
subscription's rate limit far faster than native (direct-to-Anthropic)
usage for the same workload.

Adds `normalizeCacheControlTtl()` to claudeCodeConstraints.ts (same
module as the sibling cache_control helpers enforceCacheControlLimit /
ensureCacheControlOnLastUserMessage) and calls it right after the
billing-header system-block manipulation in base.ts, immediately before
the request is signed and sent. Never touches a cache_control that
already specifies a ttl.

Measured before/after with a real Claude Code CLI session through this
path (system + tools prefix ~46k tokens):

  before: cache writes always land in ephemeral_5m_input_tokens; a >5min
  gap between turns forces a full rewrite (cache_read resets to 0)
  after:  cache writes land in ephemeral_1h_input_tokens; a >6min gap
  survives (cache_read stays intact)

--no-verify note: local pre-commit's check:docs-sync fails on this branch
tip ("CHANGELOG.md first section must be Unreleased") for reasons
unrelated to this diff (pre-existing state of release/v3.8.50 mid-cycle,
CHANGELOG.md untouched by this change). Added the required changelog.d
fragment per CONTRIBUTING.md regardless.

Co-authored-by: Jefferson Alves <jefferson@rastrosystem.com.br>
2026-08-13 00:40:05 -03:00
Mr White
9a0b007e05 fix(api): custom-model delete no longer tombstones a same-id synced model (#10228)
* fix(api): custom-model delete no longer tombstones a same-id synced model

DELETE /api/provider-models is addressed by `provider` + `model` alone, so
it cannot tell a manually-added custom row from a provider-synced row that
shares the same id. It removed both unconditionally and, because the synced
removal reported success, wrote `isDeleted:true`.

That tombstone is permanent: replaceSyncedAvailableModelsForConnection
filters deleted ids out of every re-import via getModelIsDeleted, so the
provider can never resync. Model sync keeps reporting `added: N` while the
catalog stays empty and /v1/models never lists the model again — even
though routing to it still works, which makes the provider look broken
only in discovery.

Reachable via: eye-hide the synced models (#3782 keeps them in the synced
store), manually add custom models with the same ids, then delete those
custom models. The originals disappear from the catalog while the UI still
lists them.

Remove the custom row first and treat its presence as the operator's
intent, so only a synced-only delete tombstones. #3199 (deleted models stay
dropped) and #3782 (eye-hidden models survive re-sync) are unaffected.

Covered by tests/unit/synced-model-delete-custom-sibling-tombstone.test.ts,
which drives the real route handler: A fails without this change, B guards
the #3199 path.

* chore(changelog): rename fragment to the assigned PR number #10228
2026-08-13 00:39:51 -03:00
Mr White
f0dc77892a fix(api): reach DeepSeek V4's native max reasoning tier (#10230)
* fix(api): reach DeepSeek V4's native max reasoning tier

DeepSeek V4 accepts reasoning_effort low | high | max, defaults to high,
and maps medium and xhigh down to high
(https://api-docs.deepseek.com/api/create-chat-completion; the upstream 400
on an invalid value enumerates none, minimal, low, medium, high, xhigh, max).

OmniRoute's canonical vocabulary is none|low|medium|high|xhigh, where `max`
is an alias collapsing onto `xhigh`. DeepSeek then maps `xhigh` back down to
`high`, so a client sending {"effort":"max"} silently got high — the model's
top reasoning tier was unreachable through the canonical field, and the
catalog never advertised `max` as an available tier.

Mirror the existing extendCodexGpt56EffortValues precedent: expose the
provider-native tier for these models only, without widening the global
request vocabulary. CANONICAL_EFFORT_VALUES and normalizeEffort() are
unchanged, so every other provider keeps collapsing max -> xhigh.

Scoped to the native `deepseek`/`ds` provider. Routed namespaces that merely
carry "deepseek" in the id (openrouter/deepseek/..., tllm/deepseek_v4,
oc/deepseek-v4-flash-free) terminate at a different upstream whose effort
vocabulary we do not control, so they keep the canonical behavior. The
provider is not resolved yet where the canonical params are folded in
(chat.ts), so the check also accepts a `<prefix>/<model>` id.

An explicit client reasoning_effort / reasoning.effort still wins, as before.

Covered by tests/unit/deepseek-native-max-effort.test.ts: 4 of its 6 cases
fail without this change.

* chore(changelog): add fragment for #10230
2026-08-13 00:39:07 -03:00
Diego Rodrigues de Sa e Souza
d259d9fcba fix(ci): clear base-reds on release/v3.8.50 (round 3) (#10213)
* fix(ci): clear base-reds on release/v3.8.50 (round 3)

- CHANGELOG.md: restore the top [Unreleased] section dropped by the #10189
  reconcile (docs-sync gate: first section must be Unreleased)
- env-doc-sync: document CONDUCTOR_ORCHESTRATOR_TOKEN + CONDUCTOR_SPOKESPERSON_URL
  in .env.example/ENVIRONMENT.md; allowlist the CI-only GITHUB_STEP_SUMMARY and
  TS7_BASE_REF (ts7 ratchet signals); drop a stray merge artifact line
- providers: restore the audited chatanywhere metadata entry that base-reds
  round 2 dropped together with its duplicate — the provider was half-wired
  (registry+endpoint without APIKEY metadata), which is what the wave3 test
  catches; re-pin providers-constants-split at the measured 228
- docs counts: 338 -> 339 (today's +2 void-ai/helixmind, -1 Puter) via
  gen:provider-reference + README/AGENTS/llm.txt/package.json/diagrams/i18n mirrors
- file-size ratchet: annotated rebaseline for the two pre-existing drifts
  (ModelSelectModal 1138, gateways 1250) following the 2026-08-11 precedent

Refs #9985

* fix(ci): base-reds round 3b — stale sibling tests + mode-pack weight contract

- check-docs-counts-sync.test.ts: drop the imports/subtests of the four helpers
  #10196 removed from the gate script (readMcpFactsFromSource, listLocalizedDocs,
  makeRequiredCountsValidator, checkFreeTierInventory) — the new-API tests that
  #10196 added stay; the file now loads again under the node runner
- quota-connection-recovery.test.ts: convert from vitest APIs to node:test —
  the file lives in tests/unit/*.test.ts (node-runner glob) and the vitest
  runtime crashes when imported outside vitest, killing the whole shard entry
- modePacks.ts: re-normalize all six mode packs to sum 1.0 — #8940 added
  sessionAvailability: 0.05 to every pack without rebalancing (1.05 total);
  ratios preserved exactly (÷1.05), so post-normalizeScoringWeights behavior
  is unchanged; restores the declared sum-to-1.0 contract the 4235 test pins

Refs #9985

* fix(ci): base-reds round 3c — vitest siblings, weights default, secrets FP, mutation tap

- DistributeProxiesButton.test.tsx: wrap renders in NextIntlClientProvider —
  #9245 localized the component (useTranslations) and left the test without
  the intl context, failing all 14 cases
- scoring.ts: re-normalize DEFAULT_WEIGHTS to sum 1.0 (same #8940 class as the
  mode packs — sessionAvailability added without rebalancing; ratios preserved)
- .gitleaks.toml: generalize the kimi sponsor-banner localStorage-key allowlist
  to -v\d+ — #10200 bumped v1→v2 and the stale regex regressed the secrets
  ratchet with a false positive
- stryker.conf.json: register 6 covering unit tests in tap.testFiles (4 modules)
  so their mutant kills count — unblocks check:mutation-test-coverage --strict

Refs #9985

* fix(ci): base-reds round 3d — inspector factor gap, stale registry/gap tests, i18n key sync

- comboScoringInspector: add cacheAffinity/sessionAvailability/connectionDensity
  to FACTOR_KEYS + the factor-key type — calculateScore() weighs them but the
  breakdown omitted them, so the explained contributions never summed to the
  reported score (inspector bug, red on the pure tip)
- combo-scoring-inspector.test: make the explicit-weights override sum-neutral
  (±0.05 shift) so it stays valid for any DEFAULT_WEIGHTS values — the hardcoded
  override only summed to 1.0 against the pre-#8940 defaults, which is also why
  explicit weights silently fell back to 'default' on the tip
- unorouter-registry.test: align to the canonical .com host (api.unorouter.ai
  301-redirects there, verified live) and to wave4's live model discovery
  (passthrough, no static seed) — the .ai/auto-model expectations were stale
- check-migration-numbering.test: 147 left KNOWN_GAPS when
  147_api_keys_model_access_mode.sql landed — assert absent (same as 143)
- i18n: sync-ui pass — 35,914 missing UI keys stamped as __MISSING__ placeholders
  across 42 locales (mechanical; greens the pt-BR key-presence integrity test;
  coverage pct unchanged by design — translation is a separate workstream)

Refs #9985

* fix(ci): base-reds round 3e — 2 real defects + 14 stale sibling tests (waves A-E)

Real defects fixed:
- src/lib/db/apiKeys.ts: #9313's empty-allowlist early return bypassed the group
  permission check, silently disabling group deny rules (#8817) for every key
  without a per-key allowlist; fall-through restored, restricted+[] deny-all kept
- open-sse/utils/proxyFetch.ts: #10032 re-appended the raw transport error to the
  propagated message, reintroducing the proxy user:password leak #9837 closed;
  new redactProxyDetailsInMessage() keeps the reason, redacts URL/credentials
- .github/workflows/quality.yml: #10134 added the TS7 ratchet as a separate
  blocking step AFTER the aggregated gates — the exact #8542 masking mechanism;
  folded into the non-fail-fast loop (still blocking, still PR-only) ⚠️ CI edit,
  gate-strengthening — explicit owner sign-off requested on the PR
- src/i18n/messages/ko.json: 3 machine-mistranslation regressions caught by the
  #8244 glossary checker (장애인→비활성화됨, 양말5://→socks5://, 비클로드→Claude가 아닌)

Stale sibling tests aligned to deliberately-moved contracts (each cites its mover):
request-log-detail-layout + -stream (#9245 intl provider), repro-8542 pin update,
quality-rail-gate-membership (#10134 shape), agentSkills-routes 45→46 (#9058),
cloudflare-ai-catalog-8717 (#8804 supersedes #8808), executor-xai (#9994),
vision-bridge-claude-wire (#9463 minimax→openai), sse-auth forced-pin (#8893),
tls-proxy-context (strengthened leak guards), rate-limit-local-error-classification
(#9164/#9342), minimax-thinking-signature (#9463), codebuddy-cn (#9723 +1 test),
github-copilot-custom-model (#9050), providers-g4f-batch3 (#9584),
synced-capability-warmup (#9199, stricter), sidebar-tools-group (#8221),
oauth-modal-grok-cli-paste (#9245); agentSkills/catalog.ts comment 45→46;
file-size rebaseline for proxyFetch (+19, annotated)

Refs #9985

* fix(ci): base-reds round 3f — waves F-J: 9 more real defects + stale sibling sweep

Real production defects fixed (all red on the pure tip, each with its origin):
- routeGuard.ts: #8949 accidentally DELETED the /api/providers/[id]/login
  local-only pattern — the route spawns a browser, so the loopback gate for a
  process-spawning route was gone (Hard Rules #15/#17); restored (314 guard
  tests green)
- agentSkills generator: #9058's category dispatch gave the config category an
  empty body, wiping skills/config-codex-cli/SKILL.md at the #10131 sync;
  fixed + SKILL.md regenerated via the official generator
- imageRegistry: #9982 broke same-provider bare aliasing (antigravity preview
  id sent upstream unresolved); new resolveSameProviderBareAlias() keeps the
  fal cross-provider fix intact
- imageRegistry: #9982's prefix strip handed the bare nano-banana ids to fal-ai,
  violating the pinned 2026-07-31 operator decision (adobe-firefly owns them);
  fal entries made prefix-only (dispatch already re-prefixes)
- mediaGeneration/fal.ts: the missing-credential 401 guard was lost when #10198
  deleted the superseded falHandler — tests were hitting the live network
- bottleneckPatch/rateLimitManager: #9041's merge clobbered #9604, resurrecting
  the Bottleneck v2.19.5 heartbeat bug (reservoir never refills); patched the
  library defect at the root and re-aligned chat-rate-limit-body-lock to the
  working reservoir contract
- processSupervisor.mjs: #9761 regressed the Node spawn to bare "node" (the
  #9156 launchd bug) and dropped #9209's ipv4first args; both restored
- openai-responses/pureHelpers: #9423's Agent null-sentinel was unreachable on
  the schemaless JSON-string path; gate extended
- i18n en.json: #8222's regen reverted the #9976 unclosed-tag fix and #8559's
  combo-cooldown copy; #9038 shipped 40 t() calls with no messages (runtime
  MISSING_MESSAGE); all restored/added + official sync-ui stamps, and vi's
  zero-marker policy re-established via the sanctioned translation backend

Stale sibling tests aligned (movers cited inline): chat-helpers (#9447),
executor-antigravity (#9351), video-fal-grok (#9982), visionBridge (#9759),
web-session-credentials (#8974), production-build-module-integrity (positive
anchor added), agentSkills-generator/skillManifestsLint/skills-injection/
agentSkillTools-mcp/listCapabilities-a2a (#9058), memory-settings (#10010),
model-catalog-policy-invalidation (#8906), model-alias-seed (#9485),
reactive-context-compaction (#8949), combo-provider-wildcard (broken upsert
helper), oauth-google-loopback (43-locale resurrected-key removal)

Validation: 501/501 across the 47 touched test files; typecheck:core, lint,
file-size, docs-sync all green.

Refs #9985

* fix(ci): base-reds round 3g — wave K/L: 4 more real defects + stale alignments

Real defects:
- base/reasoningEffort.ts: the stale duplicate cherry-pick #9612 re-added the
  codex minimal→low rewrite that #9883 had deliberately removed (OMP minimal
  passthrough); block removed again
- cursorImages.ts: #9840 wired prepareCursorImageForWire (sharp re-encode,
  fail-closed) into the SHARED resolveCursorImages, breaking zai-web and
  conol-web image uploads (HTTP 400 'undecodable'); new prepareForWire opt-out,
  Cursor default path unchanged (8 cursor suites green)
- modelCapabilities/snapshot: catalog prepare still issued 323 per-model reads
  of model_context_overrides + max_input_tokens overrides, violating #9199's
  bulk-load contract; both now resolve from the snapshot single pass
- v1-models-discovery-conformance: re-pinned to the bounded 30s SWR window
  (#9199/#10198) — the old 'stale-first regardless of age' contract is gone

Stale tests aligned (movers cited inline): codex-tools-strict-default (#9828
redundant-oneOf strip), devin-providers (#9245 i18n), db-migrationrunner-
constants-split (147→151 renumber #8228), gitlab-duo-oauth-setup (#9245),
chatcore-extracted-modules (#9161 outbound-protocol keying)

compression-api CI failures were cascade artifacts of codex-tools-strict-default
failing in the same force-exit shard process — no own defect (171/171 local).

Refs #9985

* fix(test): compression-api — register both describes before the runner starts

The DATA_DIR setup + route/db top-level awaits sat BETWEEN the two describes;
under --test-force-exit (the CI unit-runner flag) the process exits once the
already-registered tests finish, so on slow CI machines the whole second
describe died as 'Promise resolution is still pending' — the recurring
CI-only shard-2 failure that never reproduced locally without the flag.
Moved to the top of the file; 10/10 under --test-force-exit locally.

Refs #9985

* fix(quality): freeze modelCapabilities.ts at 1006 (annotated) — snapshot routing growth

Refs #9985

* fix(quality): move the modelCapabilities freeze into the frozen map (nested schema)

Refs #9985

* fix(i18n): translate all 39,718 pending UI keys across 42 locales (owner-approved)

Mass-translated every __MISSING__ placeholder via the official i18n:sync-ui
--translate-markers pipeline (operator backend), restoring i18nUiCoverage to the
100 baseline (was 89.9 after the merge-storm UI landings + the 42 keys #9038
never shipped).

Post-pass repairs, all caught by the existing gates:
- glossary: retired renderings the machine reintroduced normalized again
  (提供商→提供者 zh-CN/zh-TW, 鏈接→連結, 文檔→文件, 調用→呼叫, 供應商→提供者,
  響應→回應, 不活躍→未啟用 zh-TW; 클로드→Claude, 옴니루트→OmniRoute ko);
  DATA_DIR forbidden rendering avoided via 数据文件夹 rephrase
- ICU integrity: 120 values with renamed/dropped {params} repaired (39
  positional renames, 81 reset to the en source — functional over fluent)

Validation: glossary/pt-BR/vi/deno-relay/settings-keys/value-drift/google-
loopback suites 76/76; placeholder diff en×42 locales = 0; worst-locale
coverage = 100.0%.

Refs #9985

---------

Co-authored-by: backryun <bakryun0718@proton.me>
2026-08-13 00:02:25 -03:00
Diego Rodrigues de Sa e Souza
f1eb0b8357 refactor(providers): remove the Puter provider at its owner's request (#10210)
Remove the Puter provider (id `puter`, alias `pu`) entirely, at the
request of Puter's owner, Nariman Jelveh:

- registry entry (open-sse/config/providers/registry/puter/) and
  PuterExecutor (open-sse/executors/puter.ts), with their registrations
- API-key preset card (gateways.ts), provider icon and public SVG asset
- 33 free-model catalog entries (pool `puter`)
- authHint i18n key across all 43 UI locales
- credential-requirement frozen-list entry and related comments
- docs: ARCHITECTURE, CODEBASE_DOCUMENTATION, FREE_TIERS (removal note),
  PROVIDER_REFERENCE regenerated (337 providers), translated doc mirrors,
  llm.txt + its 42 i18n mirrors, README/AGENTS/package.json counts
  (338→337 providers, 144→145 migrations) and the 5 canonical SVGs
- migration 152 cleans up stored puter connections/keys/custom models;
  historical usage records are preserved (same principle as migration 151)
- regression guard: tests/unit/puter-provider-removed.test.ts; puter
  fixtures in shared tests swapped for neutral providers; translate-path
  golden snapshot regenerated

Historical CHANGELOG mentions are intentionally preserved; the removal
carries its own CHANGELOG entry.

Co-authored-by: backryun <bakryun0718@proton.me>
2026-08-12 16:54:50 -03:00
Diego Rodrigues de Sa e Souza
ecc89eef14 feat(providers): integrate audited free-tier gateways (#9210)
* feat(providers): add Zylo UnoRouter and Poolside registries

* feat(providers): integrate audited free-tier gateways

* feat: add wave2 free-tier provider registries

* feat(providers): add Mixlayer Speka and TokenReply registries

* feat: add wave 2 free-tier provider registries

* fix: align meganova provider slug

* feat(providers): integrate wave2 free-tier gateways

* feat(providers): add Wave 3-A free-tier registries

* feat(providers): add HelyxAI Auriko and Poixe registries

* feat(providers): add Naga AI and Chat Oripe registries

* feat(providers): integrate wave3 free-tier gateways

* feat(providers): add FreeInference registry

* feat(providers): add Free.ai registry

* feat(providers): integrate wave4 free-tier gateways

* docs: synchronize provider and free-tier inventories

* refactor(providers): split audited gateway catalog

* feat(providers): add audited Void AI and HelixMind gateways

* feat(providers): finalize audited free-tier integration

* test(providers): update APIKEY split count to 229 after rebase onto release/v3.8.50

The rebase merged the release catalog (201 APIKEY providers) with the PR's
28 free-tier additions, yielding 229 total. Correct the characterization
count so the partition assertion reflects the true merged state.

---------

Co-authored-by: diegosouzapw <diegosouzapw@users.noreply.github.com>
Co-authored-by: backryun <bakryun0718@proton.me>
2026-08-12 16:19:25 -03:00
Diego Rodrigues de Sa e Souza
f6ccd3cf9f fix(quality): green release/v3.8.50 base-reds round 2 (#9985) (#10131)
* fix(quality): green release/v3.8.50 base-reds round 2 — gateways/conol/deepai corruption, migrations, docs, ratchets, dashboard-typecheck

Base-red fix for issue #9985 after the 2026-08-11 merge storm (99 PRs).

Real defects fixed:
- gateways.ts: close regolo entry (was swallowing naga-ac + chatanywhere from #9421), drop stale duplicate chatanywhere entry (#9594)
- conol-web + deepai registry: correct ../shared import depth + deepai executor:default
- modelSelectModalHelpers: close isProviderModelHidden (#9011)
- driverFactory.test.ts: restore eaten test-closing brace (#9173)
- usageTracking: remove duplicate cache_* props
- modelCapability{Overrides,ResolutionSnapshot,Capabilities}: max_token -> max_output_tokens (#9199 vs #8908) + test align
- videoGeneration: drop duplicate handleFalVideoGeneration import (mediaGeneration/fal canonical, #9982)
- responseSanitizer: cast input_tokens_details before .cached_tokens access
- EditConnectionModal: missing alibaba code fields, hoist validationPsd, providerPageHelpers Badge variant union
- FreeBudgetCard: t() -> labels.noApiKey
- peerRouting + cliRuntime: ProcessEnv typing
- image-combo.test.ts: type any -> unknown
- fal.test.ts: moved to tests/unit/services (collected path) 14 tests green
- remove duplicate 143_job_registry.sql (146 canonical), KNOWN_GAPS fix

Docs/ratchets (owner-authorized rebaselines, annotated):
- CHANGELOG 3.8.50 living section restored + 42 i18n mirrors
- MCP-SERVER.md 104->105 tools + i18n
- ENVIRONMENT.md/.env.example: ADOBE_FIREFLY_CHROME_HEADED + DEBUG_CLAUDE_NONSTREAM
- fabricated-docs allowlist: TELEGRAM proposal env vars
- file-size: 5 grown files + proxyFetch 1207->1220
- dead-code 230->248, codeql 2->9 (drift from merged PRs, not this PR)
- untrack _tasks symlink; agent-skills-sync --apply (config-codex-cli)

* fix(changelog): reformat two feature fragments to the bullet convention (#9239, #9490)

* fix(quality): prune stale ESLint suppressions (base-red)

* fix(quality): resolve open-sse type errors + catalog/build regressions (base-red round 3)

Storm-merge splices repaired in the base-fix PR #10131:
- doctor.ts: AppConfig missing brokerSocketPath
- conol-web.ts: Buffer not assignable to BodyInit (Uint8Array)
- tinycms.ts: TinyCmsExecutor.execute return matches BaseExecutor (response/url/transformedBody)
- tinycmsSigner.ts: encodeInto never-narrowing guard + dead wasm URL fallback (Turbopack)
- virtualFactory.ts: options slot for resolutionSnapshot
- bottleneckPatch.ts: insufficient-overlap casts (as unknown as)
- imageCombo.ts: narrow handleImageGeneration union result
- browser-worker.ts: AppConfig + turn.capabilities splice
- conolDiscovery.ts: getProviderOutboundGuard from Policy module
- catalog.ts: drop removed SWR hooks (getCatalogStaleWhileRevalidateMs + accessors), CatalogCachePolicy -> inline settings, resolve 4-arg call
- catalogCache.ts: remove dead inFlight/promise refs
- chat.ts: add isProviderBreakerFailureStatus import
- model-catalog-cache-swr-8728.test.ts: align to #9199 new API (policy injection removed)

* fix(quality): align UI test fixtures to current component contracts (base-red vitest)

- setup-wizard: provide required serverState prop (component gained it in a merged PR)
- grok-device-oauth-modal: next-intl stub resolves grok flow keys to EN labels
- provider-quota-widget: label now inline (PR #8916 removed AutoRefreshButtonLabel extraction) — test the widget
- use-provider-connections-cursor-refresh + phase1f: match /api/providers?provider=<id> query form; hoist heavy dynamic imports to module scope (timeout flake)
- home-topology: mock next/navigation useRouter (component added node-click navigation)
- cooling/lobe/AutoComboCatalog: raise cold-import describe timeouts to 30-60s
- request-logger-*: align to current detail-view contract

* fix(search): guard params.token undefined in serper headers (typecheck base-red)

* fix(search): guard token headers + non-null providerConfig (typecheck base-red)

* fix(changelog): restore base CHANGELOGs eaten by merge auto-resolve (43 files)

---------

Co-authored-by: diegosouzapw <diegosouzapw@users.noreply.github.com>
Co-authored-by: backryun <bakryun0718@proton.me>
2026-08-12 16:09:34 -03:00
Diego Rodrigues de Sa e Souza
85c4292ce3 fix(security): correct XML double-unescape and non-CSPRNG nonce from CodeQL sweep (#10154)
* fix(security): correct XML double-unescape and non-CSPRNG nonce from CodeQL sweep

Two real defects surfaced by the 2026-08-12 code-scanning triage.

decodeXmlText decoded `&amp;` before `&quot;`/`&#39;`, so `&amp;quot;` — the encoding of
the literal text `&quot;` — collapsed to `"` in a second pass. The decoded values feed the
workspace-root trust comparison in parseTrustedCodexEnvironment, so an encoded path could
decode into a different path than the client declared. Decoding `&amp;` last fixes it.

The tinycms nonce fell back to `Date.now()` plus a non-cryptographic PRNG when
`crypto.randomUUID` was absent. That nonce is signed into the provider's anti-replay
payload, so the fallback produced a predictable value silently. It is now always
`randomUUID()` from node:crypto, which is present on every supported runtime.

Both guards are mutation-validated: reverting either fix makes the new test fail.

Refs #9985

* fix(security): reword tinycms nonce comment so the CSPRNG regression test holds

---------

Co-authored-by: backryun <bakryun0718@proton.me>
2026-08-12 16:03:58 -03:00
Diego Rodrigues de Sa e Souza
3852ae533a fix(docker): eliminate npm-bundled CVEs from the published image (#10182)
* chore(repo): re-untrack the _tasks self-referential symlink

`caf768e3c4` untracked it; the DeepAI merge (44069c5f54, #9443) re-added
it. It is an absolute symlink pointing at one machine's checkout, and
AGENTS.md keeps `_tasks/` out of the main repo entirely. While tracked,
`check-tracked-artifacts.mjs` fails on pre-commit, so no commit can be
made on this branch at all — this restores the precedent fix purely to
unblock committing, and is unrelated to the Docker change that follows.

* fix(docker): eliminate npm-bundled CVEs from the published image

Trivy reported 9 HIGH/MEDIUM CVEs against the npm CLI's own bundled
node_modules inside the published image (brace-expansion, ip-address,
tar, undici under /usr/local/lib/node_modules/npm/node_modules).

The base stage claimed `npm install -g npm@latest` shipped patched
copies. It does not: npm@12.0.2 (latest) bundles brace-expansion 5.0.7,
ip-address 10.2.0, tar 7.5.19 and undici 6.27.0 — all still vulnerable.
No npm release fixes them, so that step was buying zero CVEs.

Overlay the patched versions onto npm's bundled tree instead, pinned and
semver-compatible with the ranges npm's own tree declares (minimatch ->
brace-expansion ^5.0.5, socks -> ip-address ^10.1.1, node-gyp -> tar
^7.5.4 and undici ^6.25.0, so undici stays on 6.x). Removing npm from
the runner stages was not viable — the app shells out to npm at runtime
(installers/utils.ts::runNpm, system/autoUpdate.ts,
system/globalPackagePath.ts, api/system/version) — and the old comment
asserting otherwise is corrected.

---------

Co-authored-by: backryun <bakryun0718@proton.me>
2026-08-12 15:59:02 -03:00
Diego Rodrigues de Sa e Souza
c19da4db46 fix(security): resolve open CodeQL alerts (#10188)
Alert 806 (js/insecure-randomness, open-sse/executors/tinycms.ts): the
TinyCMS nonce is signed into x-secure-signature and reused as
x-secure-nonce / x-session-id, so the Math.random() fallback made a
signed request predictable and replayable. Use randomUUID() from
node:crypto unconditionally.

Alert 811 (js/double-escaping, chatgpt-web adapters/environment.ts):
decodeXmlText() decoded &amp; before &quot; / &#39;, so the bare & it
produced was re-consumed and the text was unescaped twice
(&amp;quot; collapsed to "). These values become the trusted Codex
sandbox cwd / workspace_roots, so the double-unescape silently rewrote
the workspace boundary. Decode &amp; last.

Alerts 813/814 (js/incomplete-url-substring-sanitization, test files):
replace the includes() URL checks with exact comparisons
(new URL(url).hostname === ... and an explicit === over the recorded
URL array). Both assertions get strictly tighter.

Regression guards: tests/unit/tinycms-secure-nonce-randomness.test.ts
and tests/unit/chatgpt-web-environment-double-unescape.test.ts, both
failing before the fix and passing after.

Co-authored-by: backryun <bakryun0718@proton.me>
2026-08-12 15:58:58 -03:00
Diego Rodrigues de Sa e Souza
423b616503 fix(dashboard): retarget Kimi promo CTA to the API platform aff link (#10200)
Moonshot cannot sell coding plan subscriptions to most new users, so the
Get Kimi Code traffic could not convert. At their request the dashboard
banner and the README promotional spots now point at
platform.kimi.ai?aff=omniroute with partner-approved copy, in all 43
locales. The dismissal key bumps to -v2 so the retargeted banner shows
once to users who dismissed the old one, and the in-app title finally
adopts the founding Open Source Friend framing agreed in July (title key
renamed to foundingFriendTitle per the value-drift gate rename rule).
Kimi Code provider pages keep kimi.com/code on purpose: they serve
existing coding plan subscribers. Also un-excludes the now-fixed
kimiSponsorBanner.test.tsx from vitest.config.ts (#8618 is closed).

Co-authored-by: backryun <bakryun0718@proton.me>
2026-08-12 15:58:53 -03:00
Diego Rodrigues de Sa e Souza
00a3edc3ce docs(readme): add olud.ai directory badge (#10205)
olud.ai featured OmniRoute in its open-source directory (health score
96/100). The badge is dynamic, it reads the live star count and rank, so
it never goes stale. Placed with the other ranking badges (Trendshift,
Star History).

Co-authored-by: backryun <bakryun0718@proton.me>
2026-08-12 15:58:49 -03:00
Diego Rodrigues de Sa e Souza
37d70785c7 fix(build): repair broken production build, red lint gate and SWR crash (#10198)
The Build CI job is advisory, so eight module-level defects from eight
different PRs accumulated on release/v3.8.50 until `npm run build` failed
with 7 Turbopack errors and `npm run lint` with 14.

Build (link-time):
- modelSelectModalHelpers.ts: a lost `}` swallowed PROVIDER_TEST_CHUNK_SIZE
  into isProviderModelHidden's body (#9011).
- videoGeneration.ts: handleFalVideoGeneration imported twice; the standalone
  falHandler.ts is superseded by the provider-neutral mediaGeneration/fal.ts
  and is removed here (#9982 over #9969).
- catalog.ts: re-exported and called the injectable SWR policy that #9199
  deliberately replaced with a fixed 30s bound. Fixed on the consumer side —
  restoring the accessor would resurrect the unbounded window #9199 removed
  after measuring a 41s catalog build in production.
- tinycmsSigner.ts: generated wasm-bindgen glue kept a sidecar
  `new URL('wasm_signer_bg.wasm', import.meta.url)` that no file backs;
  Turbopack resolves it statically. The module ships inlined as WASM_BASE64
  and the only caller always passes it explicitly (#8736/#10087).
- conolDiscovery.ts: imported getProviderOutboundGuard from outboundUrlGuard,
  which does not export it. Fixed on the consumer side: outboundUrlGuard.ts is
  loaded by the packaged CLI without a tsconfig, so it must stay free of
  `@/`-aliased imports (#7682).

Runtime (the build never caught this one):
- catalogCache.ts::scheduleBackgroundRefresh had two dangling statements
  referencing undeclared `inFlight`/`promise`, so EVERY stale-while-revalidate
  read threw a ReferenceError. Surfaced by realigning the #8728 suite, which
  #9199 left asserting a removed contract.

Lint:
- driverFactory.test.ts: a case inserted between the preceding test's `finally`
  and its `});` left the file unparseable, so the SQLite driver-cascade suite
  (26 tests) had not run since 2026-08-11 (#9173).
- providerModelsConfig.ts: imported an executor directly, crossing the G14
  boundary; routed through a new open-sse/services/zaiWebCredentials.ts (#8451).
- image-combo.test.ts: 11 `any` violations, now typed (#9499).

Validation: npm run build exit 0, npm run lint clean, typecheck:core clean,
41/41 tests green across the affected suites.

Refs #9011 #9982 #9199 #8728 #8736 #10087 #8974 #9173 #8451 #9499

Co-authored-by: backryun <bakryun0718@proton.me>
2026-08-12 15:58:40 -03:00
Diego Rodrigues de Sa e Souza
bfad01ecb3 docs(readme): fold the evolution section into one compact table with New markers (#10207)
Co-authored-by: backryun <bakryun0718@proton.me>
2026-08-12 15:44:50 -03:00
Diego Rodrigues de Sa e Souza
cffaeebb27 docs(readme): expand the v3.8.51 teaser into an in-flight feature grid with provider numbers (#10204)
Co-authored-by: backryun <bakryun0718@proton.me>
2026-08-12 15:04:54 -03:00
Diego Rodrigues de Sa e Souza
db8d2b2013 docs(readme): add cycle-evolution section — 291→338 providers and the v3.8.51+ teaser (#10203)
Co-authored-by: backryun <bakryun0718@proton.me>
2026-08-12 14:47:45 -03:00
Diego Rodrigues de Sa e Souza
8bd17be8aa docs: refresh every stale count to measured values + harden docs-counts gate (#10196)
* docs(reference): regenerate PROVIDER_REFERENCE from live provider modules

The catalog was hand-stale at 291 since 2026-08-05 while the live provider
modules define 338 unique IDs. The generator also omitted the NOAUTH_PROVIDERS
module entirely (10 providers) and hardcoded the executor count in its footer;
both are now sourced from the live modules.

Refs #9985

* docs: refresh stale counts across README/AGENTS/llm.txt and architecture docs

Every count updated to values measured from the live code on 2026-08-12:
providers 291/271/248/236/226/212->338, migrations 110/117/130->144, MCP tools
94/99/104->105 (base 42->43), scopes 13/32->31, strategies 17/18->19,
Auto-Combo factors 12/13->14 (sessionAvailability row added to the table),
executors 67/78/84/89->101, quality gates ~48->~80, locales 29/30/39/40+->43
(41 non-source), A2A skills 5->6 (list-capabilities), free tier 43 pools/516
models/~1.53B/~2.15B->42/495/~1.51B/~2.13B, contributors 500+->320+ (324
unique emails), llm.txt version 3.8.47->3.8.50. llm.txt i18n mirrors resynced
(headers preserved, body mirrored).

Refs #9985

* docs(diagrams): sync SVG hero/pillars/comparison/cli/tier numbers

Text nodes and aria-labels only; layout, coordinates and animation values
untouched. providers 278/290->338 (cli list footer 264->334 more), MCP tools
104->105, strategies 18->19, free tier 43 pools/460+/516 models->42/495,
headline ~1.53B/~2.15B->~1.51B/~2.13B. All six SVGs re-validated as XML.

Refs #9985

* feat(check): harden docs-counts gate - live provider source, llm.txt, migrations, SVGs

The gate trusted PROVIDER_REFERENCE.md as the provider total, so a hand-stale
doc (291 vs 338 live) kept it falsely green. New STRICT checks: doc total vs
the live provider modules (same collections the generator unions), provider
count in llm.txt and package.json description, migration count vs
README/AGENTS/llm.txt, and a canonical-number sweep (providers/MCP
tools/strategies/pools) over the six README SVG diagrams with
coordinate/attribute-safe patterns. TDD: 9 new unit tests (red first on the
missing exports, green after) in tests/unit/check-docs-counts-sync.test.ts.

Refs #9985

* docs(readme): refresh What's New range and add v3.8.50 cycle highlights

---------

Co-authored-by: backryun <bakryun0718@proton.me>
2026-08-12 14:24:56 -03:00
Diego Rodrigues de Sa e Souza
be62ca04f7 chore(repo): hygiene sweep — approved deletions, ignore repairs, tracked-artifacts guard, Hard Rule 23 (#10193)
* chore(repo): remove one-shot reports and stale docs approved by owner

* chore(repo): untrack _references and superpowers planning artifacts

* fix(repo): repair .gitignore anchors and un-ignore published CLI entrypoint

* feat(ci): extend check:tracked-artifacts never-allowed classes

* docs(agents): add Hard Rule 23 (_tasks append-only) and sync scripts/ layout

* feat(ci): generic never-track rule for every root underscore path (_*)

---------

Co-authored-by: backryun <bakryun0718@proton.me>
2026-08-12 14:24:18 -03:00
Diego Rodrigues de Sa e Souza
a543992b96 docs: restructure — planning artifacts to _tasks, duplicate merges, nav gaps closed (#10190)
* docs: migrate planning artifacts out of docs/ (superpowers, sessions, proposals)

* docs(ops): relocate redis/production and contribution guides, move ROADMAP to repo root

* docs: merge duplicate guides (troubleshooting, chatgpt-web codex, docker channels, memory backend)

* docs: close fumadocs nav gaps (meta.json manifests + index)

* chore(check): drop dead allowlist entries after docs restructure

* docs: prettier formatting follow-up on merged guides

* docs: point README at merged guide targets and allow ROADMAP.md at repo root

---------

Co-authored-by: backryun <bakryun0718@proton.me>
2026-08-12 14:23:52 -03:00
Diego Rodrigues de Sa e Souza
4eb65acdc0 docs(changelog): reconcile the full v3.8.50 cycle — 467 uncovered commits + 224 contributor credits (#10189)
* docs(changelog): aggregate v3.8.50 cycle fragments into the living section

* docs(changelog): cover the full v3.8.50 cycle with contributor credits

* docs(changelog): v3.8.50 contributors table + 42 i18n mirrors

* docs(changelog): order the v3.8.50 maintenance bullets before the contributors table

---------

Co-authored-by: backryun <bakryun0718@proton.me>
2026-08-12 14:23:35 -03:00
backryun
838e4d688c chore(repo): remove tracked local artifacts (#10178) 2026-08-12 11:40:58 -03:00
backryun
e9020f0c0c fix: enforce OpenAI model lifecycle without silent reroutes (#8627) 2026-08-12 09:04:41 -03:00
backryun
bd472200d5 [v3.8.50] Fix Z.ai web browser transport and model capabilities (#8451)
* fix: complete Z.ai web browser transport

* refactor: address Z.ai review feedback

* test(zai-web): reconcile the #8014 endpoint guard with the chats/new + signed flow

Rebasing onto release/v3.8.49 pulled in #8503, which repointed CHAT_URL to
/api/v2/chat/completions and added an endpoint probe. This branch already
targets v2, so the executor conflict resolved to this branch's superset
(NEW_CHAT_URL + signature constants alongside the same v2 CHAT_URL). The two
tests needed adapting, because #8503's assertions assume the pre-rework flow:

- executor-zai-web.test.ts: the completion URL now carries the request
  signature as a query string, so an exact-equality check on the endpoint can
  never match. Assert the v2 prefix instead.
- zai-web-chat-endpoint-8014-probe.test.ts: the probe drove the executor with a
  bare cookie credential and no captcha proof, which now routes through the
  browser transport — fetch was never called and the probe captured nothing.
  Supplied a direct-path credential, and matched on pathname across all
  requests (the executor also probes the homepage for the frontend version and
  calls /api/v1/chats/new first).

The guard's intent is unchanged and slightly strengthened: it now asserts no
request reaches the stale unversioned path and that exactly one completions
request is issued, against v2.

54/54 across the zai suites; typecheck:core and eslint clean.

* fix(zai-web): surface upstream error frames instead of finishing empty

Reported on this PR: HTTP 200, `out=0`, stream "complete", no content and no
diagnosis.

Cause. HTTP-level failures are already handled — fetchUpstream turns any !ok
response into a makeErrorResult with the sanitized body. The gap is a 200 whose
SSE body carries an error payload: parseZaiFrame returns null for it,
drainSseDeltas drops it, and buildZaiStreamingBody then closes with an empty
assistant message + stop + [DONE]. The caller reads that as a successful empty
completion, so a rejected signature, an expired captcha and a stale token all
look identical — which is why this had to be diagnosed by reading code rather
than logs. Hard Rule #6.

Fix. parseZaiFrame now classifies an affirmatively error-shaped frame
(`error` at the top level or under `data`, string or {detail|message|msg}) as a
terminal delta, checked before the delta paths so it cannot fall through to the
"no usable delta" null. The stream emits it as `[Z.ai error] <message>`,
matching the mid-stream convention the other web executors already use
(zed-hosted's createErrorChunk) — the 200 is on the wire, so the status cannot
change, but the caller must not be left reading a blank success. Content
streamed before the failure is preserved. Message goes through
sanitizeErrorMessage (Rule #12).

Deliberately NOT changed: a contentless frame still parses to null. That is
live-validated behaviour, not an oversight — z.ai emits phase frames with no
delta_content, and executor-zai-web.test.ts pins it ("returns null for frames
with no usable delta"). Treating "nothing parseable arrived" as a failure would
invent policy on top of an observed protocol and risk false errors on the happy
path, so this only adds recognition of explicit error frames.

Tests (TDD, RED then GREEN): zai-web-silent-empty-repro.test.ts — 7 cases.
Error frame classified and terminal; surfaced through the stream with the
upstream's own text; surfaced after partial content without losing it; plus a
REGRESSION GUARD that contentless/phase-only frames are still skipped, and two
controls that the happy path and reasoning-only output are untouched. The guard
and controls passed before the fix; the four error cases did not.

94/94 across the zai + stream suites; typecheck:core, eslint and check:file-size
clean.

* refactor(sse): extract the zai-web transports so the complexity ratchet holds

The v3.8.49 merge-train rebaseline (#8686) set the ceiling to the tip's own
measurement, leaving zero headroom, so this branch's +5 cyclomatic / +3 cognitive
own-growth had nowhere to sit once rebased onto it.

Eight violations, all in code this branch introduces, resolved by extraction —
no behaviour change:

- `execute` (152 lines, complexity 25, cognitive 20) now delegates to
  `resolveZaiRequest()` for the four client-error rejections and to a
  `fetchViaSignedApi()` method for the CAPTCHA/signature path, so it reads as
  "validate, pick a transport, shape the response".
- `fetchThroughBrowser` (126 lines, cognitive 16) hands its image decoding to
  `resolveZaiBrowserAttachments()`, its Playwright options to
  `buildZaiBrowserChatOptions()`, and its call-log payload to
  `buildZaiBrowserAuditBody()`.
- `configureZaiBrowserEffort` (cognitive 35 — the worst of the set) repeated a
  wrap-and-relabel try/catch four times inside an if/else. `runStage`, which
  already existed one function below, is now module-scoped and reused, and the
  toggle collapses to `checked !== config.enabled` (same four cases).
- `validateWebCookieProvider` (complexity 19) moves its can-we-probe-this
  cascade into `resolveWebCookieProbe()`, which returns either a rejection or
  the URL + headers to use.
- `acquireBrowserContext`'s creation closure (complexity 17) hands cookie and
  localStorage seeding to `seedContextSession()`.

That last extraction also clears a violation that predates this branch —
`acquireBrowserContext` was already over the 80-line ceiling — so cyclomatic
lands at 2187 against a baseline of 2188.

Verified: check:complexity-ratchets green both metrics; typecheck:core clean;
ESLint clean on all four files; 85 tests across the zai-web, web-cookie
validation, browser-pool and model-test-runner suites pass.

* fix(zai-web): surface upstream errors on the non-streaming path

collectZaiNonStreaming ignored delta.error — a 200 whose SSE body carries
an error frame (rejected signature, expired captcha, stale token) came
back as a successful empty completion. Now it throws on an error frame,
matching the streaming path's [Z.ai error] convention; the caller's
existing try/catch returns makeErrorResult(502) instead of an empty 200.

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

---------

Co-authored-by: backryun <busan011@ormbiz.co.kr>
Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
2026-08-12 08:41:03 -03:00
backryun
acc066db3f [v3.8.50] feat(devin-desktop): replace public Windsurf provider (#8228)
* feat(devin-desktop): replace public Windsurf provider

* fix(migrations): renumber Devin Desktop migration to 151 (avoid 147 collision)

147_windsurf_to_devin_desktop.sql collided with the released
147_api_keys_model_access_mode.sql — getMigrationFiles throws
"Migration version collision detected" on every DB start. Base occupies
slots up to 150, so renumber the new migration to 151 and point the
windsurf→devin RENAMED_MIGRATION_COMPATIBILITY entries (and tests) at it.
147 is freed in KNOWN_GAPS since 147_api_keys now owns the slot.

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

---------

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
2026-08-12 08:40:18 -03:00
backryun
5f5c0bc94a fix(tinycms): align executor and signer contracts (#10087) 2026-08-12 08:28:13 -03:00
backryun
c9282248eb fix(types): restore provider breaker status guard (#10088) 2026-08-12 08:25:10 -03:00