Commit Graph

1155 Commits

Author SHA1 Message Date
Teguh Rijanandi
a58902e4a7 feat(i18n): complete Indonesian translation (1391 keys) 2026-09-03 09:33:01 +07:00
Sutarto Jordan Chrisfivo
98579f98c1 fix(auth): protect root /responses rewrite
Add /responses to PUBLIC_PREFIXES in dashboardGuard so pre-rewrite remote
requests require API key validation as intended.
2026-09-03 09:29:12 +07:00
vianhanif
15687d1913 fix(chat,docker): return 503 for rate-limited providers and bundle node-machine-id
- chat: always return 503 Service Unavailable when all credentials are rate-limited
- Dockerfile: explicitly copy node-machine-id into standalone runtime image
2026-09-03 09:25:17 +07:00
anojndr
acb5c34cdc fix(opencode): route Muse Spark models to Responses API and declare vision
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.
2026-09-03 09:24:18 +07:00
openhands
b870b5d41b fix(security): close SSRF guard bypasses in ssrfGuard.js (#3714)
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
2026-09-03 09:22:22 +07:00
Outis
1f190bd00b fix(kiro): preserve inline images in OpenAI MITM
Forward Kiro userInputMessage.images as OpenAI-compatible image_url content parts.
2026-09-03 09:21:37 +07:00
Zafar
70f15aa50b feat(antigravity,gemini): add Gemini 3.8 Flash support and bump IDE fingerprint to 2.11.0
Co-authored-by: Schnee111 <daffamaarif.dev@gmail.com>
Co-authored-by: AhooraZen <ahoora935137@gmail.com>
Co-authored-by: anojndr <anojndr@gmail.com>
Co-authored-by: Emirhan <emirhan551952@gmail.com>
2026-09-03 09:13:45 +07:00
Lek Huda
1fe996db6a fix(translator): route Gemini thinking through reasoning_effort on OpenAI-compatible wire 2026-09-03 09:12:34 +07:00
decolua
c24a854278 feat(cli-tools): support saving and managing custom API key presets
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-03 09:06:08 +07:00
decolua
1fc2a81d65 fix(kiro): remove redundant top-level systemPrompt field from payload
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-03 09:06:02 +07:00
decolua
f6c59d30b0 fix(gemini): convert prefixItems and ensure array items in schema sanitizer
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-03 09:05:57 +07:00
LucasOl1337
ac9120fde3 fix(claude): support Fable 5.1
- 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
2026-09-02 20:42:53 +07:00
Federico Liva
ee7a961633 fix: strip the [1m] context marker Claude Code appends to the model name
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).
2026-09-02 20:20:32 +07:00
Matt Van Horn
f68d2f5ee5 fix(antigravity): preserve client identity on model catalog requests
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
2026-09-02 20:14:59 +07:00
Federico Liva
ed1bd0c528 fix(claude): drop server_tool_use blocks carrying a foreign id
Anthropic validates server_tool_use.id against ^srvtoolu_[a-zA-Z0-9_]+$
and 400s the whole request when one does not match. A combo that falls
back to a provider with its own built-in tools (z.ai/glm emits
OpenAI-style call_ ids for analyze_image) leaves such blocks in the
history, so every later Claude turn fails.

Extend normalizeClaudePassthrough to drop those blocks (reusing the
existing loop), drop the paired tool_result / web_search_tool_result
referencing a dropped id, and drop empty text blocks plus messages left
with no content. Well-formed srvtoolu_ blocks and regular tool_use ids
are untouched.
2026-09-02 20:07:16 +07:00
docaohieu2808
925cb4aade fix(ui): apply persisted theme before first paint to avoid flash on reload
Theme was applied from the client store in useEffect (after hydration),
so a reload painted the default light theme for a frame before the
stored dark theme was reapplied. Add a blocking head script that reads
the persisted zustand theme key and sets the dark class on
documentElement before first paint, mirroring applyTheme() including
system -> prefers-color-scheme resolution.
2026-09-02 20:05:52 +07:00
openhands
b9c92cb83c feat(quota): add usage tracking for Groq
First slice of #3701: quota tracking for Groq via x-ratelimit-* response
headers on the models endpoint (no dedicated quota endpoint exists, and
reading usage costs zero tokens).

