Stage 7 of issue #10321 moves the optional ML and browser automation dependency closures out of the desktop bundle into checksummed, versioned packs installed on demand through the omniroute packs command.
- scripts/build/optionalPackStaging.mjs stages pack members under .build/optional-packs, creates release tarballs, and emits optional-packs.index.json with per-member SHA-256 checksums.
- scripts/packs provides manifest, install, remove, and verification helpers plus the packs CLI commands.
- Runtime lookup includes installed pack node_modules directories, while LLMLingua and browser executors continue to degrade gracefully when packs are absent.
The measured darwin-arm64 staging closure was about 534 MB of the 929 MB standalone node_modules tree (57%).
better-sqlite3 v13 ships Node-API prebuilds for every packaged platform
(darwin/linux/linuxmusl/win32 x x64/arm64) inside the npm tarball, so the
Electron-ABI node-gyp source rebuild in prepare-electron-standalone.mjs is
obsolete. Replace it with a fail-fast prebuild verification that mirrors
better-sqlite3 lib/binding.js selection, and strip build/deps/src so the
packaged loader can only resolve the prebuild.
Verified locally on darwin-arm64: the same darwin-arm64.node prebuild loads
under both Node 24 (NODE_MODULE_VERSION 137) and Electron 43.3.0 under
ELECTRON_RUN_AS_NODE (148); DB create/migrate/read/write/close/reopen pass
in both runtimes and cross-runtime on each other's database files.
Issue #10321 Stage 6.
* fix(chat-body-admission): process-wide budget (#10110)
Remove per-session admission lanes that multiplied the documented
"in one process" heavy/bytes bound by up to 64. All requests now admit
against ONE process-global ChatAdmissionController so the bound holds
against fake-credential sharding.
Per-request session identity survives only as a fairness scheduling key:
waiters are grouped per key and served round-robin (#9654) against the
shared budget — one connection's burst cannot starve others.
- src/shared/middleware/chatBodyAdmission.ts: delete lane map + LRU/TTL
eviction; ChatAdmissionController is now the global budget with per-key
FIFO queues + round-robin dispatchFair(). PerConnectionAdmissionController
returns the same shared controller for every session. resolveSessionId
stays as a scheduling key with honest re-scoping docs. snapshot() emits
process-wide aggregates.
- tests/unit/chat-body-admission-aggregate-10110.test.ts: new U6 suite — 6
deterministic tests (LRU-no-mint, TTL-no-mint, shared byte budget,
16 MiB config, same-session recreation, round-robin fairness). RED on
release/v3.8.50, GREEN post-fix.
- tests/unit/per-connection-admission-9654.test.ts: rewrite the tests that
encoded the defect (per-session isolation) to assert the global-budget
contract.
- docs/reference/ENVIRONMENT.md: OMNIROUTE_CHAT_ADMISSION_MAX_QUEUED_BYTES
documented as process-wide; VIRTUAL_TTL_MS/VIRTUAL_MAX_SESSIONS deprecated.
* docs(changelog): add #10322 fragment for process-wide admission budget
* ci: retrigger checks after transient npm ci network failure in shard 3/4 (ETIMEDOUT)
---------
Co-authored-by: Brandon Bennett <brandonbennett@macbookair.myfiosgateway.com>
* docs(ops): recommend TCP liveness and HTTP /healthz readiness for k8s
Stock Docker HEALTHCHECK hits /api/monitoring/health (deep). Orchestrators
should not use that path for kubelet liveness. Document /healthz vs deep
health, note same-process event-loop limits, and link related issues.
* docs: add changelog fragment for #10297
---------
Co-authored-by: Ravi Tharuma <RaviTharuma@users.noreply.github.com>
Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com>
* feat(cli): refuse ephemeral container auto-config writes
Detect containerized OmniRoute and block CLI/API config writes into
throwaway homes unless a bind mount or explicit opt-in is present, and
honor compose host-profile CLI_CONFIG_HOME mounts outside the container home.
Co-authored-by: Cursor <cursoragent@cursor.com>
* chore(changelog): name fragment for #10057
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: yansigit <yansigit@users.noreply.github.com>
Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com>
* fix(providers): make the monsterapi deprecation from #8676 actually apply
#8676 marked MonsterAPI deprecated after its domain stopped resolving, but
wrote the flag as `isDeprecated`. Nothing reads that key. The field the
codebase consumes is `deprecated`:
src/shared/validation/providerSchema.ts declares `deprecated`
ProviderCard.tsx strikethrough + block icon + reason
ProviderTestSlideOver.tsx warning
providerOnboardingCatalog.ts Boolean(provider.deprecated), sorts last
ProviderOnboardingWizard.tsx deprecated badge
scripts/docs/gen-provider-reference.ts gates the DEPRECATED note
Zod object schemas ignore undeclared keys, so `isDeprecated` never failed
validation - it was dropped silently. The deprecation therefore had no effect
anywhere, and tests/unit/8676-monsterapi-deprecation.test.ts asserted the same
unread key, so it stayed green while guarding nothing.
The committed docs/reference/PROVIDER_REFERENCE.md is the visible proof: the
generator renders predibase (which uses `deprecated`) with a DEPRECATED note,
while monsterapi still advertised "Get API key at monsterapi.ai" - a domain
that does not resolve (probed 2026-08-13: api.monsterapi.ai and monsterapi.ai
both 000, against api.openai.com 401 as a reachability control).
Rename the key, repair the regression test to assert the consumed field and to
reject the undeclared one, and refresh the generated reference row.
* fix(providers): name the changelog fragment for PR #10234
Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
---------
Co-authored-by: pacocartones <pacocartones@users.noreply.github.com>
Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
Co-authored-by: adevwithpurpose <adevwithpurpose@users.noreply.github.com>
* feat(providers): add local ZCode ACP backend
* test(snapshots): regenerate translate-path golden for zcode provider
The new local ZCode ACP backend (zcode://app-server/stdio) was added to the
provider catalog but the translate-path golden snapshot was not regenerated,
so the combined suite (provider-translate-path-golden.test.ts) failed on the
merged tip. Regenerate the snapshot to include the zcode translate-path entry.
Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
* docs(env): document ZCODE_* vars for the local zcode provider
Registers the 11 ZCODE_* env vars read by the zcode executor (.env.example
+ docs/reference/ENVIRONMENT.md) so the env-doc-sync gate stays green.
Co-authored-by: Diego Souza <8016841+diegosouzapw@users.noreply.github.com>
* test(autoCombo): include zcode in the glm-family provider set
#10184's local zcode backend advertises the full GLM_SHARED_MODELS
line-up (registry/zcode, authType none) — same documented case as auggie
and devin-cli-agentic. Update auto/glm provider-set assertion to include
it.
Co-authored-by: Diego Souza <8016841+diegosouzapw@users.noreply.github.com>
---------
Co-authored-by: roomhacker <roomhacker@bezrabotnyi.com>
Co-authored-by: adevwithpurpose <adevwithpurpose@users.noreply.github.com>
Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
Implements the secure, opt-in Video Bridge for issue #9760, including bounded FFmpeg frame extraction, capability-aware routing, telemetry, settings UI, localization, documentation, and regression coverage.
* feat(providers): add tencent-aistudio-web cookie provider (tasw)
* fix(sse): remove orphaned DevinDesktopExecutor import from executor index
The "devin-desktop" executor key is unused (devin-desktop provider config
resolves to executor "devin-cli"); the imported ./devin-desktop.ts file
was never present, so executors/index.ts failed to load (ERR_MODULE_NOT_FOUND)
and broke every unit test that imports the executor registry (e.g.
tests/unit/deepseek-web.test.ts). Stale base sync carried this into the branch.
Remove the dead import/registration/export.
* fix(providers): restore DevinDesktopExecutor registration in executor index
The previous commit removed the devin-desktop executor import/registration/
export from open-sse/executors/index.ts, but the devin-desktop provider
registry still resolves executor "devin-desktop" and
tests/unit/devin-providers.test.ts asserts hasSpecializedExecutor("devin-desktop")
is true. The removal broke 6 tests in that file. Restore the three lines so
the live Devin Desktop executor keeps serving the provider.
Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
* fix(providers): correct tencent-aistudio-web wrapper shape + provider count sync
Return {response,url,headers,transformedBody} instead of a raw fetch Response
(the executor contract every other executor in this file follows) and
re-wrap the upstream body so it uses the local Response constructor, not the
undici-patched one from globalThis.fetch.
Regenerate docs/reference/PROVIDER_REFERENCE.md and sync the 339->340
provider-count claims (README, AGENTS.md, llm.txt + 42 i18n mirrors,
package.json, promise-pillars/comparison-table/cli-terminal SVGs) that this
PR's new provider invalidated.
Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
* docs(providers): sync readme-hero.svg provider count claim (339->340)
Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
* fix(providers): register tencent-aistudio-web web-session credential metadata + golden
Add the WEB_SESSION_CREDENTIAL_REQUIREMENTS entry for tencent-aistudio-web
(cookie-based, matching the executor's raw Cookie-header credential) and
regenerate the translate-path golden snapshot to include the new provider —
both were failing CI unit tests that enumerate every registered provider.
Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
* fix(providers): align tencent-aistudio-web test with the wrapper-shape contract
The test asserted res.status/res.json() directly against executor.execute()'s
return value, matching the pre-fix (broken) raw-Response shape. Update it to
read res.response.status/res.response.json() — the {response,url,headers,
transformedBody} contract every executor in this codebase follows.
Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
---------
Co-authored-by: openhands <openhands@all-hands.dev>
Co-authored-by: MeRezaRezaei <MeRezaRezaei@users.noreply.github.com>
Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
Co-authored-by: adevwithpurpose <adevwithpurpose@users.noreply.github.com>
Deploying the internal gateway was a manual build/pack/scp/npm-i/pm2-restart sequence with no record of what landed and no proof it served traffic. On 2026-08-14 that shipped a package built from a branch predating #10373: the process came up, health said 'healthy', and every request returned 502 until a human hit it.
scripts/ops/deployCanary.ts holds the policy as pure functions — refuse an artifact that is not traceable to the release line (reusing #10427), and grade the deploy on health PLUS at least one real completion. Zero probes fails: 'no probe ran' must never read as 'everything is fine', which is exactly how a broken egress path hides behind a green health check. Remote steps are argv arrays, never shell strings (Hard Rule #13), ordered so the rollback anchor is captured before the install overwrites it.
scripts/ops/deploy-canary.mjs performs the side effects, supports --dry-run, and prints the rollback command when the smoke fails.
Closes#10429
The packaged artifact stamped dist/BUILD_SHA but nothing verified the SHA belonged to the release line, so a tarball built from a feature branch installed and served traffic indistinguishably from a release build. That is how the internal gateway ended up running a build that predated #10373 and answered every request with 502 'Executor result must contain a Response' — identifying it required SSH plus grepping the compiled chunks.
scripts/build/buildProvenance.ts classifies a build SHA against the release ref (pure functions, injected git probe). A missing SHA fails even with the canary override: an unidentifiable artifact cannot be vouched for. validate-pack-artifact enforces it on real packs (skipped under --policy-only, which runs without a build); OMNIROUTE_ALLOW_CANARY_BUILD=1 records a deliberate off-release-line build instead of failing it. /api/monitoring/health now exposes system.buildSha — absent when unknown, never fabricated.
Closes#10427
Any process that opened the DB without setting DATA_DIR resolved to ~/.omniroute/storage.sqlite — the operator's live database, provider credentials included. tests/_setup/isolateDataDir.ts only covers the npm scripts; the documented single-file test command and ad-hoc probes bypassed it (one did exactly that during #10334).
resolveWritableDataDir now redirects a test-context process with no DATA_DIR to a throwaway temp dir, stable per process. Redirect rather than throw, so the documented single-file command keeps working; OMNIROUTE_ALLOW_DEFAULT_DATA_DIR=1 opts back in and records the intent.
Closes#10428
Audit of every numeric claim in the README, AGENTS.md and the README SVGs against
live code, plus a changelog/credit reconciliation over the full v3.8.50 cycle.
Corrected numbers (all measured, not estimated):
- Provider circuit breaker thresholds were scaled up in code for 500+ connection
deployments (`providerFailureThreshold`: OAuth 3 -> 10, API key 5 -> 15) but the
docs still published the pre-scale values. Fixed in AGENTS.md (now a table that
also separates the provider-level threshold from the per-connection one and lists
the provider cooldowns), in the README alt text and inside resilience-layers.svg
(visible label and aria-label).
- "40+ free forever" was unsourced. Measured from the free-tier catalog as every
provider whose free access renews or needs no key (recurring-monthly, -daily,
-uncapped, -credit, keyless; one-time signup credits and discontinued pools
excluded): 56. Updated in the README and promise-pillars.svg.
- Cycle-evolution table: v3.8.49 shipped 290 providers, not 291, and the model row
compared the v3.8.49 free-tier catalog (516) against today's full catalog. Both
columns now use the same metric - distinct documented models, 1185 -> 1202.
- Tech-stack row: 95 domain modules -> 117.
The free-forever count is now enforced by check:docs-counts so it cannot drift
again; it is derived from freeType in the live catalog, like every other gated
number.
Changelog reconciliation (`scripts/release/list-uncovered-commits.mjs`):
uncovered cycle commits drop from 149 to 62. 75 user-facing commits gained a bullet
with author attribution, the 45 ref-less direct pushes and 29 chore/ci/test/docs
commits were consolidated into rollup bullets, and the contributors table grew from
147 to 161 rows - 14 contributors who had landed work with no credit at all
(including @amartinawi, @pacocartones and @excessivechaos) are now credited.
The remaining 62 carry no PR/issue ref, which is the ceiling of ref-based coverage.
CHANGELOG.md and its i18n mirrors are added to .prettierignore: check:changelog-
integrity compares base bullets as exact strings, and Prettier normalizes markdown
emphasis inside them (*from* -> _from_), so any PR that staged the changelog turned
the merge-integrity job red. scripts/release/* is the changelog's formatter of
record, the same precedent already used for ENVIRONMENT.md and PROVIDER_REFERENCE.md.
Co-authored-by: Xiangzhe <bakryun0718@proton.me>
* docs: add rate limiting guide for free providers (429/400/401)
Community-reported troubleshooting for auto-discovered issues when
rotating through free/no-auth providers (opencode, felo-web, auggie).
Documents the verified env-var combo that eliminates
intermittent 429/400/401 failures in cron/agent automation:
OMNIROUTE_ROTATE_ON_400=true,
OMNIROUTE_CHAT_MAX_HEAVY_IN_FLIGHT=4,
OMNIROUTE_STRUCTURE_LIMIT=off
Includes root-cause breakdown (provider quota vs passthrough 401 vs
concurrency amplification), verification steps via /monitoring/health,
and escalation for hard quota exhaustion.
* docs(providers): fix fabricated env var and breaker states in rate-limit guide
Replace OMNIROUTE_STRUCTURE_LIMIT (does not exist in the codebase) with
OMNIROUTE_CHAT_ADMISSION_QUEUE_MS and document the real rate-limit knobs
(RATE_LIMIT_MAX_WAIT_MS / RATE_LIMIT_MAX_QUEUE_DEPTH / RATE_LIMIT_AUTO_ENABLE).
Correct the circuit breaker states to the actual enum (CLOSED/DEGRADED/OPEN/HALF_OPEN)
and point the health-check note at circuitBreakers.providerBreakers[].state.
Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
---------
Co-authored-by: Bruno <bruno@nousresearch.com>
Co-authored-by: mrcram2021 <mrcram2021@users.noreply.github.com>
Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
Co-authored-by: adevwithpurpose <adevwithpurpose@users.noreply.github.com>
* fix(db): prune pre-migration backups so db_backups stops growing unbounded
createPreMigrationBackup() wrote a VACUUM INTO snapshot on every migration run
and never pruned. On a long-lived instance db_backups/ reached 48.999 files /
204 GB against a 5,3 MB live database; a second devbox showed the same shape
(5.711 files / 24 GB).
The retention policy already existed in cleanupDbBackups() but nothing on the
migration path reached it — its only callers are backup.ts and the
/api/db-backups route, neither of which runs during a migration.
migrationRunner.ts cannot import backup.ts: core.ts imports migrationRunner.ts
and backup.ts imports core.ts, so that edge would close a cycle. The policy
therefore moves to a new core-free module, backupRetention.ts, which both call
sites share — cleanupDbBackups() now delegates to it rather than duplicating it.
At the migration call site the operator's maxFiles/retentionDays are read
through the adapter already open for the run; going through getDbInstance()
would re-enter database initialization. Pruning never throws, so housekeeping
cannot fail a migration.
Closes#10421
* chore(db): declare backupRetention as an intentionally-internal db module
check:db-rules requires every src/lib/db/ module to be either re-exported by
localDb.ts or listed in INTENTIONALLY_INTERNAL. backupRetention.ts is a shared
primitive consumed only by db/backup.ts and db/migrationRunner.ts — the same
category as the migrationRunner entry — so it belongs in the allowlist rather
than in the public re-export surface.
* test(db): include backupRetention in the audited INTENTIONALLY_INTERNAL list
check-db-rules-classification.test.ts freezes the exact membership of
INTENTIONALLY_INTERNAL, so adding the 40th entry has to be reflected there too
— the gate script and this test pin the same contract from opposite sides.
---------
Co-authored-by: Xiangzhe <bakryun0718@proton.me>
Makes the ProviderErrorRule `scope` field real at the persistence layer, exclusively for agentrouter (owner decision; every other provider keeps byte-identical behavior).
checkFallbackError now surfaces `ruleScope` behind the HONORS_RULE_LOCK_SCOPE_PROVIDERS allowlist, and the agentrouter 403 path consults the rules before the generic apikey-FORBIDDEN early-return. markAccountUnavailable honors scope "connection" with a temporary connection cooldown instead of a per-model lockout — guarded so a permanent state can never be downgraded to a transient retry loop — and combo now skips the exhausted account within the same request, which also stops force-reusing the just-cooled connection via allowRateLimitedConnection.
Documented in RESILIENCE_GUIDE §7 with the honest limits (disableCooling connections keep per-model behavior; the 6h model-access cooldown is clamped by mlSettings.maxCooldownMs, 30min by default; same-request skip needs targets carrying their own connectionId).
Closes#10334
#10290 moved bailian-coding-plan from the Coding Plan host to the documented
Token Plan one, but the provider/translate-path golden still pinned
coding-intl.dashscope.aliyuncs.com, so tests/unit/provider-translate-path-golden.test.ts
fails on the release tip.
Regenerates the snapshot (UPDATE_GOLDEN=1) — the diff is exactly the two
bailian-coding-plan URLs, every other provider byte-identical — and fixes the
same stale host in the endpoint matrix of
docs/providers/ALIBABA-QWEN-PROVIDER-FAMILIES.md.
This golden covers every provider's resolved URL, which is why neither the
focused tests nor typecheck caught the change: only the unit shard runs it.
Refs #9603
Co-authored-by: Xiangzhe <bakryun0718@proton.me>
* fix(sse): surface Qwen/Alibaba personal Token Plan quota in dashboard and preflight
The personal Token Plan (5-hour / 7-day sliding windows) has no official
OpenAPI and the inference API key cannot read it. Add a cookie-authenticated
fetcher for the console gateway shared by home.qwencloud.com and the Model
Studio console (contract captured live from a logged-in session):
- open-sse/services/qwenTokenPlanQuotaFetcher.ts: POST /data/api.json
(IntlBroadScopeAspnGateway / sfm_bailian) for usage + quota-config +
subscription; sec_token resolved best-effort from the dashboard HTML;
per-window parse (fields are omitted while a window is Temporarily
Removed); 60s usage cache, 1h tier cache.
- usage/qwen-token-plan.ts leaf + registration in the usage dispatcher,
USAGE_FETCHER_PROVIDERS, USAGE_SUPPORTED_PROVIDERS,
PROVIDER_LIMITS_APIKEY_PROVIDERS and bespoke preflight/monitor windows.
- Also adds bailian-coding-plan to USAGE_SUPPORTED_PROVIDERS /
PROVIDER_LIMITS_APIKEY_PROVIDERS: the coding-plan fetcher existed but the
dashboard filtered those connections out (UI gap).
Refs #9603 (Problema 1 — quota missing; the 429 recovery half is a
follow-up).
* docs(env): document Qwen Token Plan quota env vars + regen omni-settings skill
QWEN_CLOUD_COOKIE, QWEN_CLOUD_SEC_TOKEN, QWEN_TOKEN_PLAN_HOST and
QWEN_TOKEN_PLAN_DASHBOARD_URL added to .env.example and
docs/reference/ENVIRONMENT.md (check:env-doc-sync), with the generated
omni-settings skill refreshed (check:agent-skills-sync).
Refs #9603
* revert: keep hand-tuned omni-settings thinking-budget section
The agent-skills-sync drift predates this PR (hand improvement from #10169
not yet synced into the generator source) — it fails on every open PR and
belongs to a base-reds fix, not this branch. Regenerating here would erase
the intentional content.
* feat(dashboard): add the Qwen/Model Studio console cookie field to the connection modal
The Token Plan quota fetcher is cookie-authenticated (the inference API key
cannot read the console gateway), but no modal field existed to paste that
cookie — so the quota was unconfigurable from the dashboard and the fetcher
could only ever return its 'needs a cookie' message.
Adds the field for qwen-cloud-token-plan and bailian-coding-plan alongside the
existing ollama-cloud / alibaba console-cookie inputs (same password-input,
blank-keeps-stored semantics), pre-fills it when editing a connection, and
extends the providerSpecificData string/length validation to the two new keys.
Tests: tests/unit/qwen-token-plan-cookie-field.test.ts (RED before, GREEN
after) covers persistence + trimming, the blank-input no-overwrite rule and
schema acceptance/rejection.
Refs #9603
* docs(dashboard): correct the Qwen console cookie instructions
The placeholder claimed the cookie looks like 'token=...'; the qwencloud
portal actually issues 'login_qwencloud_ticket=...' alongside cna/cnaui/aui
(mirroring login_aliyunid_ticket on the Alibaba console), so the hint pointed
at the wrong value.
Replaces the guesswork with the verified retrieval steps in all three places
an operator can hit — the modal field hint, the fetcher's 'needs a cookie'
message and .env.example/ENVIRONMENT.md: log in to home.qwencloud.com >
Billing > Subscription, F12 > Network, reload, filter by api.json, click a
request to cs-data.qwencloud.com and copy the WHOLE Cookie request header.
Also documents that the value must go on one line (it contains '=' and ';')
and that it dies with the browser session.
Refs #9603
* fix(dashboard): tolerate partial form objects in the qwen cookie branch
Adding bailian-coding-plan to QWEN_TOKEN_PLAN_PROVIDERS routed callers that
previously matched NO branch in assignQuotaScrapingProviderData into the new
one, which assumed the two new fields are always present. Older callers build
a partial form object, so buildAddProviderSpecificData threw:
TypeError: Cannot read properties of undefined (reading 'trim')
(tests/unit/dashboard/agentrouter-connection-modal-fields.test.ts)
Reads the new fields with optional chaining and adds a regression test that
calls the helper with those keys deleted for both providers.
Refs #9603
* refactor(dashboard): move quota-scraping form logic into a UI-free module
tests/unit/qwen-token-plan-cookie-field.test.ts imported QuotaScrapingFields
directly, which pulls `@/shared/components` and, through that barrel,
untranspiled ESM (@lobehub/icons). The node:test runner cannot parse it and
the whole test file died in CI with:
SyntaxError: Unexpected token 'export'
at @lobehub/icons/es/Ai21/components/Mono.js
(It passed locally, so only the CI shard surfaced it.)
Extracts the pure pieces — QWEN_TOKEN_PLAN_PROVIDERS, QuotaScrapingFieldValues,
EMPTY_QUOTA_SCRAPING_FIELDS and assignQuotaScrapingProviderData — into
quotaScrapingFieldValues.ts. The component imports them and re-exports the
public names, so every existing importer keeps its current path. The unit test
now targets the UI-free module.
Refs #9603
* fix(providers): point bailian-coding-plan at the Token Plan endpoint and its console
Two independent defects kept this provider unusable with a valid Alibaba
Token Plan key (verified live 2026-08-14 with the owner's key and cookie):
1. Wrong inference host. The catalog entry is named "Alibaba Token Plan",
links to token-plan-overview and its hint asks for a Token Plan key, but
the registry pointed at coding-intl.dashscope.aliyuncs.com — the Coding
Plan host, which rejects Token Plan keys with 401 invalid_api_key. The
documented Anthropic base URL for Token Plan is
token-plan.ap-southeast-1.maas.aliyuncs.com/apps/anthropic
(https://www.alibabacloud.com/help/en/model-studio/more-tools). Against
the new host the same key returns 200 for all six registry models and a
real completion; auth stays on x-api-key.
2. Wrong console identity for quota. The personal Token Plan is sold through
two consoles sharing one backend, and the gateway validates the session
against the console declared in the request: an Alibaba console cookie
(login_aliyunid_ticket) sent with the QwenCloud identity is refused with
BailianGateway.Login.NotLogined. resolveConsoleSite() now picks host,
cornerstoneParam.consoleSite/domain and Origin/Referer from the cookie's
login ticket, falling back to the provider. With that switch the same
cookie returns usage/subscription/quota-config.
Also routes bailian-coding-plan quota through the Token Plan fetcher (the
Coding Plan call returns "Bad Request" for these accounts), keeping the old
fetcher as the fallback for real Coding Plan keys, and labels the plan by
console ("Alibaba Token Plan (Pro)" vs "Qwen …").
Live validation: inference 200 (qwen3.7-plus answered "FUNCIONA"); quota
12,934/40,000 credits, 67.7% remaining, resets 2026-08-20.
Refs #9603
---------
Co-authored-by: Xiangzhe <bakryun0718@proton.me>
* feat(sse): add Vertex AI DeepSeek OCR transformation to the registry
Adds VERTEX_DEEPSEEK_TRANSFORMATION (request/response mapping for the
Vertex AI DeepSeek OCR MaaS endpoint) and registers the
"vertex-deepseek-ocr" provider in OCR_PROVIDERS, modeled on litellm's
VertexAIDeepSeekOCRConfig. buildRequest treats the resolved baseUrl as
the complete Vertex endpoint URL (project/location resolved upstream),
matching the existing Mistral passthrough pattern.
* feat(sse): resolve Vertex AI DeepSeek OCR auth and endpoint URL
Adds resolveVertexOcrAccessToken (mints a Vertex OAuth access token from
a Service Account JSON apiKey, reusing open-sse/executors/vertex.ts's
existing JWT-bearer exchange — no new OAuth flow) and
resolveVertexOcrBaseUrl (derives the project/location "openapi/chat/
completions" endpoint from providerSpecificData or the Service Account
JSON's project_id). Both live in open-sse/handlers/ocr.ts, not the
src/app/api/v1/ocr route, since routes may not import executor
implementations directly (EXECUTOR_IMPORT_RESTRICTION in
eslint.config.mjs) — the route re-exports/consumes them across that
boundary. handleOcr now prefers credentials.accessToken over apiKey so
the minted token (not the raw Service Account JSON) is sent upstream.
* docs(api): document the vertex-deepseek-ocr /v1/ocr provider
Adds the vertex-deepseek-ocr row to the /v1/ocr provider table and a
short section on its Vertex AI auth/endpoint resolution, and lists the
new provider/model id in openapi.yaml alongside mistral and
azure-document-intelligence.
* docs(skills): regenerate omni-inference skill for the Vertex OCR provider
---------
Co-authored-by: Xiangzhe <bakryun0718@proton.me>
OmniRoute ships `frame-ancestors 'none'` + `X-Frame-Options: DENY` on every
route, so the VS Code Simple Browser renders a blank tab — which is what the
OmniCopilot extension's `dashboardOpen: "editor"` mode uses.
Add the build-time opt-in `DASHBOARD_ALLOW_EMBED=vscode`. When set, the HTML
pages are served with `frame-ancestors 'self' vscode-webview:` and without
`X-Frame-Options` (XFO cannot express a custom scheme and would veto the
relaxed CSP). Unset — the default — nothing changes.
The API surface stays strictly unframable in both modes. Its exclusion list is
derived from the `rewrites()` table plus `/api`, `/a2a`, `/healthz`, so a future
root-level API alias is excluded automatically instead of silently becoming
framable. The two generated `source` patterns are complementary by construction:
every pathname matches exactly one, so there is no gap (a page with no security
headers) and no order-dependent overlap.
Closes#10273
Co-authored-by: Xiangzhe <bakryun0718@proton.me>
* feat(ocr): transformation layer on ocrRegistry (Mistral shape canonical)
* feat(ocr): Azure Document Intelligence provider (prebuilt-read, analyze+poll)
* feat(ocr): generic dispatch with per-provider transformation and DI poll loop
* test(ocr): align sanitized-500 assert with HR#12 error sanitization
The test's own title ("returns a sanitized 500") describes the new
behavior mandated by HR#12 (never leak err.message in a response body).
The old regex asserted the pre-sanitization leak (`OCR request failed:
socket closed`) as expected output, which contradicted its own title
and the sanitization this task intentionally introduced in
open-sse/handlers/ocr.ts. Scoped to this single assertion only.
* fix(ocr): fail fast on non-ok poll responses instead of misleading 504
pollOcrOperation now checks pollRes.ok and returns a sanitized 502
immediately (logging the upstream status via console.error) instead of
looping until the 30-attempt cap and surfacing a misleading timeout for
what was actually an auth/upstream error during polling.
* feat(ocr): route/docs for multi-provider /v1/ocr
- Route: map the connection's providerSpecificData.baseUrl onto
credentials.baseUrl (resolveOcrCredentials) so azure-document-intelligence
connections resolve their endpoint the same way every other custom-endpoint
provider does (src/lib/providers/validation/*); previously handleOcr only
saw a baseUrl when a caller set it directly, so the DB-backed Azure
connection endpoint was never forwarded.
- v1OcrSchema.model is already a free-form string, no schema change needed.
- Docs: add the /v1/ocr provider table + example + Azure poll-flow note to
API_REFERENCE.md, and describe the provider/model prefix + async poll
behavior in openapi.yaml.
- Test: tests/unit/ocr-route-contract.test.ts covers getAllOcrModels/
parseOcrModel for both providers and resolveOcrCredentials's mapping.
* chore(quality): rebaseline deadExports for the OCR/image-to-text series
* docs(skills): regenerate omni-inference skill for the multi-provider /v1/ocr
The generated agent skill mirrors docs/reference/API_REFERENCE.md; updating the
/v1/ocr section left it stale and tripped the merge-integrity gate.
---------
Co-authored-by: Xiangzhe <bakryun0718@proton.me>
* test(bridge): explicit native-vision skip guard + skip log
* feat(bridge): configurable describe output cap (modalityBridgeVisionMaxChars)
* feat(dashboard): maxChars field on Modality Bridge vision tab
Add the "Max description characters" field to the Vision tab's Advanced
panel (modalityBridgeVisionMaxChars, clamped to the 100-50000 schema
range with 0 treated as the explicit "unlimited" sentinel), wire the
en.json copy and sync it across all 42 locales, and document the new
setting in GUARDRAILS.md.
* fix(bridge): allow explicit 0 to disable the describe cap
updateSettingsSchema previously rejected modalityBridgeVisionMaxChars: 0
because the field's range was min(100).max(50000), so a dashboard PATCH
sending the explicit "unlimited" sentinel would 400. Widen the schema to
z.union([z.literal(0), z.number().int().min(100).max(50000)]) so 0
validates as its own valid value, not just an implicit default.
* chore(i18n): resync locale keys after release merge
* chore(quality): rebaseline deadExports for the OCR/image-to-text series
---------
Co-authored-by: Xiangzhe <bakryun0718@proton.me>
* feat(bridge): optional-sharp image normalization util (long-edge 2048)
* feat(bridge): normalize fetched images before vision describe self-call
Route the bridge's own fetchRemoteImageAsDataUri() output through
normalizeDataUri() (long-edge cap 2048) before handing it to the vision
model — matches the resize cap OpenAI/Anthropic already apply, cutting
upload bytes/latency. Scoped to the bridge's self-fetched images only,
never the user's raw passthrough payload (HR#20 opt-in principle).
* test(bridge): height-dominant long-edge coverage
Add a 100x4096 PNG case to image-normalize.test.ts alongside the existing
width-dominant one, so normalizeImageBuffer's long-edge cap is proven on
both axes.
* fix(bridge): type sharp's callable default export (TS2349)
* chore(quality): rebaseline deadExports for the OCR/image-to-text series
---------
Co-authored-by: Xiangzhe <bakryun0718@proton.me>
agentrouter.org signals temporary quota exhaustion with HTTP 403/400 and a Chinese body (用户额度不足) instead of 429, so clients like Claude Code treat it as permanent and abort, and the fallback engine classified it as a generic apikey AUTH_ERROR.
New registry open-sse/config/upstreamStatusRestatement.ts restates those statuses to 429 with a synthetic Retry-After at a single hook in chatCore's providerFailure block (after parseUpstreamError), so classification, combo aggregation and the client response all see a retryable error. 无权访问模型 (permanently no model access) is veto-listed and never restated.
agentrouter classification rules are registered in providerErrorRules.ts and reach the real checkFallbackError path through resolveRuleMatchBody() with an exclusive FULL_TEXT_RULE_PROVIDERS allowlist — every other provider keeps its previous behavior byte-for-byte.
Known limitations tracked in #10334: the rules' scope field is informational (persistence applies per-model lockout for agentrouter), the 403-only model-access rule has no production path yet, and errors embedded in 200 SSE streams are not restated.
Refs #10334
* fix(providers): raise default provider probe timeout from 5s to 8s
The validationRead and modelsProbe presets in safeOutboundFetch.ts used a
fixed 5000ms timeout for the periodic credential health check and on-demand
connection test. Several real free-tier providers (Cerebras, Cloudflare AI
observed in practice) routinely take close to 5s to answer a lightweight
/models probe, which is indistinguishable from a real outage under that
budget — the connection flaps between "active" and "error" in the
dashboard/topology view purely from being near the edge of the timeout, not
from any actual failure.
Raised the default to 8000ms and made it configurable via
OMNIROUTE_PROVIDER_PROBE_TIMEOUT_MS (validated: falls back to 8000ms for
non-numeric or sub-1000ms values) so it can be tuned per-deployment without a
code change. validationWrite and modelsPagination presets are untouched.
Added tests/unit/safe-outbound-fetch-probe-timeout.test.ts covering the
default, env override, invalid-value fallback, and that the other two
presets are unaffected.
* docs(.env.example): document OMNIROUTE_PROVIDER_PROBE_TIMEOUT_MS
* Merge branch 'release/v3.8.50' into fix/provider-probe-timeout
Resolved merge conflict in .env.example: kept both Provider probe section (PR)
and Proxy/relay fetch section (release branch).
Added docs/reference/ENVIRONMENT.md entry for OMNIROUTE_PROVIDER_PROBE_TIMEOUT_MS.
---------
Co-authored-by: diegosouzapw <diegosouzapw@users.noreply.github.com>
Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
The provider-level breaker fields in PROVIDER_PROFILES
(providerFailureThreshold, providerFailureWindowMs, providerCooldownMs,
degradationThreshold, maxBackoffMultiplier, backoffEscalationCount) are
now env-overridable via OMNIROUTE_PROVIDER_BREAKER_<CATEGORY>_<FIELD>
variables, with the historical hardcoded defaults preserved when unset.
This makes the provider-level fuse (the entire-provider cooldown applied
after repeated upstream failures) tunable from the deployment surface,
matching the existing per-key circuit breaker knobs. Operators can now
raise thresholds to tolerate transient upstream sheds without
blacklisting the provider, or lower them to fail over faster on
premium routes — without rebuilding from source.
Closes#10040
Category-by-category field map (defaults preserved):
- oauth: FAILURE_THRESHOLD=10, FAILURE_WINDOW_MS=900000, COOLDOWN_MS=300000,
DEGRADATION_THRESHOLD=5, MAX_BACKOFF_MULTIPLIER=8, BACKOFF_ESCALATION_COUNT=2
- apikey: [REDACTED:auth_header], FAILURE_WINDOW_MS=1800000, COOLDOWN_MS=600000,
DEGRADATION_THRESHOLD=7, MAX_BACKOFF_MULTIPLIER=4, BACKOFF_ESCALATION_COUNT=3
- local: FAILURE_THRESHOLD=2, FAILURE_WINDOW_MS=300000, COOLDOWN_MS=60000
(local category omits the adaptive v2 fields)
Docs:
- .env.example — 15 new commented entries grouped under a
"Provider-level circuit breaker thresholds and cooldowns" section.
- docs/reference/ENVIRONMENT.md — 15 new rows documenting the
provider-level breaker surface.
Tests:
- tests/unit/provider-breaker-env-overrides.test.ts — 4 cases:
1. Every new env var is wired in constants.ts via envInt().
2. Every new env var is documented in ENVIRONMENT.md.
3. Every new env var is listed in .env.example.
4. The historical defaults are preserved as the envInt fallback.
Behavior tests (loading the actual module with controlled env vars) are
left to upstream CI; the static source-shape test is sufficient here
because the envInt() helper is a plain function whose only dependency
is process.env at module load time.
Co-authored-by: Tiangao (hermes) <montigaud@aikumi.pro>
* 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>
* 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>
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>