GLM/ZhipuAI rejects system role messages with 422 'Input should be
user or assistant'. When memory injection adds a system-role message,
GLM combo targets fail because the system message survives into the
upstream request.
Fix:
- injection.ts: add glm, glmt, glm-cn, zai, qianfan to
PROVIDERS_WITHOUT_SYSTEM_MESSAGE so memory is injected as user role
- roleNormalizer.ts: add exact 'glm' model match to
MODELS_WITHOUT_SYSTEM_ROLE for Pollinations and bare model ids
Test: 22 new unit tests covering all GLM variants + regression checks
for openai/anthropic providers.
Closes#1701
Add 'Invalid signature in thinking block' to COMBO_BAD_REQUEST_FALLBACK_PATTERNS
so combo routing falls through to the next target instead of returning 400 directly.
This error occurs when extended thinking signatures expire between turns,
which is a model-specific issue that won't be fixed by retrying the same provider.
Closes#1696
The previous 90% default treated Codex accounts as unavailable while they
still had ~10% quota remaining. A 99% threshold reserves only a minimal
safety margin near true exhaustion while preserving usable quota.
- Export DEFAULT_QUOTA_THRESHOLD_PERCENT=99 from quotaCache.ts
- Replace CODEX_QUOTA_THRESHOLD_PERCENT in auth.ts with shared constant
- Update quota policy tests to match new default
Co-authored-by: dhaern <manker_lol@hotmail.com>
The stream readiness gate from PR #1693 validates SSE body content.
The test harness checked only `headers.accept` (lowercase) but executors
set `Accept` (capital A), causing the harness to return JSON instead of
SSE for streaming requests. Fixed by checking both header casings.
- Replace polynomial regex /\/+$/ with loop-based stripTrailingSlashes()
across 8 enterprise provider configs (azure-openai, azureAi, bedrock,
datarobot, oci, sap, watsonx, audioSpeech) — fixes js/polynomial-redos
- Add prototype-pollution denylist guard in usageHistory.ts to reject
__proto__/constructor/prototype as model keys — fixes
js/prototype-polluting-assignment (#167, #168)
- Suppress 3 false-positive js/insufficient-password-hash alerts in
chatgpt-web.ts and builtins.ts where SHA-256 is used for cache-key
derivation, not password storage (#176, #177, #178)
- Add stripTrailingSlashes unit tests with ReDoS regression check
Update the deterministic fallback API key to use hyphens instead of
underscores so generated keys match the expected format.
Also set API_KEY_SECRET in unit tests that exercise API key creation to
ensure consistent resolver behavior under test.
The JSDoc comment in cors.ts explains why Access-Control-Allow-Origin
is intentionally excluded from CORS_HEADERS. The test regex was
matching the comment text, causing a false failure.
- api-keys: remove flaky console.log assertion (route now uses Pino
structured logger via console.error, not console.log)
- chat-pipeline: update REQUIRE_API_KEY test to reflect authz pipeline
enforcement moved to route layer (handleChat no longer checks it)
- chat-pipeline: accept both 'Invalid'/'Incorrect' API key error formats
- CodexExecutor: isCodexResponsesWebSocketRequired now defaults to HTTP
unless codexTransport='websocket' is set in providerSpecificData
- CodexExecutor: store defaults to false unless openaiStoreEnabled=true
- CodexExecutor: WS unavailable now falls back to HTTP via super.execute()
instead of returning 503 (dead code path after guard refactor)
- Meta AI: X-FB-Friendly-Name updated from useAbraSendMessageMutation
to useEctoSendMessageSubscription
- Proxy middleware: tests now verify authz/pipeline.ts (refactored from proxy.ts)
- Chat pipeline: accept both 'Invalid'/'Incorrect' API key error messages
- Qwen retry: selective setTimeout mock to avoid tripping body read timeout
- Treat status 499 as terminal non-retryable error in both priority and
round-robin combo loops — no fallback to other models when client is gone
- Propagate AbortSignal from request into handleComboChat so the combo
loop can detect client disconnects before starting new model attempts
- Make retry/fallback delays abort-aware via signal.addEventListener
- Add 5 unit tests covering 499 early-exit, signal.aborted pre-check,
multi-model abort, 502 contrast behavior, and abort-during-wait
Integrated into release/v3.7.2 — implements conversation continuity for muse-spark-web executor with SHA-256 prefix hashing, TTL cache, and eviction-on-error
Centralize remote image downloads behind a shared helper that
validates outbound URLs, enforces redirect and size limits, and
applies request timeouts before bytes are read.
Wire the helper into image generation and vision bridge flows so
remote image inputs and result URLs follow the same fetch policy and
block redirects to private hosts. Update key management routes to use
structured logging and document the WebSocket bridge secret in the
example environment file.
The billing header fingerprint was computed from the first user message text
via computeFingerprint(), which changes every conversation turn. This mutated
the system[] prefix on each request, invalidating Anthropic's prompt-cache
prefix and forcing ~100% cache_create (vs 96% cache_read with stable prefix).
Now uses a per-day SHA-256 hash of the date + ccVersion, keeping the billing
header format while preserving prompt-cache prefix stability across turns.
Includes 6 unit tests.
The prompt_cache_key was derived from the account-wide workspaceId, meaning
all conversations from the same OAuth account shared one cache partition.
The official Codex CLI uses conversation_id (a unique UUID per session).
Priority: body.session_id > body.conversation_id > workspaceId.
Session IDs are captured BEFORE deletion from the body.
Includes 10 unit tests.
When a provider returns 429 with quota_exhausted reason, set cooldown until
tomorrow 00:00 instead of exponential backoff. Includes isDailyQuotaExhausted()
detection in chat handler and unit tests.
Co-authored-by: clousky2020 <clousky2020@users.noreply.github.com>
Integrated into release/v3.7.1 — fixes Electron installer shipping empty node_modules. Adds separate extraResources FileSet, CI smoke test job, and cross-platform packaged app validation script. Closes#1636.
Prevent upstream 400 failures when clients omit prior
reasoning_content in multi-turn tool-calling conversations.
Capture reasoning_content from streaming and non-streaming assistant
responses, persist it in a memory-plus-SQLite cache keyed by
tool_call_id, and re-inject it on later requests when available.
Add the reasoning cache migration, service layer, authenticated API
endpoints, dashboard tab, and unit coverage to support inspection,
cleanup, and crash recovery.
- PR #1630: Set NPM_CONFIG_LEGACY_PEER_DEPS=true in Dockerfile, remove duplicate COPY
- PR #1631: Hide deprecated gemini-claude-* models from catalogs, redirect to Claude 4.6
- Fix provider-models-route test to match renamed model display name
- Update CHANGELOG.md with both PR entries
- Use jsonc-parser to update only provider.omniroute in opencode.json,
preserving MCP servers, comments, and other provider entries
- Add /api/cli-tools/keys endpoint returning raw keys for CLI tools UI
(session-auth protected via requireCliToolsAuth)
- Fix OpenCode guide step 3 to use ICU-style {baseUrl} placeholder
instead of broken {{baseUrl}} across all 40+ locales
- Restore valid OpenCode light/dark SVG logos (were broken HTML downloads)
- Add customCliTab translation key to en.json
- Add modelLabels support for human-readable model names in config
- Fix 9 additional locale files (bn, fa, gu, in, mr, sw, ta, te, ur)
that were added after the original PR and still had double-braces
Co-authored-by: JasonLandbridge <JasonLandbridge@users.noreply.github.com>
- Add 'github' to hasPerModelQuota() so 429 on one model doesn't lock the
entire connection — same pattern already used for Gemini
- Add 'github' to getLimiterKey() for model-scoped Bottleneck rate limiter
keys (github:connectionId:model)
- Add unit tests for hasPerModelQuota, shouldMarkAccountExhaustedFrom429,
lockModelIfPerModelQuota (account-fallback-service.test.ts)
- Add integration test for model-scoped limiter keys (rate-limit-manager.test.ts)
Co-authored-by: slewis3600 <slewis3600@users.noreply.github.com>