- usage/groq.js: parse request+token limit/remaining headers; Go-style
  duration reset headers ("2m59.56s") resolve to future timestamps;
  missing key/401/403 -> message, 2xx without headers -> soft
  "not tracked yet" with quotas:{}
- registry/groq.js: transport.usage.url (reuses validateUrl) +
  features {usage, usageApikey}
- services/usage.js: groq entry in USAGE_HANDLERS
- ProviderLimits/utils.js: parseQuotaData case (absolute used/total,
  codex/kiro style)
- tests: groq-usage.test.js (registry flags, header parsing, soft
  not-tracked path, missing key/401, parseQuotaData)
2026-09-02 20:04:35 +07:00
decolua
44e4b80bbe fix(models): filter dead opencode free model
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-02 20:01:15 +07:00
dajinglingpake
9d3f7646d1 fix(i18n): translate combo vision adapter label 2026-09-02 19:45:34 +07:00
decolua
009cac6326 fix(claude): bump CC fingerprint to 2.1.258 for new-model access
Anthropic gates newly released models (e.g. claude-fable-5-1) to Claude
Code >= 2.1.251; the spoofed 2.1.92 client got HTTP 400 on every request.
Bump User-Agent + billing-header version to 2.1.258 and refresh the
providers baseline snapshot.

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-02 11:27:21 +07:00
decolua
38f031f4c9 feat(models): capability toggles for custom models with upsert and live caps refresh
- AddCustomModelModal lets users pick vision/reasoning caps when adding a model
- POST /api/models/custom whitelists caps to booleans
- aliasRepo.addCustomModel upserts — re-adding updates caps/name in place
- /api/models includes custom llm models with stored caps overriding the heuristic
- useModelCaps refetches on customModelChanged instead of trusting a stale cache

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-01 10:45:51 +07:00
decolua
90b52e06ff # v0.5.59 (2026-08-29)
## Features
- **Search**: new web search providers — Antigravity (Google Search grounding
  on the existing OAuth account pool, citations keyed and merged by URL) and
  Xquik (X search with `x-api-key` auth, cursor pagination, credit-based
  usage), both on `POST /v1/search`. Based on #3437 by @Nautilaceae
- **Search**: ollama-search and zai-search borrow a chat provider's API key
  instead of requiring their own connection, driven by a new
  `credentialFallback` registry field. zai-search later folded into the `glm`
  provider itself so the web search page shows the shared connection
- **Models**: daily background sync of model capabilities from models.dev —
  modalities keyed by model id (majority of sources must declare one),
  context/output limits keyed by provider + model, strictly additive and
  sitting below the hand-written tables. ETag + mtime cache, 60s startup
  delay, `MODEL_CATALOG_SYNC=off` to disable
- **Models**: add GLM-5.3-Flash (1M context, natively multimodal), DeepSeek
  V4 Vision, Grok 4.5/4.6 (500k context); correct glm-4.6v/4.5v video input
  and output limits, backfill glm-4.6v on glm-cn
- **Usage**: show the Zed plan quota on the dashboard — plan, edit
  predictions, hosted model requests and billing-cycle reset; unlimited rows
  render as "N used · Unlimited"
