Full-source eslint no-undef sweep over src/ + open-sse/ (config
listing node/web globals) found two remaining undeclared-variable
regressions of the same merge-loss family:
* api/providers/test-batch: branch commit f0adfb20 added a
providerId.startsWith(ANTHROPIC_COMPATIBLE_PREFIX) check but never
imported it; the master merge kept the buggy line. Every batch
test / provider-group filter touching a non-openai-compatible
provider threw ReferenceError (|| does not short-circuit).
* utils/stream.js finalizeStream: no-usage fallback called
appendRequestLog(), a stub upstream had marked no-op and whose
export chain was dropped by the merge. The call site only fires
when a stream ends without valid usage and now threw
ReferenceError inside the terminal callback. Removed the dead
call (behaviour identical: the stub wrote nothing).
All other no-undef reports are browser globals absent from the
scan config, not source bugs. next build --webpack passes; smoke
server boots and auth-rejects unauthenticated /v1 + /api traffic
as expected; unit suite 1746 pass / 103 fail (was 1738/111).
Audit of every branch-owned line the -X theirs merge dropped from
the 32 pre-merge commits found three more real regressions:
* src/sse/handlers/chat.js: merge kept the capsOverride feature
(bb8d67ba) but reverted the import block, so getCustomModels and
capabilitiesFromServiceKind were undefined. The runtime error was
swallowed by the feature's own fail-open try/catch — custom
models silently lost their vision override. Restored both imports.
* open-sse/providers/capabilities.js: TRUST_UPSTREAM_VISION (the
floor that keeps vision on for unknown models on upstream-validating
gateways like openrouter) was left as dead code by the merge —
upstream rewrote step 4 as refine() and dropped the check.
Re-applied it on top of the new refine() so catalog/limits
refinement still applies.
* tests/unit/chat-connection-pin.test.js: mock auth module lacked
isModelAllowedForKey added by f0adfb20.
* .gitignore: re-add .pi-subagents/.
The -X theirs merge of origin/master (v0.5.69) silently reverted five
branch-only hunks because upstream had no conflict-region counterpart
and simply won the three-way pick:
* services/usage.js: re-register the commandcode USAGE handler +
import. Without it the dashboard Quota Tracker fell through to
'Usage API not implemented for commandcode'. The handler module
(services/usage/commandcode.js) and registry usage block survived;
only the dispatch entry was dropped.
* ProviderLimits/utils.js: restore parseQuotaData 'commandcode' case
(currency-credit rows need unit "$" + remainingPercentage
forwarding, else $0.05 balances render as 0%).
* profile + providers/[id] pages: restore Math.max(1000, ...) connect
timeout floors so a stray '60' is never interpreted as 60ms.
* .gitignore: re-add .commandcode/ CLI local state.
Verified: tests/unit/commandcode-usage.test.js (6) and
usage-dispatch.test.js (2, asserts every provider routes to a real
handler) pass standalone; full unit run 1742 pass / 107 fail vs
1738/111 before this fix (remaining failures pre-existing, unrelated).
* page.js referenced setProviderStrategies (line 166) and
providerStrategies (line 597) but never declared the
useState — providers page threw ReferenceError on mount.
* The destructure of useNotificationStore() was also dropped
by the merge of origin/master; 5 sites in the file called
notify.error/.success/.warning.
* Both were present before the merge (commits de9e00c6 +
upstream master versions). The statusFilter commit
(d1d4e0f0) was the last state-block edit and survived,
but adjacent state lines were lost during the conflict
resolution.
* handleChatCore() referenced capsOverride (line 162) and
streamErrorPatterns (line 475) but the destructured param list
did not include them. Callers that did not pass these (e.g.
open-sse/handlers/responsesHandler.js, unit callers, older
client builds) would trigger 'capsOverride is not defined' /
'streamErrorPatterns is not defined' ReferenceError mid-request.
* Both defaulted to null. capsOverride is read by capability merge
(line 162, already guarded by '|| {}'). streamErrorPatterns is
read in the early-peek hook (line 484) which was null-safe
only because the variable happened to be in scope when chat.js
spread it in; responsesHandler never passed it and would crash.
* Unblocks all callers regardless of which fields they pass.
* Add /api/usage/request-details/raw endpoint serving a single
stored request detail verbatim (raw payloads), with /raw doc
clarifying it stays gated by the dashboard auth layer.
* Add RawDetailModal opened from a new 'Raw' button in
RequestDetailsTab. Modal loads /raw, exposes per-section copy
buttons and a 'Copy all (JSON)' that bundles every section.
* Capture the raw provider SSE text inside the streaming
transform (cap 64KB) and forward it through
onStreamComplete.rawProviderText so handler stores it as the
providerResponse. response.content stays the extracted user
text. Tool-call-only turns remain so the marker.
* Accumulate from translated client-facing chunks instead of
raw provider shapes so Responses, Claude delta types, and
Gemini/Antigravity parts all contribute.
* Drop redaction from the list endpoint; raw access is now via
the dedicated /raw endpoint. Tests cover the new behavior.
- Add muse-spark-1.2-contributor as responses-only model on OpenCode Go
- Normalize object tool schemas without properties in OpenCode Go executor
- Make fallback Responses call_ids unique across same-millisecond calls
- Make Responses output coercion fail-soft for circular and non-stringifiable values
- Recognize Fable weekly windows and normalize to weekly fable (7d)
- Fall back to 100% available weekly Fable window when Anthropic payload omits it
- Forward remaining percentages and enforce canonical Claude quota order in Quota Tracker
Co-Authored-By: Claude Code <noreply@anthropic.com>
- Group Antigravity Gemini text models into single 'Gemini (Flash / Pro)' quota
- Group Claude models into single 'Claude (Sonnet / Opus)' quota
- Prune stale or legacy model keys from hidden quota visibility list
Co-Authored-By: Claude Code <noreply@anthropic.com>
- Add muse-spark-1.3-contributor as responses-only model on OpenCode Go with dedicated executor
- Key Responses→chat streaming tool calls by item_id to prevent parallel tool calls merging into index 0
- Standardize tool coercions and call_id clamping in Responses API translation
Claude adaptive requests without an explicit effort are normalized to
output_config.effort: "high" instead of forwarding the unsupported
literal value "auto" which Anthropic rejects with HTTP 400.
- add a dedicated OpenCode Go executor that always sends x-opencode-session
- preserve a valid caller-provided native OpenCode session header
- translate downstream Agent session IDs into opaque, stable, Agent-scoped IDs
- forward the original provider session seed and client tool on both initial and credential-refresh requests
- Sync codebuddy-cn catalog and capabilities with copilot.tencent.com server payload
- Fix thinkingCanDisable semantics for glm-5.3 and deepseek-v4 models
- Add missing glm-5.2 thinking levels to thinkingLevels.js
- Add glm-5-turbo model to glm and glm-cn registries
- Registry/constants: drop qmodel_preview/gm51model, add lite,
qmodel_38max (Qwen3.8-Max), qfmodel (Qwen3.8-Flash), gmodel (GLM-5.3),
gfmodel (GLM-5.3-Flash)
- capabilities: add PROVIDER_CAPABILITIES['qoder'] so opaque internal
ids resolve to their real models' context windows and limits
- executor: preserve image blocks instead of flattening away, convert
Claude-style image blocks, and hash images into chat_record_id
- tests: cover image preservation, data-URI and Claude-block conversion
- build(docker): use CN mirrors for apk and npm
## Features
- **Fetch**: add Ollama Cloud web fetch provider
- **Gemini / Antigravity**: add Gemini 3.8 Flash support and bump IDE fingerprint to 2.11.0
- **Claude**: add Claude Fable 5.1 support (adaptive thinking with `output_config.effort`), bump Claude Code fingerprint to 2.1.258 for new-model access
- **Providers**: add client-side status filter (All / Active / Inactive / No connection) on the Providers dashboard; add max height and scroll for connection list
- **Providers & Models**: streamline tokenrouter model catalog down to 22 flagship/newest models and add missing provider icons; refresh Codebuddy-CN catalog (add hy4-preview/hy3/glm-5.3/kimi-k3-1, drop EOL glm-5.0/glm-4.7)
- **Models**: capability toggles (vision, reasoning) when adding custom models with upsert and live caps refresh
- **CLI tools**: support saving and managing custom API key presets
- **Quota**: add usage and rate-limit tracking for Groq via `x-ratelimit-*` headers
- **i18n**: complete Indonesian translation (1391 keys)
## Fixes
- **Security**: close SSRF guard bypasses in `ssrfGuard.js` (alternate IPv6 encodings, hostname trailing dots, wildcard DNS resolution check, safe redirect handling) (#3714)
- **Model markers**: strip the `[1m]` context marker Claude Code appends to model names (`claude-opus-5[1m]`) preventing model resolution failures (#3690)
- **Claude**: drop `server_tool_use` blocks carrying foreign IDs to avoid Anthropic 400 rejections; never anchor cache breakpoints on `defer_loading` tools (#3567)
- **Antigravity**: strike-break optimistic quota readings that keep 429ing by blocking the connection+model pair for 15m after 3 strikes (#3681); preserve client identity on model catalog requests (#3414)
- **Auth**: protect root `/responses` rewrite requiring API key validation in dashboardGuard
- **Chat & Docker**: return 503 Service Unavailable when all credentials are rate-limited; explicitly bundle `node-machine-id` into standalone Docker runtime image
- **OpenCode**: route Muse Spark models to `/zen/v1/responses` and declare vision support; filter inactive free model
- **Kiro**: preserve inline images as OpenAI-compatible `image_url` parts in OpenAI MITM; remove redundant top-level `systemPrompt` from payload
- **Usage**: read Responses-shape `cached_tokens` in `extractUsageFromResponse` for non-streaming traffic
- **Models**: support single model lookup with provider-prefixed IDs (e.g. `cc/claude-sonnet-5`)
- **Translator**: route Gemini thinking through `reasoning_effort` on OpenAI-compatible wire; convert `prefixItems` and ensure array items in Gemini schema sanitizer
- **UI**: apply persisted theme before first paint to prevent flash on reload; translate combo vision adapter label
Non-streaming codex traffic recorded cached_tokens: 0 even when upstream
prompt caching worked. The Claude-format branch (which OpenAI Responses
usage also matches) never read input_tokens_details, and the OpenAI
branch ignored a top-level flat cached_tokens. Read both in both
branches; Responses prompts are cache-inclusive so canonicalizeUsage
passes the value through without folding. 5 new regression tests.
Support single model lookup by replacing the one-segment models route
with a catch-all route that preserves capability kind paths while
allowing provider-prefixed IDs like cc/claude-sonnet-5.
Adds a client-side status filter (All / Active / Inactive / No
connection) to the Providers page, applied over the already-fetched
provider + connection list. Status derives from getProviderStats
(total, allDisabled); noAuth providers count as Active. Filter composes
with the existing search across all provider sections. Part of #3699.
Google's quota API can report remaining quota while generation endpoints
keep returning 429 (sprint/weekly dual-pool mismatch). handleAntigravityQuotaError
trusted remainingPercentage > 0 as healthy and returned null, causing 429 retry
loops across multi-account pools.
Add a strike-based circuit breaker to the optimistic and unavailable quota paths:
- After 3 strikes (429/409) within 60s for the same connection+model, cache-block
that pair for 15 minutes by synthesizing an entry in the shared RAM quota cache.
- Re-assert active strike blocks across refreshes so optimistic readings cannot
resurrect a broken pair prematurely.
- Reset strikes and clear synthesized cache entry upon successful request.
- Keep exact-resetAt handling for genuine 0% exhausted readings.
Closes#3681
- chat: always return 503 Service Unavailable when all credentials are rate-limited
- Dockerfile: explicitly copy node-machine-id into standalone runtime image
Route all Muse Spark models (not just 1.2) on OpenCode Free to
/zen/v1/responses via isMuseSparkModel(), fixing HTTP 500 on
muse-spark-1.3-contributor-free. Declare vision:true on Muse Spark
models so image input is no longer stripped; register 1.3 in the
registry and capabilities. Scoped to opencode only — other providers
keep Chat Completions routing.
Closes four SSRF guard bypasses reported in #3714:
- Block alternate IPv6 encodings (hex format, NAT64, IPv4-compatible, IPv4-mapped) by parsing to 16-bit groups
- Normalize trailing dots on hostnames to prevent FQDN bypasses
- Add assertPublicUrlResolved() with DNS resolution to block wildcard DNS domains resolving to private/metadata IPs
- Add fetchPublic() to safely handle and validate HTTP redirects
- add claude-fable-5-1 to the Claude Code model catalog (1M context,
permanent adaptive thinking)
- centralize the spoofed Claude Code version and update both request
and billing identities to 2.1.257 (Fable 5.1 rejects < 2.1.251)
- send output_config.effort without the redundant thinking switch for
permanently adaptive models
- add regression coverage for capabilities, headers, billing identity
and adaptive-effort payload
# Conflicts:
# open-sse/providers/registry/claude.js
# open-sse/providers/shared.js
# open-sse/utils/claudeCloaking.js
# tests/__baseline__/providers-baseline.json
With the 1M-context beta enabled, Claude Code sends model: "claude-opus-5[1m]".
The marker is a client-side annotation — it matches no combo name, no alias and
no provider/model pair — so the request dies at model resolution with
"Invalid model format" and the client reports "There's an issue with the
selected model". Every request from that session fails until the beta is
switched off.
New open-sse/utils/modelMarkers.js exporting stripModelContextMarker(modelStr)
-> { model, contextMarker }. handleChat strips the marker before resolution and
normalizes body.model so downstream logging and translation see the real name.
Only a trailing marker is stripped, so a model whose name genuinely contains
brackets is left alone.
The capability itself travels in anthropic-beta: context-1m-2025-08-07, which
the default executor already forwards untouched — only the routing key needed
cleaning.
Fixes#3690.
Tests: tests/unit/model-context-marker.test.js (6 cases).
Restrict the legacy IDE-version override to generation endpoints so
catalog and other passthrough requests keep their original User-Agent
and metadata.ideVersion, letting newer Antigravity releases see current
models like Gemini 3.7 Flash in the MITM selector.
Fixes#3414