Closes#2213
Conflict resolution: kept both images field (HEAD) and origin field (PR),
kept toolsAttached return value (HEAD) while incorporating all 8mbe
improvements (schema normalization, synthetic user guard, orphaned tool
results, alternating role enforcement). All 16 tests pass.
Blackbox's `/api/chat` now rejects requests whose `validated` field
doesn't match the frontend `tk` token (exported from app.blackbox.ai's
Next.js bundle), returning HTTP 403 even when the session cookie is
valid and the subscription is active. The previous executor sent a
random UUID, which works only until Blackbox enforces the check.
This change:
- Adds `resolveBlackboxValidatedToken()` that returns
`BLACKBOX_WEB_VALIDATED_TOKEN` when set, otherwise falls back to the
legacy random UUID (no regression for users who already work).
- Detects 403 responses whose body indicates a token-specific failure
("invalid validated token", "validation token", etc.) and replaces
the generic "cookie expired" message with explicit guidance to set
BLACKBOX_WEB_VALIDATED_TOKEN. The cookie-expired path is preserved
for non-token 401/403.
- Documents the env var in `.env.example` and
`docs/reference/ENVIRONMENT.md` (env-doc-sync check passes).
Deliberately NOT included: runtime scraping of Blackbox's Next.js
chunks to auto-extract `tk`. That coupling to their bundle hash would
silently break on every frontend deploy — the env override is the
stable path for operators who have already resolved the token.
Reported by @kazimshah39 with detailed root-cause analysis.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Custom OpenAI-/Anthropic-compatible providers ship with synthetic IDs
like "openai-compatible-chat-<uuid>". ApiManagerPageClient was grouping
models by raw `model.owned_by`, so the user saw the full synthetic id
in the model picker.
Wrap `model.owned_by` with the existing centralized
`getProviderDisplayName` helper (already used by Endpoint, Health, and
Combos pages). The helper detects the dynamic-compatible pattern and
renders it as "Compatible (openai)" / "Compatible (anthropic)" — a
small but meaningful improvement that takes the dashboard one step
closer to issue #260's "no raw IDs in the UI" goal.
Showing the user-entered node name ("Poe") instead of the generic
"Compatible (openai)" label remains a follow-up (requires fetching
/api/provider-nodes here and matching by id/prefix); tracked separately.
Reported by @pulyankote with a complete surface-by-surface table.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
When a Qoder connection lands in OAuth/CLI-flavored mode but the user
has pasted a Personal Access Token, the provider test route surfaces
"Local CLI runtime is not installed" plus a cascading 401 from
DashScope. Neither error tells the user "you picked the wrong auth
mode, switch to API Key".
The runtime check now detects this state (Qoder + non-apikey authType +
a token present on the connection or providerSpecificData) and surfaces
a single actionable message: "Qoder OAuth/Local CLI mode is selected
but the Qoder CLI is not detected. If you have a Personal Access Token,
switch this connection to API Key auth instead."
Non-Qoder providers and Qoder in real OAuth/CLI mode without a token
still get the original generic message.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The toggle is labeled "Block Claude Extra Usage" but the success
notification simply said "Claude extra-usage blocked / allowed". Users
who interpreted the toggle as "Allow Extra Usage" then read the message
as inverted ("I enabled it and it tells me it's blocked").
New text spells out the toggle→effect relationship:
- ON → "Claude extra-usage blocking enabled (extra usage will be blocked)"
- OFF → "Claude extra-usage blocking disabled (extra usage is allowed)"
No functional change. Reported by @uwuclxdy.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
When OmniRoute translates Claude responses to the OpenAI format,
prompt_tokens was summing input + cache_read + cache_creation. Anthropic
pads short prompts up to a 1024-token minimum to create a cache, so:
Request: {"messages":[{"role":"user","content":"hi"}]}
Claude usage: input=8, cache_read=4, cache_creation=2000
Dashboard "Total In": 8
HTTP response prompt_tokens: 2008 (8 + 4 + 2000)
That 250x inflation broke downstream billing systems (Sub2API, NewAPI,
OneAPI) that trust prompt_tokens as the source of truth.
This change:
- prompt_tokens = input_tokens + cache_read_input_tokens (matches what
the dashboard reports as "Total In", preserves issue #1426's intent
that cache reads are billable input)
- cache_creation_tokens stays visible in
prompt_tokens_details.cache_creation_tokens for auditing — it just no
longer inflates the headline number
Reported by @downdawn with a complete root-cause trace.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Anthropic-native clients (Claude Code, @anthropic-ai/sdk) authenticate
via x-api-key per the Messages API contract. extractApiKey only read
Authorization: Bearer, so:
- usage_history.api_key_id was NULL for all x-api-key traffic (~50% of
real-world traffic invisible in Costs/Analytics)
- api_keys.last_used_at never updated for keys delivered via x-api-key
- per-key policies (allowedModels, budget, rateLimits, accessSchedule,
expiresAt) were bypassed for every Anthropic-native client
The fallback honors x-api-key (case-insensitive) when no Authorization:
Bearer is present. Bearer still wins when both are set, preserving
back-compat for clients that already send both. Reported by
@Forcerecon with a complete repro and validation plan.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The PATH_REGEX introduced in 1a39c31f used a greedy character class
([\w\-./\\]+) that can backtrack quadratically on inputs like
"///////..." — flagged by CodeQL js/polynomial-redos (#232).
Replace it with simple whitespace tokenization + a length-bounded
prefix-check helper. Same observable behaviour (paths replaced with
<path>), but linear time regardless of input shape. Adds a 4096-char
input cap as defence in depth.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The PATH_REGEX introduced in 1a39c31f used a greedy character class
([\w\-./\\]+) that can backtrack quadratically on inputs like
"///////..." — flagged by CodeQL js/polynomial-redos (#232).
Replace it with simple whitespace tokenization + a length-bounded
prefix-check helper. Same observable behaviour (paths replaced with
<path>), but linear time regardless of input shape. Adds a 4096-char
input cap as defence in depth.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Fixes the 4 fixable alerts opened in the recent scan and adds enforceable
guardrails so future development follows the same pattern.
Code fixes:
- src/mitm/cert/install.ts: pass certPath/certName/action via exec()'s env
option instead of string-interpolating them into the bash script
(CodeQL js/shell-command-injection-from-environment #225)
- scripts/docs/{gen-provider-reference,add-frontmatter,fix-internal-links}:
escape backslash before other regex/markdown metacharacters
(CodeQL js/incomplete-sanitization #227, #228, #229)
Documentation (mandatory patterns):
- docs/security/PUBLIC_CREDS.md — embedding public upstream OAuth/Firebase
identifiers via resolvePublicCred(); never as string literals
- docs/security/ERROR_SANITIZATION.md — routing every error response through
sanitizeErrorMessage()/buildErrorBody(); never raw err.stack/err.message
- CLAUDE.md: 4 new Hard Rules (#11-#14) + Security section + scenario notes
- AGENTS.md, CONTRIBUTING.md: cross-reference the two new docs
- SECURITY.md: extended Hard Security Rules with the new mandatory patterns
- docs/README.md: index entries pointing to the two new docs
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Fixes the 4 fixable alerts opened in the recent scan and adds enforceable
guardrails so future development follows the same pattern.
Code fixes:
- src/mitm/cert/install.ts: pass certPath/certName/action via exec()'s env
option instead of string-interpolating them into the bash script
(CodeQL js/shell-command-injection-from-environment #225)
- scripts/docs/{gen-provider-reference,add-frontmatter,fix-internal-links}:
escape backslash before other regex/markdown metacharacters
(CodeQL js/incomplete-sanitization #227, #228, #229)
Documentation (mandatory patterns):
- docs/security/PUBLIC_CREDS.md — embedding public upstream OAuth/Firebase
identifiers via resolvePublicCred(); never as string literals
- docs/security/ERROR_SANITIZATION.md — routing every error response through
sanitizeErrorMessage()/buildErrorBody(); never raw err.stack/err.message
- CLAUDE.md: 4 new Hard Rules (#11-#14) + Security section + scenario notes
- AGENTS.md, CONTRIBUTING.md: cross-reference the two new docs
- SECURITY.md: extended Hard Security Rules with the new mandatory patterns
- docs/README.md: index entries pointing to the two new docs
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Embed Gemini, Antigravity and Windsurf public OAuth/Firebase identifiers
(extracted from upstream CLI binaries) through a XOR-masked byte sequence
in open-sse/utils/publicCreds.ts instead of source literals, so pattern
scanners (GitHub Secret Scanning, Semgrep) stop raising false positives on
every release. decodePublicCred passes raw values through unchanged for
users who already have plaintext in their .env (no migration needed).
- New utils/publicCreds.ts with decode/encode + tests
- Replace 6 hardcoded Google client_id/secret in oauth.ts + providerRegistry
- Drop literals from .env.example (comment-only documentation)
- Sanitize error messages inside buildErrorBody so every caller (incl.
createErrorResult) is covered; cursor.ts now reuses the shared helper
- Cover the new helpers with unit tests (publicCreds + error sanitization)
Resolves the open code-scanning js/stack-trace-exposure findings and the
secret-scanning Google API Key alert without breaking existing setups.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Embed Gemini, Antigravity and Windsurf public OAuth/Firebase identifiers
(extracted from upstream CLI binaries) through a XOR-masked byte sequence
in open-sse/utils/publicCreds.ts instead of source literals, so pattern
scanners (GitHub Secret Scanning, Semgrep) stop raising false positives on
every release. decodePublicCred passes raw values through unchanged for
users who already have plaintext in their .env (no migration needed).
- New utils/publicCreds.ts with decode/encode + tests
- Replace 6 hardcoded Google client_id/secret in oauth.ts + providerRegistry
- Drop literals from .env.example (comment-only documentation)
- Sanitize error messages inside buildErrorBody so every caller (incl.
createErrorResult) is covered; cursor.ts now reuses the shared helper
- Cover the new helpers with unit tests (publicCreds + error sanitization)
Resolves the open code-scanning js/stack-trace-exposure findings and the
secret-scanning Google API Key alert without breaking existing setups.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Normalize tool schemas: strip additionalProperties and empty required arrays
- Merge consecutive assistant messages and adjacent user turns after role normalization
- Prepend synthetic user message when conversation starts with assistant
- Convert orphaned toolResults to inline text when assistant with toolUses is missing
- Enforce strictly alternating user/assistant roles in history
- Use deterministic uuidv5 conversationId based on first message for session caching
- Ensure origin field is present on all userInputMessage entries
- Resolve <<<<<<< HEAD conflict in RoutingTab.tsx by keeping the
HEAD version with aria-disabled and pre-computed titleText
- Fix inconsistent indentation in CLI help text (providers commands
and CLI Tools section)
After cherry-picking PR #2231, the function signature changed from
positional (provider, model) to object ({ provider, model }). Fixes the
2 pre-existing tests that still used the old positional style.
- chatCore.ts: pass {requestId:skillRequestId,messageIndex:0} to cacheReasoningFromAssistantMessage
- responseSanitizer.ts: widen isDeepSeekV4Model regex to match all deepseek-v4 variants
Fixes Anthropic HTTP 400 errors (~49/h on claude-opus-4-7) by preserving
the latest assistant message's thinking blocks verbatim instead of
rewriting them to redacted_thinking.
Co-authored-by: NomenAK <anton@nomenak.dev>
The root package.json was updated in 52f3285d to drop Node 20.x support
(http-proxy-middleware 4.x requirement). electron/package.json had no
engines field declared, leaving the desktop build implicitly permissive.
Adds the same constraint (>=22.22.2 <23 || >=24.0.0 <27) to keep the
electron workspace consistent with the root engine policy.
Refs: #2228
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
http-proxy-middleware 4.x (introduced via #2228) requires Node >=22.15.0.
Updated engines.node to >=22.22.2 <23 || >=24.0.0 <27 (drops 20.x).
BREAKING CHANGE: users on Node 20.x must upgrade to Node 22.22.2+ or 24+.
Refs: #2228
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- antigravity: AntigravityCredentials.projectId widened to string|null
to match base ProviderCredentials shape post-#2227 squash merge.
- responses-handler: heartbeat assertion updated for #2233's new
openai-responses-in-progress shape (was: keepalive comment).
- search-registry: expected count is now 12 (ollama-search +
zai-search both landed in this release).
After merging PRs #2221 (ModelSync shared loopback readiness gate + IPv4 force)
and #2219 (Antigravity loadCodeAssist bootstrap + fetchAvailableModels fallback)
into release/v3.8.0, two test suites needed updates to match the new routing:
- tests/unit/model-sync-route.test.ts:
* resetStorage() now calls __resetLoopbackReadinessForTests() so the
module-level __loopbackReadyPromise cache does not leak between tests.
* Every fetch mock now answers the /__readiness_probe__/ URL with 404 so
the gate opens immediately (any HTTP response satisfies the probe).
* Self-fetch target URL assertions updated from http://localhost/...
to http://127.0.0.1:20128/... per PR #2221's IPv4-force.
- tests/unit/provider-models-route.test.ts:
* The Antigravity discovery-retry test now treats loadCodeAssist calls as
non-fatal failures so the discovery path is still exercised.
* The expected discovery URL sequence is updated to the new
fetchAvailableModels-first order introduced by PR #2219.
The local-aliases-precedence path used `typeof aliases[parsed.model] === "string"`
to guard string-only operations, but TypeScript does not narrow the variable
`directTarget` from that index-expression test — the variable retained the union
type ModelAliasValue (string | object), so `indexOf`/`slice` were typed as
property accesses on the object branch and the strict-core typecheck failed.
Refactors to capture `directTarget` first and run `typeof directTarget === "string"`
on the variable, which TS does narrow. No runtime semantics change — local-aliases
tests still pass.