Commit Graph

489 Commits

Author SHA1 Message Date
Diego Rodrigues de Sa e Souza
a7e445edea fix(sse): don't mark a valid Qoder PAT expired on a generic Cosy 500 (#3247) (#3283)
A working Qoder PAT was reported as "expired". The validator probes the Cosy endpoint
(api1.qoder.sh) — which IS the correct PAT path (the executor falls back to it after the
expected 401 from api.qoder.com). The bug was the verdict: isCosyAppError (added by #2860)
marked ANY Cosy 500 with "success":false as an auth failure, including a generic
{..."msgCode":500,"message":"Internal Server Error"} server fault — contradicting the
older #1391 "5xx = valid bypass" rule.

Narrow it: a Cosy 500 only marks the PAT invalid when the body carries an EXPLICIT auth
signal (unauthorized/forbidden/expired/token invalid/...); a generic Internal Server Error
falls back to valid-bypass. #2860's protection for genuine auth rejections is preserved.

Regression test: tests/unit/qoder-cli.test.ts — the two pre-existing generic-500 cases now
assert valid:true (they encoded the #3247 bug) + a new explicit-auth-signal case asserts
valid:false. 13/13 green.
2026-06-06 03:11:58 -03:00
Diego Rodrigues de Sa e Souza
80c546eba9 fix(sse): strip reasoning_effort for non-reasoning Groq models (#3258) (#3277)
Regression of #764. Claude Code → Groq (llama-3.3-70b-versatile) returned HTTP 400
because the model was treated as reasoning-capable: supportsReasoning() defaulted to true,
so applyThinkingBudget did not strip reasoning params, and the claude→openai translator
forwarded reasoning_effort (and re-injected it from output_config.effort) — which Groq
rejects on non-reasoning models.

- providerRegistry: mark llama-3.3-70b-versatile + llama-4-scout supportsReasoning:false
  (gpt-oss / qwen3-32b keep reasoning — they accept reasoning_effort).
- stripThinkingConfig: also strip output_config.effort so the translator can't re-inject
  reasoning_effort downstream.

Regression test: tests/unit/thinking-budget-groq-3258.test.ts (RED before, GREEN after);
existing thinking-budget suites stay green (45/45).
2026-06-06 02:48:38 -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
MikeTuev
c2520bf5b7 fix(sse): strip every <omniModel> tag, not just the first (#454) (#3248)
Strip ALL <omniModel> tags before forwarding to provider (global regex variant).

Integrated into release/v3.8.12. Thanks @MikeTuev.
2026-06-05 21:21:40 -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
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
Wilson
ccc4425744 fix(auto-combo): include no-auth OpenCode Free (#3189)
Integrated into release/v3.8.11
2026-06-05 00:50:08 -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
diegosouzapw
b6bda19919 fix(quality): explicit promise-existence checks + protocol-aware WebDAV URL
SonarCloud new-code findings:
- combo.ts / sync-models route: `if (cachedPromise)` -> `!= null` (intentional
  in-flight-promise reuse; explicit existence check, no behaviour change).
- ObsidianSourceCard WebDAV URL: inherit window.location.protocol instead of
  hard-coding http:// (https when behind a TLS proxy) — clears the http hotspot.
2026-06-03 23:53:03 -03:00
Ahmet Çetinkaya
84b5caeeb9 fix(sse): bound Antigravity 429 retry loop and lock quota-exhausted accounts for full reset window (#3122)
* fix(sse): bound Antigravity short-retry 429 loop per endpoint

A persistent 429 on the short-retry branch (retryAfterMs ≤ 60s) looped
forever on the same endpoint because the branch did `urlIndex--; continue`
without checking the shared retry counter. Production log showed 77
consecutive 429s on one daily endpoint/account with zero fallback.

Gate the short-retry branch on `retryAttemptsByUrl[urlIndex] < MAX_AUTO_RETRIES`
(mirroring the already-bounded sibling), so a persistent 429 retries at most
3× per endpoint across all 3 base URLs then returns the 429 to the account-
fallback layer.

Regression test: 'bounds a persistent short-retry 429' in
tests/unit/executor-antigravity.test.ts — asserts 12 total attempts
(3 endpoints × 4) and a returned 429 with zero hang.

* fix(sse): lock Antigravity quota-exhausted account for full reset window

After the retry-loop bound, OmniRoute fell over to the next account but
re-selected the exhausted one first on every subsequent request (~60s wasted
per request). Root cause: the 429 body 'Individual quota reached. Contact
your administrator to enable overages. Resets in 164h27m24s.' was not
recognized as quota exhaustion, so the model was locked for only ~5s instead
of the real 6.8-day reset window.

Two detector fixes (mirrors Antigravity-Manager rate_limit.rs set_lockout_until):

1. classify429.ts — add QUOTA_PATTERNS: /individual quota reached/i,
   /quota reached/i, /enable overages/i so looksLikeQuotaExhausted() fires.
2. accountFallback.ts — same patterns in classifyErrorText(); extend
   parseRetryFromErrorText() to parse 'Resets? in XhYmZs' (reusing the
   existing computeDurationMs helper) so the exact reset duration reaches
   recordModelLockoutFailure as exactCooldownMs (uncapped, per user choice).

The lockout machinery already stores until = now + cooldownMs with no clamp,
bypasses getScaledCooldown when exactCooldownMs > 0, and keeps the longer of
existing/new, so the full 164h window flows through intact.

New patterns stay specific — plain 'too many requests'/'rate limit exceeded'
messages still classify as rate_limit.

Verified end-to-end against the real message:
- classify429 → quota_exhausted
- parseRetryFromErrorText → 592044000 ms (164h27m24s exactly)
- checkFallbackError → usedUpstreamRetryHint: true, cooldownMs: 592044000

* refactor(account-fallback): simplify error parsing and add cooldown safety

- Implement a 30-day cap on parsed retry durations to prevent indefinite account lockouts.
- Replace manual string matching with `looksLikeQuotaExhausted` for more robust quota detection.
- Streamline regex logic in `parseRetryFromErrorText` for better readability.
- Add unit tests for extreme cooldown values and free-tier exhaustion scenarios.

* fix(429): drop over-broad /quota reached/ pattern, keep specific matches

The bare /quota reached/ would also flag transient per-minute limits like
'request quota reached, retry in 60s' as quota_exhausted (multi-hour lock).
The Antigravity message is still caught by /individual quota reached/. Added a
regression assertion proving the transient case stays a rate_limit.

---------

Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com>
2026-06-03 18:29:59 -03:00
NMI
27b822e412 fix(tools): keep opaque object schemas open (#3097)
* fix(tools): keep opaque object schemas open

* test: align opaque-object-schema expectations with additionalProperties:true

The opaque-schema fix intentionally injects additionalProperties:true on empty
object schemas (incl. the web_search passthrough shim and null/missing parameter
fallbacks). Update the pre-fix snapshot assertions to match the new behavior.

---------

Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com>
2026-06-03 18:29:31 -03:00
payne
0594af6a6c feat(cursor): vision (image_url) input + tool-commit/output-constraint enhancements (#3104)
* feat(cursor): vision (image_url) input + tool-commit/output-constraint enhancements

Add image/vision input to the Cursor provider's agent.v1 endpoint, plus the
supporting prompt-engineering and resilience work developed alongside it.

Vision input
- Decode OpenAI `image_url` parts (base64 `data:` URIs and remote `http(s)` URLs)
  and inline them as `SelectedContext.selected_images[]` — field numbers pinned
  from the cursor-agent agent.v1 protobuf descriptor (SelectedImage.data oneof,
  uuid, optional Dimension, mime_type). Cross-checked against composer-api's shape.
- New `resolveCursorImages` helper: SSRF-guarded remote fetches via the repo's
  canonical `parseAndValidatePublicUrl` (always public-only for client URLs),
  <=1 MiB per image (pre-decode + streaming cap), `image/*` enforced, max 12
  images, sanitized `CursorImageError` (no stack/path leakage).
- `openai-to-cursor` translator now preserves `image_url` parts instead of
  dropping them; executor `buildRequest` resolves images and attaches them to
  the user turn. The no-image path is byte-identical to before (test-asserted).

Supporting cursor enhancements
- Tool-commit directive (raises composer-2.5 tool-call rate ~53% -> ~88%),
  `tool_choice` none/required/specific handling, and output constraints
  (`response_format` / `max_tokens` / `stop` surfaced as prompt instructions).
- `cursorSessionManager`: clear pending tool-call mappings on session close.
- `cursorVersionDetector`: export `FALLBACK_VERSION` as a single source of truth.

Tests & docs
- New unit suite for the image encoder + resolver (field layout, byte-identical
  no-image path, SSRF / oversize / bad-base64 / too-many rejections, sanitized
  error body), translator image-preservation tests, and live e2e tests
  (base64 + remote URL, gated on `CURSOR_E2E_TOKEN`).
- Documented `CURSOR_TOOL_DIRECTIVE` and `CURSOR_IMAGE_FETCH_TIMEOUT_MS` in
  `.env.example` and `docs/reference/ENVIRONMENT.md`.

* fix(cursor): address review — redirect SSRF, large-payload guard, stream OOM, case/NaN nits

Resolves the gemini-code-assist review on #3104:
- SSRF via redirect (critical): fetchImageBytes now uses redirect:"manual" and
  re-validates every hop through parseAndValidatePublicUrl, so a public URL can't
  30x-redirect to a private/link-local address. Bounded to 3 redirects.
- Large data URL (high): reject on raw payload length before the whitespace-strip
  regex, so an oversized data URL can't burn CPU.
- Stream read (high): readCapped consumes the body as an async iterable (Node
  Readable + Web Streams) or via getReader, capping mid-read; uncapped
  arrayBuffer() is only a last resort.
- data: scheme (medium): match case-insensitively (RFC 2397) while preserving the
  original payload.
- NaN timeouts (medium): CURSOR_IMAGE_FETCH_TIMEOUT_MS and CURSOR_STREAM_TIMEOUT_MS
  fall back to defaults when the env value isn't a positive integer.

Adds tests: redirect-to-private blocked, redirect-to-public followed, too-many-
redirects rejected, uppercase DATA: accepted.

* fix(cursor): defend image fetch against DNS-rebinding SSRF

Address the @codex review on #3104: parseAndValidatePublicUrl only checks the
hostname string, so a public-looking host that (re)resolves to a private /
link-local / metadata IP would still be fetched. Each hop now resolves the host
via dns.lookup({all:true}) and rejects if ANY answer is private (isPrivateHost),
before connecting. IP literals are skipped (already validated by the URL guard).

This narrows but doesn't fully close the TOCTOU window vs fetch's own
resolution; a connection-time IP filter on the shared outbound guard would
close it for every caller. Adds unit tests for the IP gate and a mocked
DNS-rebinding case (public host -> 127.0.0.1, fetch never reached).
2026-06-03 18:24:41 -03:00
Aoxiong Yin
261a910820 fix(codex): preserve native Responses passthrough tools and history (#3107)
* fix(codex): preserve tool_search hosted tool

* fix(codex): preserve native custom tools

* fix(codex): preserve native assistant commentary history
2026-06-03 18:12:54 -03:00
Paijo
43b1392876 fix(autoCombo): rotate across all connections, never waste provider capacity (#3078)
Integrated into release/v3.8.9. Clean merge, all 146 vitest tests pass.
2026-06-03 06:36:47 -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
0f900f1d56 fix(usage): handle MiniMax coding plan percent quotas
Support MiniMax Coding Plan quota responses that expose remaining usage as
percentages instead of request counts, including the text quota surfaced under
the `general` model.

Also document Codex CLI WebSocket configuration, clarifying that bare ChatGPT
model ids must be used instead of `codex/`-prefixed ids.
2026-06-03 00:41:10 -03:00
diegosouzapw
705ab8e690 fix(quality): clear SonarCloud quality-gate findings on PR #2930
Quality Gate was failing on New Code with 1 hotspot, 1 vulnerability and
4 reliability bugs. Resolved each at the source (no SonarCloud mark-as-safe):

Reliability (4 bugs → rating back to A):
- usage.ts: drop duplicate `case "opencode-go"` (S1862) — the 2nd case was
  dead (first match wins) and would have called the wrong usage fetcher.
  Also de-duplicate the same id in USAGE_FETCHER_PROVIDERS (mirrors the switch;
  prevented a double quota-fetcher registration).
- ApiManagerPageClient.tsx: a dangling `.find(...)?.timestamp || null` expression
  (S905) discarded the better matcher — `lastUsed` silently lost the
  name-fallback for legacy logs without apiKeyId. Assign the complete matcher.
- chat.ts / auth.ts: `Promise` used in a boolean conditional (S6544). Both are
  intentional (memoized promise / mutex default), not a forgotten await —
  made the intent explicit via `!== null` and `??` (behaviour identical).

Security (vulnerability → rating back to A):
- nvidia-startswith-diag.ts: neutralize CR/LF before logging env-derived
  values (S5145 log injection) in the ad-hoc diagnostic script.

Security Hotspot (reviewed → 0 open):
- pluginWorker.ts uses `vm.runInContext` to run plugin code — that IS the
  worker's purpose, inside a hardened vm sandbox (createContext, require
  allow-list of just `crypto`, 10s timeout); not eval/new Function. Suppress
  S1523 scoped only to pluginWorker.ts in sonar-project.properties, with the
  same documented-justification pattern as the existing hotspot suppressions.
2026-06-03 00:17:56 -03:00
Paijo
39a673b7d6 fix(pollinations/duckduckgo): wire session pool for fingerprint rotation (#3049)
* fix(pollinations): wire session pool + add idle pruning

PollinationsExecutor.getPool() returned null because poolConfig was
never set. Now sets DEFAULT_POOL_CONFIG in constructor so anonymous
requests get fingerprint rotation and 429 cooldown management.

Also:
- Use acquireBlocking() instead of acquire() to wait for available session
- Add startAutoPrune() for periodic idle session cleanup (5min idle timeout)
- Improve execute() error handling with proper finally block

* fix(duckduckgo-web): wire session pool for fingerprint rotation

DuckDuckGoWebExecutor had poolConfig set but never called getPool()
in execute(). Added session acquisition via acquireBlocking() and
merges fingerprint headers into fetch calls for rate limit evasion.

* fix: address PR #3049 review comments

- duckduckgo-web: fix session leak (add finally release), fix race
  condition (report status before release), remove duplicate sessionHeaders
- pollinations: fix race condition (move release to finally, report before)

* fix: address all PR #3049 review comments

- duckduckgo-web: add pool reporting on 429/500 early returns (was missing)
- duckduckgo-web: add sessionHeaders to retry fetch on 401/403
- sessionPool: add .unref() to setInterval to prevent keeping process alive

---------

Co-authored-by: oyi77 <oyi77@users.noreply.github.com>
2026-06-01 19:46:58 -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
Tentoxa
aa8033dae9 chore: bump Claude Code identity to 2.1.158 + sync anthropic-beta flags from live captures (#3010)
Integrated into release/v3.8.8 — free_tier_exhausted error rule (identity/beta-flags already in release). Thanks @Tentoxa!
2026-06-01 07:57:55 -03:00
Paijo
e4eeb6dc7f refactor: Make SessionPool modular & provider-agnostic (#2978)
Integrated into release/v3.8.8. Applied review fixes: typed DefaultExecutor.execute(input: ExecuteInput), guarded result?.response?.status, removed the unused cloakbrowser dependency. Session-pool modular refactor + 36 tests green. Thanks @oyi77!
2026-06-01 07:44:52 -03:00
diegosouzapw
6d2e695882 fix(sse): stop Codex multi-account family-revocation cascade on quota-sync
The Quota / Providers dashboard (POST /api/usage/provider-limits ->
syncAllProviderLimits, GET /api/usage/[connectionId]) calls
refreshAndUpdateCredentials() per connection, in concurrent chunks. For
rotating-refresh providers (Codex/OpenAI share one Auth0 client_id) the
single-use refresh_token is rotated on every refresh; refreshing siblings
concurrently makes Auth0 revoke the whole token family (openai/codex#9648),
killing every account but the last with [403] <!DOCTYPE html>. On the affected
VM expires_at was persisted as ~0 so needsRefresh() was effectively always
true -> every codex account refreshed on every page visit -> guaranteed cascade.

Fix #1: refreshAndUpdateCredentials skips proactive refresh for rotating
providers (rotationGroupFor) and reuses the current access_token for the quota
fetch; genuine expiry is handled by the reactive, serialized 401 path.

Fix #2 (defense in depth): serializeRefresh inserts a settle gap between two
QUEUED sibling refreshes (default 2000ms, CODEX_REFRESH_SPACING_MS, '0' to opt
out) but releases a lone refresh immediately, adding no latency to the reactive
request path.

Tests: codex-quota-sync-no-proactive-refresh (skip + non-rotating guard),
refresh-serializer-spacing (default/opt-out + lone-vs-queued behavior).
2026-05-31 19:13:41 -03:00
Tentoxa
1ba24c67df fix: address review feedback — remove Opus-only flag from static base, add redact-thinking to CC bridge
- Remove mid-conversation-system-2026-04-07 from ANTHROPIC_BETA_BASE
  (Opus-only — already handled correctly in the dynamic selectBetaFlags())
- Add redact-thinking-2026-02-12 to CLAUDE_CODE_COMPATIBLE_ANTHROPIC_BETA
- Update STEALTH_GUIDE.md documented beta string to include redact-thinking
2026-05-31 18:17:34 -03:00
Tentoxa
7b6007bc2d chore: bump Claude Code identity to 2.1.158 + sync beta flags from HAR captures
- Bump CLAUDE_CLI_VERSION / CLAUDE_CODE_VERSION from 2.1.146 to 2.1.158
  (matches claude-cli 2.1.158 captures)
- Add redact-thinking-2026-02-12 to the always-sent beta tier
  (present in Haiku, Sonnet, and Opus CC 2.1.158 captures)
- Add mid-conversation-system-2026-04-07 for Opus full-agent only
  (present in Opus capture, absent from Sonnet/Haiku)
- Remove afk-mode-2026-01-31 from heavy-agent beta flags
  (not present in any real Claude Code 2.1.158 capture)
- Add mid-conversation-system-2026-04-07 to ANTHROPIC_BETA_BASE
- Update CC-compatible bridge version strings
- Fix STEALTH_GUIDE.md outdated version + Stainless version (0.81.0 → 0.94.0)
- Update claude-beta-flags-2454 tests to match real CC behavior
2026-05-31 18:17:34 -03:00
diegosouzapw
7ec85ce054 Merge PR 3015 into release/v3.8.8 2026-05-31 18:09:29 -03:00
Diego Rodrigues de Sa e Souza
a6de2a58c5 Merge pull request #2965 from soyelmismo/fix/oom-memory-leak-caches
fix(oom): prevent unbounded memory growth causing heap OOM crashes
2026-05-31 18:06:18 -03:00
oyi77
c6cc8bb334 fix(usage): export pure helper functions for unit testing
Adds 17 missing exports to the __testing object in usage.ts so
usage-utils.test.ts can import and test them. Also adds the
test file itself.

- toDisplayLabel, getClaudePlanLabel, createQuotaFromUsage
- getMiniMaxQuotaResetAt, isMiniMaxTextQuotaModel
- getMiniMaxSessionTotal, getMiniMaxWeeklyTotal
- createMiniMaxQuotaFromCount, getMiniMaxAuthErrorMessage
- getMiniMaxErrorSummary
- mapCodeAssistSubscriptionToPlanLabel
- mapCodeAssistTierIdToLabel
- mapSubscriptionTierStringToPlanLabel
2026-06-01 02:31:15 +07:00
diegosouzapw
c2d7ac9359 fix(payload-rules): read DB-persisted rules when no in-memory override (#2986)
Payload rules are written to the DB (key_value settings.payloadRules) and
mirrored into an in-memory runtimeOverride. After a restart, if runtimeOverride
is null — the boot applyRuntimeSettings hook didn't run in this module instance,
or a separate bundle instance in the standalone Next.js build — getPayloadRulesConfig
fell back to the (usually empty) config/payloadRules.json file and returned no
rules, so saved rules appeared to vanish.

The persistence/apply path is correct on inspection (DB write+read both use the
'settings' namespace; the boot hook sets the override with force:true); the null
override is an environment/bundling effect. Make getPayloadRulesConfig read the
DB-persisted rules (via getCachedSettings, the source of truth) before the file
when no override is set, so saved rules deterministically survive a restart.

Closes #2986
2026-05-31 11:31:34 -03:00
diegosouzapw
f5d74cd76d fix(routing): honor client reasoning.effort for gpt-5.5 + route suffixed variants to codex (#2877)
(A) For a Codex-only account, a bare gpt-5.5 Responses request was rerouted to
codex with the model hardcoded to gpt-5.5-medium (chatHelpers.ts). The Codex
executor reads a model-name suffix as an explicit modelEffort that, per #2331,
overrides the client's reasoning.effort — so a genuine reasoning.effort=xhigh
was silently demoted to medium. Keep the bare gpt-5.5 id (the connection
fallback still supplies the default effort); the executor precedence is
untouched, so #2331 stays intact.

(B) gpt-5.5-xhigh/-high/-low misrouted to the openai provider (only bare gpt-5.5
was codex-preferred), so codex-only users got 'No credentials for provider:
openai'. Add the suffixed variants to CODEX_PREFERRED_UNPREFIXED_MODELS so they
infer codex before the /^gpt-/ → openai fallback.

Closes #2877
2026-05-31 09:05:35 -03:00
diegosouzapw
d22564df4d fix(session-pool): round-robin fingerprints in SessionFactory
createSession() used rotator.random() despite its docstring saying 'next
available fingerprint'. Random draws collide in a small profile pool,
producing duplicate fingerprints (and duplicate sess-<fp>-<ms> ids) across
a warm pool — flaky tests and look-alike sessions. Switch to rotator.next()
so each pooled session gets a distinct fingerprint, as documented.
2026-05-31 07:05:21 -03:00
oyi77
a365706d65 feat: add pool support to DuckDuckGo Web and LLM7 providers
- duckduckgo-web.ts: Add poolConfig (2-5 sessions, 1s cooldown)
- providerRegistry.ts: Add poolConfig field to RegistryEntry type, configure for llm7
- default.ts: Read poolConfig from registry in constructor

DuckDuckGo Web now uses pool for VQD token rotation.
LLM7.io now uses pool with 2s cooldown for its 1 req/s rate limit.
2026-05-31 07:05:21 -03:00
diegosouzapw
eb7348aeb9 fix(resilience): route-restriction 403 must not mark a connection unavailable (#2929)
Fireworks Fire Pass (fpk_*) keys return 403 "Fire Pass API keys are not
authorized for this route." on /models while still serving chat. Two paths
wrongly penalized the key:

- validateOpenAILikeProvider returned "Invalid API key" for any 403 on the
  models endpoint without trying the chat probe.
- checkFallbackError classified the 403 as AUTH_ERROR (retryable cooldown), and
  even a generic fall-through hit the transient-cooldown default — marking the
  connection unavailable.

Fix: validateOpenAILikeProvider now inspects the 403 body and falls through to
the chat probe for "not authorized for this route" responses (401 and generic
403 still fail fast). checkFallbackError short-circuits such route-restriction
403s to { shouldFallback: false, cooldownMs: 0 } so the connection is not cooled
down. Per CLAUDE.md, a generic api-key 403 should be recoverable unless terminal.

Closes #2929
2026-05-31 01:38:05 -03:00
diegosouzapw
fa4bd6c68c Merge PR 2951 into release/v3.8.8 2026-05-30 22:01:04 -03:00
diegosouzapw
4e51bc686c Merge PR 2946 into release/v3.8.8 2026-05-30 22:00:41 -03:00
diegosouzapw
1a9b2bfd85 Merge PR 2943 into release/v3.8.8 2026-05-30 22:00:38 -03:00
Diego Rodrigues de Sa e Souza
697946381d fix(auth): prevent Codex multi-account refresh_token family revocation (#2941)
Integrated into release/v3.8.8
2026-05-30 21:18:33 -03:00
terence71-glitch
187bc509bb fix(sse): bypass web-search fallback on Claude -> Claude passthrough (#2960)
Integrated into release/v3.8.8
2026-05-30 21:17:52 -03:00
soyelmismo
b4c0ce6519 fix: address Gemini Code Review feedback on PR #2951
- Add !has(key) guard before eviction to avoid evicting entries
  that are about to be updated (combo.ts, apiKeyRotator.ts,
  codexQuotaFetcher.ts)
- Use optional chaining for provider?.toUpperCase() null safety
- Replace Object.values() with for-in in estimateSizeFast hot path
2026-05-30 13:03:02 -05:00
oyi77
d698e957fb fix: combo credential resolution ignores target.providerId — prefer combo target's providerId over model-inferred provider
Root cause: model string provider prefix (e.g. "xiaomi" from
"xiaomi/mimo-v2-flash") differs from the credential DB provider ID
(e.g. "opengate") when a combo target has a custom providerId. The
pre-selection and execution flows both looked up credentials using the
model-inferred provider, which didn't match any DB entry.

Fix A (chat.ts): checkModelAvailable now uses target.providerId when
available, falling back to modelInfo.provider.

Fix B (chat.ts): handleSingleModelChat now accepts runtimeOptions.providerId
and preferentially uses it for credential lookup instead of re-resolving
the provider from the model string.

Fix C (model.ts): add "xiaomi" alias for "xiaomi-mimo" so direct
(non-combo) model requests to xiaomi/mimo-* also resolve correctly.
2026-05-30 22:21:29 +07:00
NomenAK
a1e0bc7469 fix(claude): harden tool cloak + schema sanitizer (adversarial review round)
Addresses confirmed findings from an adversarial review of the prior commits:

- schema sanitizer: a truncation placeholder in a SCALAR annotation keyword
  (description/title/pattern/format) was coerced to {}, which is itself invalid
  draft-2020-12 and re-triggered the exact "input_schema is invalid" 400 the
  sanitizer exists to prevent. Placeholders are now only coerced to {} in
  subschema-expecting positions; scalar keywords are left untouched.
- schema sanitizer: numeric-string coercion is folded into
  stripInvalidSchemaConstructs so it also covers contains / propertyNames /
  additionalItems (which coerceSchemaNumericFields never visited).
- schema sanitizer: stop stripping the valid `default` keyword on the Claude
  native/passthrough surface (the #1782 default-strip is a translator concern;
  tool schemas here were previously forwarded verbatim). sanitizeClaudeToolSchema
  is now a single stripInvalidSchemaConstructs pass.
- tool-name cloak: consult TOOL_RENAME_MAP / EXTRA_TOOL_RENAME_MAP before the
  generic PascalCase fallback, so the CLIProxyAPI path uses the established
  fingerprint-evasion aliases (subagents->SubDispatch, session_status->CheckStatus,
  webfetch->WebFetch, ...) identically to the native path instead of weaker
  first-letter casing.
- kill-switch: CLAUDE_DISABLE_TOOL_NAME_CLOAK is now honoured inside
  cloakThirdPartyToolNames, so BOTH the native and CLIProxyAPI executor paths
  respect it (previously only base.ts did); .env.example + ENVIRONMENT.md updated.

Regression tests added for each. Verified end-to-end through the live CPA path:
mixture_of_agents, subagents, and a tool carrying placeholder descriptions and
`default` values all return 200 with original names restored on the response.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30 14:35:53 +00:00
NomenAK
23dad7d93d fix(claude): apply tool cloak + schema sanitize on the CLIProxyAPI executor path
The native Claude OAuth guard in executors/base.ts is bypassed when
`upstream_proxy_config.mode = cliproxyapi` routes the request through the
CliproxyAPI executor — it has its own execute()/transformRequest() and never
reaches BaseExecutor.execute(), so the cloak/sanitizer never ran for that
(common) deployment. Wire the same guards into
CliproxyapiExecutor.transformRequest (Anthropic-shape branch), composing with
the existing bisected `mcp_*` reserved-namespace rewrite:

- sanitizeClaudeToolSchemas() on transformed.tools.
- cloakThirdPartyToolNames() with skip = mcp-reserved, so applyMcpToolNameRewrite
  keeps authority over `mcp_*` (its bisected `Mcp_X` form) and the two reverse
  maps stay disjoint / single-hop. Both merge into the non-enumerable
  _toolNameMap the response stream already uses to restore the caller's names.

cloakThirdPartyToolNames is now non-mutating (clones changed entries) to respect
transformRequest's no-input-mutation contract, and takes an optional `skip`
predicate.

Verified end-to-end through the live CPA path: a real ~100-tool harness payload
that returned the "out of extra usage" placeholder now returns 200 with original
tool names restored on the response stream; `mcp_*` tools and genuine PascalCase
Claude Code tools are unaffected.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30 13:59:36 +00:00
NomenAK
3b2d075402 fix(claude): address tool-cloak PR review — preserve boolean schemas, null-guards, docs
Follow-up commit on PR #2943 review:

- Preserve boolean schemas in `sanitizeClaudeToolSchemas` (Gemini Code Assist,
  high severity). `additionalProperties: false` is the canonical JSON Schema
  lock-down for object tools; the previous coercion silently turned it into the
  permissive `{}`, which would invite models to hallucinate extra arguments
  during tool calling. Same rule now applies to per-property boolean schemas
  under `properties`. Placeholder strings still get the permissive `{}` slot —
  booleans get preserved verbatim.

- Defensive null guards in `cloakThirdPartyToolNames` for `tools[]` and
  `messages[]` entries that might be `null`/`undefined`. Prevents a runtime
  `TypeError` if a malformed payload reaches the cloak.

- Document `CLAUDE_DISABLE_TOOL_NAME_CLOAK` in `.env.example` and
  `docs/reference/ENVIRONMENT.md` (env/docs contract was failing in CI).

- Regression tests covering all of the above (5 boolean preservation cases,
  2 null-tolerance cases).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30 13:30:04 +00:00
NomenAK
7e7faad079 fix(claude): sanitize tool schemas + cloak third-party tool names on native Claude OAuth
Native Claude OAuth (claude->claude passthrough) forwards client tool
definitions verbatim. Anthropic's first-party Messages API then rejects:
  - invalid tool input_schemas (deep-truncation placeholders such as
    `enum: "[MaxDepth]"`, or index-keyed objects where arrays are required), and
  - tool names it fingerprints as a third-party agent harness (specific
    blacklisted names like `mixture_of_agents`, or a large enough set of
    recognizable snake_case agent tool names),
both surfaced as a misleading `400 You're out of extra usage` placeholder
(the SSE stream is refused — not a real billing event). The same request
succeeds on translator-backed providers (OpenAI/Codex), which already sanitize
and re-shape tool payloads — so the gap is specific to the native passthrough.

Adds the missing guards on the native Claude OAuth path (executors/base.ts):
  - sanitizeClaudeToolSchemas(): coerce/drop invalid draft-2020-12 constructs
    (non-array enum/required/anyOf/..., placeholder schema slots -> {}).
  - cloakThirdPartyToolNames(): deterministically alias non-Claude-Code tool
    names (Claude Code canonical mapping where one exists, else PascalCase),
    tracked in the existing per-request _toolNameMap so remapToolNamesInResponse
    restores the caller's original names. Opt out via
    CLAUDE_DISABLE_TOOL_NAME_CLOAK=true.

Genuine Claude Code tool names (PascalCase) and already-valid schemas are
left untouched, so existing first-party traffic is unaffected.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30 12:56:35 +00:00
soyelmismo
c6f17d8e78 fix: resolve CPU leak from Bottleneck limiter accumulation and unbounded in-memory caches
Root cause: Bottleneck rate limiter instances in rateLimitManager accumulate
without cleanup. Each instance runs an internal heartbeat setInterval every
250ms. Under heavy load with many provider:connection:model combinations,
hundreds of limiters accumulate causing CPU to grow ~0.1%/min until server
collapse (~2% after 5 minutes of intensive use).

Changes:
- rateLimitManager: Add idle limiter eviction in watchdogTick() using the
  previously defined but unused INACTIVE_LIMITER_MS threshold. Populate
  limiterLastUsed on every getLimiter() call. Clean up all 3 Maps
  (limiters, lastDispatchAt, limiterLastUsed) consistently.
- combo.ts: Add size-based FIFO eviction to rrCounters, resetAwareConnectionCache,
  and resetAwareQuotaCache Maps. Convert per-target log.info calls in combo
  execution loops to log.debug?. to reduce serialization overhead.
- chatCore.ts: Fix double-serialization in estimateTokens(JSON.stringify(x))
  calls (estimateTokens already handles objects). Make trace() conditional
  on OMNIRROUTE_TRACE/DEBUG env vars. Make per-request usage logging conditional.
- apiKeyRotator.ts: Add eviction guards to _keyHealth and _connectionExtraKeys
  Maps (MAX 500 entries each). Ensure removeConnectionIndex cleans all 3 Maps.
- codexQuotaFetcher.ts: Add eviction guard to connectionRegistry and quotaCache
  Maps (MAX 200 entries each).
2026-05-30 07:28:16 -05:00
diegosouzapw
1b0d3c75e9 Merge release/v3.8.8 into refactor/pages-v3-B (Monitoring+Costs+Quota) + fix transform bug
Conflicts: migrations 073/074/075->077/078/079 (collision w/ base 073-076); localDb union (quota + tokenLimits/plugins re-exports); i18n en/pt-BR deep-merge (base translations win over __MISSING__ placeholders; sidebar.costsSection=Custos); CLAUDE.md keeps base coverage rule (>=40%).

Pre-existing #2859 bugs surfaced by validation & fixed: chatCore onStreamComplete 'await import'->'import().then()' (await was outside async fn, broke tsx transform of 17 chat-* test files); documented 5 QUOTA_* env vars in ENVIRONMENT.md to fix env-doc-sync drift.
2026-05-30 02:02:38 -03:00
Diego Rodrigues de Sa e Souza
191009dd23 Release v3.8.7 (#2919)
* feat(plugins): WordPress-style plugin system backend

* fix(plugins): address code review feedback

- Path traversal guard: validate entryPoint stays within plugin dir
- install() now handles direct plugin directories (not just parent dirs)
- Non-null assertion replaced with explicit null check
- require efficiency: allowedModules map moved outside function
- Source wrapper: add newlines to prevent trailing comment issues
- Config validation: validate values against configSchema on save
- Dynamic import comment: clarify Node.js caching behavior

Co-Authored-By: OpenClaude (mimo-v2.5-pro) <openclaude@gitlawb.com>

* fix(plugins): replace vm with child_process, add auth to all routes

Addresses all remaining code review feedback:

1. **Loader rewrite**: Replaced Node.js vm module with child_process.fork()
   for proper process-level isolation. Complies with Rule 3 (no eval).
   Each plugin runs in a separate Node.js process with IPC communication.

2. **Auth on all routes**: Added requireManagementAuth to all 6 plugin
   API route files (list, install, scan, details, activate, deactivate, config).

3. **Env filtering**: Only safe env vars passed to plugin processes unless
   "env" permission is granted.

Co-Authored-By: OpenClaude (mimo-v2.5-pro) <openclaude@gitlawb.com>

* fix(plugins): security + ESM fixes for loader and manager

loader.ts:
- Fix IPC: use process.send()/process.on("message") instead of worker_threads.parentPort
- Fix ESM: write host script as .mjs (not .js) to force ESM execution
- Add timeout: 10s default on callHook() with Promise.race
- Add SIGKILL escalation: SIGTERM first, then SIGKILL after 3s grace
- Fix env filtering: use allowlist (safeKeys) instead of passing all env vars
- Clear timeout on successful IPC response (no timer leak)

manager.ts:
- Fix path traversal: use fs.realpath() instead of startsWith()
- Fix imports: use registerHook/unregisterHooks from hooks.ts
- Register hooks individually via registerHook(event, name, handler)

hooks.ts:
- Copied from feat/plugin-custom-hooks (canonical registry)

* feat(discovery): add discovery tool stub service

Phase 1 scaffold for automated provider discovery:
- DiscoveryConfig, DiscoveryResult types
- probeEndpoint() for URL availability checking
- scanProvider() stub (Phase 2 will implement real scanning)
- getDiscoveryResults() stub
- Default config: disabled (opt-in)

* chore(plugins): slop cleanup — pino logger, remove redundant sorts

- index.ts: replace console.log/error with pino structured logging
- hooks.ts: remove redundant .sort() in emitHookBlocking/runOnResponse (already sorted on registration)
- manager.ts: add readFile import

* test(plugins): add scanner, loader, manager unit tests

- scanner: 9 tests (discovery, hidden dirs, validation, entry point, multiple)
- loader: 5 tests (type contracts, Plugin/PluginContext/PluginResult interfaces)
- manager: 6 tests (singleton, lifecycle methods, error on unknown)
- Total: 20 tests, all passing

* fix(settings): add missing home page pin keys to updateSettingsSchema

* feat(plugins): add i18n keys to all 42 locales

* fix(settings): add missing security keys to updateSettingsSchema and add tests

* fix(usage): analytics route reads combo_name/requested_model from call_logs only

The 3.8.6 variant of #2904 added SELECTs of combo_name/requested_model
against usage_history, but those columns only exist in call_logs (no
migration adds them to usage_history). This returned HTTP 500 on
/api/usage/analytics. Restore the working query shape from the 3.8.7
variant. Fixes 18 failing usage-analytics-route tests.

* fix(types,test): resolve noImplicitAny in progressiveAging + align semaphore test to #2903 gate pruning

- progressiveAging: type compression results so messages[0].content is
  indexable (was TS7053 against {}); restores typecheck:noimplicit:core gate.
- services-branch-hardening: #2903 (perf-ram) prunes idle rate-limit gates
  on zero; assert no-running/empty-queue without assuming the entry persists.

* fix(analytics): address merged review regressions

* fix(executor): normalize max effort for openai shape providers

* Make zero-latency combo optimizations opt-in

* Address zero-latency combo review feedback

* chore(release): sync v3.8.7 touchpoints + credit contributors

- llm.txt → 3.8.7 (Current version + Key Features header)
- CHANGELOG: add Dmitry Kuznetsov & Nikolay Alafuzov to 3.8.6 Hall of Contributors
- version already 3.8.7 across package.json/open-sse/electron/openapi (from #2909)

* fix(cleanup): restore usage history cutoff boundary

* docs(changelog): rank 3.8.6 contributors in a commits table with their PRs

* fix(dashboard): theme ReactFlow Controls +/- buttons for dark mode

* fix(settings): add missing home page pin keys to updateSettingsSchema

* fix(settings): add missing security keys to updateSettingsSchema and add tests

* fix(executor): normalize max effort for openai shape providers

* Make zero-latency combo optimizations opt-in

* Address zero-latency combo review feedback

* fix(analytics): address merged review regressions

* fix(cleanup): restore usage history cutoff boundary

* feat(plugins): WordPress-style plugin system backend

* fix(plugins): address code review feedback

- Path traversal guard: validate entryPoint stays within plugin dir
- install() now handles direct plugin directories (not just parent dirs)
- Non-null assertion replaced with explicit null check
- require efficiency: allowedModules map moved outside function
- Source wrapper: add newlines to prevent trailing comment issues
- Config validation: validate values against configSchema on save
- Dynamic import comment: clarify Node.js caching behavior

Co-Authored-By: OpenClaude (mimo-v2.5-pro) <openclaude@gitlawb.com>

* fix(plugins): replace vm with child_process, add auth to all routes

Addresses all remaining code review feedback:

1. **Loader rewrite**: Replaced Node.js vm module with child_process.fork()
   for proper process-level isolation. Complies with Rule 3 (no eval).
   Each plugin runs in a separate Node.js process with IPC communication.

2. **Auth on all routes**: Added requireManagementAuth to all 6 plugin
   API route files (list, install, scan, details, activate, deactivate, config).

3. **Env filtering**: Only safe env vars passed to plugin processes unless
   "env" permission is granted.

Co-Authored-By: OpenClaude (mimo-v2.5-pro) <openclaude@gitlawb.com>

* fix(plugins): security + ESM fixes for loader and manager

loader.ts:
- Fix IPC: use process.send()/process.on("message") instead of worker_threads.parentPort
- Fix ESM: write host script as .mjs (not .js) to force ESM execution
- Add timeout: 10s default on callHook() with Promise.race
- Add SIGKILL escalation: SIGTERM first, then SIGKILL after 3s grace
- Fix env filtering: use allowlist (safeKeys) instead of passing all env vars
- Clear timeout on successful IPC response (no timer leak)

manager.ts:
- Fix path traversal: use fs.realpath() instead of startsWith()
- Fix imports: use registerHook/unregisterHooks from hooks.ts
- Register hooks individually via registerHook(event, name, handler)

hooks.ts:
- Copied from feat/plugin-custom-hooks (canonical registry)

* feat(discovery): add discovery tool stub service

Phase 1 scaffold for automated provider discovery:
- DiscoveryConfig, DiscoveryResult types
- probeEndpoint() for URL availability checking
- scanProvider() stub (Phase 2 will implement real scanning)
- getDiscoveryResults() stub
- Default config: disabled (opt-in)

* chore(plugins): slop cleanup — pino logger, remove redundant sorts

- index.ts: replace console.log/error with pino structured logging
- hooks.ts: remove redundant .sort() in emitHookBlocking/runOnResponse (already sorted on registration)
- manager.ts: add readFile import

* test(plugins): add scanner, loader, manager unit tests

- scanner: 9 tests (discovery, hidden dirs, validation, entry point, multiple)
- loader: 5 tests (type contracts, Plugin/PluginContext/PluginResult interfaces)
- manager: 6 tests (singleton, lifecycle methods, error on unknown)
- Total: 20 tests, all passing

* feat(plugins): add i18n keys to all 42 locales

* chore(plugins): remove duplicate migration 059_create_plugins.sql

* chore(plugins): remove duplicate migration 059_create_plugins.sql (post-merge)

* fix(sse): guard non-string error.code in proxyFetch + harden model parsing (#2463) (#2923)

Integrated into release/v3.8.7

* fix(docker): add runner-web stage with Playwright Chromium (#2832) (#2846)

Integrated into release/v3.8.7

* docs(changelog): document NVIDIA NIM and error code type-crash fix (#2463)

* test: ignore NVIDIA_BASE_URL and NVIDIA_MODEL in env contract check

---------

Co-authored-by: oyi77 <oyi77@users.noreply.github.com>
Co-authored-by: OpenClaude (mimo-v2.5-pro) <openclaude@gitlawb.com>
Co-authored-by: Apostol Apostolov <theapoapostolov@gmail.com>
Co-authored-by: Halil Tezcan KARABULUT <info@hlltzcnkb.com>
Co-authored-by: R.D. <rogerproself@gmail.com>
2026-05-29 19:54:00 -03:00
mi
94682d2a76 perf(ram): reduce server memory footprint (#2903)
Integrated into release/v3.8.7
2026-05-29 13:21:36 -03:00
Diego Rodrigues de Sa e Souza
f59f8daa94 Release v3.8.6 (#2804)
* fix(gemini): preserve structured tool calls for antigravity

* fix(gemini): parse prefixed textual tool calls

* fix(antigravity): preserve textual SSE tool calls

* fix(stream): normalize textual passthrough tool calls

* fix(stream): normalize split textual tool calls

* fix(stream): suppress malformed textual tool calls

* fix(stream): suppress compact malformed tool calls

* fix(stream): emit structured textual tool calls

* fix(stream): suppress unknown textual tool calls

* fix(stream): normalize responses textual tool calls

* chore: ignore .claude/settings.local.json (per-user Claude Code permissions)

* fix(opencode-go): route qwen3.x via claude messages + repair fixMissingToolResponses for Claude-shape upstreams (#2791)

Integrated into release/v3.8.6

* fix: resolve npm install warnings — remove dead deps, relax engine constraint (#2792)

Integrated into release/v3.8.6

* fix: register missing web-cookie validators (claude-web, gemini-web, copilot-web, t3-web) (#2793)

Integrated into release/v3.8.6

* fix: Error: Unable to inspect existing database #2771 (#2795)

Integrated into release/v3.8.6

* fix(oauth): repair Google loopback callback flow (#2796)

Integrated into release/v3.8.6

* feat(logs): add clean history button (#2799)

Integrated into release/v3.8.6

* [codex] home: restore settings-driven home layout and quota auto-refresh (#2800)

Integrated into release/v3.8.6

* fix(gemini): emit signaturelessToolCallMode:text for GEMINI format models (#2801)

Integrated into release/v3.8.6

* feat(modelSpecs): align opencode-go family with upstream provider limits (#2802)

Integrated into release/v3.8.6

* chore: apply unit test fixes, polyfills, and environment precedence fixes

* docs(agents): atualiza fluxos de release e triagem

Expande os workflows de release para incluir auditoria de segurança,
CHANGELOG completo por commits, quality gate obrigatório, homologação em
VPS local, publicação oficial, deploy em Akamai e validação de artefatos.

Reorganiza a triagem de features com arquivos permanentes por bucket,
suporte a itens em andamento, regra de reclaim após 15 dias e novo
tratamento para ideias viáveis catalogadas.

Corrige a orientação de revisão de discussões para usar a ordem
cronológica real dos comentários e respostas ao identificar a última
atividade.

* fix(lockout): classify Gemini Antigravity resource exhaustion as quota_exhausted

* fix(reasoning): gate replay by interleaved field

* docs(rule-16): permit human Co-authored-by, restrict only AI/bot trailers

Rule #16 previously banned all `Co-Authored-By` trailers absolutely.
That blocked the upstream-port workflows (`/port-upstream-features` and
`/port-upstream-issues`), which must credit human upstream PR authors
and issue reporters in OmniRoute commits.

Refine the rule to ban only AI/bot-attributed trailers (Claude, GPT,
Copilot, Bot; anthropic.com / openai.com / bot-owned noreply.github.com
emails) while allowing standard human `Co-authored-by: Name <email>`
attribution.

Sync the rule across the source CLAUDE.md, the E2E shakedown doc note,
and 41 i18n translations.

* fix(gitlawb): add specialty validators for connection test — bypass /models probe

GitLawB OpenGateway API (xiaomi-mimo compatible) does not expose a /models
endpoint, causing validateOpenAILikeProvider to 404 on the initial probe
and report 'Provider validation endpoint not supported'.

Add specialty validators for both gitlawb and gitlawb-gmi that follow the
same pattern as the existing xiaomi-mimo validator: skip GET /models,
validate directly via POST /chat/completions with a minimal test message.
Any 401/403 response means an invalid key; all other responses mean auth
is OK.

Fixes test-connection returning 404 for GitLawB providers.

* test(gitlawb): add 12 unit tests for gitlawb and gitlawb-gmi specialty validators

Covers success, auth failure (401/403), non-auth acceptance (400/422/429),
network errors, and custom baseUrl overrides for both providers.

* feat(gitlawb): serve models from static registry without API-unavailable warning

GitLawB's OpenGateway API does not expose a /models endpoint per
provider-path. Previously the models route fell through to the generic
fallback which returned static catalog models with the misleading
'API unavailable — using local catalog' warning.

Now gitlawb and gitlawb-gmi are handled as static model providers
(same pattern as reka and qwen OAuth) — models are served from the
provider registry without any warning, since all registered models
are functional via POST /chat/completions.

* refactor(gitlawb): extract shared opengateway validator factory, fix docs path in test

- Extract gitlawb/gitlawb-gmi validators into buildOpengatewayValidator factory
- Fix dockerignore-docs-coverage test: update stale docs/AUTO-COMBO.md -> docs/routing/AUTO-COMBO.md

* fix(reasoning): guard interleaved capability lookup

* feat(gitlawb): dynamic model fetch with gmi-cloud fallback

Hybrid approach:
- gitlawb (xiaomi-mimo): dynamic /models endpoint → 356 models
- gitlawb-gmi (gmi-cloud): 404 fallback → local catalog gracefully
Mimics Gitlawb/openclaude's model-routing pattern

* i18n(pt-BR): complete missing translations and sync with en.json

* feat(build): nix multi-OS package manager install (#2806)

Integrated into release/v3.8.6

* fix(i18n): translate 144 new __MISSING__ pt-BR strings (#2816)

Integrated into release/v3.8.6

* chore(docs): set coverage gate to 40/40/40/40 in CLAUDE.md

Aligns the documented coverage gate with the v3.8.6 release decision
(lowered from 75/75/75/70). Matches the threshold already set in
package.json by the large feature PRs (planos 11-22).

* fix(cli): respect PORT env var in serve command (#2845)

Integrated into release/v3.8.6.

* fix(deepseek-web): return 400 when client sends tools[] - chat.deepseek.com has no tool support (#2854)

Integrated into release/v3.8.6.

* fix(qoder): reject invalid/expired PATs returning Cosy 500 error (#2860)

Integrated into release/v3.8.6.

* fix(cli): register openclaw in tool-detector (#2833) (#2850)

Integrated into release/v3.8.6.

* fix(api): include noAuth providers in /v1/models catalog (#2798) (#2814)

Integrated into release/v3.8.6.

* fix(combo): resolve custom provider targets via combo name (#2778) (#2812)

Integrated into release/v3.8.6.

* fix(translator): strip safety_identifier in openai-responses cleanup (#2770) (#2809)

Integrated into release/v3.8.6.

* fix(quota): honor explicit per-connection preflight opt-out (#2831) (#2844)

Integrated into release/v3.8.6.

* fix(usage): un-invert GitHub Copilot Free/limited quota — limited_user_quotas is remaining (#2876) (#2881)

Integrated into release/v3.8.6.

* fix(nous-research): correct baseUrl to include /chat/completions (#2826) (#2835)

Integrated into release/v3.8.6.

* fix(opencode): qwen3.x max/plus models lack vision support (#2822) (#2836)

Integrated into release/v3.8.6.

* fix(translator): pass-through tool_search built-in tool type (#2766) (#2811)

Integrated into release/v3.8.6.

* fix(github): route claude-opus-4.6 via chat completions (#2821)

Integrated into release/v3.8.6.

* docs(oauth): add Windsurf login fix design (Phase 1 hotfix + Phase 2 Firebase OAuth)

Two-phase plan to fix the broken Windsurf OAuth flow:
- Phase 1: drop the dead app.devin.ai/editor/signin PKCE path, promote
  import-token from windsurf.com/show-auth-token as the primary path
- Phase 2: port Firebase OAuth + RegisterUser flow from
  fendoushaonian/WindSurf-gRPC-API for full browser-based automation

Spec only - no code changes yet.

* docs(plan): Phase 1 windsurf login hotfix implementation plan

10 tasks covering:
- TDD assertions for flowType + 410 Gone responses
- Provider switch to import_token
- Route handler retiring authorize/start-callback-server/poll-callback
- OAuthModal UI override
- i18n sync
- Verification + PR steps

* fix(cli): replace cli-table3 with hand-rolled formatter (#2752) (#2813)

Integrated into release/v3.8.6.

* fix(skills): skip interception for unregistered client-native tools (#2815) (#2817)

Integrated into release/v3.8.6.

* feat(sse): add RTK filters for kubectl, docker-build, composer, gh (#2824)

Integrated into release/v3.8.6.

* fix(geminiHelper): support rec.image content shape + warn on dropped remote URLs (refs #2807) (#2855)

Integrated into release/v3.8.6.

* fix(cli): allow nullable/optional apiKey in cliMitmStartSchema (#2857)

Integrated into release/v3.8.6.

* fix(combo): preserve system messages during context handoff summary generation (#2865)

Integrated into release/v3.8.6.

* fix: wire CLIProxyAPI fallback settings into chatCore routing engine (#2866)

Integrated into release/v3.8.6.

* fix(usage): add opencode quota fetcher (#2852) (#2867)

Integrated into release/v3.8.6.

* feat(claude): default xhigh support for newer Opus models (#2874)

Integrated into release/v3.8.6.

* fix(cli): restore omniroute logs command stream (#2756) (#2810)

Integrated into release/v3.8.6.

* fix(combo): normalize upstream Headers for Node 24 undici interop (#2751) (#2823)

Integrated into release/v3.8.6.

* Rename proxy log Public IP to Client IP (#2880)

Integrated into release/v3.8.6.

* fix(claude): preserve max effort for supported models (#2875)

Integrated into release/v3.8.6.

* fix(oauth): switch windsurf provider to import_token flow

The PKCE auth URL targeting app.devin.ai/editor/signin returns 404
post-rebrand. Until Phase 2 ports Firebase OAuth + RegisterUser, the
only supported path is import-token via windsurf.com/show-auth-token.

- windsurf.ts: drop buildAuthUrl, set flowType=import_token
- generateAuthData returns supported:false + helpful error for windsurf/devin-cli
- tests: assert flowType + disabled stub

* fix(oauth): return 410 Gone for retired windsurf/devin-cli PKCE actions

start-callback-server, authorize, and poll-callback (GET + POST) now
return 410 Gone with a pointer to /import-token. The 410 short-circuit
runs before auth so the response is honest about the action being
permanently gone, not gated. Codex PKCE flow unchanged.

Tests: 5 new assertions cover GET + POST 410 paths and a Codex
regression check.

* refactor(oauth): annotate retired PKCE fields in WINDSURF_CONFIG

No behaviour change - comment-only update documenting that authorizeUrl,
codeChallengeMethod, callbackPort, callbackPath, apiServerUrl, and
exchangePath are no longer consumed. Active fields (inferenceUrl,
showAuthTokenUrl, firebaseApiKey, ideName) called out separately.

* fix(cli,docs): use requireCliToolsAuth in logs route + document OPENCODE quota env

Post-merge contract fixes for v3.8.6:
- src/app/api/cli-tools/logs/route.ts (#2810) now uses the shared
  requireCliToolsAuth guard (param renamed req->request) to satisfy the
  cli-tools-auth-hardening contract test.
- Document OMNIROUTE_OPENCODE_QUOTA_URL (#2867) in docs/reference/ENVIRONMENT.md
  to satisfy the env/docs sync contract.

* fix(dashboard): force import-token panel for windsurf/devin-cli

Phase 1 hotfix: hide the 'Browser Login' tab and start in Paste API Key
mode. Removes windsurf/devin-cli from PKCE_CALLBACK_SERVER_PROVIDERS so
no callback server is started for them. Codex still uses the PKCE flow.

The 'Get token' link continues to point at windsurf.com/show-auth-token
via the existing supportsTokenPaste form copy.

* fix(oauth): windsurf import-token mapTokens signature mismatch

The route at `src/app/api/oauth/[provider]/[action]/route.ts` invokes
`providerData.mapTokens({ accessToken: token })` (object), matching the
cursor/kiro signature. The windsurf provider was declared with
`mapTokens(token: string)` instead, so the entire object was stored as
`accessToken`. When the connection record reached the SQLite layer it
crashed with:

  SQLite3 can only bind numbers, strings, bigints, buffers, and null

Fix by aligning windsurf's `mapTokens` signature with the route caller
and the cursor/kiro convention. Also dedupe a copy-pasted second
`if (action === "import-token")` block in the route handler — the
second block was unreachable but identical to the first.

Adds two regression tests asserting that
`provider.mapTokens({ accessToken })` returns a string `accessToken` for
both windsurf and devin-cli, so a future signature drift trips the gate
instead of the SQLite bind error in production.

* feat(compression): expand pt-BR pack with troglodita rules (15 → 49) (#2818)

Integrated into release/v3.8.6

* fix(sse): repair RTK engine defaults so dedup and direct calls work (#2825)

Integrated into release/v3.8.6

* fix(mcp): redirect console.log/warn to stderr in --mcp stdio mode (#2840)

Integrated into release/v3.8.6

* fix(gemini-cli): prefer real project IDs over default-project (#2841)

Integrated into release/v3.8.6

* fix(opencode-go): add provider limits quota fetcher (#2861)

Integrated into release/v3.8.6

* Audit & add web cookie providers: fix 4 missing registry entries + DuckDuckGo (#2862)

Integrated into release/v3.8.6

* fix(antigravity): harden signatureless tool history (#2878)

Integrated into release/v3.8.6

* fix: provider model sync pruning and dynamic antigravity MITM proxy mappings (#2886)

Integrated into release/v3.8.6

* feat(usage): per-API-key token limits scoped to model/provider/global (#2888)

Integrated into release/v3.8.6

* fix(audio): build multipart body manually to preserve Content-Type (#2842)

Integrated into release/v3.8.6

* refactor: remove agent skill documentation files and streamline maintenance workflows

* test(stabilization): resolve unit test failures in blackbox-web, schema-coercion, translator-helper-branches, usage-service-hardening, and audio-transcription

* fix(security): mitigate Socket.dev supply-chain findings + secrets opt-in + minimal build profile (#2863) (#2871)

Two real security gaps closed and four cosmetic Socket.dev fingerprints removed.
See docs/security/SOCKET_DEV_FINDINGS.md for the per-finding maintainer
attestation.

Real bugs fixed:
- cloudSync: HMAC verification of `X-Cloud-Sig` + opt-in
  `OMNIROUTE_CLOUD_SYNC_SECRETS=true` before overwriting `accessToken` /
  `refreshToken` / `providerSpecificData` from a remote response. Closes the
  silent-credential-swap surface (a misconfigured or hostile CLOUD_URL could
  previously replace local tokens unverified).
- Zed import: split into 2-step `/discover` + `/import` flow. `/import` now
  requires `confirmedAccounts: [{ service, account, fingerprint }]` and
  re-reads the keychain server-side to filter by fingerprint, so a tampered
  discover response cannot trick the endpoint into saving an unrelated token.

Cosmetic Socket.dev mitigations:
- runElevatedPowerShell writes the elevated payload to a per-call temp `.ps1`
  file (mode 0o600) and references it via `-File`. Removes the textbook
  `-EncodedCommand <base64utf16le>` pattern flagged as malware by Socket's AI
  classifier.
- Maintainer attestation `SECURITY-AUDITOR-NOTE:` blocks added at every
  flagged call site pointing to `docs/security/SOCKET_DEV_FINDINGS.md`.

Build-time hardening:
- `OMNIROUTE_BUILD_PROFILE=minimal` (`npm run build:secure`) physically
  removes the four sensitive modules from the standalone bundle via webpack
  `NormalModuleReplacementPlugin`. Stubs throw `FeatureDisabledError` at
  runtime. Intended for the `omniroute-secure` artifact.

Tests:
- 24 new unit tests in `tests/unit/security/` covering the wrapper builder,
  HMAC verification (4 cases), credential fingerprint determinism (5 cases),
  confirmedAccounts validation + fingerprint filtering (6 cases), and the
  minimal-build stubs (5 cases).

Docs:
- New `docs/security/SOCKET_DEV_FINDINGS.md` — per-finding attestation.
- New `socket.yml` — Socket.dev v2 config pointing at the attestation.
- Updated `SECURITY.md` — supply-chain scanner section.
- Updated `.env.example` — three new env vars documented.

Backwards compatibility:
- Cloud sync token overwrite is OFF by default. Users who relied on
  it must set `OMNIROUTE_CLOUD_SYNC_SECRETS=true`. Breaking change documented
  in CHANGELOG.
- Zed import 2-step is the new default; legacy 1-step preserved behind
  `OMNIROUTE_ZED_IMPORT_LEGACY_ONE_STEP=true` and will be removed in v3.9.

Closes #2863

* fix(security): redact public Firebase Web key from windsurf spec; doc SHA-256 cache-key rationale (#2894)

Two security-scanning findings on release/v3.8.6:

- Secret-scanning alert 7 (google_api_key): the windsurf login-fix design spec
  embedded the literal public Firebase Web API key on two lines. Firebase Web
  API keys are non-sensitive by design (they identify the project; access is
  gated by Firebase Security Rules + key restrictions), but the literal trips
  secret scanning. Redacted to a placeholder; the embedded default still goes
  through resolvePublicCred per rule #11.

- Code-scanning alert 261 (js/insufficient-password-hash): tokenCacheKey() uses
  SHA-256 to derive an in-memory cache key from the session token, not for
  password-at-rest storage. Added a comment documenting why CWE-916 KDFs do not
  apply (false positive).

* fix(ci): resolve release/v3.8.6 gate failures (docs-sync, any-budget, pack-artifact) (#2895)

* fix(ci): resolve release/v3.8.6 gate failures (docs-sync, any-budget, pack-artifact)

Three CI gates failed on release/v3.8.6 (run 26630300877):

- docs-sync: CHANGELOG had a spurious "## [3.8.6-patch]" section above
  "## [3.8.6]", so the latest release no longer matched package.json (3.8.6)
  and the 41 i18n CHANGELOG mirrors were flagged as missing that section.
  Fold the lone #2752 entry into [3.8.6] and drop the patch heading.
- any-budget:t11: open-sse/handlers/chatCore.ts regressed to 1 explicit `any`
  (budget 0). Type the persist callback arg as Record<string, unknown>, which
  matches runWithOnPersist's RefreshPersistFn contract exactly.
- pack-artifact: open-sse/utils/setupPolyfill.ts ships via package.json "files"
  (bin/omniroute.mjs imports it at startup) but was missing from the pack
  policy allowlist. Allow it and add a regression test.

* fix(security): redact public Firebase Web key from windsurf spec

Redact the literal public Firebase Web API key (secret-scanning #7) to a
placeholder, mirroring the redaction on release/v3.8.6 (PR #2894) and the
windsurf fix branch. Non-sensitive public Web key; trips secret scanning.

* feat(combo): Zero-Latency Combos (Hedging, Proactive Compression, Predictive TTFT) (#2868)

* feat(combo): implement zero-latency combo optimizations (hedging, proactive compression, predictive TTFT)

* fix(combo): fix predictive TTFT skip logic and unhandled promise rejections

---------

Co-authored-by: Automation <automation@omniroute>

* feat: implement automated skill workflows and update system configuration and validation schemas

* test: eliminate dynamic cast warnings in cloud-sync unit test

* test: isolate services-branch-hardening database directory to avoid concurrency issues

* feat(providers): add 7 new web-cookie providers + research catalog + discovery tool

New providers:
- huggingchat: free LLM chat via huggingface.co/chat (no subscription)
- phind: free dev-focused AI chat via phind.com/api/agent
- poe-web: multi-model chat via poe.com GraphQL (p-b cookie)
- venice-web: privacy-focused AI chat via venice.ai (session cookie)
- v0-vercel-web: Vercel v0 code gen via v0.dev (session cookie)
- kimi-web: Moonshot Kimi chat via kimi.moonshot.cn (session cookie)
- doubao-web: ByteDance Doubao chat via doubao.com (session cookie)

Additional:
- Research catalog: docs/research/UNLIMITED_LLM_ACCESS.md
- Discovery tool design + stub: src/lib/discovery/ + migration 073
- Unit tests: 33 tests for all 7 providers
- Shared helpers consolidated in error.ts (slop cleanup)
- All registered in WEB_COOKIE_PROVIDERS + providerRegistry + webSessionCredentials

Closes #2885

* fix(typecheck): resolve typecheck errors in combo spec and compression modules

* feat(api,oauth): add `agy` (Antigravity CLI) standalone provider with CLI token import (#2899)

Add a standalone OAuth provider `agy` (Antigravity CLI) next to gemini-cli/antigravity.
It reuses the antigravity inference backend (identical Google client_id +
daily-cloudcode-pa.googleapis.com endpoint, executor and token-refresh) but ships its own
model catalog — including the Claude models the backend exposes (claude-opus-4-6-thinking,
claude-sonnet-4-6) — its own account pool, and four ways to connect:

- token-file import (paste/upload the agy oauth token JSON)
- auto-detect a local CLI login (~/.gemini/antigravity-cli/antigravity-oauth-token)
- browser OAuth (via the shared OAuthModal Google loopback flow)
- bulk / ZIP import

New routes: POST /api/providers/agy-auth/{import,import-bulk,zip-extract,apply-local}.
Catalog pinned from the live :fetchAvailableModels endpoint. Docs (openapi.yaml,
ENVIRONMENT.md, .env.example, CHANGELOG) updated; new unit tests for registration,
the token parser, and route auth-hardening.

* fix(security): redact public Firebase Web key from windsurf spec (#2896)

Redact the literal public Firebase Web API key (secret-scanning #7) to a
placeholder. Firebase Web API keys are non-sensitive by design but the literal
trips GitHub secret scanning. Mirrors the redaction landed on release/v3.8.6
(PR #2894). Embedded default still flows through resolvePublicCred (rule #11).

* Pr 2871 (#2897)

* fix(security): mitigate Socket.dev supply-chain findings + secrets opt-in + minimal build profile (#2863)

Two real security gaps closed and four cosmetic Socket.dev fingerprints removed.
See docs/security/SOCKET_DEV_FINDINGS.md for the per-finding maintainer
attestation.

Real bugs fixed:
- cloudSync: HMAC verification of `X-Cloud-Sig` + opt-in
  `OMNIROUTE_CLOUD_SYNC_SECRETS=true` before overwriting `accessToken` /
  `refreshToken` / `providerSpecificData` from a remote response. Closes the
  silent-credential-swap surface (a misconfigured or hostile CLOUD_URL could
  previously replace local tokens unverified).
- Zed import: split into 2-step `/discover` + `/import` flow. `/import` now
  requires `confirmedAccounts: [{ service, account, fingerprint }]` and
  re-reads the keychain server-side to filter by fingerprint, so a tampered
  discover response cannot trick the endpoint into saving an unrelated token.

Cosmetic Socket.dev mitigations:
- runElevatedPowerShell writes the elevated payload to a per-call temp `.ps1`
  file (mode 0o600) and references it via `-File`. Removes the textbook
  `-EncodedCommand <base64utf16le>` pattern flagged as malware by Socket's AI
  classifier.
- Maintainer attestation `SECURITY-AUDITOR-NOTE:` blocks added at every
  flagged call site pointing to `docs/security/SOCKET_DEV_FINDINGS.md`.

Build-time hardening:
- `OMNIROUTE_BUILD_PROFILE=minimal` (`npm run build:secure`) physically
  removes the four sensitive modules from the standalone bundle via webpack
  `NormalModuleReplacementPlugin`. Stubs throw `FeatureDisabledError` at
  runtime. Intended for the `omniroute-secure` artifact.

Tests:
- 24 new unit tests in `tests/unit/security/` covering the wrapper builder,
  HMAC verification (4 cases), credential fingerprint determinism (5 cases),
  confirmedAccounts validation + fingerprint filtering (6 cases), and the
  minimal-build stubs (5 cases).

Docs:
- New `docs/security/SOCKET_DEV_FINDINGS.md` — per-finding attestation.
- New `socket.yml` — Socket.dev v2 config pointing at the attestation.
- Updated `SECURITY.md` — supply-chain scanner section.
- Updated `.env.example` — three new env vars documented.

Backwards compatibility:
- Cloud sync token overwrite is OFF by default. Users who relied on
  it must set `OMNIROUTE_CLOUD_SYNC_SECRETS=true`. Breaking change documented
  in CHANGELOG.
- Zed import 2-step is the new default; legacy 1-step preserved behind
  `OMNIROUTE_ZED_IMPORT_LEGACY_ONE_STEP=true` and will be removed in v3.9.

Closes #2863

* feat: implement automated skill workflows and update system configuration and validation schemas

* test: eliminate dynamic cast warnings in cloud-sync unit test

* test: isolate services-branch-hardening database directory to avoid concurrency issues

* chore(docs): refresh generated docs collection index

Update the generated Fumadocs browser collection mapping to keep
documentation imports in sync with the current docs structure.

* docs: update generated browser docs collection manifest

Refresh the generated Fumadocs browser collection mapping so the docs site can resolve the current documentation files correctly.

---------

Co-authored-by: OpenClaw <openclaw@kuzhomesrv.local>
Co-authored-by: Dmitry Kuznetsov <139351986+dmitry@users.noreply.local>
Co-authored-by: KuzyaBot <kuzya@local>
Co-authored-by: JeferssonLemes <jeferssondev@gmail.com>
Co-authored-by: Paijo <14921983+oyi77@users.noreply.github.com>
Co-authored-by: Markus Hartung <mail@hartmark.se>
Co-authored-by: akarray <akarray@users.noreply.github.com>
Co-authored-by: Apostol Apostolov <theapoapostolov@gmail.com>
Co-authored-by: Hernan Javier Ardila Sanchez <hjasgr@gmail.com>
Co-authored-by: Dmitry Kuznetsov <dmitry@kuznetsov.me>
Co-authored-by: Nikolay Alafuzov <alafuzov_nn@rusklimat.ru>
Co-authored-by: oyi77 <oyi77@users.noreply.github.com>
Co-authored-by: Ronaldo Davi <alltomatos@users.noreply.github.com>
Co-authored-by: levonk <277861+levonk@users.noreply.github.com>
Co-authored-by: Lenine Júnior <lenine@engrene.com.br>
Co-authored-by: Annas Alghoffar <aag.annas@gmail.com>
Co-authored-by: Tushar Agarwal <76201310+Tushar49@users.noreply.github.com>
Co-authored-by: GreatLiu <eurasiaxz@qq.com>
Co-authored-by: yuna amelia <230527278+yunaamelia@users.noreply.github.com>
Co-authored-by: Randi <55005611+rdself@users.noreply.github.com>
Co-authored-by: Container <78986709+disonjer@users.noreply.github.com>
Co-authored-by: nickwizard <35692452+nickwizard@users.noreply.github.com>
Co-authored-by: Rajvardhan Patil <rajvardhanpatil7890@gmail.com>
Co-authored-by: Raxxoor <manker_lol@hotmail.com>
Co-authored-by: Muhammad Mugni Hadi <mugnimaestra3@gmail.com>
Co-authored-by: mi <123757457+soyelmismo@users.noreply.github.com>
Co-authored-by: Automation <automation@omniroute>
2026-05-29 12:44:29 -03:00
diegosouzapw
0de8c6aef4 feat(combo): export setCandidateQuotaSoftPenalty for soft-policy wiring (B/G2)
Add module-level _activeExecutionCandidates Map (executionKey → stepId →
candidate ref) and export setCandidateQuotaSoftPenalty(key, stepId, penalty)
so chatCore.ts can mark a candidate quotaSoftPenalty=true when enforceQuotaShare
returns deprioritize. Candidates are registered after buildAutoCandidates in
the auto strategy path and cleaned up via try/finally after handleComboChat.
The existing score *= QUOTA_SOFT_DEPRIORITIZE_FACTOR path in scoreAutoTargets
now has a live data path to set the flag.
2026-05-28 12:46:14 -03:00
diegosouzapw
891cd0b256 feat(open-sse): apply QUOTA_SOFT_DEPRIORITIZE_FACTOR in combo scoring (B/F7)
Adds exported constant QUOTA_SOFT_DEPRIORITIZE_FACTOR (default 0.7, env override).
Extends AutoProviderCandidate with optional quotaSoftPenalty?: boolean field.
scoreAutoTargets() multiplies score by the factor when quotaSoftPenalty === true,
deprioritizing over-fair-share keys under soft policy without fully blocking them.
2026-05-27 22:17:36 -03:00