- **Usage**: track GPT-5.3-Codex-Spark quota windows (spark_session /
  spark_weekly) from the Codex usage response (#3431)
- **Antigravity**: quota-aware routing — on 409/429 fetch live quota for the
  exact per-model resetAt and skip only the exhausted account/model pair;
  report the earliest reset when every account is blocked (#3561)
- **Antigravity**: map image `size` to the aspect-ratio model suffix (-WxH);
  add the Gemini 3.7 Flash tiers to MITM defaultModels so they show up in
  the dashboard model-mapping table
- **Dashboard**: bulk import Grok CLI accounts from JSON — paste an array or
  drag-drop multiple .json files, all OAuth connections created in a single
  call, mirroring the codex flow
- **CLI tools**: endpoint presets shared across every tool card through one
  live-resyncing store, instead of per-card localStorage copies that never
  saw each other's saved endpoints
- **Token Saver**: configurable compression timeout (`headroomTimeoutMs`) —
  the fixed 3000 ms made busy machines time out and send inconsistently
  compressed bodies, hurting prompt caching
- **i18n**: pt-BR expanded to 1132 terms

## Fixes
- **Stream**: record usage when a client closes on the terminal event — the
  Responses API has no [DONE] sentinel, so codex closed the socket on
  `response.completed` and cancelled the reader before flush() ran its usage
  side effects; the tail now lives in a once-guarded finalizeStream(). Also
  stop logging a disconnect for every completed Responses call
- **Stream**: parse the trailing NDJSON line an Ollama stream leaves behind
  without a closing newline — the final chunk carrying `done_reason` and the
  token counts was dropped
- **Session**: read the Claude Code session id from the
  `x-claude-code-session-id` header — `metadata.user_id` is dropped by
  Responses translation, splitting one conversation across several
  `prompt_cache_key` values and missing the upstream prefix cache
- **Usage**: preserve nested `cached_tokens` — the top-level-only read
  persisted `cached_tokens: 0` for every Responses-format provider (codex,
  grok-cli, …), billing cache hits at the full input rate
- **Usage**: GLM quotas accept CREDIT_LIMIT plans and multi-interval windows
  (5h session / 7d weekly) instead of overwriting a single "session" key
- **Models**: the catalog sync no longer erases its own output — deltas were
  measured against the previous run's writes (the second run cut `providers`
  from 20 entries to 5); one vote per provider in the modality tally, ETag
  restored from file on startup, and the worker thread dropped after the
  bundler rewrote its path into a module-not-found error
- **Executor**: CommandCode returns errors as a `type:"error"` event inside
  an HTTP 200 NDJSON stream — peek the first events before committing, abort
  and return a real 4xx/5xx so combo/account fallback triggers instead of
  streaming the error text as content
- **Search**: scope failure locks on the credential-fallback path — a failing
  search locked `modelLock___all` and took the shared glm key offline for
  chat as well; locks are now attributed to the connection's owner and
  scoped to `websearch:<provider>`
- **Providers**: connection tests get a 15s AbortSignal timeout instead of
  hanging and exhausting the browser socket pool; guard undefined provider
  names on the providers page
- **Antigravity**: sanitize competing-client branding via a config-driven
  rule table (Zed's Claude-agent prompt, opencode → antigravity) — upstream
  answers 429 Quota Exhausted. Applied in the executor so the shared
  openai-to-gemini translator leaves gemini/vertex/zed untouched
- **MiniMax**: preserve images on the sourceFormat-matched OpenAI transport
  — MiniMax-M3 resolved a Claude-shaped body posted to the OpenAI endpoint,
  silently dropping `image_url` blocks (#3418)
- **Claude**: decloak tool names in same-format streaming passthrough —
  OAuth-cloaked names (CLAUDE_TOOL_SUFFIX) leaked to the client and every
  tool call was rejected as unknown
- **Tools**: default a missing `tools[].type` to "custom" on Claude-format
  requests — strict Anthropic-compatible gateways (MiniMax) reject the
  request with 400 otherwise
- **Translator**: zai thinkingFormat sends the top-level `reasoning_effort`
  object GLM-5.2+ requires — every GLM-5.x request ran at the model default
  (max); gated on GLM-5.2+ since older GLM does not read it (#2721)
- **RTK**: system prompt injection matches each target wire format
  (Chat/Responses/Claude/Gemini/Kiro) and is exact-idempotent across retries,
  so distinct prompts sharing a long prefix are no longer collapsed (#3202).
  Also set the diagnostic before the silent null return on Responses
  translation failure so the panel is no longer blank
- **OpenCode**: route muse-spark through /zen/v1/responses (it 500s on
  chat/completions), normalizing the Chat fields the Responses API rejects
  and clamping max/ultra effort to xhigh
- **CLI**: install better-sqlite3 without build tools on Node 22+ (N-API
  13.0.3 ships per-platform prebuilds, `--ignore-scripts` skips the implicit
  node-gyp build); Node < 22 stays on 12.6.2, working installs untouched
- **CLI tools**: send the API key Codex actually reads —
  `[model_providers.9router.http_headers]` instead of auth.json (which left
  every request 401 and clobbered an existing ChatGPT login); subagent model
  moved to `agents.default_subagent_model`
- **OAuth**: refresh Cline tokens with the extension JSON contract
- **Dashboard**: clamp the API key mask length — keys shorter than 8 chars
  threw RangeError and crashed the media-provider detail page
- **UI**: wait for the Material Symbols font itself before revealing icons —
  `document.fonts.ready` resolved before the 4MB woff2 even started loading,
  leaving icons blank until a second load
2026-08-29 17:59:36 +07:00
decolua
2203cd8f2b test(translator): drop the golden url/header snapshot
The committed snapshot had drifted from the registry: five providers
mismatched on a plain checkout and alitp-intl was missing entirely.
Remove it so the suite regenerates from the current registry.
2026-08-28 18:19:53 +07:00
decolua
2fd99eae5d fix(session): read Claude Code session id from its request header
Claude Code carries the session in metadata.user_id, which the Responses
API translation drops before the executor resolves a cache session. The
request then fell through to the assistant-text hash and the per-connection
fallback, so one conversation was split across several prompt_cache_key
values and the upstream prefix cache kept missing.

Fall back to the x-claude-code-session-id header, which survives every
translation. The body stays authoritative when both are present.
2026-08-28 18:17:55 +07:00
Agung Gunawnan
df85e16d7a fix(providers): time out connection tests and guard undefined names
Apply a 15s AbortSignal timeout in fetchWithConnectionProxy when the
caller supplies none, so provider connection tests stop hanging and
exhausting the browser socket pool. Also make matchSearch return false
for falsy provider names instead of crashing the providers page.
2026-08-28 17:06:36 +07:00
Ahoora5678
dff648496c fix(antigravity): sanitize competing-client branding in system prompts
Antigravity flags requests whose system prompt identifies another vendor's
client and answers 429 Quota Exhausted. Move the existing Zed/Claude prompt
rewrite into a config-driven rule table and add case-preserving opencode ->
antigravity mapping.

Applied in the executor so only Antigravity requests are rewritten - the
shared openai-to-gemini translator also serves gemini, gemini-cli, vertex
and zed, which must not be touched.
2026-08-28 17:01:18 +07:00
Paulo Schuller
88676b3037 fix(oauth): refresh Cline tokens with extension JSON contract 2026-08-28 16:58:27 +07:00
fasilu
bb3cb43e09 fix(dashboard): clamp API key mask length for short keys
"•".repeat(apiKey.length - 8) threw RangeError when the key was
shorter than 8 chars, crashing the media-provider detail page.
2026-08-28 16:53:14 +07:00
Óscar Fonseca
4a371d1d9f fix(usage): preserve nested cached_tokens in canonicalizeUsage
buildUsage() only emits cache reads under prompt_tokens_details, so the
top-level-only read dropped the count for every Responses-format provider
(codex, grok-cli, ...), persisting cached_tokens: 0 and billing cache hits
at the full input rate. Mirror the cache_creation fallback already used
just above.
2026-08-28 16:46:05 +07:00
alfep
d91e8b85e0 feat(antigravity): add Gemini 3.7 Flash tiers to MITM defaultModels
Registry/pricing/CLI catalog already had gemini-3.7-flash-{high,medium,low}
but MITM_TOOLS.antigravity.defaultModels was missing them, so the tiers
never showed up in the dashboard model-mapping table.
2026-08-28 16:44:07 +07:00
snower
993c6eb469 feat(headroom): make the compression request timeout configurable
The 3000 ms timeout on /v1/compress was fixed, so busy or slow machines
timed out often and sent the LLM an inconsistently compressed body,
hurting prompt caching. Add a headroomTimeoutMs setting, thread it from
the chat handler down to compressWithHeadroom, expose it in the Token
Saver dashboard, and normalize invalid values back to the 3000 ms default.
2026-08-28 16:34:33 +07:00
turingcat
28d005772a fix(minimax): preserve images on matched OpenAI transport
Prefer the sourceFormat-matched runtime transport over a model's
declared targetFormat when both apply. MiniMax-M3 previously resolved
to a Claude-shaped body while being posted to the already-selected
OpenAI endpoint, silently dropping image_url blocks from OpenAI
clients. Fixes #3418.
2026-08-28 16:34:05 +07:00
fasilu
2a9213c5bd feat(antigravity): map image size to aspect-ratio model suffix
Resolve body.size through sizeToAspectRatio and append the ratio as a
-WxH suffix so the executor's parseImageConfig picks it up. Also fall
back to gemini-3.1-flash-image when a non-image model reaches the
image handler.
2026-08-28 16:33:23 +07:00
decolua
ec6692808b fix(search): scope failure locks so search cannot take chat offline
Two problems on the credentialFallback path, where a search provider
borrows a chat provider's connection:

- the lock was attributed to the search provider id, but the connection
  belongs to the chat provider, so markAccountUnavailable looked it up
  under the wrong provider and read a stale backoffLevel
- with no model argument the lock key is `modelLock___all`, which
  isModelLockActive treats as blocking every model — one failing search
  would have taken the shared glm key offline for chat as well

Attribute the lock to the provider that owns the connection, and scope
it to `websearch:<provider>`, passed to getProviderCredentials too so
the lock is read back under the same key.
2026-08-28 16:18:46 +07:00
Amir Seify
e5a13c3ab7 feat(usage): show Zed plan quota on the dashboard
Add a Zed usage handler so connected Zed accounts appear on
/dashboard/quota. Reads GET /client/users/me for plan, edit
predictions, optional hosted model requests and billing-cycle reset.

Render unlimited rows as "N used · Unlimited" instead of 0 / ∞, and
surface overdue-invoice / token-billing messages.
2026-08-28 16:16:01 +07:00
Bertho Joris
67d9182e1a fix(executor): handle CommandCode in-stream errors for combo and account fallback
CommandCode returns errors as a type:"error" event inside an HTTP 200
NDJSON stream instead of a non-200 status, so the existing combo/account
fallback logic (keyed off response.status) never triggered and the error
text was streamed to the client as if it were content.

Peek the first NDJSON events before committing to a stream; on a
type:"error" event, abort and return a proper 4xx/5xx Response instead.
Normal streams are replayed losslessly (buffered prefix + rest of the
stream) through the existing translator, so the happy path is unchanged.
Add CommandCodeExecutor.parseError() so parseUpstreamError() can extract
a clean message/status from the synthesized error body.
2026-08-28 16:15:19 +07:00
decolua
9dbdca0e5e refactor(search): fold zai-search into the glm provider
The separate zai-search entry showed "No connections" on the web search
page because credentials live on the `glm` connection, not on it. Every
other provider that does both chat and search (antigravity, kimi, xai,
gemini) declares webSearch on the provider itself, so do the same here.

- glm gains serviceKinds ["llm", "webSearch"] and the MCP searchConfig
- the request builder / normalizer move from "zai-search" to "glm"
- drop the zai-search registry entry and its svg logo, which also
  removes the only need for svg logo support in getProviderIconSrc

ollama-search keeps its own entry and credentialFallback: its search
endpoint is unrelated to the ollama chat transport.
2026-08-28 16:12:04 +07:00
decolua
5a86f6a8d2 feat(search): add ollama-search and zai-search with credential fallback
Register two web search providers that reuse an existing chat provider's
API key instead of requiring their own connection:

- ollama-search (POST ollama.com/api/web_search) reuses the `ollama` key
- zai-search (POST api.z.ai MCP web_search_prime) reuses the `glm` key

A new `credentialFallback` registry field drives this: when a search
provider has no connection of its own, the search handler falls back to
the linked chat provider's credentials.

Also teach getProviderIconSrc to serve .svg logos for providers that
ship vector art.
2026-08-28 16:04:45 +07:00
huohua-dev
eb312bd470 fix(claude): decloak tool names in same-format streaming passthrough
translateResponse() short-circuited untouched on claude->claude streaming,
so OAuth-cloaked tool names (CLAUDE_TOOL_SUFFIX) leaked to the client and
every tool call was rejected as unknown. Add decloakStreamChunk(), the
streaming counterpart of decloakToolNames(), and call it on the same-format
path using the already-plumbed state.toolNameMap.
2026-08-28 15:40:40 +07:00
Daniel Gonçalves Araujo
fcfcced4ab fix(usage): support CREDIT_LIMIT and multi-interval GLM quotas
GLM quota parsing only accepted TOKENS_LIMIT and wrote every limit to a
single "session" key, so credit-based plans showed nothing and later
intervals overwrote earlier ones. Accept CREDIT_LIMIT too and derive the
quota key from the limit unit (5h session, 7d weekly, tokens, custom).
Moves the parser into its own usage/glm.js, re-exported from misc.js.
2026-08-28 15:35:23 +07:00
qingyong
56a40765e9 fix(translator): zai thinkingFormat sends reasoning.effort object
Z.ai / GLM-5.2+ require a top-level reasoning_effort (low/high/max)
alongside thinking:{type:"enabled"} to control reasoning depth; the zai
branch previously only set thinking and dropped reasoning_effort, so every
GLM-5.x request ran at the model default (max). Gate the field behind
GLM-5.2+ (thinkingEffortSupported in capabilities.js) since older GLM
(4.x, 5.0, 5.1, 5-turbo, 5v-turbo) do not read it, and map client levels
to the exact low/high/max values z.ai accepts.

extractThinking now checks reasoning_effort/reasoning.effort before the
thinking object so a client-supplied effort is not overwritten by
thinking:{type:"enabled"} mapping to mode:auto.

Fixes #2721
2026-08-28 12:32:41 +07:00
KunN-21
cadef6c4ff fix(rtk): make system prompt injection format-safe and idempotent
Caveman/Ponytail injection now matches each target wire format instead of
assuming an OpenAI-shaped body:

- Chat arrays append a text block; Responses arrays append input_text and
  create typed message items
- Claude inserts before the final cache-control block; Gemini preserves the
  snake/camel systemInstruction wrapper
- Kiro updates systemPrompt and its mirrored first-user prefix atomically,
  rolling back if the pair fails to converge
- Format label decides Claude/Gemini before the wire-shape sniff, since their
  bodies also carry messages[]/contents[] and Anthropic rejects a "system"
  role inside messages[]
- Delimiter-aware dedup makes injection exact-idempotent across retries, so
  distinct prompts sharing a long prefix are no longer collapsed
- Every write is fail-open on frozen or proxied bodies

Saver order and X-9Router-Token-Saver: off behavior are unchanged.

Fixes #3202.
2026-08-28 11:47:56 +07:00
anojndr
ab044e6d6d fix(opencode): route Muse Spark through the Responses API
muse-spark-1.2-contributor-free returned HTTP 500 on /zen/v1/chat/completions.
The model is only served by /zen/v1/responses, so route it there via a per-model
targetFormat and normalize the Chat fields the Responses API rejects
(max_tokens -> max_output_tokens, reasoning_effort -> reasoning{effort,summary}),
clamping max/ultra down to the highest effort the model accepts (xhigh).

Routing stays per-model: the other free models (big-pickle, hy3-free, mimo,
nemotron, laguna) are not served by /responses and keep /chat/completions.
2026-08-28 11:33:14 +07:00
decolua
14401c433c fix(ui): wait for the icon font itself before revealing Material Symbols
`document.fonts.ready` resolved before the 4MB Material Symbols woff2 even
started loading — it runs in <head>, ahead of any element that would trigger
the lazy fetch. The `fonts-loaded` class landed early, so icons rendered
blank until a second load served the font from disk cache.

Load the face explicitly and swap visibility for opacity, with a 3s fallback
so icons never stay hidden if the font fails.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-28 11:32:28 +07:00
warelik
e08ac6dada fix(tools): default Claude tool type when missing
Strict Anthropic-compatible gateways (e.g. MiniMax) reject Claude-format
requests with HTTP 400 when tools[].type is missing. Normalize each
missing/falsy tools[].type to "custom" before dispatch when the final
request format is Claude. Built-in tool types (computer_use, bash,
web_search_*) are passed through untouched.
2026-08-28 11:16:44 +07:00
Nguyen Thanh Dat
f9d82c6575 fix(stream): parse the trailing NDJSON line an Ollama stream leaves behind
createSSEStream splits on "\n" and keeps the remainder, which only flush()
parses. That call omitted targetFormat, so parseSSELine required a "data: "
prefix and dropped whatever an NDJSON provider left without a closing
newline. The !parsed.done guard compounded it: the SSE sentinel and an
Ollama final chunk both carry done:true, but the latter is the real last
chunk holding done_reason and the token counts.

Pass targetFormat and scope the sentinel check to formats that emit one, so
the tail reaches the translator. Accumulate its usage into state the same way
the transform loop does, so finalizeStream logs those tokens instead of null.
2026-08-27 20:53:02 +07:00
decolua
2f17352cc2 feat(search): add Antigravity as a web search provider
Route POST /v1/search with provider "antigravity" through Google Search
grounding on v1internal:generateContent, using the existing Antigravity
OAuth account pool. Grounding chunks become citations with the grounded
sentence as snippet and its surrounding answer text as content.

Upstream repeats a source across chunks, so citations are keyed by URL
and their snippets merged. A missing projectId is reported up front —
upstream answers a fabricated or absent project with a misleading
"no valid license" 403.

Based on the approach in #3437 by @Nautilaceae.
2026-08-27 20:48:43 +07:00
vianhanif
90a0005845 fix(cli): install better-sqlite3 without build tools on Node 22+
The runtime hook pinned better-sqlite3 12.6.2, whose prebuilds stop at
Node ABI 141 — on Node 26 the install fell back to a node-gyp source
build and failed on machines without build tools, silently degrading to
the sql.js fallback.

Node >= 22 now installs 13.0.3, which is N-API and ships per-platform
prebuilds inside the package. Two things were needed to make that
actually work:

- npm injects an implicit `node-gyp rebuild` for any package shipping a
  binding.gyp, so the install still demanded build tools; `--ignore-scripts`
  skips it and uses the bundled prebuild as-is.
- the binary check only looked at build/Release, which 13.x no longer
  creates, so every start re-ran npm install; it now also accepts
  prebuilds/<platform>-<arch>.node.

Node < 22 stays on 12.6.2 (13.x requires Node >= 22), and an existing
working install is left untouched either way.
2026-08-27 20:28:26 +07:00
Fábio A.
e79ae6e7c5 i18n(pt-BR): expand translation to 1132 terms
Add 144 missing pt-BR strings covering Usage, Endpoint & Key security
notices, 9Remote, Media Providers, Proxy Pools, Combo & Vision Adapter,
Token Saver, Agent Skills and Quota Tracker.
2026-08-27 20:06:10 +07:00
decolua
a68ada1c83 feat(cli-tools): share endpoint presets across every tool card
Each card kept its own copy of the localStorage preset logic inside
BaseUrlSelect, so an endpoint saved on one card was invisible to the
others until a reload, and a URL typed into the custom field was
forgotten the moment the card collapsed.

Move the store into cliEndpointPresets.js and publish a change event
so open cards resync live. Applying settings now remembers the
endpoint unless it matches a built-in option, and each card passes
its configured URL as currentUrl so BaseUrlSelect can preselect the
matching preset instead of always falling back to 127.0.0.1.
Deleting a preset falls back to the first real option rather than
clearing the field.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-27 18:54:44 +07:00