The Codex CLI WS->HTTP fallback rewrite (resolveResponsesApiModel) prefixes a
bare model id with codex/ whenever codex/<id> resolves to codex. Codex accepts
arbitrary model strings, so a combo name with no slash (e.g. n8n-text,
paid-premium) was rewritten to codex/<combo> and sent to Codex instead of being
resolved as a combo — regressing combos via /v1/responses in v3.8.9+. Skip the
rewrite when the bare id is a combo (getComboByName). + unit test.
- grok-web: the credential hint named only "sso" while Grok needs both "sso"
and "sso-rw"; users pasted just sso and hit anti-bot 403s. Name both
(credentialName + placeholder). The underlying Cloudflare anti-bot 403 is
upstream and tracked separately on #3180.
- vertex: the Service Account JSON placeholder was an untranslated stub literal
("Vertex Service Account Placeholder") in 40 locales, making the field look
broken even though SA-JSON auth is fully supported. Replace with real
instructional text (zh localized; pt-BR already translated).
Guard test pins both hints.
The NVIDIA key-validation chat probe used models[0] (z-ai/glm-5.1), which
requires the 'Public API Endpoints' account permission and has DEGRADED
windows. Accounts lacking that permission see the probe hang until the
validation timeout, surfacing as a misleading 'Upstream Error' on a valid key.
Probe the universally-available meta/llama-3.1-8b-instruct instead, still
overridable via providerSpecificData.validationModelId. + unit test.
getProviderConfig falls back to { name: providerId } for unknown ids, so the
label precedence config.name || p.name let the raw internal UUID of a custom
provider shadow the friendly name HomePageClient already resolved into p.name.
Extract resolveTopologyNodeLabel (entry name first) + unit test.
Image routes now resolve a requested model the same way across /v1/images/generations
and /v1/images/edits, via a shared resolver: built-in id -> custom provider prefix ->
bare combo/alias name (e.g. "image" -> its single image target). Previously a bare
combo name fell through to "Invalid image model".
/v1/images/edits gains two capabilities for custom OpenAI-compatible providers:
- multipart edit forwarding to the node's {base_url}/images/edits (was hard-rejected
unless chatgpt-web);
- JSON/data-URL edit input (images:[{image_url:"data:..."}]), converted to the same
fields the multipart reader produces (was "Invalid multipart body").
The chatgpt-web conversation-continuation edit flow is unchanged.
The db-backups import route statically imported better-sqlite3, which is
stripped from the Next standalone server's node_modules in the packaged
Electron app. Loading the route then crashed with "Cannot find module
'better-sqlite3'" on the Windows installer, even though node:sqlite was
available. Route the upload integrity-check through openDatabaseAsync
(better-sqlite3 -> node:sqlite -> sql.js), matching every other DB path.
Adds a guard test so no API route can reintroduce a direct native import.
Follow-up to #3204: deleting a synced (fetched) model removed it from the
synced set, but the DELETE route didn't mark it hidden and the re-import path
didn't skip hidden ids, so the next auto-fetch re-added it. Now the route
marks the id hidden on delete and replaceSyncedAvailableModelsForConnection
filters hidden ids, so the deletion sticks.
Co-authored-by: tjengbudi <tjengbudi@users.noreply.github.com>
The image-generation handler read credentials.baseUrl (always undefined),
so custom OpenAI-compatible image providers fell back to the Gemini endpoint
(401). Resolve from providerSpecificData.baseUrl like the chat path, and
rewrite prefix/model to the internal node id before the exact-id lookup.
Co-authored-by: ngocquynh85 <ngocquynh85@users.noreply.github.com>
normalizeDiscoveredModels only copied record.inputTokenLimit, but OpenRouter
returns the window as context_length / top_provider.context_length, so every
synced model fell back to the 128K default. Read context_length (and
top_provider.max_completion_tokens for output) as a fallback.
Co-authored-by: pulyankote <pulyankote@users.noreply.github.com>
* chore(release): open v3.8.10 development cycle
Bump 3.8.9 → 3.8.10 across package.json, lockfile, electron, open-sse, and
docs/reference/openapi.yaml; add the [3.8.10] CHANGELOG section (root + 41 i18n
mirrors) as the integration target for the cycle. Entries land here as work
merges into release/v3.8.10; finalized by the release flow.
* fix(providers): resolve web provider alias collisions
Assign unique aliases to HuggingChat, Kimi Web, and Qwen Web so they no longer shadow primary providers or trigger startup warnings.
Add a unit test to enforce provider alias uniqueness and prevent future collisions. Also expand local ignore and VS Code exclude rules for agent, build, and worktree artifacts.
* fix(responses): normalize image_url parts across input paths (#3150)
Normalize image_url parts across all Responses input paths. Integrated into release/v3.8.10.
* fix(api-manager): preserve API key expiration local time (#3146)
Preserve API key expiration local time + clear button. Integrated into release/v3.8.10.
* Strip previous_response_id for stateless Responses upstreams (#3143)
Strip previous_response_id for stateless Responses upstreams (auto/strip/preserve). Integrated into release/v3.8.10.
* fix(opencode-plugin): map thinking cap to interleaved in model+combo (#3138)
Map caps.thinking to ModelV2.capabilities.interleaved for opencode-plugin. Integrated into release/v3.8.10.
* fix(providers): use synced models as fallback for all providers (#3148)
Use synced models as authoritative local catalog for all providers (+regression test). Integrated into release/v3.8.10.
* fix(qoder): bifurcate validation by token type — PAT→Cosy, regular API key→dashscope (#3149)
Bifurcate Qoder validation by token type (PAT→Cosy, regular→dashscope) +regression test. Integrated into release/v3.8.10.
* fix(antigravity): dynamic model resolution via MITM alias table (#3144)
Dynamic antigravity MITM model resolution in the executor (+bug fix +regression test; DB import dropped from client-reachable config). Integrated into release/v3.8.10.
* Feature/batch allow big (#3128)
Podman deployment options + larger upload body-size limits (+CONTAINER_HOST docs). Integrated into release/v3.8.10.
* fix(fireworks): preserve fully-qualified router/model IDs (#3133) (#3160)
Fireworks router IDs (accounts/fireworks/routers/...) were double-prefixed
with accounts/fireworks/models/ → upstream 404. Add optional
acceptedModelIdPrefixes to the registry entry and skip the prepend when the
model already starts with an accepted prefix.
Co-authored-by: KooshaPari <KooshaPari@users.noreply.github.com>
* fix(llama-cpp): route to configured local baseUrl instead of OpenAI (#3136) (#3161)
llama-cpp was missing from the local-provider group in buildUrl(), so it
fell through to the OpenAI baseUrl and returned an OpenAI 401. Add the
case to resolve the connection's providerSpecificData.baseUrl.
Co-authored-by: tjengbudi <tjengbudi@users.noreply.github.com>
* fix(t3-chat-web): parse cookies + convexSessionId from stored credential (#3007) (#3162)
The executor read credentials.cookies/convexSessionId, but the pipeline
only stores the pasted string under apiKey → t3.chat always 400'd. Parse
both values from apiKey (fallback accessToken), mirroring validation.ts.
Co-authored-by: minhtran162 <minhtran162@users.noreply.github.com>
* fix(minimax): stop capping MiniMax-M3 / M2.7 max_tokens at 8192 (#3141) (#3163)
MiniMax-M3 had no MODEL_SPECS entry and capitalized MiniMax-M2.7 missed
its lowercase spec (case-sensitive lookup) → both fell to the 8192 default
cap. Add the M3 spec (512K output), alias the capitalized ids, and make
getModelSpec lookups case-insensitive.
Co-authored-by: totaltube <totaltube@users.noreply.github.com>
* fix(github-copilot): discover model catalog live from api.githubcopilot.com (#3120, #3121) (#3164)
The github (Copilot) provider had a static hardcoded catalog with no
discovery source, so Import Models never refreshed (#3120) and advertised
non-entitled models that 400 on use (#3121). Add a live /models fetch with
fallback to the static list.
Co-authored-by: gabrielmoreira <gabrielmoreira@users.noreply.github.com>
* fix(combo): invalidate nested-combo cache on edits + log DATA_DIR (#3147) (#3165)
Editing a combo did not invalidate the 10s nested-combo expansion caches
(chat.ts getCombosCachedForChat + chatCore.ts getCombosCached; the exported
clearCombosCache was dead code), so a removed nested target/model could be
served as a phantom for up to 10s. Wire a shared monotonic combos-cache
version in readCache (bumped by invalidateDbCache("combos") on every combo
write); both cache layers treat a version mismatch as a miss.
Also log the resolved DATA_DIR/SQLITE_FILE absolute path at DB init so the
reporter's 'persists across restart + volume wipe' symptom (a multi-replica
Docker volume/DATA_DIR mismatch, not a routing bug) is diagnosable from logs.
Includes consolidated CHANGELOG entries for #3133/#3136/#3007/#3141/#3120/#3121.
Co-authored-by: ViFigueiredo <ViFigueiredo@users.noreply.github.com>
* fix(web-tools): parse bare JSON tool calls (#3157)
Parse bare JSON tool calls for deepseek-web (#2820) + fuzzy tool-name matching. Integrated into release/v3.8.10.
* fix(misc): minor fixes across reasoning cache, account fallback, binary manager (#3177)
Misc: ProviderProfile export, DeepSeek reasoning regex, binary guard. Integrated into release/v3.8.10.
* fix(kiro): minor OAuth social exchange tweaks (#3176)
Kiro social OAuth: optional targetProvider passthrough. Integrated into release/v3.8.10.
* deps: bump hono from 4.12.18 to 4.12.23 (#3179)
Bump hono to 4.12.23. Integrated into release/v3.8.10.
* fix(providerRegistry): update kilocode format and executor (#3166)
kilocode: openai format + default executor (matches kilo-gateway) + registry test. Integrated into release/v3.8.10.
* feat(metrics): cross-request TTFT and gap latency after tool calls (#3173)
Cross-request TTFT + gap-after-tool latency metrics (+test). Integrated into release/v3.8.10.
* feat(dashboard): provider stats API endpoint and dashboard page (#3175)
Provider stats dashboard + API (SQL moved to db module per Hard Rule #5, +test). Integrated into release/v3.8.10.
* fix(usage): sequential+spaced OAuth quota sync, reactive force-refresh, actionable 401 (#3156)
Sequential+spaced OAuth quota sync, reactive force-refresh on 401, actionable 401 in UI. Integrated into release/v3.8.10.
* fix(healthcheck): per-provider proactive-refresh skip list (rescue short-TTL OAuth) (#3159)
Per-provider proactive-refresh skip list (OMNIROUTE_HEALTHCHECK_SKIP_PROVIDERS) to rescue short-TTL OAuth. Integrated into release/v3.8.10.
* feat(quota): show OAuth token expiry on provider cards (small, blue, informative) (#3178)
Show OAuth token expiry on provider cards (small, blue, informative). Integrated into release/v3.8.10.
* fix(providers): empty refresh must not resurface just-cleared synced models (#3181)
Empty refresh must not resurface just-cleared synced models (fixes the release-blocking provider-models-route test). Integrated into release/v3.8.10.
* chore(release): v3.8.10 — 2026-06-04 (finalize CHANGELOG)
---------
Co-authored-by: Wilson <pedbookmed@gmail.com>
Co-authored-by: Xiangzhe <32761048+xz-dev@users.noreply.github.com>
Co-authored-by: Jan Leon <Jan.gaschler@gmail.com>
Co-authored-by: M.M <mr.maatoug@gmail.com>
Co-authored-by: Hernan Javier Ardila Sanchez <hjasgr@gmail.com>
Co-authored-by: Markus Hartung <mail@hartmark.se>
Co-authored-by: KooshaPari <KooshaPari@users.noreply.github.com>
Co-authored-by: tjengbudi <tjengbudi@users.noreply.github.com>
Co-authored-by: minhtran162 <minhtran162@users.noreply.github.com>
Co-authored-by: totaltube <totaltube@users.noreply.github.com>
Co-authored-by: gabrielmoreira <gabrielmoreira@users.noreply.github.com>
Co-authored-by: ViFigueiredo <ViFigueiredo@users.noreply.github.com>
Co-authored-by: PizzaV <103120356+pizzav-xyz@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Nicolas Lorin <androw95220@gmail.com>
Kiro (AWS CodeWhisperer) tops out at Claude Opus 4.7 in the registry, but the latest Opus 4.8 is already served by the Claude Code provider and Kiro's executor passes the model id through to CodeWhisperer verbatim. Expose claude-opus-4.8 on the kiro provider so clients can select it via kiro/claude-opus-4.8.
- providerRegistry: add claude-opus-4.8 (1M context, 128k output) above 4.7
- pricing: add claude-opus-4.8 (and the previously-missing 4.7) to the kiro pricing block at Kiro's standard Opus rate so usage cost is non-zero
- tests: assert kiro exposes claude-opus-4.8 with matching context/output + pricing
The webpack build failed: route.ts imported '../internal/codex-responses-ws/
modelResolution' (resolves to api/v1/internal/, which doesn't exist). The module
lives at api/internal/codex-responses-ws/. Switched to the @/app/api/... alias.
typecheck/tests passed (tsx resolves leniently; tests import the module directly),
only the production build caught it.
* feat(providers): implement bulk paste for extra API keys
Adds `parseExtraApiKeys` utility to process multi-line key inputs.
Integrates bulk paste functionality into the provider connection modal.
Users can now paste multiple API keys, one per line, into the input field.
Provides notifications for successfully added keys and ignored duplicates.
Adds a "Delete all" button to clear all extra API keys.
Updates i18n messages for new features and improved key masking/pluralization.
* refactor(providers): streamline API key bulk paste and i18n
Remove unused return from `handleAddParsedExtraKeys` to clean up code.
Adjust `onPaste` to allow default paste for single-line input, improving UX.
Remove obsolete bulk paste UI translation keys to reduce bundle size.
Update pluralization for bulk paste messages to ensure correct grammar.
Refine Portuguese (Brazil) API key translations for clarity.
* fix(logs): apply code review feedback - robust signature, immediate fetch on tab restore, reuse memoized apiKeyCount
* test(logs): extract pure polling/signature helpers + cover them (#3109)
Hard rule #8: the perf fix touched src/ without tests. Extract computeLogsSignature,
shouldAutoRefresh and resolveInitialVisibility into a pure module and unit-test
them (change-detection, first-page polling guard, SSR/hidden-tab visibility init).
Also fixes visibleRef to honor the real visibilityState on mount instead of
hardcoding true (no poll when mounted in a hidden tab).
---------
Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com>
* fix(cli): handle Windows exe healthchecks with spaces
* Update src/shared/services/cliRuntime.ts
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
* Update tests/unit/cli-runtime-extended.test.ts
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
* fix(cli): pass command to spawn unquoted; export shouldUseShellForCommand
Remove the manual "${command}" interpolation into the shell command (hard rule
#13 violation, and redundant — Node quotes for cmd.exe when shell:true; .exe runs
with shell:false where the OS handles spaces via argv). Export the helper and add
a cross-platform test asserting non-Windows never uses the shell.
---------
Co-authored-by: Empire Rider <anuruddhawijesiri@gmail.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com>
* fix(sse): bound Antigravity short-retry 429 loop per endpoint
A persistent 429 on the short-retry branch (retryAfterMs ≤ 60s) looped
forever on the same endpoint because the branch did `urlIndex--; continue`
without checking the shared retry counter. Production log showed 77
consecutive 429s on one daily endpoint/account with zero fallback.
Gate the short-retry branch on `retryAttemptsByUrl[urlIndex] < MAX_AUTO_RETRIES`
(mirroring the already-bounded sibling), so a persistent 429 retries at most
3× per endpoint across all 3 base URLs then returns the 429 to the account-
fallback layer.
Regression test: 'bounds a persistent short-retry 429' in
tests/unit/executor-antigravity.test.ts — asserts 12 total attempts
(3 endpoints × 4) and a returned 429 with zero hang.
* fix(sse): lock Antigravity quota-exhausted account for full reset window
After the retry-loop bound, OmniRoute fell over to the next account but
re-selected the exhausted one first on every subsequent request (~60s wasted
per request). Root cause: the 429 body 'Individual quota reached. Contact
your administrator to enable overages. Resets in 164h27m24s.' was not
recognized as quota exhaustion, so the model was locked for only ~5s instead
of the real 6.8-day reset window.
Two detector fixes (mirrors Antigravity-Manager rate_limit.rs set_lockout_until):
1. classify429.ts — add QUOTA_PATTERNS: /individual quota reached/i,
/quota reached/i, /enable overages/i so looksLikeQuotaExhausted() fires.
2. accountFallback.ts — same patterns in classifyErrorText(); extend
parseRetryFromErrorText() to parse 'Resets? in XhYmZs' (reusing the
existing computeDurationMs helper) so the exact reset duration reaches
recordModelLockoutFailure as exactCooldownMs (uncapped, per user choice).
The lockout machinery already stores until = now + cooldownMs with no clamp,
bypasses getScaledCooldown when exactCooldownMs > 0, and keeps the longer of
existing/new, so the full 164h window flows through intact.
New patterns stay specific — plain 'too many requests'/'rate limit exceeded'
messages still classify as rate_limit.
Verified end-to-end against the real message:
- classify429 → quota_exhausted
- parseRetryFromErrorText → 592044000 ms (164h27m24s exactly)
- checkFallbackError → usedUpstreamRetryHint: true, cooldownMs: 592044000
* refactor(account-fallback): simplify error parsing and add cooldown safety
- Implement a 30-day cap on parsed retry durations to prevent indefinite account lockouts.
- Replace manual string matching with `looksLikeQuotaExhausted` for more robust quota detection.
- Streamline regex logic in `parseRetryFromErrorText` for better readability.
- Add unit tests for extreme cooldown values and free-tier exhaustion scenarios.
* fix(429): drop over-broad /quota reached/ pattern, keep specific matches
The bare /quota reached/ would also flag transient per-minute limits like
'request quota reached, retry in 60s' as quota_exhausted (multi-hour lock).
The Antigravity message is still caught by /individual quota reached/. Added a
regression assertion proving the transient case stays a rate_limit.
---------
Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com>
When the Codex CLI falls back from WebSocket to HTTP (after 1008 Policy
Violation or reconnect exhaustion), it POSTs to /v1/responses with the
bare model id it was configured with (e.g. "gpt-5.5") — never the
provider-prefixed form. OmniRoute's normal routing resolved that bare id
to openrouter, not codex, producing:
"No credentials for provider: openrouter"
Fix: add resolveResponsesApiModel() (extending modelResolution.ts) and
call it in /v1/responses before delegating to handleChat. The function
applies the same codex-preference logic as resolveCodexWsModelInfo:
bare "gpt-5.5" → codex/gpt-5.5 has a codex provider → rewrite ✅
bare "gpt-4o" → codex/gpt-4o not in registry → pass through ✅
"anthropic/x" → has "/" → skip resolution → pass through ✅
Errors are caught; original request is returned on any failure.
8 unit tests added (TDD — watched each fail before implementing).
Refs: openai/codex#15492, openai/codex#13041, openai/codex#13039
zh-CN and ru were each missing 9 whole sections (~823 keys: quotaPlans, activity, agentBridge, trafficInspector, cliCommon, cliCode, cliAgents, acpAgents, agentSkills) added after the last sweep, so those UI strings fell back to English. Filled via the canonical i18n sync+translate pipeline (scripts/i18n/sync-ui-keys.mjs --translate-markers). Both catalogs are now at full key parity with en.json (70 sections / 8025 keys), 0 __MISSING__ markers, valid JSON, Prettier-clean.
The provider Playground (LlmChatCard) only added the providerId/ prefix to models without a slash, so vendor-namespaced ids (moonshotai/kimi-k2.6, nvidia/zyphra/...) were sent bare and rejected with 'Ambiguous model' when the same id exists under multiple providers. Extracted qualifyPlaygroundModel() which always prefixes with the provider unless already qualified. Bug 1 ('unhashable type: dict') is an upstream NVIDIA NIM server error, not OmniRoute. Tests: 4 cases for the qualifier.
createProviderConnection deduped apikey connections only by (provider, name). Adding the same key under a different/blank name created a duplicate row. It now also matches by the decrypted key value (AES-GCM ciphertext is non-deterministic, so we decrypt+compare plaintext, trimmed) and updates the existing connection instead. Tests cover same-key dedup, whitespace-variant dedup, and distinct-key separation.
No-auth providers (OpenCode Free) have no connection row, so handleImportModels returned early and /api/providers/[id]/models 404'd — the import button silently no-op'd. The route now serves the provider's registry/static model catalog when called with a no-auth provider id, and handleImportModels falls back to the provider id when there is no connection. Test: route returns the opencode catalog (source local_catalog) and still 404s for unknown ids.
grok-web only sent the 'sso' cookie; Grok's anti-bot now rejects (403 code 7) requests missing the paired 'sso-rw' write cookie. Adds buildGrokCookieHeader() which emits sso plus sso-rw when the pasted blob carries it (never a phantom sso-rw from a bare value), used by both the executor and the connection validator. Updates the grok-web authHint. Tests: 6 new buildGrokCookieHeader cases; grok-web 62/62 unchanged.
A per-model subscription/permission 403 from a passthrough provider (hasPerModelQuota) now locks only the failing model instead of cooling the whole connection, so free models on the same key keep serving and repeated paid-model 403s don't escalate a connection-wide backoff. Generalizes the grok-web 403 precedent; terminal/credential 403s still deactivate the connection (guarded by resolveTerminalConnectionStatus). TDD: 3 tests in sse-auth.test.ts (2 reproductions fail pre-fix, regression guard passes both ways).
Implements per-API-key context source configuration table and CRUD
functions required by the Obsidian PR. Restores getApiKeyContextSource
in obsidian.ts (was stubbed to null during conflict resolution).
11/11 tests pass in obsidian-config.test.ts.
Add a global OMNIROUTE_CODEX_WS_ENABLED kill-switch for Codex WebSocket transport, defaulting to enabled if feature flag lookup fails.
Track Xiaomi MiMo monthly token usage from OmniRoute usage history and expose it through the usage fetcher provider list.
Support MiniMax Coding Plan quota responses that expose remaining usage as
percentages instead of request counts, including the text quota surfaced under
the `general` model.
Also document Codex CLI WebSocket configuration, clarifying that bare ChatGPT
model ids must be used instead of `codex/`-prefixed ids.
Quality Gate was failing on New Code with 1 hotspot, 1 vulnerability and
4 reliability bugs. Resolved each at the source (no SonarCloud mark-as-safe):
Reliability (4 bugs → rating back to A):
- usage.ts: drop duplicate `case "opencode-go"` (S1862) — the 2nd case was
dead (first match wins) and would have called the wrong usage fetcher.
Also de-duplicate the same id in USAGE_FETCHER_PROVIDERS (mirrors the switch;
prevented a double quota-fetcher registration).
- ApiManagerPageClient.tsx: a dangling `.find(...)?.timestamp || null` expression
(S905) discarded the better matcher — `lastUsed` silently lost the
name-fallback for legacy logs without apiKeyId. Assign the complete matcher.
- chat.ts / auth.ts: `Promise` used in a boolean conditional (S6544). Both are
intentional (memoized promise / mutex default), not a forgotten await —
made the intent explicit via `!== null` and `??` (behaviour identical).
Security (vulnerability → rating back to A):
- nvidia-startswith-diag.ts: neutralize CR/LF before logging env-derived
values (S5145 log injection) in the ad-hoc diagnostic script.
Security Hotspot (reviewed → 0 open):
- pluginWorker.ts uses `vm.runInContext` to run plugin code — that IS the
worker's purpose, inside a hardened vm sandbox (createContext, require
allow-list of just `crypto`, 10s timeout); not eval/new Function. Suppress
S1523 scoped only to pluginWorker.ts in sonar-project.properties, with the
same documented-justification pattern as the existing hotspot suppressions.
In checkModelAvailable and handleSingleModelChat, when the combo target's
providerId is merely the prefix already encoded in the model string (e.g. "p2"
from "p2/test-model"), prefer the fully-resolved provider (e.g. the generated
custom node id openai-compatible-chat-e2e-p2) so the executor resolves the
custom baseUrl from the connection instead of falling back to the base provider
(real openai). Intentional providerId overrides (providerId not encoded in the
model string) are preserved.
Also fixes the resilience-http-e2e combo tests (cooldown window + DB-write
visibility for the cooled-down-primary skip).
The "recent" memory strategy maps to the internal "exact" retrieval path, whose
post-query relevance filter (score > 0) silently dropped recent memories whose
text didn't overlap the current prompt. Since the user-facing strategy enum is
only recent|semantic|hybrid (no "exact"), forwarding the prompt as `query` for
"recent" always engaged that filter, so recency-based injection returned nothing
when the prompt was unrelated to the stored memory.
Skip query forwarding for the "recent" strategy so retrieveMemories returns the
most recent memories (ORDER BY created_at DESC) regardless of prompt overlap.
Semantic/hybrid still forward the query for vector search.
Fixes the chat-pipeline + memory-pipeline integration memory-injection tests.
Back-merge to resolve PR #2930 (release/v3.8.8 -> main) conflicts. Release is a
superset of main's features, so all ~44 content conflicts resolved to the
release ("ours") version; generated .source/* dropped.
Reconciliation:
- auth.ts: port #3058 (getProviderSearchPool expands custom provider_nodes
prefixes to internal connection ids) — release lacked this main fix.
- quota-plan-registry.test.ts: align knownProviders() 6 -> 10 (pre-existing
stale assertion vs the registry).
Addresses findings from the multi-agent PR review of the #3066 fix:
- manager.stub.ts comments: the previous inline comment claimed the throwing ops
(getMitmStatus/startMitm/stopMitm) are "dynamic-import paths that should never hit
the stub at runtime" — factually wrong: those are static imports too, baked into the
bundled build just like getAllAgentsStatus. Rewrote the file header to describe the
real split — exports with a safe degraded value return it (getCachedPassword/
setCachedPassword/clearCachedPassword → null/no-op, getAllAgentsStatus → []) while
getMitmStatus/startMitm/stopMitm throw STUB_ERROR — and trimmed the inline comment.
Comment-only; no runtime/build change (the export still exists).
- stub-drift guard test: now scans ALL of src/ instead of only src/app —
src/lib/tailscaleTunnel.ts statically imports getCachedPassword/setCachedPassword
from @/mitm/manager and is pulled into routes transitively, so the src/app-only scan
had a false-negative blind spot. Also skips inline `type` imports (erased at build,
need no runtime export) and detects stub exports from declaration AND `export { … }`
forms (no false-positive if the stub later uses class/re-export).
Verified: next-config suite 4/4, typecheck:core / lint clean.