Commit Graph

1717 Commits

Author SHA1 Message Date
Ronaldo Davi
23c4086a81 fix(api): raise provider apiKey cap for cookie-based web providers (#6715) (#6759) 2026-07-10 15:06:09 -03:00
Chirag Singhal
6105bc1713 feat(fusion): let judge use its own knowledge and override the panel (#6804)
The judge prompt said to write an answer 'grounded in that analysis',
implicitly capping output at the panel's union. When all panel members
miss or are collectively wrong on something, the judge should apply its
own reasoning as a full participant and override consensus, while keeping
an honesty guard against fabrication. Adds a regression test.

Co-authored-by: Chirag Singhal <chirag127@users.noreply.github.com>
2026-07-10 15:06:03 -03:00
Diego Rodrigues de Sa e Souza
63fb1cbfa6 fix(i18n): translate provider visibility/free-paid filter labels across 15 locales (#6694) (#6719) 2026-07-10 08:25:33 -03:00
Diego Rodrigues de Sa e Souza
58e2dea022 fix(resilience): release combo session-stickiness pin on a terminal/quality-rejected account (#6692) (#6733)
applySessionStickiness() gated the sticky pin only on 5h/weekly usage headroom,
which is orthogonal to account availability, so a credits_exhausted/banned/
expired/rate-limited connection (or a quality-validation-rejected 200) kept
being re-promoted forever, defeating failover for that conversation.
2026-07-10 08:24:49 -03:00
Diego Rodrigues de Sa e Souza
33786137d1 feat(oauth): accept 9router camelCase Codex export in bulk import (#6665) (#6697)
* feat(oauth): accept 9router camelCase Codex export in bulk import (#6665)

* fix(changelog): restore CHANGELOG bullets eaten by release sync

* fix(changelog): re-restore CHANGELOG bullet after further release sync

* fix(changelog): re-restore CHANGELOG bullet after further release sync

* fix(changelog): re-restore #6697 bullet after release sync (#6678 landed)

* fix(changelog): re-restore CHANGELOG bullet after further release sync

* fix(changelog): re-restore #6697 bullet after #6700 release sync

* fix(changelog): re-restore #6697 bullet after release sync

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

* fix(changelog): restore #6126 bullet eaten by ancestry merge; re-insert only #6697's own

* chore(sync): merge release tip + restore own CHANGELOG bullet

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

* chore(sync): merge release tip + restore own CHANGELOG bullet

* chore(changelog): re-sync after release merge — preserve sibling bullets

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
2026-07-10 07:28:37 -03:00
Diego Rodrigues de Sa e Souza
e310aa7e41 fix(api): close HEAD requests immediately instead of hanging (#6400) (#6608)
* fix(api): close HEAD requests immediately instead of hanging (#6400)

Next.js 16's App Router route-handler pipeline (send-response.js) already
skips piping a Response body for HEAD, but its page-rendering pipeline
(pipe-readable.js -> pipeToNodeResponse, used for every app-router page/layout
render, including the not-found boundary any unmatched path falls through to)
has no such check and always streams the full rendered body regardless of
method. Combined with Node's default keep-alive framing, this left some
clients unsure whether the (implicitly bodyless) HEAD response had actually
finished.

Add scripts/dev/head-response-guard.cjs, wired into both the dev/start custom
server (run-next.mjs) and the packaged standalone server
(standalone-server-ws.mjs) at the same tier as the existing
http-method-guard.cjs/peer-stamp.mjs wrappers: for every inbound HEAD request
it discards any body bytes the inner handler writes and forces
Connection: close once .end() is called, independent of route existence or
auth state.

Regression guard: tests/unit/head-request-closes-6400.test.ts

* chore(changelog): restore #6400 bullet before re-sync

* chore(sync): merge release tip + restore #6608 bullet

* chore(sync): merge release tip + restore #6400 bullet

* chore(changelog): re-sync after release merge — preserve #6574 rerank bullet

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
2026-07-10 07:27:32 -03:00
Diego Rodrigues de Sa e Souza
16e5b4d444 fix(providers): register openrouter rerank provider (#6574) (#6681)
* fix(providers): register openrouter rerank provider (#6574)

* fix(changelog): restore CHANGELOG bullets eaten by release sync

* fix(changelog): re-restore CHANGELOG bullet after further release sync

* fix(changelog): re-restore CHANGELOG bullet after further release sync

* fix(changelog): re-restore CHANGELOG bullet after further release sync

* fix(changelog): correct CHANGELOG restoration (previous attempt had a script-path bug)

* fix(changelog): re-restore CHANGELOG bullet after further release sync

* fix(changelog): re-restore #6681 bullet after #6700 release sync

* chore(sync): merge release tip + restore own CHANGELOG bullet

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

* chore(sync): merge release tip + restore own CHANGELOG bullet
2026-07-10 07:24:16 -03:00
Diego Rodrigues de Sa e Souza
50881c31d0 chore(release): merge-train — batch-validate queued PRs once, --admin with evidence (#6784)
Merges every queued PR into a throwaway detached worktree cut from origin/<base>,
runs the fast-gates parity suite ONCE on the final train tip, and prints the
evidence line that authorizes gh pr merge --squash --admin per member
(merge-gates.md §7). Conflicting PRs are ejected and reported, the train
continues. Never pushes, never merges PRs, never stashes.
2026-07-10 07:23:27 -03:00
Diego Rodrigues de Sa e Souza
1ddb102a90 feat(release): changelog.d/ fragments — eliminate the CHANGELOG merge-storm cascade (#6783)
* feat(release): changelog.d/ fragments — kill the CHANGELOG-eat merge-storm cascade

Every PR used to edit the same top lines of CHANGELOG.md (its bullet), so in a
merge-storm each merge conflicted every sibling (CHANGELOG-eat / DIRTY cascade),
forcing a re-sync push + full CI re-run per PR per merge — O(N^2) CI runs.

A PR now adds ONE new file under changelog.d/{features|fixes|maintenance}/ with its
bullet; two PRs never touch the same file. scripts/release/aggregate-changelog.mjs
(npm run changelog:aggregate) folds fragments into the living section and deletes
them at release reconciliation. check:changelog-integrity (already wired in the
merge-integrity CI job — zero workflow change) now also validates fragment
well-formedness. This PR dogfoods the convention: its own entry is a fragment.

* chore(changelog): fragment filename matches PR number (#6783)
2026-07-10 07:22:49 -03:00
Hamsa_M
1bc6da5318 feat(cli): add CLI tools for pi, omp, letta, codewhale and jcode (#6318)
* feat(cli): add CLI tools for pi, omp, letta, codewhale and jcode

* fix(build): resolve CI build and lint errors

* fix(cli): resolve merge conflicts, add tests, align error handling for cli-additions

Resolve duplicate codewhale key from base merge, add unit/integration
tests for omp/letta settings routes and the omp DB module, and align
omp-settings/letta-settings error handling with sanitizeErrorMessage()
+ the pattern used by sibling jcode/pi/codewhale routes in this PR.

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

* chore(quality): correct cliRuntime.ts file-size baseline to actual post-merge line count

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

* fix(changelog): re-restore #6318 bullet after release sync

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

* fix(merge): restore #6126 clinepass files reverted by release auto-resolve + baseline re-merge

The release sync's auto-resolve reverted sibling PR #6126's clinepass work
(registry, catalog, oauth constants, clineAuth.ts, token-refresh case, tests)
and the file-size baseline — all outside this PR's scope. Restored to the
release versions, re-applied only this PR's own baseline entries, restored the
#6126 CHANGELOG bullet (re-inserting only this PR's own).

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

* fix(db): re-export db/omp from localDb (check:db-rules #2)

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

* fix(db): keep localDb.ts at the 800-line cap after the omp re-export

Folded the MemoryVecMeta type re-export into the memoryVec named-export block
(inline 'type' specifier) so adding the db/omp line stays within the new-file
cap.

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

* fix(cli): reduce #6318 scope to omp + letta (pi/codewhale/jcode already shipped)

pi, codewhale, and jcode landed via a separate PR before this one was
reconciled — re-adding parallel versions of their catalog entries, routes,
dashboard card, and i18n strings would have been a straight regression
(duplicate "pi" key silently shadowing the release's own entry, orphaned
JcodeToolCard/BaseUrlSelect/ApiKeySelect/cliEndpointMatch UI files with no
release-side wiring, and unrelated formatting/refactor drift in
codewhale-settings/pi-settings/config-generator/routeGuard picked up along
the way).

This PR now ships only the two tools that are genuinely new: omp (Oh My Pi)
and letta. Both settings routes shell out to `which omp`/`which letta` to
detect the local install, so they're loopback-gated in
LOCAL_ONLY_API_PREFIXES (Hard Rules #15/#17) in addition to the shared
requireCliToolsAuth() guard every cli-tools route requires
(tests/unit/cli-tools-auth-hardening.test.ts) — neither route had the guard
wired in yet. cli-catalog-counts.test.ts is updated to the real cardinality
(8 agent entries / 32 total, since omp+letta are both category "agent";
pi/codewhale/jcode were always category "code" and are unaffected). The
integration tests for omp/letta now pass a Request object to GET/DELETE and
assert the 401-when-auth-required path, matching the pattern already used by
the codewhale/jcode sibling routes. complexity-baseline.json is back to the
release's 2053 (the #6318 rebaseline note is gone — dropping the duplicate
JcodeToolCard.tsx/BaseUrlSelect.tsx removed the violations it was covering);
file-size-baseline.json's cliTools.ts entry shrank 955->915 to match the
smaller real file. CHANGELOG bullet rewritten to describe only omp+letta,
with a note on why pi/codewhale/jcode aren't part of this PR; also restores
the Kiro External IdP bullet that a prior merge auto-resolve had dropped
from the living section.

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

* test(cli-tools): align cli-tools-schema registry count with omp+letta (30→32)

Second exact-count guard missed in the scope-reduction pass; same legitimate
alignment as cli-catalog-counts.

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

* fix(cli-tools): omp entry needs docsUrl (CliCatalogEntrySchema requires it)

https://github.com/can1357/oh-my-pi — verified official repo.

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

* chore(quality): cliTools.ts frozen 915→916 (+1 omp docsUrl line, own growth)

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

* chore(changelog): restore base + re-insert #6318 bullet after release sync

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

* chore(sync): merge release tip + restore own CHANGELOG bullet

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

---------

Co-authored-by: hamsa0x7 <hamsa0x7@users.noreply.github.com>
Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com>
Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
2026-07-10 01:27:52 -03:00
Moseyuh333
eeec4d9e87 feat(chaos): big update - optimize, fix bugs, add features, enhance UX (#6728)
* feat(chaos): add Chaos Mode — multi-model parallel/collaborative execution

- New DB column chaos_mode_enabled on api_keys table
- API key create/PATCH routes support chaosModeEnabled toggle
- Core library src/lib/chaos/chaosConfig.ts for persistent config
- API routes: GET/PUT/DELETE /api/chaos/config
- Chaos execution POST /api/skills/collect/chaos with key auth
- Dashboard page at /dashboard/chaos with full config UI
- Sidebar entry in Agentic Features section
- Chaos mode toggle in API Key editor permissions panel
- i18n keys for chaos config (en.json)

* feat(chaos): big update — optimize, fix bugs, add features

=== Changes ===

1. NEW: src/lib/chaos/chaosExecutor.ts — shared execution engine
   - Removed ~150 lines of duplicate dispatch logic between two API routes
   - Single executeChaosRun() function used by both endpoints
   - Added concurrency limit (max 10 parallel requests)
   - Added proper TypeScript interfaces (ChaosRunInput, ChaosRunResult)
   - Added error logging throughout

2. FIX: src/app/api/skills/collect/chaos/route.ts
   - Was MISSING logger import (log.error was undefined at runtime)
   - Reduced from 388 lines → 142 lines by delegating to shared executor
   - Added maxTokens support in schema validation

3. REFACTOR: src/app/api/chaos/run/route.ts
   - Simplified to thin wrapper: auth + validate + delegate to executor
   - Added maxTokens support

4. ENHANCE: src/lib/chaos/chaosConfig.ts
   - Added maxTokens config field (256-128k, default 4096)
   - Persisted per-instance via settings table

5. ENHANCE: UI — ChaosConfigPageClient.tsx
   - Loads available providers from /api/models for dropdown autocomplete
   - Added datalist-based provider selector in overrides section
   - Added Max Tokens configuration input
   - Added expandable provider list showing all detected providers
   - Fixed duplicate override detection

* fix(chaos): fetch providers from /api/providers instead of /api/keys

* fix(chaos): remove dead code isOverrideDuplicate, fix maxTokens fallback to include global config

* fix(chaos): resetConfig now shows error on HTTP failure (was silent)

* feat(dashboard): Chaos Mode — multi-model parallel/collaborative execution

Splits the PR down to only the genuinely new Chaos Mode feature (drops the
duplicate Skill Collector/GitHub-discovery portion already shipped via
#6186). Replaces the loopback fetch() dispatch (hardcoded to the wrong port)
with the established in-process synthetic-Request/route-handler pattern used
by src/lib/batches/dispatch.ts, moves settings persistence off raw SQL, and
adds unit test coverage for chaosConfig, chaosExecutor and the 3 chaos API
routes.

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

* fix(chaos): fix external Bearer-auth bypass and stale config cache in tests

validateApiKey() returns a plain boolean for both the deployment-time env key
and a DB-backed key, so branching on `keyInfo === true` in
verifyChaosKey() (src/app/api/skills/collect/chaos/route.ts) treated every
valid API key as having full env-key access, silently skipping the
chaosModeEnabled permission check entirely. Now always resolves through
getApiKeyMetadata() and only bypasses the per-key check for the synthesized
env-key record (id: "env-key").

Also exports invalidateChaosConfigCache() from chaosConfig.ts and wires it
into the route tests' resetStorage() — the in-process config cache was
surviving DB resets between tests, causing state to leak across cases.

Fixes CHANGELOG-eat from the release merge (re-inserted the Chaos Mode
bullet against the base CHANGELOG.md, verified additive via
check-changelog-integrity.mjs) and re-syncs against release/v3.8.47 tip.

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

* docs(changelog): Chaos Mode overhaul bullet referencing #6728 after release sync

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

* fix(merge): restore #6126 clinepass files reverted by release auto-resolve + baseline re-merge

The release sync's auto-resolve reverted sibling PR #6126's clinepass work
(registry, catalog, oauth constants, clineAuth.ts, token-refresh case, tests)
and the file-size baseline — all outside this PR's scope. Restored to the
release versions, re-applied only this PR's own baseline entries, restored the
#6126 CHANGELOG bullet (re-inserting only this PR's own).

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

* fix(dashboard): chaos client hook must not import the server Pino logger

useChaosConfigData ("use client") pulled @/sse/utils/logger → shared Pino →
logRotation/dataPaths → node:fs into the browser bundle, breaking next build
(Turbopack: Can't resolve 'fs') — caught by the DAST smoke's isolated build.
console.error matches every other dashboard client component.

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

* test(api-manager): align switch-count invariant with the extracted toggle components

The Self-service block now renders 4 inline switches; the #5731 quota-bypass
and #6728 chaos-access toggles were extracted into dedicated components. The
type="button" invariant is preserved AND extended: the test now also asserts
each extracted component's switches declare type="button".

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

* chore(sync): merge release tip + restore own CHANGELOG bullet

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

---------

Co-authored-by: Moseyuh333 <Moseyuh333@users.noreply.github.com>
Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com>
2026-07-09 23:49:48 -03:00
Hamsa_M
a5c555b0de feat(icons): prioritize local SVG icons over LobeHub npm for faster rendering (#6317)
* feat(icons): prioritize local SVG icons over LobeHub npm for faster rendering

* docs(changelog): add #6317 local-icons New Features bullet

---------

Co-authored-by: hamsa0x7 <hamsa0x7@users.noreply.github.com>
Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com>
2026-07-09 23:20:14 -03:00
Jan Leon
6103fd7239 fix: Stabilize live dashboard WebSocket routing (#6335)
* fix(dashboard): allow anonymous WS handshake + public /api/health/ping

The live-dashboard WebSocket descriptor handshake (GET /api/v1/ws?handshake=1)
and the lightweight GET /api/health/ping liveness probe both 401'd for
unauthenticated callers, even though both are metadata-only reads intended
to be public. clientApiPolicy required a bearer/dashboard-session before the
WS route handler could even return its own wsAuth/protocol descriptor, and
/api/health/ping was never added to PUBLIC_READONLY_API_ROUTE_PREFIXES
despite its own docstring documenting it as "No auth required".

clientApiPolicy.evaluate() now allows an anonymous
{kind:"anonymous", id:"ws-handshake"} subject for GET/HEAD/OPTIONS on
/api/v1/ws?handshake=1 — the route handler still performs its own real
wsAuth/dashboard/API-key decision before opening the socket — and
/api/health/ping is now in PUBLIC_READONLY_API_ROUTE_PREFIXES.

Re-scoped from the original PR per review-group-prs analysis: the
overlapping hardcoded /live-ws path-derivation change (useLiveDashboard.ts,
ws/route.ts) is dropped here since it conflicts with #6072's different
(dynamic, env-derived) approach to the same problem; only the
non-overlapping auth-policy win ships in this PR.

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

* chore(changelog): resync CHANGELOG.md after merging release/v3.8.47

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

---------

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com>
Co-authored-by: JxnLexn <JxnLexn@users.noreply.github.com>
2026-07-09 23:19:32 -03:00
Thiago Reis
3a28b3b5e8 feat: add Kiro API key authentication (#6587)
* feat(oauth): add Kiro long-lived API key auth (#6587)

New /api/oauth/kiro/api-key route + KiroService.validateApiKey let a
Kiro account be linked with a long-lived AWS CodeWhisperer/Kiro API
key instead of the interactive OAuth device flow, with live
per-account model discovery (ListAvailableModels, 5-minute cache)
layered over the existing static registry fallback.

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

* fix(changelog): re-restore #6587 bullet after release sync

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

* fix(merge): restore #6126 clinepass files reverted by release auto-resolve + baseline re-merge

The release sync's auto-resolve reverted sibling PR #6126's clinepass work
(registry, catalog, oauth constants, clineAuth.ts, token-refresh case, tests)
and the file-size baseline — all outside this PR's scope. Restored to the
release versions, re-applied only this PR's own baseline entries, restored the
#6126 CHANGELOG bullet (re-inserting only this PR's own).

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

* chore(quality): freeze public-creds FP — AWS region default in validateApiKey signature

Same class as the existing minimax fn-param FPs: CRED_KEY_RE matches the
apiKey: param annotation and captures the region default "us-east-1",
which is not a credential.

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

* fix(kiro): keep hard-failure reject semantics + kill public-creds fn-param FP at the source

- getKiroUsage: exhausted non-auth attempts now REJECT with the last HTTP-status
  failure in the pre-#6587 format (usage-service-hardening relies on it); auth
  failures keep the soft social-auth message.
- validateApiKey: region default moved out of the parameter list (the
  check-public-creds CRED_KEY_RE matches the apiKey: annotation and flags any
  literal in the signature); drops the brittle line-keyed allowlist entry.

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

---------

Co-authored-by: strangersp <strangersp@users.noreply.github.com>
Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com>
2026-07-09 22:49:06 -03:00
NOXX - Commiter
ece4bf7b53 feat(kiro): support enterprise External IdP (Your organization) logins (#6363)
* feat(kiro): support enterprise External IdP ("Your organization") logins

Kiro's enterprise "Your organization" sign-in federates through the org's own
identity provider (e.g. Microsoft Entra ID) and produces an `external_idp`
token that is fundamentally different from AWS Builder ID / IAM Identity Center
(AWS SSO-OIDC, refresh token starts with `aorAAAAAG`) and the Google/GitHub
social flow. Its `~/.aws/sso/cache/kiro-auth-token.json` carries an org-IdP JWT
access token, an IdP refresh token, a per-tenant `tokenEndpoint`, a public
`clientId` (no secret) and `scopes` (`codewhisperer:conversations …`).

Before this change every import path rejected these tokens (the
`aorAAAAAG` format gate + no client secret), and the runtime/quota calls would
have failed even if imported, so organization accounts could not be used.

This adds full external_idp support:

- New `open-sse/services/kiroExternalIdp.ts`: public-client refresh_token grant
  builder (`buildExternalIdpRefreshParams`), a token-endpoint SSRF allowlist
  (`validateExternalIdpTokenEndpoint` — Microsoft/Okta/Auth0/OneLogin/Ping/
  Google/Cognito, https only), scope normalization, JWT identity extraction
  (`preferred_username`/`upn`/`email`), and the `TokenType: EXTERNAL_IDP`
  header constants.
- Runtime executor (`open-sse/executors/kiro.ts`): send
  `TokenType: EXTERNAL_IDP` for external_idp accounts. CodeWhisperer only binds
  the org-IdP bearer to the Amazon Q Developer profile with this header;
  without it every call returns `ValidationException: Invalid ARN <clientId>`.
- Runtime + import token refresh (`open-sse/services/tokenRefresh.ts`,
  `src/lib/oauth/services/kiro.ts`): refresh external_idp tokens with a
  form-encoded public-client `refresh_token` grant against the org IdP's
  `tokenEndpoint` instead of AWS OIDC / the Kiro social endpoint.
- Quota (`open-sse/services/usage/kiro.ts`): send the same header on
  `GetUsageLimits` so organization quota resolves.
- Import routes: `POST /api/oauth/kiro/import` gains an external_idp branch
  (skips the `aorAAAAAG` gate, refreshes via the org IdP, stores
  clientId/tokenEndpoint/scope/region/profileArn); `GET /auto-import` now
  recognizes external_idp tokens in `~/.aws/sso/cache`, reads the profile ARN
  from the Kiro IDE `profile.json` (org tokens can't enumerate it via
  `ListAvailableProfiles`), and persists the connection. The profile.json
  reader is factored into a shared `readKiroIdeProfileArn()` helper.
- Validation schema (`kiroImportSchema`): accept `tokenEndpoint` + `scopes`.

Tests: new `tests/unit/kiro-external-idp.test.ts` (endpoint allowlist, scope
normalization, identity extraction, public-client refresh body, the org IdP
refresh path, and the `TokenType: EXTERNAL_IDP` header gating). Also hardens
`kiro-windows-auto-import-3363.test.ts` to isolate `USERPROFILE` (Windows
`os.homedir()` reads it, not `HOME`) so the probe never reads a real on-host
Kiro login.

* fix(changelog): restore #6363 bullet after release resync (CHANGELOG-eat guard)

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

* fix(changelog): re-restore #6363 bullet after release sync

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

* fix(merge): restore #6126 clinepass files reverted by release auto-resolve + rebaseline own tokenRefresh growth

The release sync's merge auto-resolve silently reverted sibling PR #6126's
clinepass work (registry entry, catalog, oauth constants, clineAuth.ts, the
clinepass token-refresh case, and its tests) — all outside this PR's Kiro
external-IdP scope. Restored every affected file to the release version; the
remaining diff is Kiro-IdP-only. Rebaselined tokenRefresh.ts 2182->2249 (+67,
this PR's own external_idp refresh branch) with justification, and restored
the #6126 CHANGELOG bullet (re-inserting only this PR's own).

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

---------

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com>
Co-authored-by: artickc <artickc@users.noreply.github.com>
2026-07-09 21:39:09 -03:00
Imam Wahyu Widodo
d3331f8bca feat(providers): ClinePass OAuth login — dual-auth on top of #5942 (reconciles #5924) (#6126)
* feat(providers): rebase ClinePass dual-auth (OAuth + BYOK) onto release/v3.8.47

ClinePass now offers both sign-in methods on its dashboard page: OAuth
(reusing the Cline WorkOS flow, primary "Connect" button) or a pasted
BYOK API key ("Manual API key"), instead of only the API-key-only
provider shipped in #5942.

- Registry: authType oauth + oauth urls, alias aligned to "cp" (matches
  the OAUTH_PROVIDERS catalog alias so <alias>/<modelId> routing
  resolves); keeps the #6165 forceStream:true fix (streaming-only API).
- Executor: new buildClinepassHeaders() (src/shared/utils/clineAuth.ts)
  picks buildClineHeaders() for an OAuth accessToken or a plain Bearer +
  Cline identification headers for a BYOK key — extracted to a leaf
  module to avoid growing the frozen open-sse/executors/default.ts.
- Refresh: dispatch clinepass to the shared refreshClineToken() (was
  falling through to the generic refresh and failing silently).
- Catalog: admit the BYOK path through a dedicated
  DUAL_AUTH_APIKEY_PROVIDER_IDS gate (src/lib/providers/catalog.ts) so
  POST /api/providers accepts an apikey connection without flipping
  isOAuth off (which would break the primary Connect->OAuth routing).
- Dashboard: render both "Connect" + "Manual API key" buttons for
  clinepass (ConnectionsHeaderToolbar.tsx, EmptyConnectionsPlaceholder.tsx).
- Dedup: removed the now-redundant API-key-only APIKEY_PROVIDERS_GATEWAYS
  entry so ClinePass is listed once (OAuth-primary).
- oauth.ts: added the clinepass catalog entry (was reverted by staleness
  during rebase); src/lib/oauth/providers/index.ts: clinepass -> cline.

This branch was ~167 commits / weeks behind release/v3.8.47; a real
merge surfaced 61 conflicting files, several of which are already-shipped
fixes (forceStream #6165, zed-hosted, requesty, agentrouter CC-wire-image,
NVIDIA/Mistral/kimi executor fixes, chatCore hardening) that a naive
resolution would have silently reverted. Reconstructed clean on top of
current release/v3.8.47, isolating and re-applying only the clinepass
dual-auth feature and preserving every already-shipped fix untouched.

tokenRefresh.ts's frozen-file cap raised by the irreducible 1-line
`case "clinepass":` switch label (config/quality/file-size-baseline.json,
justified inline); open-sse/executors/default.ts stays under its cap via
the buildClinepassHeaders() extraction.

Regression guard: tests/unit/clinepass-provider.test.ts (15/15, extended
with the dual-auth admission-gate and alias-consistency guards).

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

* test(providers): update APIKEY_PROVIDERS spread-merge count 171->170

The ClinePass dual-auth rebase (this PR) removed the now-redundant
API-key-only APIKEY_PROVIDERS_GATEWAYS.clinepass entry (dedup — clinepass
is OAuth-primary now, with its BYOK path admitted through the
DUAL_AUTH_APIKEY_PROVIDER_IDS gate instead of a second catalog entry),
which drops the total APIKEY_PROVIDERS spread-merge count by one.
tests/unit/providers-constants-split.test.ts hardcoded the prior count
(171); updated to 170 to match, confirmed via CI (Unit Tests fast-path
1/2 and 2/2 both failed on the stale count).

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

* fix(oauth): register clinepass in PROVIDERS enum to fix Unknown provider error

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

* chore(test): rebaseline oauth-providers-config.test.ts frozen size for clinepass entries

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

* fix(changelog): re-restore #6126 bullet after release sync

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

---------

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
Co-authored-by: hajilok <hajilok@users.noreply.github.com>
Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com>
2026-07-09 20:37:40 -03:00
janeza2
9906dfc1ba fix(providers): update web model discovery (#6308)
Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com>
2026-07-09 19:54:05 -03:00
Moseyuh333
b9d18dd8c4 Continue fix bugs and upgrade skill_collector (#6294)
* fix(skills): gate skill-collector CLI detection behind management auth + loopback

PR #6294 fork-main bundled genuinely new skill-collector CLI-detection routes
(GET /api/skills/collect/detect, POST /api/skills/collect/install) on top of
content already shipped via #6186. This reconstructs the PR against the
current release tip, keeping only the new detect/install routes and their
SKILL.md, and drops the 3 already-merged commits so two post-merge quality
fixes on /api/github-skills (Zod validation + sanitizeErrorMessage) are not
reverted.

- GET /api/skills/collect/detect spawned a child process per CLI_TOOL_IDS
  entry via getCliRuntimeStatus(), unauthenticated and reachable over any
  tunnel. All 3 routes (github-skills GET/POST, skills/collect/detect,
  skills/collect/install) now require requireManagementAuth(), matching
  every sibling /api/skills/* route.
- Classified /api/skills/collect/ in LOCAL_ONLY_API_PREFIXES and
  SPAWN_CAPABLE_PREFIXES (routeGuard.ts / spawnCapablePrefixes.ts) and added
  src/app/api/skills/collect to SPAWN_CAPABLE_ROUTE_ROOTS in
  check-route-guard-membership.ts so the automated gate actually scans it
  (Hard Rules #15 + #17).
- omniroute_github_skills_install MCP tool now reports the honest
  action: "planned" instead of "installed", matching the REST route.
- Dropped docker-compose.drive-d.yml, start.sh, and the unrelated
  @types/node/settings.ts changes (personal dev-machine / out-of-scope).
- Added route-level tests for all 3 routes + the 3 MCP tools (auth-required
  and no-stack-trace-leak assertions) and a route-guard regression test.

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

* fix(quality): register new routeGuard covering test in stryker.conf.json

check:mutation-test-coverage --strict (Fast Quality Gates) flagged
tests/unit/authz/route-guard-skills-collect.test.ts as a covering unit test
for src/server/authz/routeGuard.ts that was missing from tap.testFiles, so
its mutant kills would silently not count toward the mutation-test baseline.

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

* chore: resync CHANGELOG after merging release/v3.8.47

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

---------

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com>
Co-authored-by: Moseyuh333 <Moseyuh333@users.noreply.github.com>
2026-07-09 19:43:43 -03:00
nowhats-br
2a5a819dbe fix: update Dockerfile with --allow-scripts for better-sqlite3 compil… (#6700)
* fix(docker): compile better-sqlite3 via direct node-gyp rebuild in the Dockerfile

The `builder` stage installs dependencies with `npm ci --ignore-scripts` (deliberate
supply-chain hardening) and then re-enables the native build for the one package
that needs it. `npm rebuild better-sqlite3` re-runs that indirectly through the
package's own install script, which under npm 11 depends on npm's script-allowlist
machinery correctly re-enabling it — some self-hosted build environments (e.g.
Dokploy) hit a broken/mismatched native binding through that indirection.

Invoke `node-gyp rebuild` directly inside `node_modules/better-sqlite3` instead,
bypassing npm's script-running layer entirely, so the compile step is deterministic
regardless of npm version or ignore-scripts allowlist behavior.

Rebased onto the current release/v3.8.47 tip: dropped this branch's stale
electron/package.json + package-lock.json diff (would have reverted the
electron 42->43 ABI-148 fix from #6605) and the unconsumed root `allowScripts`
package.json field (npm does not read that key; has zero effect).

Regression guard: tests/unit/dockerfile-better-sqlite3-node-gyp-6700.test.ts.

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

* fix(changelog): restore CHANGELOG bullets eaten by release sync

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

* fix(changelog): re-restore CHANGELOG bullet after further release sync

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

* fix(changelog): re-restore CHANGELOG bullet after further release sync

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

* fix(changelog): re-restore CHANGELOG bullet after further release sync

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

* fix(changelog): correct CHANGELOG restoration (previous attempt had a script-path bug)

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

* fix(changelog): re-restore #6700 bullet after #6496 release sync

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

* fix(changelog): re-restore CHANGELOG bullet after further release sync

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

---------

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com>
Co-authored-by: nowhats-br <nowhats-br@users.noreply.github.com>
2026-07-09 18:57:06 -03:00
SeaXen
889fffddbe fix(cloudflare-relay): use Service Worker syntax with body_part metadata (#6416) (#6496)
* fix(providers): Cloudflare relay Worker uses Service Worker syntax + body_part

CONTEXT: #6416/#6618 fixed the multipart Content-Type but the emitted
worker source still used ES-module syntax (`export default { fetch }`)
with `main_module` metadata. Cloudflare's Workers upload API parses a
plain `application/javascript` script part as Service Worker syntax
regardless of `main_module`, and `main_module` requires the script to
actually be an ES module — so the upload was still rejected.

CHANGE: buildCloudflareWorkerScript() now emits Service Worker syntax
(`addEventListener("fetch", ...)`, no top-level `export`) and the
upload metadata uses `body_part` instead of `main_module`.

Also restores the SSRF-guard bracket-stripping regex for bracketed
IPv6 hosts (`[::1]`, `[fd00::1]`) that an earlier revision of this
change accidentally double-escaped, with regression coverage added to
tests/unit/relay-deploy-5128.test.ts. Updates the sibling
tests/unit/proxy-pool-cloudflare-workers-deployer.test.ts assertion
that still expected the old ES-module contract.

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

* fix(changelog): restore CHANGELOG bullets eaten by release sync

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

* fix(changelog): re-restore CHANGELOG bullet after further release sync

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

* fix(changelog): re-restore CHANGELOG bullet after further release sync

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

* fix(changelog): re-restore CHANGELOG bullet after further release sync

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

* fix(changelog): correct CHANGELOG restoration (previous attempt had a script-path bug)

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

---------

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com>
Co-authored-by: SeaXen <SeaXen@users.noreply.github.com>
2026-07-09 18:43:19 -03:00
SeaXen
6557f44bd2 feat(settings): 9router-style Routing Strategy card + sticky parity (#6678)
* feat(dashboard): 9router-parity Routing Strategy card + provider/combo sticky override (#6678)

Add a Routing Strategy settings card (Settings -> Routing) surfacing account
round-robin/sticky-limit knobs plus a new combo-level sticky round-robin
(comboStickyRoundRobinLimit), and a per-provider account-routing override
(providerStrategies) wired into getProviderCredentials() ahead of the global
fallback strategy. Rebased onto release/v3.8.47 (credit-preserving
reconstruction: unrelated package.json/electron/proxyDispatcher drift from the
PR's stale base was dropped, only the author's own 12 files were re-applied).
Split ProviderAccountRoutingCard/RoutingStrategyCard into smaller
hook+subcomponent pieces to stay under the frozen complexity/file-size gates;
rebaselined ProviderDetailPageClient.tsx/auth.ts's frozen file-size caps for
the small additive growth.

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

* chore(quality): register combo-rr-sticky-9router.test.ts in stryker tap.testFiles (#6678)

CI's Fast Quality Gates -> check:mutation-test-coverage --strict flagged the new
test as missing from stryker.conf.json's tap.testFiles (it covers the mutated
module open-sse/services/combo/rrState.ts). Adds the single entry, alphabetized
next to the existing combo-rr-fallback-advance-948.test.ts.

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

* fix(changelog): restore CHANGELOG bullets eaten by release sync

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

* fix(changelog): re-restore CHANGELOG bullet after further release sync

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

* fix(changelog): re-restore CHANGELOG bullet after further release sync

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

---------

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com>
Co-authored-by: SeaXen <SeaXen@users.noreply.github.com>
2026-07-09 18:22:43 -03:00
Arthur Bodera
d76aa40ee6 fix(chatgpt-web): render citations as markdown links (#6635)
* fix(providers): render ChatGPT-web citation markers as Markdown links

ChatGPT Web responses leaked raw chatgpt.com UI citation markup (private-use
marker tokens like `citeturn0search0`, `entity[...]`) instead of real
Markdown links, since these are normally resolved client-side by chatgpt.com's
own JS using `message.metadata.content_references`.

cleanChatGptText() now resolves content_references (grouped webpages, footnote
sources, inline webpage/url mentions) into `[label](url)` Markdown links for
the streaming and non-streaming response builders and the GPT-5.5 Pro
stream_handoff polled-answer path, falling back to stripping any marker with
no resolvable source.

The citation parsing/rendering logic was extracted into a new pure sibling
module (open-sse/executors/chatgpt-web/citations.ts), decomposed into small
per-reference-type helpers, to keep the executor under the frozen file-size
cap and the complexity/cognitive-complexity ratchets. Regression tests moved
to a dedicated tests/unit/chatgpt-web-citations.test.ts for the same reason.

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

* fix(changelog): restore CHANGELOG bullets eaten by release sync

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

* fix(changelog): re-restore CHANGELOG bullet after further release sync

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

* fix(changelog): re-restore CHANGELOG bullet after further release sync

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

---------

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com>
Co-authored-by: Thinkscape <thinkscape@users.noreply.github.com>
2026-07-09 17:37:20 -03:00
Chirag Singhal
237bd59173 fix(api): sanitize catch-block error.message in middleware/hooks routes (#6645)
* fix(api): sanitize catch-block error.message in middleware/hooks routes

POST /api/middleware/hooks and PUT /api/middleware/hooks/[name] returned
the raw error?.message in their 500 response bodies (Hard Rule #12),
which could leak internal SQLite error text/paths on a DB failure. Both
now route through sanitizeErrorMessage() from open-sse/utils/error.ts,
matching the pattern already used elsewhere in the codebase.

Regression guard: tests/unit/middleware-hooks-error-sanitization.test.ts

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

* test(mutation): register middleware-hooks-error-sanitization in stryker tap.testFiles

The mutation test-coverage gate (check:mutation-test-coverage --strict)
flagged tests/unit/middleware-hooks-error-sanitization.test.ts as
covering open-sse/utils/error.ts but missing from stryker.conf.json's
tap.testFiles list.

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

* fix(changelog): restore CHANGELOG bullets eaten by release sync

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

* fix(changelog): re-restore CHANGELOG bullet after further release sync

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

---------

Co-authored-by: Chirag Singhal <chirag127@users.noreply.github.com>
Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com>
2026-07-09 17:31:52 -03:00
Hernan Javier Ardila Sanchez
912ff8d1c4 fix(vision-bridge): auto-reroute non-vision models to fastest vision model when images detected (#6640)
* fix(electron): bump electron 42→43 + build better-sqlite3 from source (ABI 148) (#6605)

fix(electron): bump electron 42→43 + rebuild better-sqlite3 from source against the Electron ABI (148).

Electron 43 raises NODE_MODULE_VERSION to 148; better-sqlite3@12.11.1 has no electron-v148 prebuild, so the packaged app died with 'Nenhum driver SQLite disponível'. prepare-electron-standalone now compiles better-sqlite3 from source against the electron headers into build/Release (where 'bindings' resolves it). Validated by Electron Package Smoke (green) + local (node_register_module_v148).

Supersedes #6378. (--admin: the only reds are SonarQube/SonarCloud failing on a coverage-report artifact digest-mismatch — a GitHub Actions infra flake, not this diff; Sonar is green on main and the diff touches only the electron build.)

* deps: bump the development group across 1 directory with 6 updates (#6588)

deps: bump the development group (6 updates). Rebased onto current main; all checks green after the electron-smoke fix (#6605).

* fix(proxy): force CONNECT tunnel for HTTP proxied requests (undici 8.7) + production deps bump (#6620)

fix(proxy): force CONNECT tunnel for HTTP proxied requests (undici 8.7) + production deps bump.

undici 8.6+ changed ProxyAgent to forward plain-HTTP via request-proxy instead of CONNECT, breaking OAuth refresh through a connection proxy (501). proxyDispatcher now passes proxyTunnel:true. Validated: Unit Tests 3/8 (the OAuth-proxy test) green, new regression test green (fails without the fix on undici 8.7), SonarQube green.

Supersedes #6380. (--admin: the only red is Electron Package Smoke failing on a next-build artifact 'digest-mismatch' — a GitHub Actions infra flake corrupting the asar ('file data stream has unexpected number of bytes'); the better-sqlite3 rebuild itself succeeded (gyp ok) and the electron path is unchanged from #6605 which passed the smoke. Not this diff.)

* fix(vision-bridge): auto-reroute non-vision models to fastest vision model when images detected

The VisionBridgeGuardrail was describing images as text via a vision model
and sending text to the original (non-vision) model. This defeated the purpose
when the final target was already vision-capable (auto/vision, combos with
vision targets) and never actually rerouted requests to a vision model.

Changes:
- Individual non-vision models + images → reroute  to the fastest
  available vision-capable model (via getBestVisionModel), keeping images intact
- Auto/ prefix models (auto/vision, auto) → skip guardrail entirely, letting
  the auto-combo resolver handle vision-capable model selection
- Combo mappings with non-vision targets → keep existing describe behavior
  (fallback path via checkModelHasComboMapping)
- chat.ts: sync modelStr from body.model after guardrail execution so downstream
  routing uses the rerouted model

* fix(vision-bridge): use getBestVisionModel auto-routing instead of fixed model

Address Gemini review feedback: getBestVisionConfig({}) with empty object
bypassed auto-routing by always defaulting to a fixed model. Auto-select
the best vision model from available providers instead.

* fix: compact modelStr sync to stay under file-size cap (1632)

* fix: remove debug log, orphaned brace to keep file under cap

* chore: trigger CI re-run with file-size fix and PR evidence

* chore: rebaseline chat.ts frozen cap to 1754 (PR #6640 +3 lines)

* fix(auto-combo): respect hidden models from dashboard toggle

getHiddenModelsByProvider() only queried modelCompatOverrides and
customModels namespaces, missing the hiddenModels namespace used by
the dashboard hide/unhide toggle. Auto-combo candidates now filter
out models the user explicitly hid.

* fix(changelog): restore CHANGELOG bullets eaten by release sync

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

---------

Co-authored-by: Diego Rodrigues de Sa e Souza <8016841+diegosouzapw@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: herjarsa <herjarsa@users.noreply.github.com>
Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com>
2026-07-09 17:15:53 -03:00
Diego Rodrigues de Sa e Souza
5e5447a2ba fix(sse): count gate/combo-rejected requests in per-api-key usage (#6698)
Requests rejected before handleChatCore — a pipeline-gate rejection
(provider circuit breaker OPEN / model cooldown) or a combo whose
targets were all exhausted — short-circuited in chat.ts and only wrote
a call_logs row (dashboard/logs). They never reached persistFailureUsage,
so no usage_history row was created and the per-api-key usage counter
(getApiKeyUsageRows reads usage_history) never incremented. An API key
whose traffic was entirely gate/breaker-rejected showed zero requests
despite real usage.

Route both rejection paths through recordRejectedRequestUsage(), which
writes the call_logs row (unchanged visibility) AND a usage_history row
attributed to the api key with success:false, mirroring persistFailureUsage.

Regression guard: tests/unit/rejected-request-usage.test.ts.
2026-07-09 16:50:29 -03:00
MikeTuev
0d20205f92 fix(sse): preserve server-tool literal names in message history and tool_choice (#6586)
* fix(sse): preserve server-tool literal names in message history and tool_choice

The v3.8.36 guard (isAnthropicServerToolType, #2943) protects Anthropic
server tools (web_search_20250305, bash_20250124, ...) from the tool-name
cloak only in the tools[] array. The same reserved literal names were still
rewritten in message-history tool_use blocks and in tool_choice, and
remapToolNamesInRequest had no guard at all (bash -> Bash).

The resulting asymmetry — tools[] keeps 'web_search' while the history
reference becomes 'WebSearch' — makes Anthropic reject every follow-up turn
of a native web-search conversation:

  [400] Tool 'WebSearch' not found in provided tools

Collect the declared server-tool names once per request and skip them in
every rewrite path of both remapToolNamesInRequest and
cloakThirdPartyToolNames (tools[], message history, tool_choice). Plain
custom tools with the same names (no server type) remain remapped/cloaked
exactly as before, symmetrically in all sections.

Surfaced on Claude Code 2.1.x native WebSearch; same class as CLIProxyAPI
#1094/#1179. TDD: 5 failing repro tests -> guard -> 7/7 green (92/92 across
the remapper suite), typecheck:core clean.

* fix(sse): skip null entries in tools[] before server-tool type check

Review follow-up (gemini-code-assist): a null element in tools[] made the
new isAnthropicServerToolType(tool.type) check throw. The crash path is
pre-existing (String(tool.name) on the next line threw identically), but
the guard is cheap and mirrors the null checks already used in
cloakThirdPartyToolNames. Adds a regression test (8/8 green).
2026-07-09 16:49:59 -03:00
KooshaPari
abfced8b28 feat(sandbox): native Apple Container, WSL, OrbStack, Podman runtime support (#6611)
* feat(sandbox): native Apple Container, WSL, OrbStack, Podman runtime support

* fix(skills): align sandbox fallback kill container-name convention

sandbox.ts's docker-fallback kill path (used only when cachedProvider is
unexpectedly null) still targeted the pre-PR omniroute-sandbox-${id}
container name, while containerProvider.ts's SANDBOX_NAME now produces
omniroute-${id}. Align the fallback naming so it matches the provider
convention, with a regression test covering kill()/killAll() before a
provider has ever been resolved.

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

* fix(docs): document SKILLS_SANDBOX_RUNTIME and drop unrelated env leftovers

Two fixes surfaced by CI's env/docs contract gate:

- Add the SKILLS_SANDBOX_RUNTIME row to docs/reference/ENVIRONMENT.md so
  the new container-runtime override introduced by this PR is documented,
  matching .env.example.
- Remove the Substrate/Bifrost/OTEL .env.example blocks that leaked in
  from this branch's stale main-based history during the release-branch
  sync merge — none of that belongs to this PR (native container
  runtimes for the skill sandbox) and none of it exists on
  release/v3.8.47 yet.

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

---------

Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com>
Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
2026-07-09 16:48:49 -03:00
enjoyer-hub
e697670046 fix(cli): detect WinGet Claude Code on Windows (#6647)
* fix(electron): bump electron 42→43 + build better-sqlite3 from source (ABI 148) (#6605)

fix(electron): bump electron 42→43 + rebuild better-sqlite3 from source against the Electron ABI (148).

Electron 43 raises NODE_MODULE_VERSION to 148; better-sqlite3@12.11.1 has no electron-v148 prebuild, so the packaged app died with 'Nenhum driver SQLite disponível'. prepare-electron-standalone now compiles better-sqlite3 from source against the electron headers into build/Release (where 'bindings' resolves it). Validated by Electron Package Smoke (green) + local (node_register_module_v148).

Supersedes #6378. (--admin: the only reds are SonarQube/SonarCloud failing on a coverage-report artifact digest-mismatch — a GitHub Actions infra flake, not this diff; Sonar is green on main and the diff touches only the electron build.)

* deps: bump the development group across 1 directory with 6 updates (#6588)

deps: bump the development group (6 updates). Rebased onto current main; all checks green after the electron-smoke fix (#6605).

* fix(proxy): force CONNECT tunnel for HTTP proxied requests (undici 8.7) + production deps bump (#6620)

fix(proxy): force CONNECT tunnel for HTTP proxied requests (undici 8.7) + production deps bump.

undici 8.6+ changed ProxyAgent to forward plain-HTTP via request-proxy instead of CONNECT, breaking OAuth refresh through a connection proxy (501). proxyDispatcher now passes proxyTunnel:true. Validated: Unit Tests 3/8 (the OAuth-proxy test) green, new regression test green (fails without the fix on undici 8.7), SonarQube green.

Supersedes #6380. (--admin: the only red is Electron Package Smoke failing on a next-build artifact 'digest-mismatch' — a GitHub Actions infra flake corrupting the asar ('file data stream has unexpected number of bytes'); the better-sqlite3 rebuild itself succeeded (gyp ok) and the electron path is unchanged from #6605 which passed the smoke. Not this diff.)

* fix(cli): detect WinGet Claude Code on Windows

* chore(merge): drop unrelated main-drift from PR fork (deps/electron/proxy files belong to #6620, not this PR)

Restores electron/package-lock.json, electron/package.json, package-lock.json,
package.json, open-sse/utils/proxyDispatcher.ts, scripts/build/prepare-electron-standalone.mjs
and tests/unit/proxy-dispatcher-family.test.ts to origin/release/v3.8.47's content.
The PR fork branched from a state of main that already includes #6620 (proxy CONNECT
tunnel fix + deps bump), which is not yet synced into release/v3.8.47 — the 3-way
merge would otherwise silently carry that unrelated content into this doc-only PR.

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

* chore(quality): rebaseline cliRuntime.ts file-size freeze for #6647 (1100->1110)

The file was already exactly at the frozen 1100-line cap on release/v3.8.47.
PR #6647's WinGet Claude Code detection path adds 10 lines (irreducible —
the 62-char package folder name forces Prettier's 100-char width to break
the path.join call across the same multi-line form used by every other
long path in this function), tripping the Fast Quality Gates check:file-size
job. Bumping the frozen cap to the file's real new size per the documented
allowlist-with-justification policy (this is a pass/fail policy gate, not
the ratchet metrics system).

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

---------

Co-authored-by: Diego Rodrigues de Sa e Souza <8016841+diegosouzapw@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: quanturbo <faralechko@gmail.com>
Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com>
2026-07-09 16:46:41 -03:00
Paijo
1291e9bf26 fix(providers): web-cookie fallback validation reports unsupported instead of a false valid (#6309)
validateWebCookieProvider() previously required a providerRegistry.ts entry and
returned "Provider not found in registry" for web-cookie-only providers like
lmarena, gemini-business, poe-web, venice-web and v0-vercel-web. A fallback to
WEB_COOKIE_PROVIDERS[provider].website was proposed, but live verification showed
probing `${website}/models` does not reliably signal session validity for these
(redirects/SPA 200s regardless of cookie validity) — it would report an expired
or garbage cookie as valid, which is worse than an honest "not supported". Until
each provider has a verified, side-effect-free auth probe against its real API
host, the fallback now returns `unsupported: true` with no network call. Also
reverts the probe transport from validationRead back to directHttpsRequest,
which fixes a globalThis.fetch mock/patch-timing mismatch that made the
pre-existing tests/unit/provider-validation-web-cookie-auth007.test.ts hit the
live network in CI, and adds the missing Cookie header to the probe request.

Regression guard: tests/unit/web-cookie-validation-fallback.test.ts.

Co-authored-by: oyi77 <oyi77@users.noreply.github.com>
Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
2026-07-09 16:45:01 -03:00
Hamsa_M
aba98fd227 fix(cli): per-agent DNS, startup guards, and batched Windows hosts writes (#6338)
DNS toggle in AgentBridge was broken for 8 of 9 agents: addDNSEntry/
removeDNSEntry always resolved the legacy Antigravity default hosts
regardless of which agent's dns_enabled flag was flipped. Both now
accept an optional agentId and resolve hosts via ALL_TARGETS; the
[id]/dns route passes id through and returns 404 for an unknown agent
instead of silently falling back to the defaults.

startMitmInternal() now wraps generateCert(), the provisionDnsEntries()
call, and the PID-file write in try/catch so a mid-startup failure
can't orphan the already-spawned MITM child process.

On Windows, addDNSEntries/removeDNSEntries batch every missing/present
entry into a single elevated PowerShell invocation instead of one UAC
prompt per host line.

Scope note: this PR originally bundled an unrelated SkillOpt feature
(DB migration, 6 API routes, dashboard UI) and a checks-free CI build
workflow alongside this DNS/startup fix. Both were dropped here as
out-of-scope per review-group-prs analysis (2-implementing plan);
only the DNS/startup-guard delta (dnsConfig.ts, manager.ts, the [id]/dns
route, and their tests) is applied.

Co-authored-by: hamsa0x7 <hamsa0x7@users.noreply.github.com>
Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
2026-07-09 16:44:50 -03:00
backryun
0e4145d257 fix(providers): remove obsolete providers (glhf, kluster, cablyai, inclusionai) (#6675)
Drop dead catalog/registry entries, keep Synthetic as the GLHF replacement path,
regenerate provider reference/docs counts, and lock APIKEY family-split + file-size
gates so CI stays green.

Ignore prettier on freeModelCatalog.data.ts so dense one-line budget rows are not
expanded past the 800-line new-file cap.
2026-07-09 16:44:28 -03:00
Diego Rodrigues de Sa e Souza
a6ea6074cb fix(cli): compression REST fallback uses canonical defaultMode + JSON object cells (#6571) (#6682)
* fix(cli): compression REST fallback uses canonical defaultMode + JSON object cells (#6571)

* test(cli): align existing compression-command tests to the #6571 canonical field contract (engine→strategy/defaultMode)
2026-07-08 16:50:11 -03:00
ThongAccount
1188847f0f feat: add setting for provider/model-specific parameters (#6649)
* fix(electron): bump electron 42→43 + build better-sqlite3 from source (ABI 148) (#6605)

fix(electron): bump electron 42→43 + rebuild better-sqlite3 from source against the Electron ABI (148).

Electron 43 raises NODE_MODULE_VERSION to 148; better-sqlite3@12.11.1 has no electron-v148 prebuild, so the packaged app died with 'Nenhum driver SQLite disponível'. prepare-electron-standalone now compiles better-sqlite3 from source against the electron headers into build/Release (where 'bindings' resolves it). Validated by Electron Package Smoke (green) + local (node_register_module_v148).

Supersedes #6378. (--admin: the only reds are SonarQube/SonarCloud failing on a coverage-report artifact digest-mismatch — a GitHub Actions infra flake, not this diff; Sonar is green on main and the diff touches only the electron build.)

* deps: bump the development group across 1 directory with 6 updates (#6588)

deps: bump the development group (6 updates). Rebased onto current main; all checks green after the electron-smoke fix (#6605).

* fix(proxy): force CONNECT tunnel for HTTP proxied requests (undici 8.7) + production deps bump (#6620)

fix(proxy): force CONNECT tunnel for HTTP proxied requests (undici 8.7) + production deps bump.

undici 8.6+ changed ProxyAgent to forward plain-HTTP via request-proxy instead of CONNECT, breaking OAuth refresh through a connection proxy (501). proxyDispatcher now passes proxyTunnel:true. Validated: Unit Tests 3/8 (the OAuth-proxy test) green, new regression test green (fails without the fix on undici 8.7), SonarQube green.

Supersedes #6380. (--admin: the only red is Electron Package Smoke failing on a next-build artifact 'digest-mismatch' — a GitHub Actions infra flake corrupting the asar ('file data stream has unexpected number of bytes'); the better-sqlite3 rebuild itself succeeded (gyp ok) and the electron path is unchanged from #6605 which passed the smoke. Not this diff.)

* feat(db): add provider param filter config store (key_value namespace)

Add paramFilters.ts module for CRUD against provider_param_filters
namespace in the key_value table, with in-memory cache + generation
counter invalidation. Supports denylist/allowlist per provider and
per model, plus auto-learn flag.

Migration 118 documents the namespace (no schema change).

Issue: #6625

* feat(proxy): add detectUnsupportedParam regex for auto-learning

Add UNSUPPORTED_PARAM_RE and detectUnsupportedParam() to extract
the offending parameter name from upstream 400 error messages like
'Unsupported parameter(s): thinking'.

Issue: #6625

* feat(proxy): extend stripUnsupportedParams with config-driven denylist/allowlist

Add applyConfigFilters() called after hardcoded STRIP_RULES in
stripUnsupportedParams(). Config-driven rules (DB-backed via
paramFilters.ts) support provider-level and model-level:
  1. Provider denylist (delete body[key])
  2. Model denylist (delete body[key])
  3. Provider allowlist (restore from pre-strip snapshot)
  4. Model allowlist (restore from pre-strip snapshot)

Allowlist only restores keys the client actually sent — never
introduces new params.

Issue: #6625

* feat(proxy): wire auto-learn of unsupported params into 400-downgrade loop

When a provider returns 400 with 'Unsupported parameter: X' and the
provider config has autoLearn enabled, auto-detect the param name
via detectUnsupportedParam(), persist it to the provider's block
list via addParamToBlocklist(), then strip and retry.

Issue: #6625

* test: add tests for provider param filter denylist/allowlist/auto-learn

Three new test files:
- param-filters-apply.test.ts — hardcoded rules regression + direct
  applyConfigFilters tests (no DB dependency)
- param-filters-db.test.ts — CRUD against key_value, cache invalidation,
  full filter pipeline (DB-backed config → stripUnsupportedParams),
  16 tests in isolated temp DB
- param-filters-auto-learn.test.ts — UNSUPPORTED_PARAM_RE regex matching
  and detectUnsupportedParam edge cases

All existing tests unchanged and passing.

Issue: #6625

* feat(proxy): add global auto-learn flag for unsupported params

Add isAutoLearnGloballyEnabled() and setGlobalAutoLearnEnabled() to
paramFilters.ts. The global flag (stored as key __global__ in the
provider_param_filters namespace) acts as a master switch: when
enabled, ALL providers auto-learn unsupported params from 400 errors.

In base.ts, the auto-learn check now evaluates:
  shouldAutoLearn = isAutoLearnGloballyEnabled() || perProviderConfig?.autoLearn

Global flag defaults to false (opt-in). Tests cover enable/disable/
default/no-interference-with-per-provider-config.

Issue: #6625

* fix: apply PR#6649 review feedback — model-scoped auto-learn and precedence order

Fixes from gemini-code-assist[bot] review:
- HIGH: Auto-learn now scoped to the specific model that triggered the 400
  (addParamToBlocklist(this.provider, autoLearned, model)) instead of
  adding to the provider-level blocklist globally
- HIGH: Reordered applyConfigFilters so model-level operations run AFTER
  provider-level operations (model denylist → model allowlist override
  provider allowlist → provider denylist)
- MEDIUM: Include model name in auto-learn log message

Adds regression test verifying model-level denylist beats provider-level
allowlist.

Issue: #6625
PR: #6649

* feat(ui): add provider-level param filter section to detail page

Add ProviderParamFilterSection component rendered on each provider
detail page, backed by GET|PUT|DELETE /api/providers/[id]/param-filters.

UI allows operators to configure:
- Blocked params (comma-separated, stripped from outgoing requests)
- Allowed params (comma-separated, re-added after denylist stripping)
- Auto-learn toggle (per-provider, enables auto-learning from 400 errors)

Wired into ProviderDetailPageClient.tsx between the Playground panel
and the Modals section.

Issue: #6625
PR: #6649

* feat(ui): add model-level param filter fields in compat popover

Extend ModelCompatPopover with Blocked params and Allowed params
text inputs for model-level denylist/allowlist overrides.

Model-specific block/allow data is persisted via the param-filters
API endpoint (PUT /api/providers/:id/param-filters) with the model
scope under the models key.

Both ModelRow and PassthroughModelRow now pass providerId and modelId
to the popover.

Issue: #6625
PR: #6649

* chore: gitignore .claude-flow/

* fix(param-filters): review follow-ups — auth gate, error sanitization, Zod body validation, typecheck, file-size, i18n keys

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

* chore(param-filters): drop unrelated main-drift from the fork branch (deps/electron/proxy files belong to #6620/#6605/#6588, not this PR)

* refactor(param-filters): split oversized functions — keep complexity gate at baseline

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

* refactor(param-filters): decompose config parser helpers — keep cognitive-complexity gate at baseline

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

* fix(changelog): restore sibling #6648 bullet eaten by merge auto-resolve + re-insert #6649 entry

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

---------

Co-authored-by: Diego Rodrigues de Sa e Souza <8016841+diegosouzapw@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com>
2026-07-08 14:55:00 -03:00
PizzaV
2d9f85d587 fix(mimocode): handle 400 with cooldown + account rotation (#6648)
* fix(electron): bump electron 42→43 + build better-sqlite3 from source (ABI 148) (#6605)

fix(electron): bump electron 42→43 + rebuild better-sqlite3 from source against the Electron ABI (148).

Electron 43 raises NODE_MODULE_VERSION to 148; better-sqlite3@12.11.1 has no electron-v148 prebuild, so the packaged app died with 'Nenhum driver SQLite disponível'. prepare-electron-standalone now compiles better-sqlite3 from source against the electron headers into build/Release (where 'bindings' resolves it). Validated by Electron Package Smoke (green) + local (node_register_module_v148).

Supersedes #6378. (--admin: the only reds are SonarQube/SonarCloud failing on a coverage-report artifact digest-mismatch — a GitHub Actions infra flake, not this diff; Sonar is green on main and the diff touches only the electron build.)

* deps: bump the development group across 1 directory with 6 updates (#6588)

deps: bump the development group (6 updates). Rebased onto current main; all checks green after the electron-smoke fix (#6605).

* fix(proxy): force CONNECT tunnel for HTTP proxied requests (undici 8.7) + production deps bump (#6620)

fix(proxy): force CONNECT tunnel for HTTP proxied requests (undici 8.7) + production deps bump.

undici 8.6+ changed ProxyAgent to forward plain-HTTP via request-proxy instead of CONNECT, breaking OAuth refresh through a connection proxy (501). proxyDispatcher now passes proxyTunnel:true. Validated: Unit Tests 3/8 (the OAuth-proxy test) green, new regression test green (fails without the fix on undici 8.7), SonarQube green.

Supersedes #6380. (--admin: the only red is Electron Package Smoke failing on a next-build artifact 'digest-mismatch' — a GitHub Actions infra flake corrupting the asar ('file data stream has unexpected number of bytes'); the better-sqlite3 rebuild itself succeeded (gyp ok) and the electron path is unchanged from #6605 which passed the smoke. Not this diff.)

* fix(mimocode): handle 400 with cooldown + account rotation

Treat HTTP 400 responses the same as 429: mark the account on cooldown
and continue to the next fingerprint/proxy. Previously, 400 fell through
to markSuccess and returned immediately, so only 1 of N accounts was ever
tried per request.

Refs: #5925

* chore(mimocode): drop unrelated dependency/electron drift from PR #6648's stale fork

package.json/package-lock.json (bun/eslint-config-next/cyclonedx bumps), electron/package.json,
electron/package-lock.json, open-sse/utils/proxyDispatcher.ts, prepare-electron-standalone.mjs
and tests/unit/proxy-dispatcher-family.test.ts were already present in the contributor's single
commit but are unrelated to the mimocode 400-handling fix — restored to release/v3.8.47's
versions so the PR stays scoped to open-sse/executors/mimocode.ts.

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

* fix(mimocode): classify 400 body before rotating — rate-limit-text 400s rotate, malformed 400s fail fast (#2101/#4976 guard)

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

* refactor(mimocode): extract auth-retry + 429/400 gating helpers — keep execute() under the cognitive-complexity gate

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

---------

Co-authored-by: Diego Rodrigues de Sa e Souza <8016841+diegosouzapw@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: pizzav-xyz <pizzav-xyz@users.noreply.github.com>
Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com>
2026-07-08 14:08:48 -03:00
Diego Rodrigues de Sa e Souza
fd7e4c10e5 feat(compression): omniglyph engine (context-as-image, Fable 5 direct) — stack + single mode (#6556)
* feat(compression): dependência omniglyph (file:) + smoke de import

* feat(compression): engine omniglyph — contexto-como-imagem com gates fail-closed

* fix(compression): omniglyph adapter fail-open no transform (try/catch)

* feat(compression): registra omniglyph no registry e catálogo (single mode, stackPriority 90)

* feat(compression): modo único omniglyph (async), selecionar o modo é o enable

* feat(compression): plumbing supportsVision + providerTransport até os engines

* feat(compression): estimador de tokens image-aware — modo stacked mantém a saída do omniglyph

* docs(compression): corrige comentário do prefixo base64 no decode PNG (64 chars)

* feat(compression): registra omniglyph nas listas de modo/engine (db, combo, deriveDefaultPlan, mcp)

* feat(dashboard): dedicated OmniGlyph engine screen (context-as-image)

Adds a per-engine detail page at /dashboard/context/omniglyph, alongside the
other compression engines in the sidebar. Four sections: the economics (measured
savings), a REAL before→after (dense text vs the rendered PNG page, not a mockup),
the fail-closed gate flow, and the enable control wired to /api/settings/compression
(preview engine, off by default). Sidebar entry + i18n label across all locales.

* chore(compression): consume published omniglyph@^1.0.0 from the npm registry

Replaces the local file: dependency used during the preview phase — npm ci
now resolves omniglyph from the registry with integrity, unblocking CI.

* fix(compression): satisfy v3.8.47 quality gates for the omniglyph engine

- dependency-allowlist: approve omniglyph (own package, published from
  diegosouzapw/OmniGlyph; supply-chain review done by the maintainer)
- ladder maps (#6533 guard): rank omniglyph 80 (stackPriority 90, runs after
  every text engine) with expectedReductionFactor 0.35 (measured 0.23-0.33)
- drop the two explicit any casts in omniglyph tests (no-explicit-any is
  error-level in tests since #6218)

* chore(compression): rebaseline strategySelector for the omniglyph mode dispatch

+18 lines of cohesive dispatch/type wiring at the existing mode chokepoints
(sync no-op + async single-mode branch + providerTransport on the options
types) — not extractable without hiding the dispatch boundary, mirroring the
prior compression rebaselines. Also drops an unused eslint-disable directive
in image-aware-tokens.test.ts (warning-level red under --max-warnings 0).

* chore(quality): register inherited base tests in stryker tap.testFiles

masked-200-exhaustion-fallback-6427 and headroom-codex-quota-snapshot-6379
arrived via the base merge without their stryker registration —
check:mutation-test-coverage --strict requires covering tests to be listed.

* refactor(compression): keep omniglyph wiring under the complexity gate

- extract the async single-mode resolution to engines/omniglyphSingleMode.ts
  (runCompressionAsync was at complexity 17 after the mode branch; back <=15)
- split OmniglyphContextPageClient into section components (was 161 lines in
  one function; every function now under the 80-line cap)
- complexity baseline 2052->2053: the +1 is inherited base drift (the ratchet
  does not run on fast-path merges — same pattern as the v3.8.44/46
  rebaselines); this PR's own code is measured complexity-net-zero

* chore(quality): register 3 more inherited base tests in stryker tap.testFiles

route-guard-middleware-local-only, combo-diagnostics-trace and
idempotency-fusion-collision arrived via the latest base merge without their
stryker registration (fast-path merges skip check:mutation-test-coverage).

* chore(quality): cognitive-complexity baseline 883->884 (inherited base drift)

check:cognitive-complexity measures 884 identically on the pristine
origin/release/v3.8.47 tip and on this HEAD — the PR itself is
cognitive-net-zero (single-mode resolution extracted to its own module,
page client split into section components). Same inherited-drift pattern
as the v3.8.4x release rebaselines.

---------

Co-authored-by: diegosouzapw <diegosouzapw@devbox.local>
2026-07-08 07:56:44 -03:00
Jillur Rahman
95e4bf72d4 fix(playground): accept a dashboard session for presets under REQUIRE_API_KEY (#6554)
Merged — thank you, @developerjillur! Accept a valid dashboard session for /api/playground/presets under REQUIRE_API_KEY (the Playground page authenticates via cookie, not an API key). Integrated into release/v3.8.47.
2026-07-08 01:36:12 -03:00
Jillur Rahman
ed0b9c73de perf(health): short-TTL cache for GET /api/monitoring/health (#6553)
Merged — thank you, @developerjillur! Short-TTL (1s) cache for the frequently-polled GET /api/monitoring/health, invalidated on DELETE (circuit-breaker reset). Integrated into release/v3.8.47.
2026-07-08 01:36:03 -03:00
Jillur Rahman
63d15bcb93 feat(combo): sanitized diagnostic trace on auto-combo terminal failure (#6545)
Merged — thank you, @developerjillur! Sanitized diagnostic trace on an auto-combo terminal failure (ids/reason-codes only, capped), plus an actionable reasoning-budget-exhausted message. Integrated into release/v3.8.47.
2026-07-08 01:35:55 -03:00
Jillur Rahman
8d59e1f660 fix(security): fail-closed CORS for cloud-agent management routes (#6543)
Merged — thank you, @developerjillur! Fail-closed CORS for the cookie/session-authed cloud-agent management routes (allowlist echo, credentials only for an explicitly allowlisted origin). Integrated into release/v3.8.47.
2026-07-08 01:35:46 -03:00
Jillur Rahman
899c40da67 fix(security): SSRF-guard provider validation probes (block cloud metadata) (#6542)
Merged — thank you, @developerjillur! SSRF-guards the provider-validation probes (block-metadata + no redirect) so a caller-controllable baseUrl can't relay to cloud metadata. Integrated into release/v3.8.47.
2026-07-08 01:35:38 -03:00
Jillur Rahman
2c42599e33 fix(security): loopback-gate /api/middleware/* (arbitrary JS via vm.Script) (#6541)
Merged — thank you, @developerjillur! Loopback-gates /api/middleware/* (arbitrary JS via vm.Script) for RCE parity with /api/plugins/*. Integrated into release/v3.8.47.
2026-07-08 01:35:29 -03:00
Jillur Rahman
db0830e60a fix(fusion): judge replayed a panel answer via idempotency-key collision (#6558)
Merged — thank you, @developerjillur! Namespaces the idempotency key by target provider/model + a messages digest so fusion panel/judge sub-requests can't collide on a shared client Idempotency-Key. Existing chatCore extracted-module tests were aligned to the composed-key contract. Integrated into release/v3.8.47.
2026-07-08 01:35:01 -03:00
Diego Rodrigues de Sa e Souza
639eedb1da fix(api): accept valid Codex connection edits instead of rejecting as Invalid request (#6562) (#6626) 2026-07-08 00:58:19 -03:00
Diego Rodrigues de Sa e Souza
ede77d1df0 fix(api): stop POST /api/keys hanging on the fire-and-forget Cloud sync (#6570) (#6624)
cloudEnabled defaults to true in settings.ts::getSettings() for any install
with no persisted settings row (every fresh install), so the create-key
handler's unconditional `await syncKeysToCloudIfEnabled()` always attempted a
real outbound fetch() to CLOUD_URL via syncToCloud(). When that endpoint is
unset/unreachable/slow, the HTTP response blocked until the request settled
or timed out (20-90s+), unlike sibling routes (regenerate, /api/combos) that
never touch this side effect.

syncKeysToCloudIfEnabled() is now dispatched fire-and-forget instead of
awaited; its internal try/catch already logs failures, so cloud sync still
runs in the background without blocking the response.
2026-07-08 00:33:56 -03:00
Diego Rodrigues de Sa e Souza
48e902c9d0 fix(startup): normalize non-Error throws + tolerate closed DB in instrumentation bootstrap (#6560) (#6622)
An update/restart could crash the whole server at boot with
TypeError: Cannot create property 'message' on string 'Database closed',
masking the real failure. driverFactory.ts's preInitSqlJs() cached its sql.js
WASM adapter per file path but never checked whether it had since been
closed by a racing gracefulShutdown/resetDbInstance; reusing the dead
handle made the next query throw sql.js's own raw string "Database closed"
straight out of instrumentation-node.ts's previously-unguarded
ensureDbInitialized() call. Next.js's registerInstrumentation() wrapper
unconditionally does err.message = ... on whatever register() rejects
with, and assigning .message on a primitive string throws in strict mode
-- that secondary TypeError is what actually crashed the process.

Fixed in two parts: preInitSqlJs() now evicts a closed cached adapter
instead of returning it, and a new ensureDbReadyForBoot() normalizes any
non-Error throw and retries once for a transient "database closed"
message before re-throwing anything else as a real Error.
2026-07-08 00:18:33 -03:00
Diego Rodrigues de Sa e Souza
fb3892b52e fix(auth): enforce API-key model/combo policy on the Codex Responses WebSocket bridge (#6564) (#6621)
The Codex Responses-over-WebSocket bridge authenticated the API key but
never called enforceApiKeyPolicy(), so a key restricted via
allowedModels/allowedCombos could still reach a direct Codex model
(e.g. gpt-5.5) through this transport, bypassing what the HTTP
/v1/responses path already enforces.

prepare() now builds an equivalent Request carrying an explicit
Authorization: Bearer <apiKey> header (the WS bridge's token normally
arrives via a query param) and calls enforceApiKeyPolicy() against the
client-requested model before any Codex-specific remapping or
credential selection.
2026-07-08 00:08:37 -03:00
Diego Rodrigues de Sa e Souza
78f05fc639 fix(startup): resolve AgentBridge MITM router key from existing OmniRoute key (#6403) (#6619)
AgentBridge's start/restart actions only ever checked an explicit apiKey
request field (never sent by the UI) and the ROUTER_API_KEY process env
var (unset unless manually exported), so startMitm() always spawned
server.cjs with an empty ROUTER_API_KEY and it hard-exited with
"no API key was provided". resolveRouterApiKey() now falls back to
pickApiKeyForInternalUse(), the same DB-backed selector already used by
the combo-health-check / cloud-sync-verify internal probes.
2026-07-07 23:27:34 -03:00
Diego Rodrigues de Sa e Souza
f8e179d479 fix(providers): send a Cloudflare-accepted Content-Type on Worker upload (#6416) (#6618) 2026-07-07 23:23:29 -03:00
Diego Rodrigues de Sa e Souza
318a5e5220 fix(startup): generate AgentBridge MITM certs for all 4 antigravity hosts (#6494) (#6617)
generateCert() hard-coded a single SAN entry (daily-cloudcode-pa.googleapis.com)
while server.cjs terminates TLS locally for all 4 antigravity/cloudcode-pa hosts,
so 3 of the 4 hosts served a cert whose CN/SAN didn't match and MITM interception
failed for them. Source the host list from the existing authoritative
ANTIGRAVITY_TARGET.hosts registry instead of a second hard-coded copy.
2026-07-07 23:17:22 -03:00
Diego Rodrigues de Sa e Souza
7d67fc705c fix(resilience): fall back on a 200 masking in-body credit exhaustion (#6427) (#6616)
`validateResponseQuality()` only inspected a response's top-level `error`
field when `choices` was also missing/empty (the narrower #3424 case), so a
masked HTTP 200 that echoed a non-empty stub `choices` alongside a structured
error object — or a known exhaustion phrase like "insufficient credits" /
"quota exceeded" in the error envelope — slipped through as valid, and a
`priority` combo kept hammering the exhausted target instead of failing
over.

The check now inspects the error envelope (top-level `error` object, or a
bounded exhaustion-phrase match against error.message/code/type and
top-level message/detail) unconditionally, before any shape-specific
branch — never against `choices[].message.content`, so legitimate
completions that merely mention "quota" in prose are not misclassified.

Regression guard: tests/unit/masked-200-exhaustion-fallback-6427.test.ts
2026-07-07 22:41:25 -03:00