Commit Graph

856 Commits

Author SHA1 Message Date
Paijo
6df38155a4 feat: add web-session pool observability (MCP tool + health-matrix) (#3395)
Integrated into release/v3.8.16
2026-06-08 00:48:01 -03:00
Diego Rodrigues de Sa e Souza
929caeb910 Release v3.8.15 (#3373)
* chore(release): open v3.8.15 development cycle

Version bump 3.8.14 -> 3.8.15 (root + electron + open-sse + openapi + lockfiles)
and seed the v3.8.15 changelog placeholder (root + 41 i18n mirrors).

* fix(catalog): add getTokenLimit fallback for combo targets with unknown context (#3369)

Integrated into release/v3.8.15. Fixes applied on the contributor's branch: removed duplicate JSDoc opening in accountFallback.ts and dropped a test asserting unreachable catalog behavior (models with no registry/spec/synced source are filtered before the getTokenLimit fallback at catalog.ts:499).

* fix(combo): add 429 to PROVIDER_FAILURE_ERROR_CODES to prevent infinite retry loop (#3366)

Integrated into release/v3.8.15. Comment block reconciled on the contributor's branch to remove the contradictory 'intentionally excluded' text that remained from the original code.

* fix(auto-combo): include no-auth providers declaratively (#3365)

Integrated into release/v3.8.15. Cleanup applied on contributor's branch: removed duplicate migration 095 (already exists from PR #3338), reverted CHANGELOG.md and i18n changelogs to release versions (release process owns these), dropped package version-bump noise from stale fork base. Core feature — declarative no-auth via serviceKinds metadata, declarative VEO as 'video' provider, anonymousFallback flag for opencode-zen/opencode-go — integrated cleanly.

* fix(migrations): restore 095_provider_node_custom_headers migration

The squash merge of PR #3365 accidentally deleted this migration because
the cleanup commit on the contributor's branch included 'git rm' for the
file (which was a duplicate on their branch). The migration was merged
in v3.8.14 via PR #3338 and must be present in the release branch.

Restoring from git history.

* fix: update Command Code base URL from /alpha/ to /provider/v1/ (#3372)

Integrated into release/v3.8.15.

* feat(error-rules): provider-specific error classification with scope (#3370)

Integrated into release/v3.8.15. PR has genuine value beyond #3369: (1) getProviderErrorRuleMatch now accepts native Headers objects from fetch(); (2) checkFallbackError also uses the provider rule registry — the real end-to-end wiring in the combo fallback path; (3) S4 end-to-end test proving the wiring fires. Merge commit on contributor branch resolved the add/add conflict by taking the #3370 version throughout.

* fix(auto-combo): validate web-session credentials (#3371)

Integrated into release/v3.8.15. Core feature: provider-aware web-session credential validation — hasUsableWebSessionCredential() replaces the broad Object.keys check in virtualFactory.ts, ensuring only sessions with the required storageKeys are included in auto-combo. Cleanup: removed duplicate 095 migration, reverted CHANGELOG/i18n, dropped package bump noise.

* fix(migrations): restore 095_provider_node_custom_headers (deleted again by #3371 squash)

Same issue as after #3365: git rm in the contributor cleanup commit
was included in the squash, deleting this migration from release.
Permanent fix needed: use 'git checkout origin/release -- <file>'
instead of 'git rm' when cleaning up duplicate files in contributor branches.

* fix(kiro): probe Windows %APPDATA%\kiro\storage.db in auto-import (#3363) (#3375)

Integrated into release/v3.8.15. Test fix applied: kiro-windows-auto-import-3363.test.ts now sets DATA_DIR to a fresh temp dir before importing app modules, ensuring isAuthRequired() sees an empty settings DB (no password → auth not required). This fixed test 4 (synthetic SQLite) which was getting 401 due to settings DB state leakage.

* chore(release): finalize v3.8.15 changelog — 2026-06-07

---------

Co-authored-by: Hernan Javier Ardila Sanchez <hjasgr@gmail.com>
Co-authored-by: Paijo <14921983+oyi77@users.noreply.github.com>
Co-authored-by: Muhammad Nabil Muyassar Rahman <65392758+TapZe@users.noreply.github.com>
Co-authored-by: kiro-agent[bot] <245459735+kiro-agent[bot]@users.noreply.github.com>
2026-06-07 12:16:33 -03:00
Diego Rodrigues de Sa e Souza
7db430a352 Release v3.8.14 (#3340)
* chore(release): open v3.8.14 development cycle

Version bump 3.8.13 -> 3.8.14 (root + electron + open-sse + openapi + lockfiles).
Seed the v3.8.14 changelog with the four post-tag hotfixes that shipped to
Docker/Electron in v3.8.13 but missed the immutable npm 3.8.13 (#3336 SSRF /
CodeQL #323, #3334/#3335/#3339 Electron packaging). i18n CHANGELOG mirrors get
the in-progress placeholder section.

* feat: add per-provider custom headers support for OpenAI/Anthropic-compatible nodes (#3338)

Integrated into release/v3.8.14

* fix: Kiro Builder ID token import fails with Bad credentials (#3333)

Integrated into release/v3.8.14 — adds Builder ID cached-creds + OIDC refresh path for Kiro token import, with regression tests (#3333).

* Improve code quality: auto-pr/docstrings-1780792063 (#3337)

Integrated into release/v3.8.14 — docstring for context analytics route re-export.

* fix(catalog): remove minimaxai/minimax-m3 from NVIDIA NIM tier (404 upstream) (#3329) (#3341)

NVIDIA NIM does not host minimaxai/minimax-m3 — every request returns
404 page not found, while sibling minimaxai/minimax-m2.7 on the same provider
works. Advertising a model that 404s is a catalog bug; remove it from the nvidia
tier (it remains on the tiers that actually serve MiniMax M3). Re-add only once
NVIDIA serves it.

Co-authored-by: mikmaneggahommie <mikmaneggahommie@users.noreply.github.com>

* fix(cli): write OpenCode config to ~/.config on all platforms incl. Windows (#3330) (#3343)

resolveOpencodeConfigDir used %APPDATA% on Windows, but OpenCode reads its
config from XDG ~/.config/opencode/ on every platform (on Windows:
%USERPROFILE%\.config\opencode\, NOT %APPDATA%). So a Windows user who
configured OpenCode via the dashboard had the file written where OpenCode never
looks — it silently had no effect.

Use the XDG path (XDG_CONFIG_HOME || ~/.config) unconditionally. Update the UI
note + route JSDoc, and flip the three tests that encoded the old %APPDATA%
behavior (t40 per-platform + card-note, cli-runtime-extended getCliConfigPaths).

Co-authored-by: abdulkadirozyurt <abdulkadirozyurt@users.noreply.github.com>

* fix(proxy): make auto-selection fallback opt-in (#3332) (#3344)

selectWorkingProxyFallback (Step 11 of resolveProxyForConnection) listed ALL
registry proxies, ignoring assignments and per-connection proxy_enabled, and
returned the first working one with level:'autoSelect'. So a single proxy added
to the registry silently became a global fallback for every connection's traffic.

Gate it behind a new PROXY_AUTO_SELECT_ENABLED feature flag (default off): the
fallback now no-ops unless the operator opts in. No registry proxy becomes a
silent global default anymore.

Co-authored-by: hertznsk <hertznsk@users.noreply.github.com>

* fix(sse): treat MiniMax M3 as multimodal so vision isn't stripped (#3328) (#3342)

MiniMax M3 via the opencode provider (oc/minimax-m3-free) appeared blind:
image inputs didn't reach the model, while the same model in Cline could
see them. Verified empirically that MiniMax M3 on the opencode upstream IS
multimodal -- a base64 image is described correctly (it returns 403 only
for remote image URLs, which it doesn't accept).

Root cause: OmniRoute treated MiniMax M3 as a non-vision model in two
places, so when compression was active the image was replaced with a text
placeholder before dispatch:
- compression's modelSupportsVision() heuristic (lite.ts) only matched
  gpt-4/4o/claude-3/gemini/vision -- minimax was absent -> replaceImageUrls
  stripped the image.
- the opencode minimax-m3-free catalog entry lacked supportsVision, so the
  combo vision-capability gate could also exclude/mishandle it.

Add 'minimax-m3' to the vision heuristic and supportsVision: true to the
opencode minimax-m3-free entry. TDD: a failing-then-passing test in
compression/lite.test.ts proves replaceImageUrls now keeps images for
minimax-m3 ids, plus a registry assertion mirroring the #2822 qwen test.

Reported-by: @mikmaneggahommie

* docs(i18n): translate 25 core documentation files to Indonesian (#3348)

Integrated into release/v3.8.14 — Indonesian i18n docs.

* fix(review): resolve /review-reviews battery findings (LEDGER-1..11) on v3.8.14 (#3350)

Integrated into release/v3.8.14 — /review-reviews battery hardening (LEDGER-1..11) for #3338 custom-headers + #3333 kiro, plus cycle-test drift fixes (#3329/#3330/#3332).

* fix(provider-proxy): honor per-account proxy toggles (#3349)

Integrated into release/v3.8.14 — honor per-account proxy toggles + auto-fallback opt-in via PROXY_AUTO_SELECT_ENABLED.

* fix(dashboard): remove duplicate Distribute Proxies button on provider page (#3352)

* fix(providers): reduce proxy label noise (#3346)

Integrated into release/v3.8.14 — reduce proxy label noise + a11y (aria-label/sr-only).

* fix(duckduckgo): restore bare Response contract and rebase onto release/v3.8.14 (#3323)

Integrated into release/v3.8.14 — browser-backed cookie providers (duckduckgo/claude-web) with restored executor contract + unit tests.

* fix(noauth): expose only usable model aliases (#3345)

Integrated into release/v3.8.14 — noauth usable-alias filtering + registry alias plumbing (veo-free).

* fix(dashboard): stop infinite config-load loop on Hermes Agent detail page (#3353)

* fix(electron): tree-kill the server on exit/update to release the omniroute.exe lock (#3347) (#3354)

* chore(release): finalize v3.8.14 changelog + clear release-gate drift

- CHANGELOG: finalize the v3.8.14 section (date, full New Features/Bug Fixes/
  Maintenance coverage of all 16 cycle commits, Contributors hall of 12).
- docs: document OMNIROUTE_BROWSER_POOL + WEB_COOKIE_USE_BROWSER (#3323) in
  .env.example + ENVIRONMENT.md; regenerate the id/llm.txt strict mirror (#3348
  had translated it; llm.txt mirrors must match root).
- test(proxy-fetch): #3323 made tlsClient.available a computed getter — stub it
  via Object.defineProperty instead of assignment (5 tests were red on the base).

* fix(translator): coerce Gemini functionDeclaration parameters to an OBJECT schema (#3357) (#3360)

* fix(gemini): resolve truncation/suppression of false positive textual tool call markers in backticks (#3358)

Integrated into release/v3.8.14 — Gemini/Antigravity textual tool-call marker normalization (no false-positive suppression + split-chunk buffering).

* docs(changelog): add #3358 Gemini textual tool-call normalization to v3.8.14

* fix(dashboard): surface real analytics error instead of generic placeholder (#3356) (#3361)

The Analytics page discarded the server's error body on a non-OK response and
rendered a generic "An error occurred", so users (and maintainers) could not see
why /api/usage/analytics 500'd after an upgrade. Now the route returns the real
reason via buildErrorBody (sanitized, Hard Rule #12) and the page surfaces it via
a new readFetchErrorMessage helper that handles both the OpenAI-style and legacy
error shapes.

Reported-by: @superti4r

---------

Co-authored-by: PizzaV <103120356+pizzav-xyz@users.noreply.github.com>
Co-authored-by: Someres <168349709+quanturbo@users.noreply.github.com>
Co-authored-by: Dong Mengzhe <154944819+Lang-Qiu@users.noreply.github.com>
Co-authored-by: mikmaneggahommie <mikmaneggahommie@users.noreply.github.com>
Co-authored-by: abdulkadirozyurt <abdulkadirozyurt@users.noreply.github.com>
Co-authored-by: hertznsk <hertznsk@users.noreply.github.com>
Co-authored-by: Krisna Santosa <54174372+KrisnaSantosa15@users.noreply.github.com>
Co-authored-by: Randi <55005611+rdself@users.noreply.github.com>
Co-authored-by: Wilson <pedbookmed@gmail.com>
Co-authored-by: Paijo <14921983+oyi77@users.noreply.github.com>
Co-authored-by: Ardem2025 <ardemb22@gmail.com>
2026-06-07 07:20:02 -03:00
Diego Rodrigues de Sa e Souza
a25d5f1ef6 Release v3.8.13 (#3327)
* chore(release): open v3.8.13 development cycle

Bump 3.8.12 → 3.8.13 across package.json, lockfile, electron/, open-sse/, and
docs/reference/openapi.yaml; add the [3.8.13] cycle placeholder to the root
CHANGELOG and the 41 i18n mirrors. Integration branch for the v3.8.13 cycle —
fixes/features land here via per-issue PRs and it merges to main at release time.

* fix(ci): skip auto-deploy when VPS host is unreachable from the runner (#3299)

Integrated into release/v3.8.13

* fix(dev): auto-rebuild better-sqlite3 on Node ABI mismatch at dev startup (#3301)

Integrated into release/v3.8.13

* feat(api): accept path-scoped API keys on client API routes (#3300)

Integrated into release/v3.8.13

* fix(sse): harden against empty responses causing Copilot Chat failures (#3297)

Integrated into release/v3.8.13

* fix(api): remove Completions.me rickroll provider (discussion #3293) (#3302)

Integrated into release/v3.8.13

* fix(opencode-provider): extract contextLength from live model catalog (#3298)

Integrated into release/v3.8.13

* feat(web-cookie): self-service login infrastructure + auto-refresh daemon (#3292)

Integrated into release/v3.8.13

* docs(changelog): record the v3.8.13 PRs merged this round (#3292/#3300/#3297/#3298/#3301/#3302/#3299)

* fix(auth): harden URL token extraction — drop query-string fallback, gate to client routes (security follow-up to #3300) (#3309)

Security follow-up to #3300 — integrated into release/v3.8.13

* docs: rename resolve-issues → review-issues skill references

* fix(dashboard): keep no-auth providers visible under 'Show configured only' (#3290) (#3312)

no-auth providers (opencode, duckduckgo-web, theoldllm, veoaifree-web) never
create a DB connection row so stats.total stays 0, which the configured-only
filter treated as 'unconfigured' and hid them — even though they are always
usable and appear unconditionally in /v1/models. filterConfiguredProviderEntries
now treats displayAuthType === 'no-auth' as configured.

Co-authored-by: uniQta <uniQta@users.noreply.github.com>

* fix(cli): resolve update paths relative to script + recursive backup (#3295) (#3313)

omniroute update always failed on a global install:
- getCurrentVersion() read package.json from process.cwd(), which on a global
  npm/brew install is the user's working dir, not the package root → null →
  'Could not determine current version'.
- createBackup() resolved bin/ from cwd too, and passed the 'cli' directory to
  copyFileSync → EISDIR, swallowed by the catch → 'Failed to create backup'.

Both now resolve package.json/bin relative to the script via import.meta.url,
and the backup uses cpSync({recursive:true}) so the cli/ directory is copied.

Co-authored-by: uniQta <uniQta@users.noreply.github.com>

* fix(theoldllm): read upstream body once to avoid [502] body-already-read (#3296) (#3314)

On the cached-token path the executor never enters the refresh branch, so the
same upstream Response was read with .text() twice (token-rejection check +
final body). A Response body is single-use, so the second read threw
'Body is unusable: Body has already been read', caught and surfaced as [502].

Read the body once into finalBody and only re-read after a token-rejection
refetch.

Co-authored-by: onizukashonan14-png <onizukashonan14-png@users.noreply.github.com>

* fix(sse): strip leaked internal tool envelopes from streaming output (#3311)

Integrated into release/v3.8.13

* fix(sse): expose Claude + Gemini budget tiers in the antigravity catalog (#3184) (#3303)

Integrated into release/v3.8.13 (#3184)

* fix(catalog): compute combo context_length from known targets only (#3304)

Integrated into release/v3.8.13 — live contextLength + known-targets combo context (#3298 follow-up)

* chore(i18n): add message keys for proxy UI + vscode/ollama endpoint (#3307)

Integrated into release/v3.8.13 — i18n message keys for proxy UI + vscode/ollama

* feat(dashboard): i18n the proxy settings UI (#3310)

Integrated into release/v3.8.13 — i18n the proxy settings UI

* feat(api): model catalog enrichment + MCP model-catalog tools (#3306)

Integrated into release/v3.8.13 — model catalog enrichment + MCP model-catalog tools, reconciled with #3309 URL-token hardening

* test(catalog): align Antigravity preview-alias test with #3303 budget tiers

#3303 added the Gemini `-high`/`-low` budget tiers to ANTIGRAVITY_PUBLIC_MODELS
(user-callable on the Antigravity OAuth backend, verified via #3184), but did
not update the catalog-route test that asserted `antigravity/gemini-3.1-pro-high`
must NOT be exposed. The assertion now reflects the intended behavior — the
client-visible budget alias IS surfaced — while keeping the legacy
`gemini-claude-*` alias keys unexposed. Caught running the full catalog suite
on the merged release HEAD (the #3303 round only ran the antigravity-aliases
and usage-hardening files).

* docs(changelog): record the 6 PRs merged this review round into v3.8.13

#3306/#3307/#3310 (New Features — VS Code split: catalog+MCP, i18n keys, proxy
UI i18n), #3311/#3303/#3304 (Bug Fixes — SSE envelope sanitizer, antigravity
budget tiers, combo known-targets context_length).

* chore(release): finalize v3.8.13 changelog and cleanup

Finalize the v3.8.13 changelog with release date, maintenance notes,
and contributor credits. Update MCP docs to reference the correct tool
inventory diagram, exclude nested .claude worktrees from ESLint scans,
and tighten a response sanitizer type guard.

* fix(dashboard): refresh connections after provider auth import (#3320)

Integrated into release/v3.8.13 — refresh connections after provider auth import

* fix(codex): strip client-only params on native /responses passthrough (#3317) (#3325)

A /v1/responses request against the built-in codex/ provider does an
openai-responses -> openai-responses passthrough (CodexExecutor.transformRequest
returns the body early for _nativeCodexPassthrough). It forwarded client-only
fields verbatim and the Codex upstream rejected them with 400 Unsupported
parameter: prompt_cache_retention / safety_identifier / user — breaking Factory
Droid (which injects all three). The chat-completions path already strips these
(base.ts #1884, openai-responses translator #2770) but the passthrough skips
translation. Strip the three fields in the shared block before the passthrough
return; user is removed unconditionally since Codex /responses always rejects it.

Co-authored-by: tycronk20 <tycronk20@users.noreply.github.com>

* fix(dashboard): normalize agent-bridge /state response to stop page crash (#3318) (#3326)

The Agent Bridge page seeded a well-shaped initialData default then replaced it
wholesale with the raw /api/tools/agent-bridge/state response. The route returns
{ server, agents } but the UI reads { serverState, agentStates, bypassPatterns,
mappings }, so serverState became undefined and AgentBridgeServerCard crashed on
serverState.running — surfaced as the full-page 'Internal Server Error' boundary
(client render error, not a real 5xx).

Add a shared normalizeAgentBridgeState() that maps the route shape into the page
contract (server.running/certExists -> serverState) and always returns safe
defaults (never undefined serverState). Wired into both the SSR loader (page.tsx)
and the polling hook. The legacy 'agents' entry shape differs from AgentStateEntry
so it is not coerced; full route<->page contract reconciliation (port, upstreamCa,
bypassPatterns, mappings, agentStates) is a follow-up.

Co-authored-by: tycronk20 <tycronk20@users.noreply.github.com>

* docs: VS Code/Ollama endpoints + env & i18n tooling (#3319)

Integrated into release/v3.8.13 — VS Code/Ollama docs + env & i18n tooling

* feat(provider): test-all endpoint, rate-limit overrides, visibility f… (#3267)

Integrated into release/v3.8.13 — provider test-all endpoint, rate-limit overrides, model visibility

* feat: auto-combo optimization, playground model dropdown, only-configured toggle (#3322)

Integrated into release/v3.8.13 — auto-combo candidate expansion + playground dropdown + only-configured toggle

* feat(api): VS Code Copilot Ollama-compatible BYOK endpoint (#3316)

Integrated into release/v3.8.13 — VS Code Copilot Ollama-compatible BYOK endpoint (reconciled with #3306/#3309 auth hardening)

* chore(release): document #3320 in the v3.8.13 changelog + contributor credits

---------

Co-authored-by: Felipe Almeman <4226997+zhiru@users.noreply.github.com>
Co-authored-by: Wilson <pedbookmed@gmail.com>
Co-authored-by: Hernan Javier Ardila Sanchez <hjasgr@gmail.com>
Co-authored-by: Paijo <14921983+oyi77@users.noreply.github.com>
Co-authored-by: uniQta <uniQta@users.noreply.github.com>
Co-authored-by: onizukashonan14-png <onizukashonan14-png@users.noreply.github.com>
Co-authored-by: tycronk20 <tycronk20@users.noreply.github.com>
Co-authored-by: Vinayrnani <vinayrnani@gmail.com>
2026-06-06 19:13:11 -03:00
diegosouzapw
5bebf0e53c fix(quota,sse): clear SonarCloud new-reliability findings on the v3.8.12 diff
- chipotle/grokTls: explicit null checks instead of a Promise in a boolean
  conditional (behavior-preserving; clears the 2 MAJOR reliability bugs)
- sqliteQuotaStore.poolUsage: drop the unreachable dimMap scan loops (dimMap
  was never populated) — the lightweight snapshot already returns no
  dimensions; poolUsageWithDimensions() is the plan-aware path
- BudgetTab: presentation role + keyboard handler on the checkbox wrapper
2026-06-06 06:49:46 -03:00
Paijo
1bc88d97ee fix(plugins): chain payload between emitHookBlocking handlers (#3286) (#3286)
Integrated into release/v3.8.12. Salvaged the emitHookBlocking payload-chaining fix from the now-closed plugins-v4 branch (#3221) and adapted it to the shipped release hooks.ts: each blocking handler now sees the body/metadata as mutated by previous handlers. TDD regression test included (RED before, GREEN after); existing plugins-hooks suites green (19+5), typecheck + lint clean.
2026-06-06 04:27:00 -03:00
Paijo
1c8f3bee97 fix(quota): resolve poolUsage dead code, burn rate, saturation signals, webhooks, and embeddings enforcement (#3280)
Integrated into release/v3.8.12. Quota Sharing Engine fixes: poolUsageWithDimensions promoted to the QuotaStore interface, single-snapshot burn rate, zero-weight normalization, Anthropic saturation signals, quota.exceeded webhook on block, and embeddings enforcement. Validated: 10/10 PR tests + 34 quota/embedding regression files green, typecheck + lint clean. Dropped the committed .omo/ agent-tooling artifacts.
2026-06-06 03:41:03 -03:00
Diego Rodrigues de Sa e Souza
a5d19bf4b9 fix(api): allow private webhook targets behind explicit opt-in (#3269) (#3279)
Webhooks hardcoded parseAndValidatePublicUrl, which blocks any RFC1918/loopback host —
breaking self-hosted setups that legitimately point webhooks at internal services
(n8n, Home Assistant, a LAN box). Provider URLs already had an opt-in
(OMNIROUTE_ALLOW_PRIVATE_PROVIDER_URLS); webhooks now reuse it.

- outboundUrlGuard: add parseAndValidateWebhookUrl — gates the private-host check on
  arePrivateProviderUrlsAllowed() (default OFF); protocol + embedded-credential checks
  stay unconditional.
- swap all webhook call sites (create/update/test/validate-url + dispatcher x2) to it.

Regression test: tests/unit/webhook-private-optin-3269.test.ts (RED before, GREEN after);
existing webhook SSRF/dispatcher suites stay green (33/33).
2026-06-06 02:59:09 -03:00
Diego Rodrigues de Sa e Souza
30ebe0ae2e feat(free-tiers): per-model free-token budget + Monthly Budget dashboard card (#3263)
Free-token budget catalog + per-model budget + Monthly Budget dashboard card (joins #3257 + #3263 into one).

Integrated into release/v3.8.12.
2026-06-06 02:38:35 -03:00
Felipe Almeman
6674f6a4f2 fix(db): detect SQLite driver-unavailable errors to avoid destructive rename (#3274)
Detect SQLite driver-unavailable errors to avoid destructive DB rename + optional FTS5 migration guard (split from #3073).

Integrated into release/v3.8.12. Thanks @zhiru.
2026-06-06 02:15:17 -03:00
Diego Rodrigues de Sa e Souza
2ad2bcb13f fix(embeddings): block cross-dimension failover in embedding combos (#3256)
Block cross-dimension failover in embedding combos.

Integrated into release/v3.8.12.
2026-06-06 02:07:06 -03:00
Thiago Reis
583bceb53d fix(providers): improve refresh validation and model catalog UI (#3261)
Provider refresh/validation, OpenRouter catalog and proxy UI fixes — incl. NVIDIA NIM /models-suffix path fix (real-VPS validated).

Integrated into release/v3.8.12. Thanks @strangersp.
2026-06-06 00:26:40 -03:00
Wilson
925d838d3b fix(grok-web): add TLS fingerprint impersonation to bypass Cloudflare anti-bot (#3180) (#3249)
grok-web: TLS fingerprint impersonation to bypass Cloudflare anti-bot (#3180); sanitize executor error bodies (#12).

Integrated into release/v3.8.12. Thanks @wilsonicdev.
2026-06-06 00:20:51 -03:00
Wilson
5ec8fa222a fix(usage): route agy quota through antigravity (#3232)
Route agy quota through the Antigravity usage implementation (closes #3230).

Integrated into release/v3.8.11. Thanks @wilsonicdev.
2026-06-05 13:12:40 -03:00
Felipe Almeman
2942ba874e Feat/codex device flow (#3195)
Codex public device-flow connect link (ticket-gated) + dashboard CTA. Integrated into release/v3.8.11.
2026-06-05 12:06:35 -03:00
MeAdityaB
4dbbbaacf1 fix: NVIDIA NIM API key validation timeout (bypass proxy fetch patch) (#3226)
NVIDIA NIM validation bypasses the proxy-patched fetch (504 fix) + combined with #3116 reliable probe model + test. Integrated into release/v3.8.11.
2026-06-05 11:52:27 -03:00
Diego Rodrigues de Sa e Souza
e0c6fb9f8c fix(providers): use a reliable NVIDIA validation probe model (#3116) (#3223)
The NVIDIA key-validation chat probe used models[0] (z-ai/glm-5.1), which
requires the 'Public API Endpoints' account permission and has DEGRADED
windows. Accounts lacking that permission see the probe hang until the
validation timeout, surfacing as a misleading 'Upstream Error' on a valid key.
Probe the universally-available meta/llama-3.1-8b-instruct instead, still
overridable via providerSpecificData.validationModelId. + unit test.
2026-06-05 10:17:40 -03:00
Diego Rodrigues de Sa e Souza
ec4f8c4d42 feat(api): combo/alias resolution + OpenAI-compatible edits for image routes (#3214, #3215) (#3219)
Image routes now resolve a requested model the same way across /v1/images/generations
and /v1/images/edits, via a shared resolver: built-in id -> custom provider prefix ->
bare combo/alias name (e.g. "image" -> its single image target). Previously a bare
combo name fell through to "Invalid image model".

/v1/images/edits gains two capabilities for custom OpenAI-compatible providers:
- multipart edit forwarding to the node's {base_url}/images/edits (was hard-rejected
  unless chatgpt-web);
- JSON/data-URL edit input (images:[{image_url:"data:..."}]), converted to the same
  fields the multipart reader produces (was "Invalid multipart body").

The chatgpt-web conversation-continuation edit flow is unchanged.
2026-06-05 09:37:32 -03:00
Diego Rodrigues de Sa e Souza
6fe7c6b5b1 fix(provider-models): keep a deleted synced model deleted across re-fetch (#3199) (#3212)
Follow-up to #3204: deleting a synced (fetched) model removed it from the
synced set, but the DELETE route didn't mark it hidden and the re-import path
didn't skip hidden ids, so the next auto-fetch re-added it. Now the route
marks the id hidden on delete and replaceSyncedAvailableModelsForConnection
filters hidden ids, so the deletion sticks.

Co-authored-by: tjengbudi <tjengbudi@users.noreply.github.com>
2026-06-05 02:56:03 -03:00
Diego Rodrigues de Sa e Souza
9bc2c89924 fix(openrouter): report true upstream context_length for passthrough models (#3202) (#3207)
normalizeDiscoveredModels only copied record.inputTokenLimit, but OpenRouter
returns the window as context_length / top_provider.context_length, so every
synced model fell back to the 128K default. Read context_length (and
top_provider.max_completion_tokens for output) as a fallback.

Co-authored-by: pulyankote <pulyankote@users.noreply.github.com>
2026-06-05 02:50:51 -03:00
Wilson
005ee10a1e fix(oauth): use api.anthropic.com for Claude token exchange to avoid Cloudflare bot block on VPS (#3203)
Integrated into release/v3.8.11
2026-06-05 02:47:42 -03:00
Wilson
7b4bda13b1 fix(models): allow deleting synced/fetched models (e.g. llamacpp) via DELETE /api/provider-models (#3204)
Integrated into release/v3.8.11
2026-06-05 02:44:38 -03:00
‍juandisay
0ea925ac20 feat: validate client IDs against resolvePublicCred to correctly toggle OAuth redirect URI overrides (#3206)
Integrated into release/v3.8.11
2026-06-05 02:41:42 -03:00
PizzaV
de5c842301 feat(proxy): auto-fallback proxy selection when validation fails (#3171)
Integrated into release/v3.8.11
2026-06-05 01:57:40 -03:00
PizzaV
d8363a51f2 feat(proxy): per-key proxy toggle backend with DB schema (#3170)
Integrated into release/v3.8.11
2026-06-05 01:14:49 -03:00
Diego Rodrigues de Sa e Souza
68d5a0ab27 Release v3.8.10 (#3140)
* chore(release): open v3.8.10 development cycle

Bump 3.8.9 → 3.8.10 across package.json, lockfile, electron, open-sse, and
docs/reference/openapi.yaml; add the [3.8.10] CHANGELOG section (root + 41 i18n
mirrors) as the integration target for the cycle. Entries land here as work
merges into release/v3.8.10; finalized by the release flow.

* fix(providers): resolve web provider alias collisions

Assign unique aliases to HuggingChat, Kimi Web, and Qwen Web so they no longer shadow primary providers or trigger startup warnings.

Add a unit test to enforce provider alias uniqueness and prevent future collisions. Also expand local ignore and VS Code exclude rules for agent, build, and worktree artifacts.

* fix(responses): normalize image_url parts across input paths (#3150)

Normalize image_url parts across all Responses input paths. Integrated into release/v3.8.10.

* fix(api-manager): preserve API key expiration local time (#3146)

Preserve API key expiration local time + clear button. Integrated into release/v3.8.10.

* Strip previous_response_id for stateless Responses upstreams (#3143)

Strip previous_response_id for stateless Responses upstreams (auto/strip/preserve). Integrated into release/v3.8.10.

* fix(opencode-plugin): map thinking cap to interleaved in model+combo (#3138)

Map caps.thinking to ModelV2.capabilities.interleaved for opencode-plugin. Integrated into release/v3.8.10.

* fix(providers): use synced models as fallback for all providers (#3148)

Use synced models as authoritative local catalog for all providers (+regression test). Integrated into release/v3.8.10.

* fix(qoder): bifurcate validation by token type — PAT→Cosy, regular API key→dashscope (#3149)

Bifurcate Qoder validation by token type (PAT→Cosy, regular→dashscope) +regression test. Integrated into release/v3.8.10.

* fix(antigravity): dynamic model resolution via MITM alias table (#3144)

Dynamic antigravity MITM model resolution in the executor (+bug fix +regression test; DB import dropped from client-reachable config). Integrated into release/v3.8.10.

* Feature/batch allow big (#3128)

Podman deployment options + larger upload body-size limits (+CONTAINER_HOST docs). Integrated into release/v3.8.10.

* fix(fireworks): preserve fully-qualified router/model IDs (#3133) (#3160)

Fireworks router IDs (accounts/fireworks/routers/...) were double-prefixed
with accounts/fireworks/models/ → upstream 404. Add optional
acceptedModelIdPrefixes to the registry entry and skip the prepend when the
model already starts with an accepted prefix.

Co-authored-by: KooshaPari <KooshaPari@users.noreply.github.com>

* fix(llama-cpp): route to configured local baseUrl instead of OpenAI (#3136) (#3161)

llama-cpp was missing from the local-provider group in buildUrl(), so it
fell through to the OpenAI baseUrl and returned an OpenAI 401. Add the
case to resolve the connection's providerSpecificData.baseUrl.

Co-authored-by: tjengbudi <tjengbudi@users.noreply.github.com>

* fix(t3-chat-web): parse cookies + convexSessionId from stored credential (#3007) (#3162)

The executor read credentials.cookies/convexSessionId, but the pipeline
only stores the pasted string under apiKey → t3.chat always 400'd. Parse
both values from apiKey (fallback accessToken), mirroring validation.ts.

Co-authored-by: minhtran162 <minhtran162@users.noreply.github.com>

* fix(minimax): stop capping MiniMax-M3 / M2.7 max_tokens at 8192 (#3141) (#3163)

MiniMax-M3 had no MODEL_SPECS entry and capitalized MiniMax-M2.7 missed
its lowercase spec (case-sensitive lookup) → both fell to the 8192 default
cap. Add the M3 spec (512K output), alias the capitalized ids, and make
getModelSpec lookups case-insensitive.

Co-authored-by: totaltube <totaltube@users.noreply.github.com>

* fix(github-copilot): discover model catalog live from api.githubcopilot.com (#3120, #3121) (#3164)

The github (Copilot) provider had a static hardcoded catalog with no
discovery source, so Import Models never refreshed (#3120) and advertised
non-entitled models that 400 on use (#3121). Add a live /models fetch with
fallback to the static list.

Co-authored-by: gabrielmoreira <gabrielmoreira@users.noreply.github.com>

* fix(combo): invalidate nested-combo cache on edits + log DATA_DIR (#3147) (#3165)

Editing a combo did not invalidate the 10s nested-combo expansion caches
(chat.ts getCombosCachedForChat + chatCore.ts getCombosCached; the exported
clearCombosCache was dead code), so a removed nested target/model could be
served as a phantom for up to 10s. Wire a shared monotonic combos-cache
version in readCache (bumped by invalidateDbCache("combos") on every combo
write); both cache layers treat a version mismatch as a miss.

Also log the resolved DATA_DIR/SQLITE_FILE absolute path at DB init so the
reporter's 'persists across restart + volume wipe' symptom (a multi-replica
Docker volume/DATA_DIR mismatch, not a routing bug) is diagnosable from logs.

Includes consolidated CHANGELOG entries for #3133/#3136/#3007/#3141/#3120/#3121.

Co-authored-by: ViFigueiredo <ViFigueiredo@users.noreply.github.com>

* fix(web-tools): parse bare JSON tool calls (#3157)

Parse bare JSON tool calls for deepseek-web (#2820) + fuzzy tool-name matching. Integrated into release/v3.8.10.

* fix(misc): minor fixes across reasoning cache, account fallback, binary manager (#3177)

Misc: ProviderProfile export, DeepSeek reasoning regex, binary guard. Integrated into release/v3.8.10.

* fix(kiro): minor OAuth social exchange tweaks (#3176)

Kiro social OAuth: optional targetProvider passthrough. Integrated into release/v3.8.10.

* deps: bump hono from 4.12.18 to 4.12.23 (#3179)

Bump hono to 4.12.23. Integrated into release/v3.8.10.

* fix(providerRegistry): update kilocode format and executor (#3166)

kilocode: openai format + default executor (matches kilo-gateway) + registry test. Integrated into release/v3.8.10.

* feat(metrics): cross-request TTFT and gap latency after tool calls (#3173)

Cross-request TTFT + gap-after-tool latency metrics (+test). Integrated into release/v3.8.10.

* feat(dashboard): provider stats API endpoint and dashboard page (#3175)

Provider stats dashboard + API (SQL moved to db module per Hard Rule #5, +test). Integrated into release/v3.8.10.

* fix(usage): sequential+spaced OAuth quota sync, reactive force-refresh, actionable 401 (#3156)

Sequential+spaced OAuth quota sync, reactive force-refresh on 401, actionable 401 in UI. Integrated into release/v3.8.10.

* fix(healthcheck): per-provider proactive-refresh skip list (rescue short-TTL OAuth) (#3159)

Per-provider proactive-refresh skip list (OMNIROUTE_HEALTHCHECK_SKIP_PROVIDERS) to rescue short-TTL OAuth. Integrated into release/v3.8.10.

* feat(quota): show OAuth token expiry on provider cards (small, blue, informative) (#3178)

Show OAuth token expiry on provider cards (small, blue, informative). Integrated into release/v3.8.10.

* fix(providers): empty refresh must not resurface just-cleared synced models (#3181)

Empty refresh must not resurface just-cleared synced models (fixes the release-blocking provider-models-route test). Integrated into release/v3.8.10.

* chore(release): v3.8.10 — 2026-06-04 (finalize CHANGELOG)

---------

Co-authored-by: Wilson <pedbookmed@gmail.com>
Co-authored-by: Xiangzhe <32761048+xz-dev@users.noreply.github.com>
Co-authored-by: Jan Leon <Jan.gaschler@gmail.com>
Co-authored-by: M.M <mr.maatoug@gmail.com>
Co-authored-by: Hernan Javier Ardila Sanchez <hjasgr@gmail.com>
Co-authored-by: Markus Hartung <mail@hartmark.se>
Co-authored-by: KooshaPari <KooshaPari@users.noreply.github.com>
Co-authored-by: tjengbudi <tjengbudi@users.noreply.github.com>
Co-authored-by: minhtran162 <minhtran162@users.noreply.github.com>
Co-authored-by: totaltube <totaltube@users.noreply.github.com>
Co-authored-by: gabrielmoreira <gabrielmoreira@users.noreply.github.com>
Co-authored-by: ViFigueiredo <ViFigueiredo@users.noreply.github.com>
Co-authored-by: PizzaV <103120356+pizzav-xyz@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Nicolas Lorin <androw95220@gmail.com>
2026-06-04 20:05:38 -03:00
Diego Rodrigues de Sa e Souza
5f3b1e8cde fix(db): dedup duplicate API keys per provider on connection create (#3023) (#3100)
createProviderConnection deduped apikey connections only by (provider, name). Adding the same key under a different/blank name created a duplicate row. It now also matches by the decrypted key value (AES-GCM ciphertext is non-deterministic, so we decrypt+compare plaintext, trimmed) and updates the existing connection instead. Tests cover same-key dedup, whitespace-variant dedup, and distinct-key separation.
2026-06-03 07:52:10 -03:00
Diego Rodrigues de Sa e Souza
365c29a115 fix(providers): forward Grok sso-rw cookie to fix anti-bot 403 (#3063) (#3098)
grok-web only sent the 'sso' cookie; Grok's anti-bot now rejects (403 code 7) requests missing the paired 'sso-rw' write cookie. Adds buildGrokCookieHeader() which emits sso plus sso-rw when the pasted blob carries it (never a phantom sso-rw from a bare value), used by both the executor and the connection validator. Updates the grok-web authHint. Tests: 6 new buildGrokCookieHeader cases; grok-web 62/62 unchanged.
2026-06-03 07:39:02 -03:00
diegosouzapw
51d2ca8151 feat(db): add apiKeyContextSources module + migration 092
Implements per-API-key context source configuration table and CRUD
functions required by the Obsidian PR. Restores getApiKeyContextSource
in obsidian.ts (was stubbed to null during conflict resolution).
11/11 tests pass in obsidian-config.test.ts.
2026-06-03 07:04:52 -03:00
Brandon Bennett
9db306e2f8 feat(observability): add Obsidian context source with 24 MCP tools (#3077)
Integrated into release/v3.8.9. Fixes applied: removed .serena/ from repo (gitignored), resolved import conflicts preserving agentSkillTools, fixed TS errors.
2026-06-03 06:45:04 -03:00
diegosouzapw
5943e5d528 feat: add Codex WS flag and Xiaomi MiMo usage tracking
Add a global OMNIROUTE_CODEX_WS_ENABLED kill-switch for Codex WebSocket transport, defaulting to enabled if feature flag lookup fails.

Track Xiaomi MiMo monthly token usage from OmniRoute usage history and expose it through the usage fetcher provider list.
2026-06-03 00:41:11 -03:00
diegosouzapw
326a219620 fix(memory): recent strategy must not relevance-filter by prompt
The "recent" memory strategy maps to the internal "exact" retrieval path, whose
post-query relevance filter (score > 0) silently dropped recent memories whose
text didn't overlap the current prompt. Since the user-facing strategy enum is
only recent|semantic|hybrid (no "exact"), forwarding the prompt as `query` for
"recent" always engaged that filter, so recency-based injection returned nothing
when the prompt was unrelated to the stored memory.

Skip query forwarding for the "recent" strategy so retrieveMemories returns the
most recent memories (ORDER BY created_at DESC) regardless of prompt overlap.
Semantic/hybrid still forward the query for vector search.

Fixes the chat-pipeline + memory-pipeline integration memory-injection tests.
2026-06-02 18:02:28 -03:00
diegosouzapw
e438139b03 feat(quota): live real-time codex quota on the page (cascade-safe serialized refresh)
Symptom: freshly-added Codex accounts (e.g. davi/gabriel) showed "No quota data"
even when healthy. Root cause: the quota path reuses the access_token without
refreshing rotating providers (#3019, anti Auth0 family-revocation cascade), so a
Codex account whose short-lived access_token has expired can never surface quota
from the sync — the live fetch returns "Codex token expired".

Fix (opt-in, cascade-safe):
- refreshAndUpdateCredentials gains `allowRotatingRefresh` + a pure exported gate
  `shouldAttemptRotatingRefresh`. The actual token mint is wrapped in
  `serializeRefresh` (one refresh at a time per Auth0 rotation group) — so even N
  concurrent per-account requests can never refresh siblings in parallel.
- The BULK scheduler (syncAllProviderLimits, concurrent) keeps the flag OFF →
  #3019 fully preserved (guardian test codex-quota-sync-no-proactive-refresh stays
  green). Only the on-demand, per-connection path (`GET /api/usage/[connectionId]`)
  opts in.
- Frontend: the quota page auto-fetches LIVE on open for the VISIBLE connections
  that have no cached quota (scoped to what's on screen — not all connections —
  and skips entries already cached), so expired-token Codex accounts surface real
  quota automatically and cascade-safely.

Adds unit coverage for the gate (bulk skips rotating, on-demand allows; non-rotating
always eligible). typecheck / lint clean.
2026-06-02 09:53:50 -03:00
diegosouzapw
c4a993184e fix(quota): never flag rotating-refresh providers expired from the quota sync
The quota-sync path deliberately reuses a rotating-refresh provider's (Codex/
OpenAI/Claude — see refreshSerializer ROTATION_LOCK_GROUP) access_token WITHOUT
proactively refreshing it (#3019, to avoid the Auth0 family-revocation cascade).
When that token is expired the codex usage fetch returns "token expired", and
syncExpiredStatusIfNeeded then flagged the connection testStatus="expired" — a
false-negative: the credential is still valid (expires_at in the future) and the
reactive serialized 401 path refreshes the access_token on next use.

Symptom: freshly-added Codex accounts showed "expired" with no quota on the
quota page, while a providers-page refresh turned them green. They never lost
access — only the quota sync mislabeled them.

Fix: extract the decision into the pure, exported `quotaPathShouldMarkExpired()`
and skip rotating providers (rotationGroupFor !== null). Their status is owned by
the reactive path / connection test, never the quota sync. Adds unit coverage.
2026-06-02 08:47:51 -03:00
diegosouzapw
512e980a9e feat(quota): xiaomi monthly cap + deepseek USD preset; researched plan tiers
- xiaomi-mimo: token plan is MONTHLY (per platform.xiaomimimo.com/token-plan), so
  the seed is now tokens/monthly/4.1B (was weekly).
- deepseek: prepaid in USD — its balance API is already wired (deepseekQuotaFetcher)
  and the fair-share engine supports the usd unit (COUNTABLE_UNITS). Seeded a
  usd/monthly preset so the limit is set by dollar value.
- minimax: documented the real M3 tiers (Plus ~1.633B/Max ~5.053B/Ultra ~9.796B)
  in-comment; EPSILON keeps it manual until tier-aware presets land.
- planRegistry already seeds codex/claude/glm/minimax/kimi/kimi-coding/xiaomi-mimo/
  deepseek/bailian/alibaba; PoolWizard 'Limite' step stays editable.

Researched plan structures + the tier-aware-preset follow-up are in the redesign plan.
2026-06-02 05:17:20 -03:00
diegosouzapw
f8c1213722 feat(quota): seed claude plan preset (percent 5h+weekly) in planRegistry
Claude Code (Pro/Max) is a percentage-of-plan quota (5h rolling + weekly cap,
shared Claude+Code); exact token caps are unpublished/task-variable so percent is
the practical unit. Unblocks the PoolWizard 'Limite' pre-fill for claude pools.
Researched plan structures (codex/claude/glm/kimi/minimax/xiaomi) captured in the
quota-share redesign plan.
2026-06-02 05:06:34 -03:00
diegosouzapw
831f82858f feat(quota-share): beta banner, Responses + codex-WS endpoints in card, plan presets
- Beta banner scoped to the Quota Share page (functional-but-bugs-expected) with a
  pre-filled "open an issue" link (labels quota-share,beta). Page-only.
- Endpoints card now also surfaces POST /v1/responses (codex/github) and the
  codex-only WS /v1/responses line (the Responses-over-WebSocket proxy), each gated
  on the in-scope provider slug.
- planRegistry: seed xiaomi-mimo (4.1B-token weekly "lite" cap) and kimi-coding so the
  PoolWizard "Limite" step pre-fills a fair-share limit for these no-balance-API
  providers (fair-share enforces from the proxy's own token count, not an upstream
  balance — set the real cap manually in step 2).
- docs(API_REFERENCE): document the codex Responses-over-WebSocket endpoint.
- i18n en/pt-BR for all new keys.

Tracked in _tasks/features-v3.8.8/quota-share-key-redesign.plan.md (codex-WS config
toggle + per-provider balance fetchers + %-quota attribution are planned follow-ups).
2026-06-02 05:02:52 -03:00
dangeReis
dee20ac665 fix(pii): preserve custom event names and apply stream PII sanitization fixes (#3059)
* fix(sse): defer enqueuing of event lines to align event names with data lines and prevent stop-signal event name misattribution

* fix(sse): preserve keep-alives and prevent pending event leakage on dropped chunks

* fix(sse): preserve pending event lines before other non-data lines and fix zero-window-size bypass

* fix(sse): defer lastEventLine update until after flush check to preserve previous event context on flush

* fix(sse): flush trailing pendingEventLine when stream closes

* fix(sse): preserve consecutive event lines without intervening data

---------

Co-authored-by: Ruslan Sivak <russ@ruslansivak.com>
2026-06-02 02:17:52 -03:00
dangeReis
c711ac62a7 fix(pii): preserve custom event names and apply stream PII sanitization fixes (#3056)
Co-authored-by: Ruslan Sivak <russ@ruslansivak.com>
2026-06-01 23:13:19 -03:00
Paijo
76d697bb88 chore: remove 9 dead/unreachable free providers (#3054)
* chore: remove 9 dead/unreachable free providers

Verified via HTTP probe — API endpoints return 000/404/empty:
- freetheai, enally, replicate, lepton, poolside, nomic
- astraflow, petals, nanobanana (phantom: catalog but no registry)

Also removed from: providerRegistry.ts, validation.ts,
staticModels.ts, imageValidation.ts, open-sse/config/petals.ts

* chore: remove dead astraflow providers

Remove astraflow and astraflow-cn (UCloud) — API endpoints unreachable.
Remaining dead providers (enally, freetheai, nanobanana, replicate,
lepton, petals, poolside, nomic) have working main sites but dead API
endpoints — need API keys. Will remove in follow-up.

* chore: remove 9 dead/unreachable free providers

Removed: freetheai, enally, replicate, lepton, poolside, nomic,
astraflow, petals, nanobanana

All verified as dead via live API probes (000/404/empty responses).
Cleaned from providers.ts, providerRegistry.ts, validation.ts,
staticModels.ts, and imageValidation.ts.

---------

Co-authored-by: oyi77 <oyi77@users.noreply.github.com>
2026-06-01 22:29:30 -03:00
terence71-glitch
5350b5e7f5 fix(proxy): fail closed for OAuth usage account proxies (#3051) 2026-06-01 19:47:31 -03:00
diegosouzapw
8f0615fd04 fix(security): resolve 5 CodeQL alerts, document FPs, harden deploy skills
Real fixes (alerts auto-close on next scan):
- agentSkills/generator: escape backslash before double-quote in YAML
  frontmatter so a trailing backslash can't escape the closing quote
  (js/incomplete-sanitization #297/#298)
- usage: replace /\s*\(RESTRICTED\)\s*$/ with a non-backtracking literal +
  trim (js/polynomial-redos #275)
- i18n/request: skip __proto__/constructor/prototype in deepMergeFallback
  (js/prototype-pollution-utility #274)
- scripts/ad-hoc/nvidia diag: log key presence only, never key chars
  (js/clear-text-logging #273)
- tests: regression coverage for all three production fixes (YAML round-trip,
  proto-pollution guard, RESTRICTED strip + ReDoS timing)

Docs:
- ARCHITECTURE.md: executor count 45->55, OAuth modules 15->16, agy.ts in list

Deploy skills (deploy-vps-*-cc/-cx/-ag): add --legacy-peer-deps (npm v11
peer-dep resolver crashes on the omniroute tree) and replace the
"; pm2 start" that masked a failed install with a proper && chain.
2026-06-01 19:36:48 -03:00
Paijo
9e7f3cad10 feat(plugins): plugin hook wiring + comprehensive test suite + welcome-banner example (#3045)
Follow-up to the plugins framework (#3041): wires the plugin hooks end-to-end and adds full test coverage.

- Wires `onRequest` / `onResponse` / `onError` hooks into the chat pipeline (`chatCore` now imports from the unified `hooks` registry).
- Loads active plugins on server startup (`pluginManager.loadAll()` in `server-init`) so they survive restarts.
- Ships a `welcome-banner` example plugin (`examples/plugins/`) + test fixture.
- Adds a comprehensive plugin test suite (manifest, db, config, hooks, manager lifecycle, loader IPC, permissions, scanner, welcome-banner e2e).

Integration fixes applied during review:
- `manager.install` now removes an orphaned `destDir` (DB row gone but files left on disk) before the atomic rename, guarded by path containment — it previously failed with `ENOTEMPTY` (a regression surfaced by the new lifecycle test).
- `plugins-db` / `plugins-manager-lifecycle` tests now initialize the DB via the real migration `076` (`getDbInstance`) rather than relying on ambient state, so a missing/renumbered migration fails loudly instead of being masked.

348/348 plugin tests pass; typecheck / cycles clean.

Co-authored-by: oyi77 <14921983+oyi77@users.noreply.github.com>
2026-06-01 16:20:11 -03:00
Diego Rodrigues de Sa e Souza
20c31493af feat(plugins): plugins framework + per-API-key disable-non-public-models (#3041)
Integrates two community contributions into release/v3.8.8 with security hardening and conflict resolution.

- **Plugins framework** (#2913 — thanks @oyi77): hooks + registry unification, plugin SDK (`definePlugin`), worker-thread sandbox, per-plugin hook rate limiting, SHA-256 integrity verification, semver-gated upgrade, and execution analytics. Plugin routes are loopback-only (`isLocalOnlyPath`); `child_process` exec is opt-in via `OMNIROUTE_PLUGINS_ALLOW_EXEC` (default off).
- **API key option: disable non-published models** (#3017 — thanks @androw): a per-key flag restricting the key to discovered public models (combos / `auto/*` / `qtSd/*` routing still allowed).

Hardening applied during integration: migration renumber (089/090/091), `/api/plugins` LOCAL_ONLY route-guard classification (closes the plugin-RCE vector), atomic install/upgrade with path containment, `O_EXCL` tmp-file creation (TOCTOU), rate-limit-map eviction, `validatePluginConfig` on configure, `buildErrorBody` on all plugin error paths. 246/246 tests; typecheck / cycles / docs-sync clean.

Co-authored-by: oyi77 <14921983+oyi77@users.noreply.github.com>
Co-authored-by: Nicolas Lorin <androw95220@gmail.com>
2026-06-01 15:43:55 -03:00
Hernan Javier Ardila Sanchez
482e4690eb fix(dashboard): use lightweight ping endpoint for MaintenanceBanner (fixes #3040) (#3043)
Integrated into release/v3.8.8. Applied review fixes: moved the SELECT 1 into a pingDb() db helper (no raw SQL in route, Hard Rule #5) + the 503 catch no longer leaks err.message (Hard Rule #12). Thanks @herjarsa!
2026-06-01 14:31:42 -03:00
dangeReis
e11366b647 fix(privacy): resolve PII feature flag correctly and fix PII response sanitization in streaming SSE requests (#3021)
Integrated into release/v3.8.8. Fixes the PII feature-flag resolution + adds streaming-aware SSE PII sanitization (rolling-window transform, obfuscation-hardened regexes, Luhn/CPF/CNPJ checks). Applied review fixes: block-mode + fallback errors no longer leak pattern types / upstream err.message into stream errors (Hard Rule #12); typed the createPiiTransform cast (no any); fixed a string===boolean type error; dropped the generated .source/* files. Thanks @dangeReis!
2026-06-01 08:22:09 -03:00
Diego Rodrigues de Sa e Souza
4b3987e190 Merge pull request #3022 from diegosouzapw/feat/quota-share-redesign
feat(quota): Pool Groups + correções de enforcement → v3.8.8 (+ authz peer-IP, screen fixes)
2026-05-31 22:05:18 -03:00
diegosouzapw
58a7d97b3f Merge branch 'release/v3.8.8' into feat/quota-share-redesign
# Conflicts:
#	src/lib/db/apiKeys.ts
#	src/lib/db/migrationRunner.ts
#	src/server/authz/policies/management.ts
#	src/server/authz/routeGuard.ts
#	tests/unit/route-guard-private-lan.test.ts
2026-05-31 21:08:30 -03:00
diegosouzapw
eac24ca458 feat(quota): group-level allocations + group access enforcement
upsertAllocations now propagates allocation rows to every pool in the
same group. A key allocated to pool A in group G automatically gets
an allocation row in pool B (same group), so enforceQuotaShare finds
the row when the key calls pool B's model and applies fair-share.

No change to apiKeyPolicy Check 3 (already group-correct via B5
groupSlug logic) or enforce.ts pool-match (works once propagation
supplies the row).
2026-05-31 19:24:59 -03:00
diegosouzapw
e9cc53b17f feat(quota): key scope + /v1/models expand to the whole group (real group)
- `getPoolsByGroup(groupId)` in quotaPools.ts: SELECT all pools for a group,
  exposed as public API and re-exported from localDb.ts.
- `resolveQuotaKeyScope`: pool → group → all pools in group expansion.
  The returned `poolSlugs` field now holds GROUP slugs (quotaGroupSlug of the
  group name) instead of individual pool-name slugs, one per distinct group.
  Group slug included only when the group has ≥1 valid connection (group-level
  anyValidConnection gate). Deduplication: a key in 2 pools of the same group
  expands once.
- `filterModelsToQuotaPools` (quotaCombos.ts): already matches by
  `parsed.groupSlug` ∈ poolSlugs — no logic change needed, just test alignment.
- quota-key-resolve.test.ts: updated 4 tests that asserted pool-name slugs
  (testpoola2, poolmix, etc.) to assert the group slug ("groupdemo") — this is
  alignment to the B5 group semantics, not masking; the assertions were correct
  for the old pool-slug behavior and are now correct for the new group-slug
  behavior.
- quota-catalog-filter.test.ts: updated fixtures from old quotaShared-* format
  to qtSd/<groupSlug>/... (naming changed in B3, test was already broken before B5).
2026-05-31 19:15:16 -03:00