mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-18 21:22:28 +03:00
3d27c19d1734df8a23e82ecec3424f481deccefa
907 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
a7b96b44e9 |
fix(xai): cap chat history at xAI 800-message limit (#10601)
* fix(xai): cap chat history at xAI 800-message limit xAI returns 413 when messages/input exceed 800 items. Token compression never fires on a long tool loop that still fits the context window, so trim at the executor edge after Responses expansion and drop orphaned tool pairs from the cut. * chore(changelog): attach PR number to xAI 800-message fragment * fix(xai): resolve TS2339 generic assignment in capXaiRequestHistory Drop the T extends Record<string, unknown> generic on capXaiRequestHistory and type it directly as Record<string, unknown> -> Record<string, unknown>. Assigning next.messages / next.input onto a generic T was rejected by TypeScript even though every call site already passes/consumes a JsonRecord (= Record<string, unknown>), so no caller relied on the generic preserving a narrower type. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> --------- Co-authored-by: mikolaj92 <mikolaj92@users.noreply.github.com> Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> |
||
|
|
9222528bdd |
fix(opencode): session stability, free-tier routing, and CLI defaults (#10571)
* fix(opencode): session stability, free-tier routing, and CLI defaults - Wire generateSessionId() into opencodeHeaders so x-opencode-session is a deterministic fingerprint instead of randomUUID() per request, enabling upstream prompt caching across a conversation - Thread request body through buildHeaders() so session fingerprint has access to model, system, messages, and tools - Default CLI header synthesis to ON (opt-out via false), align values with 9router proven defaults (opencode/desktop/global) - Auto-echo listing-valid model names for noAuth providers so response.model matches /v1/models listing - Short-circuit free-tier model resolution to opencode provider first to prevent prefix inference misrouting when catalog is unreachable * fix(opencode): make free-tier default flip self-consistent + add coverage PR #10571 flipped OPENCODE_SYNTHESIZE_CLI_HEADERS to on-by-default and changed the synthesized UA/client/project default values, but shipped with 2 broken assertions in the existing #5997 regression test and no coverage for the new session-fingerprinting, free-tier routing, or noAuth echoModel logic (Hard Rule #18). - Update tests/unit/opencode-cli-headers-synthesis-5997.test.ts to match the new on-by-default behavior and new default values; add an explicit opt-out coverage test so the forward-only path is still guarded. - Fix 20 further test failures in tests/unit/opencode-executor.test.ts and tests/unit/refactor-buildHeaders-opencode.test.ts caused by the same default flip (pin OPENCODE_SYNTHESIZE_CLI_HEADERS=false for the characterization suites that predate #10571; use a genuinely CLI-looking UA where the preserved-UA test requires one). - Fix a real bug found via TDD while adding the mandated free-tier routing regression test: the big-pickle/*-free short-circuit in open-sse/services/model.ts checked activeProviders?.has("opencode") literally, but getActiveProviderSet() canonicalizes every connection's provider id through resolveProviderAlias(), which rewrites "opencode" to "opencode-zen" via a manual override — so an active no-auth opencode connection could never satisfy the check. Now checks both opencode-family candidate ids. Proven with a test that fails on the original code and passes with the fix (both connections active with a stale synced catalog omitting big-pickle). - Extract the noAuth-provider echoModel aliasing in chatCore.ts into a pure, directly-testable helper (open-sse/handlers/chatCore/noAuthEchoModel.ts), matching the existing chatCore god-file decomposition pattern. - Add regression tests for generateSessionId()-based x-opencode-session fingerprinting (stable within a conversation, changes on model/message changes), the free-tier routing short-circuit, and the noAuth echoModel aliasing. - Add the changelog.d/ fragment and sync docs/reference/ENVIRONMENT.md's OPENCODE_SYNTHESIZE_CLI_HEADERS/OPENCODE_USER_AGENT/OPENCODE_CLIENT/ OPENCODE_PROJECT rows to the new defaults. Does NOT resolve whether flipping OPENCODE_SYNTHESIZE_CLI_HEADERS's default was the right call, and does NOT touch the separate open PR #10357 which flips the same flag with a different literal default value - that decision is left to the maintainer at merge time. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> --------- Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> |
||
|
|
b9cd5ed138 |
feat(providers): optional AI Horde API key and live image catalog (#10542)
* feat(providers): optional AI Horde API key and live image catalog Allow a registered Horde key on the no-auth connection and send it for chat and image jobs. List only image models that currently have workers, and generate through Horde's native async API. # Conflicts: # open-sse/config/imageRegistry.ts # src/app/(dashboard)/dashboard/providers/[id]/ProviderDetailPageClient.tsx # src/shared/constants/providers.ts # src/sse/services/auth.ts * fix(providers): validate AI Horde keys against find_user The OpenAI-compatible /v1/models probe returns 200 for any Bearer token on oai.aihorde.net, so Check always succeeded. Use Horde's /v2/find_user lookup instead; an empty key still counts as the optional anonymous path. * chore(changelog): name the AI Horde fragment for #10542 * fix(images): harden AI Horde optional-key selection and outbound fetches - Optional-key selection now honors connection health (rate-limit cooldown and terminal/unavailable test status) before handing a stored key back, rotating to the next healthy key or falling back to the anonymous no-auth path instead of using an unhealthy stored key. - Route the Horde submit/check/status/cancel and catalog calls through the repository's bounded outbound-fetch helper (timeout, no more bare fetch()) and route R2 image downloads through the established bounded remote-image fetch (SSRF host guard, DNS-rebinding pin, streaming byte cap, redirect limit) instead of an unbounded fetch(). - Extend the generation deadline to cover the full request lifecycle (catalog freshness check, submit, polling, and image download), and add a regression test proving that exceeding the deadline issues a DELETE cancel to Horde's API rather than only timing out locally. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> --------- Co-authored-by: pqr <pqr@soraka.ititti.es> Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> |
||
|
|
ebf0bf913a |
fix(settings,auth): default debugMode to false and skip account rotation on model-unsupported 400 (#10525)
* fix(settings,auth): default debugMode to false and skip account rotation on model-unsupported 400 * fix(auth): disambiguate model-unsupported from auth-credential 400 The model-unsupported guard used MODEL_ACCESS_DENIED_PATTERNS directly, which also matches auth-credential errors like 'invalid api key for model X'. Add the AUTH_CREDENTIAL_ERROR_PATTERNS exclusion (same as checkFallbackError) and use provider_model_unsupported log reason. Addresses maintainer feedback on PR #10525 Signed-off-by: Minxi Hou <houminxi@gmail.com> * fix(auth): narrow model-unsupported guard to avoid misclassifying account-scoped entitlement 400s The #10460 guard reused MODEL_ACCESS_DENIED_PATTERNS directly, which also matches ambiguous "access"/"permission" phrasing (e.g. "does not have permission to access this model") that commonly signals an ACCOUNT-scoped entitlement gap (PRO vs free tier) rather than a genuinely provider-wide unsupported model — a different account of the same provider may still have access, so those must keep rotating normally instead of being short-circuited. Extract isProviderModelUnsupported400() in accountFallback.ts: reuses the same AUTH_CREDENTIAL_ERROR_PATTERNS exclusion checkFallbackError's 400 branch already applies, narrowed to a strict subset of unambiguous "provider does not serve this model at all" phrasings. auth.ts now calls this shared helper instead of testing the broader patterns in isolation, and exposes the sanitized reason ("provider_model_unsupported") on the returned result, not just in the log line. Also fix DATA_DIR test-isolation ordering in account-fallback-service.test.ts: it was assigned after the first dynamic import of accountFallback.ts, which transitively imports src/lib/db/core.ts (DATA_DIR is captured once at module-load time), so the intended isolated test directory was silently never used. Move the assignment before any transitive DB import, and add regression tests for the 3-account rotation contract: exactly one upstream call for an unambiguous provider-wide 400 with the combo advancing to the next target, continued rotation for account-scoped 401/403/429 and for the permission/entitlement 400 case that motivated this narrowing. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> --------- Signed-off-by: Minxi Hou <houminxi@gmail.com> Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> |
||
|
|
9500adb013 |
fix(combo): surface context-overflow before compression so oversized requests fail fast with a clear error (#10225) (#10503)
* fix(combo): surface context-overflow before compression so oversized requests fail fast with a clear error (#10225) * fix(combo): make context-overflow deferral target-aware for native Codex passthrough (#10225) The deferral added by the prior commit checked only operator-named compression exclusions when deciding whether at least one target "can compress" — it never accounted for native Codex Responses passthrough targets, which chatCore.ts unconditionally excludes from compression (compressionExcluded = nativeCodexPassthrough || ...). Deferring on such a target's account let an oversized request skip both the combo preflight AND compression, reaching fetch() uncompressed. Thread the same request-shape facts chatCore.ts uses (shouldUseNativeCodexPassthrough: provider/sourceFormat/endpointPath/body/ headers) down into getKnownContextOverflow so the deferral decision can never drift from chatCore's own — a native-codex-passthrough target now never counts as "compressible", so a pool made only of such targets keeps the fast local 400 instead of a wasted round trip. Adds regression coverage: the pure getKnownContextOverflow target-aware check, an end-to-end handleComboChat proof that a native-codex-only pool fails fast with zero dispatches, and two real handleChatCore-path tests proving compression actually reduces the dispatched body when eligible, and that a still-too-large-after-compression request is rejected locally without an upstream call. --------- Co-authored-by: adevwithpurpose <adevwithpurpose@users.noreply.github.com> |
||
|
|
2230fbbe93 |
fix(resilience): keep combo quality and auth reasons separate and redact connection labels in terminal errors (#10314) (#10501)
* fix(resilience): keep combo quality and auth reasons separate and redact connection labels in terminal errors (#10314) * fix(resilience): sanitize identifiers in error text, add explicit terminal-status policy, fix classifier ordering (#10314) Four gaps in the prior combo-error-aggregation fix: - formatComboOutcomes() only redacted connection identifiers in the model label, never in the raw upstream error TEXT — a proxy echoing a connection/account id back in its error body leaked it into the client-facing terminal message. Redact both. - The terminal HTTP status was still `lastStatus` — whichever target happened to fail last, independent of the other targets' reasons. Add resolveComboTerminalStatus(): preserve a 4xx only when every eligible target's failure is genuinely "the request is invalid" (model-class); a heterogeneous mix (e.g. a quality failure + a sibling's 401) now normalizes to a 5xx-class status reflecting an infra/provider problem, never a misleading client error borrowed from an unrelated target. - classifyComboOutcome()'s ordering had `status === 408 || status >= 499` checked before `status >= 500`, making the provider branch permanently unreachable — every real 5xx (500/502/503/504) was silently mislabeled as "timeout". Fixed to an exact match (408/499) and gave 429 its own explicit `rate_limit` kind instead of falling into the generic "model" (request-invalid) bucket by accident. - Added an integration-level regression driving the real handleComboChat wiring end-to-end (quality failure + sibling 401, and a success-after- quality-failure case), not just the pure aggregation helpers. Updated three pre-existing tests whose assertions encoded the OLD last-writer-wins contract this fix intentionally supersedes (#8486 Part B antigravity retryAfter tests, two combo-routing-engine status/message tests) to the new, more precise contract; verified the underlying #8486 concern (wrong target's retryAfter header) is still honored under the new status policy. --------- Co-authored-by: adevwithpurpose <adevwithpurpose@users.noreply.github.com> |
||
|
|
15386495c2 |
fix(compression): add i18n support for less-code and terse-prose (#10498)
* fix(compression): add i18n support for less-code and terse-prose Translates less-code output style to pt-BR, vi, ja, and id. Adds missing vi translation to terse-prose caveman mode. Removes less-code from English-only allowlist and updates matrix tests. Fixes #10426 * docs(compression): add output styles coverage table Adds the requested Output Styles matrix to the compression guide covering styles, supported languages, and intensity levels. Fixes #10426 |
||
|
|
50da54484e |
fix: downgrade adaptive thinking and gate context-1m beta on model eligibility (#10119) (#10481)
* fix(sse): downgrade adaptive thinking and gate context-1m beta on model eligibility (#10119) * fix(sse): thread resolved model into DefaultExecutor's anthropic-beta merge (#10119) DefaultExecutor.buildHeaders() merged the client-negotiated anthropic-beta header without ever passing the resolved target model into mergeClientAnthropicBeta(), so the context-1m-2025-08-07 eligibility gate added earlier in this PR could not see which model a combo/fallback had actually routed to at this call site. buildHeaders() now accepts an optional model parameter (mirroring BaseExecutor.buildHeaders' existing signature and the pattern already used by grok-cli.ts/qoder.ts) and forwards it through, so an ineligible model target (e.g. Haiku) has the beta dropped instead of forwarded blind. Restores a CHANGELOG bullet (PR #10366) that a prior merge auto-resolve had dropped from this branch. --------- Co-authored-by: adevwithpurpose <adevwithpurpose@users.noreply.github.com> |
||
|
|
7a6fcfc74d |
fix: resolve adaptive latency-collapse self-lock with solo-progress and idle recovery (#10111) (#10478)
* fix(admission): resolve adaptive latency-collapse self-lock with solo-progress and idle recovery (#10111) * fix(admission): refresh recovery ceiling on updateConfig (#10111) updateConfig() clamped currentLimit to the new min/maxLimit but left recoveryCeiling pinned to the value computed at construction time, so a raised initialLimit could never recover past the stale ceiling and a lowered one could leave the ceiling above the new maxLimit. Recompute recoveryCeiling from the new initialLimit on every updateConfig call, clamped to the (possibly also new) min/maxLimit. --------- Co-authored-by: adevwithpurpose <adevwithpurpose@users.noreply.github.com> |
||
|
|
e667ab12d1 |
fix(usage): wire agentrouter balance quota into dashboard Quota UI (#10078) (#10472)
* fix(usage): wire agentrouter balance quota into dashboard Quota UI (#10078) * fix(usage): render AgentRouter wallet balance as USD in the Quota UI (#10078) The prior fix wired AgentRouter's balance into getUsageForProvider() and USAGE_SUPPORTED_PROVIDERS, but the actual dollar figure never reached the Dashboard Quota UI: quotas.balance.remaining carried a synthetic two-state percent (100/0) instead of the real dollarBalance, and the Provider Limits renderer only formats a row as "$X.XX" when isCredits/currency/creditCount are set, which the generic quota-parsing path never sets. A configured balance rendered as a bare "100% left" percentage, not USD. Shape quotas.balance.remaining as the real USD amount (clamped to 0) and add an agentrouter branch to quotaParsing.ts that builds a credits-style row (same buildCreditsQuota() pattern as DeepSeek/Claude extra-usage), so a configured balance shows a currency-formatted dollar amount and an exhausted balance always renders as exactly $0.00. --------- Co-authored-by: adevwithpurpose <adevwithpurpose@users.noreply.github.com> |
||
|
|
09680013de |
fix(providers): resolve combo names on /v1/audio/speech and /v1/videos/generations (#10471)
* fix(providers): resolve combo names on /v1/audio/speech and /v1/videos/generations `GET /v1/models` advertises combos with `owned_by: combo`, and chat, embeddings, transcriptions (#9134) and images (#8986, #9239) all resolve those names. Speech and video did not: both rejected a combo name at model validation, before any resolution could happen. POST /v1/audio/speech {"model":"my-combo","input":"hi"} -> 400 Invalid speech model: my-combo. Use format: provider/model POST /v1/videos/generations {"model":"my-combo","prompt":"a cube"} -> 400 Invalid video model: my-combo. Use format: provider/model A client picking a model out of /v1/models therefore could not tell which entries the catalogue would actually accept, and callers ended up hardcoding vendor ids for these two routes while using combo names everywhere else. Both routes now mirror the images route: detect a combo name before the provider lookup and divert to a strategy executor. The two new executors follow imageCombo — expand targets with resolveComboTargets(), filter to targets the route can actually serve, walk them in priority order, and return the first success or the last failure, with 400/401/403 treated as terminal. Two details differ from the image strategy: Speech filters at model level rather than provider level. parseSpeechModel() resolves a provider prefix without checking that the model behind it can speak, so `openai/gpt-4o` would otherwise be accepted as a target and fail only once dispatched. The filter now checks the provider's own model list, and keeps targets from dynamic provider nodes that do not enumerate models. Speech also returns the handler's Response untouched instead of building a JSON body, because that route streams audio; only the ADD-only meta headers are attached, exactly as the direct path does. The failure branch is the only place the body is read. successfulMediaGenerationResponse() gains optional `strategy` and `fallbackAttempts` so the video strategy can report them the way imageCombo does, rather than duplicating the cost calculation. Both are omitted on the direct single-model path, where neither is meaningful. Tests mirror tests/unit/combo/image-combo.test.ts for both routes: combo not found, no capable targets, empty combo, and targets present with no provider connection. 16/16 pass across the three combo test files. * fix(providers): preserve local overrides, custom models and per-target prompt rules through video combo dispatch executeVideoCombo() diverged from the direct /v1/videos/generations route in three ways: it dropped the ComfyUI-style local-override credential lookup for authType:"none" targets, its capability filter only matched the built-in video registry (skipping custom OpenAI-compatible provider nodes tagged with the "videos" endpoint), and the route validated the prompt against the unresolved combo name before combo targets were expanded — rejecting prompt-optional I2V targets that never got the chance to opt out. Extracts the shared resolution rules (resolveVideoModelTarget, isVideoPromptOptional, resolveLocalOverrideCredentials) into src/app/api/v1/_shared/videoModelResolution.ts so the direct route and the combo executor apply identical rules, moves the combo-name diversion ahead of the prompt-required check so validation runs against the real resolved target, and adds per-target prompt validation inside the combo loop so a missing prompt only rules out that target instead of the whole combo. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> --------- Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> |
||
|
|
7f0404bf82 |
fix(open-sse): stop concurrent requests colliding on dedup hash for non-OpenAI formats (#10438)
* fix(open-sse): stop concurrent requests colliding on dedup hash for non-OpenAI formats computeRequestHash() in requestDedup.ts projected the prompt content from body.messages only. The dedup site in chatCore.ts hashes the *translated* (target-format) request body, and non-OpenAI target formats don't carry a messages field: Gemini-translated bodies use `contents`, Responses-API bodies use `input`. So for those formats messages was always undefined, every prompt hashed to the same null-backed value for a given model, and concurrent requests with different prompts joined the same in-flight promise -- the second caller silently received the first caller's response verbatim (#10249). Fix: project body.messages ?? body.contents ?? body.input ?? null instead of only body.messages, keeping the rest of the canonical hash projection unchanged. Genuinely identical concurrent requests still dedupe (the intended perf behavior); different prompts under Gemini/Responses-API target formats no longer collide. Regression test: tests/unit/request-dedup-10249.test.ts reproduces the two collision scenarios from the plan-file (Gemini `contents`, Responses-API `input`), confirms the OpenAI `messages` case was already correct, and asserts identical-request dedup keeps working. Verified RED (byte-identical hashes 0b24fd88.../dc16d5b7... pre-fix) -> GREEN (distinct hashes, dedup preserved) against this exact diff. * fix(open-sse): cover nested translator shapes + system fields in dedup hash (#10438) computeRequestHash() only read top-level body.messages ?? body.contents ?? body.input, but several translated request shapes nest their prompt content: the Antigravity Cloud Code envelope under request.contents, and Kiro under conversationState.currentMessage.userInputMessage.content (plus conversationState.history). Two different concurrent prompts to those targets could hash identically and share/leak a response between callers. Adds extractPromptContent()/extractSystemContent() helpers covering every prompt-bearing shape produced by open-sse/translator/request/*.ts (OpenAI/Cursor messages, Claude messages+system, Gemini contents+ systemInstruction, Responses input+instructions, Antigravity and Kiro nesting), and folds system/instructions/systemInstruction into the canonical hash so two requests with the same user message but a different system prompt no longer collide either. --------- Co-authored-by: adevwithpurpose <adevwithpurpose@users.noreply.github.com> |
||
|
|
548316a2c4 |
fix(translator): Normalize tool call names from lowercase to PascalCase when translating upstream responses to Claude Messages API format (#10392)
* fix(translator): Normalize tool call names from lowercase to PascalCase (#1) * Fix: Map lowercase tool names from Antigravity (Gemini format) to Claude Code expected PascalCase * Fix: toolNameMap in fun restoreClaudePassthroughToolUseName * fix(translator): Normalize tool call names from lowercase to PascalCase when translating upstream responses (OpenAI, Gemini, Antigravity) to Claude Messages API format This resolves `Error: No such tool available: read`/`bash`/`write` errors when using Claude Code CLI with third-party providers that emit lowercase tool names. The fix adds case-insensitive tool name lookups in `openai-to-claude.ts`, `gemini-to-claude.ts`, and related translators, ensuring tool names like `read`/`bash` are mapped to `Read`/`Bash` before being sent to Claude Code. Includes unit tests and comprehensive changelog notes ([#10250](https://github.com/diegosouzapw/OmniRoute/pull/10250)) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> * fix(translator): Parse <tool_call> JSON and TOOL_CALL text formats fr… (#2) * fix(translator): Parse <tool_call> JSON and TOOL_CALL text formats from model output Some models (DeepSeek, Qwen) emit tool calls as text instead of proper tool_calls JSON: either <tool_call>{...}</tool_call> or TOOL_CALL Name: {...}. Extend extractXmlInvokeBlocks to handle all 3 formats in a single scan pass, picking whichever pattern appears first. Includes unit tests for all formats. * fix(translator): Parse text-format tool calls in gemini-to-claude translator Extend the Gemini->Claude translator to detect <invoke>, <tool_call> JSON, and TOOL_CALL text formats emitted inline in text parts (Antigravity/Gemini models), converting them to proper tool_use content blocks instead of leaking raw text to Claude Code. * docs(changelog): Add changelog entry for text tool call parsing fix * fix(translator): consolidate tool name casing normalization and restore thought-signature persistence (#3) * fix(translator): sanitize tool_use.id and tool_result.tool_use_id to match Anthropic schema (#4) Ensure tool IDs from OpenAI-compatible upstreams (which may contain dots, colons, or special characters) are sanitized to ^[a-zA-Z0-9_-]+$ in response translators and passthrough requests before reaching Claude endpoints. * fix(responses): preserve native tools for openai-compatible Responses targets (#5) A Responses-shaped request to a custom openai-compatible connection whose outbound protocol is Responses took a Responses -> Chat -> Responses round trip, so Codex custom tools lost their grammar (`exec`), namespace groups were flattened (`collaboration`), and tool invocations failed upstream. Gate a native Responses passthrough on the connection's configured protocol (`apiType: "responses"` / `_omnirouteForceResponsesUpstream`) so the original tool definitions reach a Responses-capable upstream unchanged. Chat-only connections keep the existing downgrade. Closes #10374 * fix(translator): add support for 'applypatch' tool name in tool call checks * test(translator): add unit test for apply_patch and applypatch tool name remapping * fix(translator): remove no-explicit-any lint errors in tool-use-id-sanitization test Type the openaiToClaudeResponse/translateNonStreamingResponse return values with narrow local shapes instead of `any`, satisfying the repo's no-explicit-any = error rule for tests/. No behavior change — the same 3 assertions still pass. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> * test: update 9568 casing regression to match #10392's consolidated fix restoreClaudeToolName's static casing map now normalizes known lowercase tool names to canonical PascalCase unconditionally on the gemini-to-claude and openai-to-claude Claude Messages API paths (not gated behind toolNameMap), superseding the earlier per-map-only fix that the original #9568 regression test locked in as "expected" (it was previously labeled a known bug case). The gemini-to-openai passthrough path is unaffected by #10392 and keeps its original pass-through assertion. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> |
||
|
|
231b16ef18 |
fix(auto): rate-limit empty-pool AUTO warnings (#10344)
Family resolves like auto/zai with no connected models logged a warn on every call (about once a minute per poll). Keep the empty-pool behavior; emit the warn at most once per label per 60s. Co-authored-by: Ravi Tharuma <RaviTharuma@users.noreply.github.com> Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com> |
||
|
|
1089c24bc8 |
Remove/mimocode sunset provider (#10186)
* remove: drop sunset MiMoCode provider from model catalog * remove: drop sunset MiMoCode provider from model catalog (shared.ts) Remove unused imports, types, and comments from shared.ts. * remove: MiMoCode provider (Xiaomi sunset) — executor, registry, no-auth config, icon, tests * refactor(providers): finish MiMoCode removal — sweep remaining no-auth references Drop the leftover mimocode entries from the no-auth provider controls, the translate-path snapshot, the eslint suppressions, and the #3061 auth-loop test. Re-point the fingerprint-pin (#6696) and proxy-noauth (#6272) tests at opencode, which exercises the same fingerprint path, so the removal does not break runtime behavior. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> * docs(providers): reconcile provider/executor counts after MiMoCode sunset The base's parallel doc-count sync (#10433) pinned 340 providers / 101 executors. With mimocode removed, live code has 339 providers and 100 executors; refresh the user-facing counts (package.json description, llm.txt, README/AGENTS, i18n llm.txt, provider reference, diagrams) so the check-docs-counts STRICT gate stays green. Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com> * test(providers): fix orphaned mimocode references after MiMoCode sunset The sunset removed mimocode/mcode from the free-onboarding candidates and from FINGERPRINT_PROVIDERS, but two tests still referenced them: - free-provider-onboarding-setup: the mimocode->theoldllm substitution introduced duplicate 'opencode' rows (impossible given the request-set dedupe) and the wrong display name; align expectations with the actual {opencode, theoldllm} dedupe behavior and 'The Old LLM (Free)' name. - combo-system-prompt-templates-5501: resolveTargetFingerprint tested with provider 'mcode', which is no longer a fingerprint provider; point it at the remaining fingerprint provider 'opencode'. Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com> --------- Co-authored-by: diegosouzapw <diegosouzapw@users.noreply.github.com> Co-authored-by: Tushar49 <Tushar49@users.noreply.github.com> Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> Co-authored-by: adevwithpurpose <adevwithpurpose@users.noreply.github.com> Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com> |
||
|
|
2d50ec0789 |
feat(routing): add quota-aware provider scheduling — Phase 2 (#10126)
* feat(quota): Phase 2 adapters, reset timers, analytics, and dashboard API
* feat(routing): add quota-aware provider scheduling (opt-in)
* fix(db): rename migration to 148_provider_quota_state.sql
* fix(quota): harden quota state route, isolate phase2 tests, slim env diff
- route: requireManagementAuth + Zod body validation + buildErrorBody
sanitization (Hard Rule #12); fix clearProviderQuotaState -> clearProviderQuota
- .env.example/ENVIRONMENT.md: drop ~20 foreign vars, keep only
OMNIROUTE_QUOTA_AWARE_ROUTING (migration 148)
- tests/unit/quota-phase2.test.ts: DATA_DIR mkdtemp + resetDbInstance teardown
Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
* chore(ci): fix docs-sync + eslint-suppression drift for quota branch
CI gates flagged on PR #10126 head
|
||
|
|
48fef207ec |
fix(adobe-firefly): renew sessions through durable CDP (#9255)
* fix(adobe-firefly): open browser sign-in and resolve provider slug in /login
POST /api/providers/[id]/login passed the connection DB id to
inAppLoginService.startLogin, but that service looks up the provider by
slug in TOKEN_EXTRACTION_CONFIGS. The lookup always missed and returned
"No extraction config" without launching a browser — so the VibeProxy
"Sign in" button for Adobe Firefly (and every other web-cookie provider)
never opened a browser.
Adobe Firefly additionally had no extraction config because its IMS JWT
is never in cookies/localStorage — it only rides on the Authorization:
Bearer header of firefly-3p.ff.adobe.io XHRs.
- Resolve the provider slug from the connection row and pass the slug
(not the DB id) to inAppLoginService.startLogin.
- Add open-sse/services/adobeFireflyBrowserLogin.ts: a Playwright
service that launches a visible browser at firefly.adobe.com and
intercepts firefly-3p requests to capture the IMS JWT + sherlockToken
cookie. Wire it into the /login route for the adobe-firefly slug.
- Fix latent bug: updateProviderConnection reads camelCase keys
(apiKey, providerSpecificData), so the previous snake_case call never
persisted extracted credentials.
* fix(adobe-firefly): open browser sign-in and resolve provider slug in /login
POST /api/providers/[id]/login passed the connection DB id to
inAppLoginService.startLogin, but TOKEN_EXTRACTION_CONFIGS is keyed by
provider slug — so browser login never launched for web-cookie providers.
Adobe Firefly also cannot use cookie extraction: the IMS JWT only appears
on Authorization headers to firefly-3p.ff.adobe.io. Add a dedicated
Playwright interceptor and persist credentials with camelCase keys that
updateProviderConnection actually reads.
* fix(adobe-firefly): use system Chrome/Edge CDP for browser sign-in
Playwright is not available inside the pkg-packaged VibeProxyServices.exe,
so import('playwright') always failed with 'Playwright not installed' and
never opened a window. Launch Chrome/Edge with --remote-debugging-port and
capture the firefly-3p Authorization Bearer via pure CDP WebSocket instead.
* fix(adobe-firefly): live x-arp-session-id / Arkose wire (stop 408 under load)
Browser generate-async requires x-arp-session-id as base64({sid,ark,ftr}) with a
real Arkose blob (sherlockToken). JWT alone frequently returns colligo HTTP 408
system under load while credits still work.
- Match live ftr magic __UDF43-m4_31ck + Arkose pk in synthetic ARP fallback
- Ranked extract of sherlockToken / x-arp from Cookie, HAR, fetch() paste, and
space-joined JWT+ARP (PasswordBox newline collapse)
- Reuse one ARP for storage upload + generate-async
- Clearer 408 errors when browser ARP is missing vs stale
- Unit suite 42/42
* fix(adobe-firefly): durable session ARP rebuild and aux_sid false-positive
Rebuild x-arp-session-id from forterToken/arkose/ff_session_guid instead of
ranking long Cookie pairs (e.g. aux_sid=…) as opaque ARP, which caused colligo
HTTP 408. Cache IMS JWT + cookie sessions, rotate ARP on 408 retries, and keep
Playwright warm-up opt-in only (headless Forter is rejected).
Also expand synthetic ARP shape with bfp/fpjs to match live successful captures.
* fix(adobe-firefly): durable session, off-screen Chrome recovery, browser sign-in
Rebuild x-arp-session-id from Cookie pieces (sid/ark/forter) so aux_sid is never
sent as ARP. Sticky ARP + submit spacing reduce mid-batch colligo 408 thrash.
Add optional managed Chrome warm (off-screen headed by default; Forter rejects
headless) and POST /api/providers/{id}/login browser sign-in that returns JWT+Cookie
after a fresh SSO. Visible sign-in resets off-screen window placement and clears
prior Adobe session when adding another account.
* fix(adobe-firefly): renew sessions through durable CDP
* fix(adobe-firefly): isolate browser sessions per account
* fix(adobe-firefly): make account login fresh and deterministic
* chore(adobe-firefly): remove obsolete browser fallback
* docs(adobe-firefly): document renewal controls
---------
Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
|
||
|
|
0a74bfbdea |
feat(cli): relay-like CLI closure — target manifest, Codex TOML, Gemini launcher, guards
- canonical executable manifest (bin/cli/cli-manifest.mjs): run/configure/completion derive targets, aliases and --model wiring from one table; drift test cross-checks manifest x cliRuntime x UI catalog (tests/unit/cli/cli-manifest-drift.test.ts) - dashboard Codex generator converged to ~/.codex/config.toml (modern Codex v0.137+, verified against codex-cli 0.147.0): conservative merge, env_key auth (key never written), refuses invalid TOML, reports legacy config.yaml as migration note - omniroute run gemini: launcher over OmniRoute's /v1beta surface via GOOGLE_GEMINI_BASE_URL + isolated GEMINI_CLI_HOME forcing gemini-api-key auth (contract proven against @google/gemini-cli 0.50.0); ACP registration kept distinct - opt-in real smoke harness for upstream CLIs (RUN_CLI_SMOKE=1, credential by env NAME, redacted output): tests/integration/upstream-cli-smoke.int.test.ts - container-guard homologation for POST /api/cli-tools/apply (422 in container, dry-run preview allowed, host write passes) + docs; guard untouched - typecheck: omniglyphAdapter union narrowing, usageTracking typed signatures (UsageLike, no any), models.ts isValidModel params — typecheck:core and typecheck:noimplicit:core now clean - relay core (prior session of this effort): omniroute run for 6 CLIs, configure picker with per-context favorites/recents, contexts with optional keychain + 0600 fallback, provider CRUD with recursive redaction, completion updates, docs |
||
|
|
cd091ab878 |
fix(sse): route bare qwen3.8-max to the canonical -preview id (#10632)
The model ships only as `qwen3.8-max-preview` across every provider that serves it (bailian-coding-plan, qoder, qwen-cloud-token-plan, qwen-web), so the bare `qwen3.8-max` missed MODEL_SPECS: the chatCore context preflight fell back to contextManager's `default: 128000` and rejected prompts with `context_length_exceeded` despite the model's real 1M window, and the unknown id would have reached the upstream verbatim.
Both symptoms share one cause, so the alias goes in BUILT_IN_ALIASES, which resolveLifecycle() applies before the preflight and before dispatch.
Merged with the inherited OmniGlyph base-red (#9985) documented: its two failing compression tests were reproduced on the pure base tip
|
||
|
|
aa912c42a7 | docs: update omni route video guides ranking layout | ||
|
|
6c50137eeb |
fix(combo): actionable recovery hint for the all_targets_skipped terminal reason (#9303) (#10510)
Co-authored-by: adevwithpurpose <adevwithpurpose@users.noreply.github.com> |
||
|
|
6ff2e7b2c2 |
fix(antigravity): heal empty-projectId accounts via retryable auto-onboarding (#10424)
* fix(antigravity): heal empty-projectId accounts via retryable auto-onboarding Accounts with an empty Cloud Code projectId get a permanent 422 "Missing Google projectId" when loadCodeAssist returns no project. The 3.8.50 bootstrap attempts to CREATE the project via onboardUser, but a single failed attempt (transient network/upstream error) was memoized forever in onboardAttemptedCache: every later request in the process skipped onboarding and 422'd, even though a retry would succeed. Replace the permanent per-token Set with a failure-backoff map: failed onboard attempts are retried after a 5-minute backoff (bounded, self-healing), the in-flight lock still dedupes concurrent calls, and success clears the failure marker and memoizes the project as before. Accounts that CAN be onboarded now heal automatically on a later request or token refresh — no user action. Tests: the existing "does not retry" case is now framed as the backoff window; a new case proves the account heals (retries onboarding and recovers the project) once the backoff expires. * chore(changelog): fragment for #10424 antigravity project autocreate * feat(antigravity): BYOP fast-fail + manual GCP project-id override Port decolua/9router#2934 + VansRouter 802a859: - tryOnboardUser now returns a three-way status; a 200 onboardUser response WITHOUT cloudaicompanionProject means Google deprecated automatic project creation for standard-tier (personal) accounts (BYOP). Such accounts are cached permanently (no pointless ~18s re-onboard) and the executor fails fast with 403 GCP_PROJECT_REQUIRED + actionable 'enter your project id' message instead of the generic 422 or a delayed 429. - Transient onboard failures keep the existing 5-min backoff heal. - Manual project-id override: the EditConnectionModal now stamps providerSpecificData.isProjectIdManual when the operator enters a project id, and tokenRefresh skips auto-discovery for flagged accounts so the manual value is never overwritten. * chore(changelog): cover BYOP fast-fail + manual override in #10424 fragment * test(antigravity): expect fast 403 GCP_PROJECT_REQUIRED when loadCodeAssist finds no project (#10424) Google now marks accounts without an onboarded project as BYOP (automatic project creation deprecated for standard-tier accounts, #2934). The PR's BYOP fast-fail path returns 403 gcp_project_required instead of the old generic 422 missing_project_id; align the #2334 executor test with that contract so CI unit-test shard 2/4 passes. * fix(antigravity): persist isProjectIdManual, fix BYOP citation, dodge refresh-retry Review follow-up on #10424: 1. EditConnectionModal: isProjectIdManual was set on updates.providerSpecificData right after the project-id field, then the OAuth path (Antigravity is always OAuth) rebuilt providerSpecificData from connection.providerSpecificData before the request went out, discarding the flag — tokenRefresh.ts was guarding a field never actually persisted. The flag now lands in the single surviving antigravity merge, with a jsdom regression test (modeled on edit-connection-modal-openai-store-toggle). 2. The '#2934' citation for the Google BYOP claim pointed at an unrelated closed issue. Swapped for the real tracking issue #8491 (empty Google projectId -> 422 class) across bootstrap/executor/test comments. 3. BYOP fast-fail now returns 422 instead of 403: chatCore's generic 401/403 -> refresh-and-retry path was hitting Google's OAuth token endpoint on every request from an affected account (pointless — refreshing cannot create a GCP project), and 422 matches the sibling missing_project_id error the client already maps to an action-needed prompt. Also: eslint-disable-next-line for the pre-existing react-hooks/set-state-in-effect baseline noise in the modal (repo convention, same pattern as 11 other dashboard files). * chore(ci): drop unused eslint-disable in EditConnectionModal form hydration The react-hooks/set-state-in-effect disable added in the previous commit is unused under the repo's pinned eslint-plugin-react-hooks (7.0.1) — the rule does not fire on this line at that version, so the unused directive tripped the whole-repo 'No new ESLint warnings' gate (max-warnings 0). Verified with the lockfile-pinned plugin: lint:json is clean (0 errors, 0 warnings). * fix(build): bound and retry the opencode-plugin npm install in prepublish The plugin's node_modules is gitignored, so every fresh CI checkout runs a full npm install inside @omniroute/opencode-plugin during build:cli. npm's unbounded fetch retries turn a stalled registry CDN connection (the recurring onnxruntime-class ETIMEDOUT flake) into a 20-30 minute hang — the DAST 'Build CLI bundle' step has been cancelled at the 30m cap repeatedly. - Bound npm fetch: --fetch-timeout 60s, 2 retries with capped backoff — a stalled connection now fails fast instead of hanging the job. - Retry the install up to 3 times with a 10s pause between attempts, so transient CDN failures recover in-build. Net effect: the step either completes (network OK) or fails quickly with a clear error (network down) — it can no longer eat the whole job budget. * ci(quality): use the npm-ci-retry action on every install step Fast Quality Gates failed on the recurring onnxruntime-node postinstall ETIMEDOUT (Microsoft CDN 150.171.x.x) - the same transient flake that has hit Vitest and dast-smoke today. Only the Build job used the retry action; the other five jobs (Docs, Fast Quality Gates, Vitest, Unit Tests, changelog) still ran a bare install and die on any CDN hiccup. Use the existing retry action (3 attempts, exponential backoff) on every install step for consistency. * Merge branch 'release/v3.8.50' into fix/antigravity-project-autocreate * test(fix): refresh expired alibaba quota sample validity and onnxruntime pin for v3.8.50 base - alibaba-free-tier-quota-fetcher.test.ts: sample quotaValidityPeriod (2026-08-16 16:00 UTC) is in the past, making every quota entry classify as expired/not_capable; bump to 2028-01-01 UTC so the text/merge classification tests exercise the intended path again. - optional-transformers-dependency.test.ts: onnxruntime-node pin assertion updated from ~1.24.3 to ~1.27.0 to match package.json (bumped by #10403); the regular-not-optional intent is unchanged. * test(fix): widen modelsDevSync lastSync wait from 200ms default to 2000ms The truthy-spellings loop asserted each enabled case completes its first fetch within waitFor's 200ms default timeout, which trips under CI runner load (observed on PR 10424 shard 2/4). Match the file's other lastSync waits (2000ms) so the sync-completion assertion is load-tolerant. --------- Co-authored-by: Rouzbeh <rqzbeh@users.noreply.github.com> |
||
|
|
48e5cf7fe4 |
fix(sse): do not ZWJ-obfuscate the substring hermes in user text (#10488)
Keep the #8350 Hermes system-prompt drops, but remove hermes from the factory obfuscate_words list so hostnames and CLI mentions stay intact. Co-authored-by: Ravi Tharuma <RaviTharuma@users.noreply.github.com> |
||
|
|
c6c134300b |
perf(electron): ship optional ML/browser deps as installable packs (#10382)
Stage 7 of issue #10321 moves the optional ML and browser automation dependency closures out of the desktop bundle into checksummed, versioned packs installed on demand through the omniroute packs command. - scripts/build/optionalPackStaging.mjs stages pack members under .build/optional-packs, creates release tarballs, and emits optional-packs.index.json with per-member SHA-256 checksums. - scripts/packs provides manifest, install, remove, and verification helpers plus the packs CLI commands. - Runtime lookup includes installed pack node_modules directories, while LLMLingua and browser executors continue to degrade gracefully when packs are absent. The measured darwin-arm64 staging closure was about 534 MB of the 929 MB standalone node_modules tree (57%). |
||
|
|
e5e1358693 |
fix(antigravity): discover live chat models dynamically (#10422)
* fix(antigravity): discover Gemini 3.7 Flash models * fix(antigravity): discover live chat models dynamically * fix(antigravity): keep provider limits sanitizer strict |
||
|
|
be6f18b849 |
fix(account-fallback): classify 'insufficient credits' as credits-exhausted (#10116)
* fix(account-fallback): classify 'insufficient credits' as credits-exhausted Command Code returns 400 'You have insufficient credits to make this request...' when an account's billing credits run out. The phrase was missing from CREDITS_EXHAUSTED_SIGNALS, so the error stayed unclassified (errorType=null) and the connection was never marked credits_exhausted — getProviderCredentials kept re-selecting the same dead account on every request instead of rotating to a healthy one. Add 'insufficient credits'/'insufficient credit' to the signal list (already used by antigravity429Engine.ts) so the error classifies as QUOTA_EXHAUSTED and the account is skipped on subsequent selections. * fix(account-fallback): harden insufficient-credit matching and preserve chatanywhere Add the common 'insufficient credit balance' variation to CREDITS_EXHAUSTED_SIGNALS alongside the Command Code 'insufficient credits'/'insufficient credit' signals, and restore the consolidated ChatAnywhere gateway entry that the stale snapshot removal would have deleted when merging into release/v3.8.50. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> --------- Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> Co-authored-by: adevwithpurpose <adevwithpurpose@users.noreply.github.com> |
||
|
|
20fcb8d205 |
fix(affinity): evict the sticky session pin on a combo per-model timeout (#10016)
A combo target that stalls past comboTargetTimeoutMs is aborted by buildTargetTimeoutRunner, which swallows the resulting rejection behind its synthetic 524. Nothing marks the account unavailable — correctly, since a stall is not a quota/auth failure — so the #6219 eviction on the generic markAccountUnavailable -> shouldFallback path in chat.ts never ran. The session pin therefore survived its full TTL and every following request in that session was handed straight back to the account that had just stalled. Seen in production on combo "coding" [priority]: one codex account pinned for a 30-minute TTL, four consecutive requests, four 120s timeouts, "all targets exhausted" each time, while four sibling codex accounts stayed healthy and unused. Classify the abort reason (new dependency-free leaf comboAbortReasons.ts) and evict the connection-matched pin. Only a genuine per-model timeout evicts: a client disconnect or a hedge cancellation says nothing about account health, so those keep the pin and its prompt-cache locality. Eviction is best-effort and never breaks the dispatch path. The dispatch itself moves into a new seam, chatDispatch.ts, which merges the per-model abort signal into the outgoing request, runs executeChatWithBreaker, and owns the eviction on both the rejection and failed-result paths. Keeping that logic out of the frozen god-file leaves chat.ts one line SHORTER than before (1844 -> 1843). Co-authored-by: alexey.nazarov@softmg.ru <alexey.nazarov@softmg.ru> Co-authored-by: fenix007 <fenix007@users.noreply.github.com> Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com> |
||
|
|
e44a409aa9 |
fix(antigravity): classify geo-blocked egress, exclude account, real connection probe (#10420)
* fix(antigravity): classify geo-blocked egress, exclude account, real connection probe Google refuses the Cloud Code model API from unsupported egress locations with 400 FAILED_PRECONDITION "User location is not supported for the API use." Previously this surfaced as a cryptic "Antigravity upstream error (400)", never excluded the account, and the dashboard connection test stayed green because it only probed the (non-geo-restricted) OAuth userinfo endpoint. - errorClassifier: new GEO_BLOCKED type + isGeoBlockedError detection (400/403 + location-not-supported wording); non-terminal classification. - chatCore fallback: GEO_BLOCKED marks the connection and caches a 24h rate-limit-until exclusion so routing moves to other accounts instead of re-selecting the same one; never bans/expires the account. - auth: GEO_BLOCKED joins the non-terminal group (no banned/expired state). - antigravityUpstreamError: geo refusals carry an actionable message (egress location vs account problem, proxy-in-supported-region guidance). - connection test: antigravity/agy now probe the REAL streamGenerateContent surface (buildProbe), so a green tick means the model path actually works and a geo-blocked egress shows red with a clear diagnosis. * chore(changelog): fragment for #10420 antigravity geo-block resilience * chore(pr): drop prettier-version drift noise, keep only real hunks The earlier format pass (local prettier differs from the repo's pinned version) rewrapped unrelated lines in chatCore.ts and the provider test route. Restore the base formatting and re-apply only the GEO_BLOCKED fallback branch and the buildProbe connection-test changes. * fix(antigravity): strip competing-agent system prompts (429 RESOURCE_EXHAUSTED) Port decolua/9router b566b20, generalized: Antigravity flags system prompts advertising competing agents ('You are a Claude agent, built on Anthropic's Claude Agent SDK.' — Zed, Claude Code, etc.) and answers with a 429 quota error. sanitizeAntigravityGeminiRequest now strips known competitor identity sentences from systemInstruction.parts before dispatch; surrounding instruction text is untouched and non-matching prompts pass through without allocation. * chore(changelog): cover competitive prompt strip in #10420 fragment * fix(antigravity): scope GEO_BLOCKED classification to Google AI surfaces Address reviewer feedback: classifyProviderError is shared across every provider, so a lookalike 'not available in your region' body from an unrelated upstream must not receive the egress-fixable 24h exclusion treatment. Gate GEO_BLOCKED behind isGeoBlockEligibleProvider, which matches the surfaces that actually emit Google's regional-availability refusal: Cloud Code / Gemini Code Assist (antigravity, agy, cloudcode*), the Gemini Developer API (gemini, gemini-cli, vertex), plus a registry-driven fallback on executor/format. Non-Google providers fall through to their existing 400/403 classification (typically null for an unclassified 400), so a permanent block still follows its own path. * ci: re-run quality gates Trigger a fresh CI run for the PR: the previous run's 'Vitest (fast-path)' job failed in 'npm ci' because the onnxruntime-node postinstall could not download its binary from the Microsoft CDN (connect ETIMEDOUT 150.171.109.118:443). No tests ran; no code changed in this commit. * fix(antigravity): guard provider before registry lookup in geo-block gate isGeoBlockEligibleProvider passes the raw provider (string | null | undefined) to getRegistryEntry(provider: string), failing typecheck:core and the ts7-diagnostics ratchet (TS2345 at errorClassifier.ts:166). Add an explicit null guard; runtime behavior is unchanged — a falsy provider already resolved to !entry -> false. * ci: re-run quality gates (vitest npm ci onnxruntime CDN flake) --------- Co-authored-by: Rouzbeh <rqzbeh@users.noreply.github.com> |
||
|
|
710e43eb97 |
fix(sse): answer tiny-budget reasoning probes with a truncated 200 (#10281) (#10284)
* fix(sse): answer tiny-budget reasoning probes with a truncated 200 (#10281) Claude Code's /model capability check sends max_tokens: 1. Reasoning models burn the whole probe on thinking, and some upstreams (e.g. api.cline.bot for deepseek-v4-flash) answer the empty outcome with a 5xx "empty response content" instead of a truncated 200. The relayed failure also marked the connection unavailable and poisoned fallback/cooldown bookkeeping for what is only a probe. Detect tiny-budget reasoning probes in the non-streaming providerFailure path and synthesize a valid truncated response (200, empty content, finish_reason "length") — the same semantics errorClassifier.ts already grants to length-truncated empty 200s. Probes no longer poison connection health. Refs #10281. * chore(changelog): add fragment for reasoning-probe truncated-200 fix (#10284) |
||
|
|
e168b2347e |
fix(combo): restrict auto combo pools to user-visible models (#10456)
Auto combos (virtual auto/* pools via virtualFactory and pure-auto named combos via expandAutoComboCandidatePool) expanded their candidate pool from the provider's STATIC registry catalog, which can include models the operator never synced or approved (e.g. openrouter/auto). The visibility filter (getHiddenModelsByProvider) only caught models explicitly flagged isHidden, so catalog-only models passed through and got routed upstream. Build the credentialed pool from the models the user actually has available (synced + custom non-hidden), falling back to the static catalog only when the operator has no synced/custom models for that provider. Applies to every provider uniformly (openai, kilocode, openrouter, ...), with per-connection scoping for synced models. Provider wildcards (providerWildcard.ts) already used the active synced catalog as the authoritative source. Regression coverage: tests/unit/combo-auto-pool-visible-only.test.ts Co-authored-by: herjarsa <herjarsa@users.noreply.github.com> |
||
|
|
4b914ad297 |
feat(sse): bring i-have-adhd to vi/ja/id parity and guard the style × language matrix (#10425)
i-have-adhd shipped in #10271 with en + pt-BR only; it now covers vi/ja/id as well, matching ponytail. Every level keeps the SHARED_BOUNDARIES clause. Adds a per-MATRIX guard (output-styles-i18n-matrix.test.ts). Every existing test is per-style, which is how less-code stayed English-only since the 9router port without anyone noticing. The guard fails on: a new style without pt-BR, a style losing a translation it had, a translation missing an intensity level, a translation that dropped the boundaries clause, and a stale KNOWN_ENGLISH_ONLY entry. Proven by mutation, not just by passing: dropping less-code from the allowlist and renaming the vi key both turned it red with the expected messages. less-code stays English-only as declared debt (KNOWN_ENGLISH_ONLY + comment); the remaining coverage work is tracked in #10426. |
||
|
|
e05ac345da |
feat(sse): honor provider-rule lock scope for agentrouter (connection vs model) (#10419)
Makes the ProviderErrorRule `scope` field real at the persistence layer, exclusively for agentrouter (owner decision; every other provider keeps byte-identical behavior). checkFallbackError now surfaces `ruleScope` behind the HONORS_RULE_LOCK_SCOPE_PROVIDERS allowlist, and the agentrouter 403 path consults the rules before the generic apikey-FORBIDDEN early-return. markAccountUnavailable honors scope "connection" with a temporary connection cooldown instead of a per-model lockout — guarded so a permanent state can never be downgraded to a transient retry loop — and combo now skips the exhausted account within the same request, which also stops force-reusing the just-cooled connection via allowRateLimitedConnection. Documented in RESILIENCE_GUIDE §7 with the honest limits (disableCooling connections keep per-model behavior; the 6h model-access cooldown is clamped by mlSettings.maxCooldownMs, 30min by default; same-request skip needs targets carrying their own connectionId). Closes #10334 |
||
|
|
abd4df63dc |
fix(sse): surface Qwen/Alibaba personal Token Plan quota in dashboard and preflight (#10290)
* fix(sse): surface Qwen/Alibaba personal Token Plan quota in dashboard and preflight The personal Token Plan (5-hour / 7-day sliding windows) has no official OpenAPI and the inference API key cannot read it. Add a cookie-authenticated fetcher for the console gateway shared by home.qwencloud.com and the Model Studio console (contract captured live from a logged-in session): - open-sse/services/qwenTokenPlanQuotaFetcher.ts: POST /data/api.json (IntlBroadScopeAspnGateway / sfm_bailian) for usage + quota-config + subscription; sec_token resolved best-effort from the dashboard HTML; per-window parse (fields are omitted while a window is Temporarily Removed); 60s usage cache, 1h tier cache. - usage/qwen-token-plan.ts leaf + registration in the usage dispatcher, USAGE_FETCHER_PROVIDERS, USAGE_SUPPORTED_PROVIDERS, PROVIDER_LIMITS_APIKEY_PROVIDERS and bespoke preflight/monitor windows. - Also adds bailian-coding-plan to USAGE_SUPPORTED_PROVIDERS / PROVIDER_LIMITS_APIKEY_PROVIDERS: the coding-plan fetcher existed but the dashboard filtered those connections out (UI gap). Refs #9603 (Problema 1 — quota missing; the 429 recovery half is a follow-up). * docs(env): document Qwen Token Plan quota env vars + regen omni-settings skill QWEN_CLOUD_COOKIE, QWEN_CLOUD_SEC_TOKEN, QWEN_TOKEN_PLAN_HOST and QWEN_TOKEN_PLAN_DASHBOARD_URL added to .env.example and docs/reference/ENVIRONMENT.md (check:env-doc-sync), with the generated omni-settings skill refreshed (check:agent-skills-sync). Refs #9603 * revert: keep hand-tuned omni-settings thinking-budget section The agent-skills-sync drift predates this PR (hand improvement from #10169 not yet synced into the generator source) — it fails on every open PR and belongs to a base-reds fix, not this branch. Regenerating here would erase the intentional content. * feat(dashboard): add the Qwen/Model Studio console cookie field to the connection modal The Token Plan quota fetcher is cookie-authenticated (the inference API key cannot read the console gateway), but no modal field existed to paste that cookie — so the quota was unconfigurable from the dashboard and the fetcher could only ever return its 'needs a cookie' message. Adds the field for qwen-cloud-token-plan and bailian-coding-plan alongside the existing ollama-cloud / alibaba console-cookie inputs (same password-input, blank-keeps-stored semantics), pre-fills it when editing a connection, and extends the providerSpecificData string/length validation to the two new keys. Tests: tests/unit/qwen-token-plan-cookie-field.test.ts (RED before, GREEN after) covers persistence + trimming, the blank-input no-overwrite rule and schema acceptance/rejection. Refs #9603 * docs(dashboard): correct the Qwen console cookie instructions The placeholder claimed the cookie looks like 'token=...'; the qwencloud portal actually issues 'login_qwencloud_ticket=...' alongside cna/cnaui/aui (mirroring login_aliyunid_ticket on the Alibaba console), so the hint pointed at the wrong value. Replaces the guesswork with the verified retrieval steps in all three places an operator can hit — the modal field hint, the fetcher's 'needs a cookie' message and .env.example/ENVIRONMENT.md: log in to home.qwencloud.com > Billing > Subscription, F12 > Network, reload, filter by api.json, click a request to cs-data.qwencloud.com and copy the WHOLE Cookie request header. Also documents that the value must go on one line (it contains '=' and ';') and that it dies with the browser session. Refs #9603 * fix(dashboard): tolerate partial form objects in the qwen cookie branch Adding bailian-coding-plan to QWEN_TOKEN_PLAN_PROVIDERS routed callers that previously matched NO branch in assignQuotaScrapingProviderData into the new one, which assumed the two new fields are always present. Older callers build a partial form object, so buildAddProviderSpecificData threw: TypeError: Cannot read properties of undefined (reading 'trim') (tests/unit/dashboard/agentrouter-connection-modal-fields.test.ts) Reads the new fields with optional chaining and adds a regression test that calls the helper with those keys deleted for both providers. Refs #9603 * refactor(dashboard): move quota-scraping form logic into a UI-free module tests/unit/qwen-token-plan-cookie-field.test.ts imported QuotaScrapingFields directly, which pulls `@/shared/components` and, through that barrel, untranspiled ESM (@lobehub/icons). The node:test runner cannot parse it and the whole test file died in CI with: SyntaxError: Unexpected token 'export' at @lobehub/icons/es/Ai21/components/Mono.js (It passed locally, so only the CI shard surfaced it.) Extracts the pure pieces — QWEN_TOKEN_PLAN_PROVIDERS, QuotaScrapingFieldValues, EMPTY_QUOTA_SCRAPING_FIELDS and assignQuotaScrapingProviderData — into quotaScrapingFieldValues.ts. The component imports them and re-exports the public names, so every existing importer keeps its current path. The unit test now targets the UI-free module. Refs #9603 * fix(providers): point bailian-coding-plan at the Token Plan endpoint and its console Two independent defects kept this provider unusable with a valid Alibaba Token Plan key (verified live 2026-08-14 with the owner's key and cookie): 1. Wrong inference host. The catalog entry is named "Alibaba Token Plan", links to token-plan-overview and its hint asks for a Token Plan key, but the registry pointed at coding-intl.dashscope.aliyuncs.com — the Coding Plan host, which rejects Token Plan keys with 401 invalid_api_key. The documented Anthropic base URL for Token Plan is token-plan.ap-southeast-1.maas.aliyuncs.com/apps/anthropic (https://www.alibabacloud.com/help/en/model-studio/more-tools). Against the new host the same key returns 200 for all six registry models and a real completion; auth stays on x-api-key. 2. Wrong console identity for quota. The personal Token Plan is sold through two consoles sharing one backend, and the gateway validates the session against the console declared in the request: an Alibaba console cookie (login_aliyunid_ticket) sent with the QwenCloud identity is refused with BailianGateway.Login.NotLogined. resolveConsoleSite() now picks host, cornerstoneParam.consoleSite/domain and Origin/Referer from the cookie's login ticket, falling back to the provider. With that switch the same cookie returns usage/subscription/quota-config. Also routes bailian-coding-plan quota through the Token Plan fetcher (the Coding Plan call returns "Bad Request" for these accounts), keeping the old fetcher as the fallback for real Coding Plan keys, and labels the plan by console ("Alibaba Token Plan (Pro)" vs "Qwen …"). Live validation: inference 200 (qwen3.7-plus answered "FUNCIONA"); quota 12,934/40,000 credits, 67.7% remaining, resets 2026-08-20. Refs #9603 --------- Co-authored-by: Xiangzhe <bakryun0718@proton.me> |
||
|
|
20ea78c943 |
feat(sse): restate agentrouter quota 403/400 as retryable 429 with provider-scoped error rules (#10335)
agentrouter.org signals temporary quota exhaustion with HTTP 403/400 and a Chinese body (用户额度不足) instead of 429, so clients like Claude Code treat it as permanent and abort, and the fallback engine classified it as a generic apikey AUTH_ERROR. New registry open-sse/config/upstreamStatusRestatement.ts restates those statuses to 429 with a synthetic Retry-After at a single hook in chatCore's providerFailure block (after parseUpstreamError), so classification, combo aggregation and the client response all see a retryable error. 无权访问模型 (permanently no model access) is veto-listed and never restated. agentrouter classification rules are registered in providerErrorRules.ts and reach the real checkFallbackError path through resolveRuleMatchBody() with an exclusive FULL_TEXT_RULE_PROVIDERS allowlist — every other provider keeps its previous behavior byte-for-byte. Known limitations tracked in #10334: the rules' scope field is informational (persistence applies per-model lockout for agentrouter), the 403-only model-access rule has no production path yet, and errors embedded in 200 SSE streams are not restated. Refs #10334 |
||
|
|
964a3fe442 |
feat(sse): add i-have-adhd output style to compression catalog (#10271)
Adds `i-have-adhd` as the 5th entry in OUTPUT_STYLE_CATALOG — a port of the github.com/ayghri/i-have-adhd skill (MIT), following the same integration shape as ponytail. Action-first output shaping: the next action leads, multi-step work is numbered, no preamble/recap/closers — which also trims output tokens. lite/full/ultra levels in en + pt-BR, each ending in SHARED_BOUNDARIES so code, paths, commands, errors and URLs stay verbatim. The agent-harness-specific upstream rules (restate plan state, time estimates) are reworded as conditionals so they hold for plain chat clients too. Per the D-A1 registry contract, one catalog entry is the whole change: the injector, the settings panel, the Zod schema and the telemetry all enumerate the catalog, so no other production file moves. Dedicated test mirrors ponytail-catalog.test.ts (7 tests). |
||
|
|
90458a613c |
fix(sse): stop the executor-contract guard from hot-looping the router (#10373)
The `instanceof Response` guard from #10256 broke two ways: 1. `instanceof` is nominal against `globalThis.Response`, but proxyFetch dispatches through the npm undici package's fetch, whose Response is a different class — so valid upstream responses were rejected as contract violations. Replaced with `isResponseLike()` (instanceof fast path + structural brand/member probe); genuinely malformed shapes still throw. 2. The thrown error had no `.status`, so it fell through to chatCore's BAD_GATEWAY default — an internal defect was treated as a flaky provider, cooling the connection down and retrying forever. It now carries status 500 + `executor_contract_violation`, registered as request-scoped and terminal (no cooldown, no breaker, no retry). batch_api.test.ts went from exit 124 (infinite hang, pinning Unit shard 4/4 in every open PR) to exit 0, 22/22 passing. Closes #10360 |
||
|
|
13098989e8 | fix(types): narrow refresh token rotation inputs (#10257) | ||
|
|
97aac6ac6c |
fix(ci): clear base-reds on release/v3.8.50 (round 4) (#10260)
* fix(ci): clear base-reds on release/v3.8.50 (round 4) Drains the HARD failures reported by Release-Green run 31693210948 on issue #9985 (ESLint errors: 2) plus the merge-integrity red every open PR is inheriting. - ESLint error 1: @omniroute/opencode-plugin/src/index.ts had a stray extra '});' (introduced by #9316) that broke parsing with 'unexpected file in NFT list' on the build path. - ESLint error 2: cli-env-inline-comment-10100.test.ts used new Function to extract parseEnvValue from the bin entrypoint (no-new-func, Hard Rule #3). Extracted the helper to bin/cli/utils/parseEnvValue.mjs and import it from both the entrypoint and the test (same behavior, no eval). - open-sse-typecheck (Fast Quality Gates): open-sse/utils/stream.ts imported sseCommentsEnabled twice (#9378) causing TS2300 Duplicate identifier; removed the duplicate import. - Merge integrity (changelog + generated skills): skills/omni-settings/SKILL.md was edited manually by #10169 without updating the generator source, so check:agent-skills-sync failed on every PR (Generated: 1). Moved the curated thinking-budget content into a <!-- skill:custom-start --> block (the documented preservation mechanism), which the generator now keeps in sync. Refs #9985 * fix(tests): align wave1-a poolside test with #10216 probed catalog #10216 published Poolside's two authenticated-probe models (poolside/laguna-xs-2.1, poolside/laguna-s-2.1) as static seeds, but the wave1-a free-tier test still asserted 'no invented static model ids' (entry.models === []), failing every open PR. Separate poolside from the empty-models assertion and pin its probed catalog explicitly so a future catalog change is a deliberate update, not a silent drift. * fix(pack): register parseEnvValue.mjs in PACK_ARTIFACT_REQUIRED_PATHS The extract of parseEnvValue to bin/cli/utils/parseEnvValue.mjs added a new direct import to bin/omniroute.mjs, which pack-artifact-entrypoint-closures enforces against PACK_ARTIFACT_REQUIRED_PATHS. Register the module so a future tarball omission fails loudly. * fix(combo): restore default same-model retry semantics after #10217 #10217 wired config.failoverBeforeRetry into the same-model retry guard in both the priority/auto and round-robin loops, but DEFAULT_COMBO_CONFIG defaulted the flag to true — flipping same-model retry off for every combo that never touched the setting, not just the opt-in case. Round-4 bisect ( |
||
|
|
d2fd88dfbc |
fix(combo): make failoverBeforeRetry actually skip the same-model retry (#10217)
* fix(deps): bump nanoid, dompurify for 2 new Dependabot alerts (#189, #190) Bumps: nanoid ^3.3.17 (was transitive, now overridden), dompurify ^3.4.13 (with monaco-editor scoped override). Closes Dependabot #189, #190. Remaining #182-#188 (js-yaml + mermaid) already closed by #9651 merge — awaiting Dependabot re-scan. npm audit → 0 vulnerabilities. * fix(repo): harden .gitignore to also ignore a _tasks symlink (/_tasks) _tasks is a SEPARATE nested git repo (gitignored). The pattern _tasks/ (trailing slash) ignores only a directory, not a SYMLINK named _tasks. A self-referential _tasks symlink can slip in via git add -A and, once pulled, checkout materializes it over the real _tasks repo (destroying plans/specs/hands-off). Anchored /_tasks ignores the symlink too, preventing re-capture. * fix(combo): make failoverBeforeRetry actually skip the same-model retry Both same-target retry loops (priority/auto and round-robin) checked isTransient/maxRetries/providerExhausted but never consulted config.failoverBeforeRetry, so a rate-limited model still got maxRetries+1 back-to-back attempts on itself before falling back to a sibling — the config option (#2417) was only ever wired into skipUpstreamRetry, a separate lower-level mechanism. Now the same-model retry is skipped when failoverBeforeRetry is set AND a sibling target is actually available; with no sibling left, it still retries same-model since skipping would just burn the last attempt for nothing. --------- Co-authored-by: diegosouzapw <diegosouzapw@users.noreply.github.com> Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouza.pw@outlook.com> Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com> Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> |
||
|
|
4e1d21f756 |
docs(settings): Thinking Budget modes + fix Auto i18n collision (#10169)
Co-authored-by: RaviTharuma <RaviTharuma@users.noreply.github.com> |
||
|
|
06f41cda63 |
fix(combo): isolate session stickiness by combo (#10137)
Co-authored-by: Bryan Nathan <bryan@users.noreply.github.com> |
||
|
|
a2e5bd1dfc |
fix(combo): default chaos SSE to comment-only for OpenAI-compatible clients (#10128)
* fix(deps): bump nanoid, dompurify for 2 new Dependabot alerts (#189, #190) Bumps: nanoid ^3.3.17 (was transitive, now overridden), dompurify ^3.4.13 (with monaco-editor scoped override). Closes Dependabot #189, #190. Remaining #182-#188 (js-yaml + mermaid) already closed by #9651 merge — awaiting Dependabot re-scan. npm audit → 0 vulnerabilities. * fix(repo): harden .gitignore to also ignore a _tasks symlink (/_tasks) _tasks is a SEPARATE nested git repo (gitignored). The pattern _tasks/ (trailing slash) ignores only a directory, not a SYMLINK named _tasks. A self-referential _tasks symlink can slip in via git add -A and, once pulled, checkout materializes it over the real _tasks repo (destroying plans/specs/hands-off). Anchored /_tasks ignores the symlink too, preventing re-capture. * fix(combo): default chaos SSE to comment-only for OpenAI-compatible clients --------- Co-authored-by: diegosouzapw <diegosouzapw@users.noreply.github.com> Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouza.pw@outlook.com> Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com> Co-authored-by: herjarsa <herjarsa@users.noreply.github.com> Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> |
||
|
|
99d19f8f35 |
fix(kimi): normalize MFJS tool schemas (#10079)
Remove unsupported root-level anyOf constraints only on Kimi and Moonshot OpenAI tool requests while preserving nested schemas and caller-owned inputs. Mark Kimi Web models as unable to execute function tools so combo routing filters them correctly. |
||
|
|
1a8d38655d | fix(reasoning): preserve and replay assistant turns (#10045) | ||
|
|
c9daf99e37 |
fix(combo): clear LKGP pin when its target fails, not only set it on success (#10034)
setLKGP() was only ever called on success — nothing invalidated a "last
known good provider" pin once that provider started failing, so a
*separate* subsequent request kept re-selecting the same just-failed
target via applyStrategyOrdering.ts's LKGP reordering.
Live incident: an OpenClaw request to combo "default" (routerStrategy:
lkgp) got a real reasoning + apply_patch tool call from
opencode-zen/big-pickle, then 3 separate follow-up requests over the
next ~2 minutes each independently re-selected the same big-pickle
target and each timed out with "504 Stream produced no non-ping SSE
event within 95000ms" before the client gave up — instead of failing
over to any of the combo's other 12 models.
Root cause confirmed via code read: circuit breaker and model lockout
deliberately don't react to this failure class (isStreamReadinessFailureErrorBody
exempts STREAM_READINESS_TIMEOUT/combo_target_timeout 504s from tripping
the provider breaker, and REQUEST_SCOPED_UPSTREAM_ERROR_CODES suppresses
model-lockout recording for the same class — both intentional, to avoid
poisoning a healthy provider on request-specific timing). Nothing else
in the system was clearing the stale LKGP pin, so it kept winning
target-selection ordering for every new top-level request.
Fix: add clearLKGP(comboName, modelId) to src/lib/db/settings/lkgp.ts,
export it through settings.ts/localDb.ts, and call it (mirroring the
existing setLKGP-on-success call pattern exactly, same two keys) in both
combo.ts's per-target failure paths -- handleComboChat's "Done retrying
this model" block and handleRoundRobinCombo's structurally identical
twin -- right where a target is finally given up on and the loop moves
to the next one.
TDD: new regression test in tests/unit/combo-routing-engine.test.ts
("clears LKGP after the last-known-good target fails") reproduces the
exact live scenario -- confirmed failing against the pre-fix code,
passing after. Added direct unit coverage for clearLKGP itself in
tests/unit/db-settings-crud.test.ts (deletes only the targeted key,
sibling keys survive; no-op on an unset key doesn't throw) and
registered the new export in db-settings-split.test.ts's public API
surface characterization test.
Test plan:
- Full combo/LKGP-related suite (combo-routing-engine, db-settings-crud,
db-settings-split, combo-strategy-fallbacks,
combo-selected-connection-success,
delete-provider-connection-invalidates-lkgp-8887, db-read-cache) --
183/183 passing.
- npx tsc --noEmit -- clean for all changed files (pre-existing unrelated
errors elsewhere in the same test files confirmed identical against a
pristine upstream/release/v3.8.50 checkout, zero diff at those lines).
- npm run lint -- clean (new test's any usage properly typed, not left
to inflate the file's frozen any-budget suppression).
⚠️ base-red inherited: #9985
|
||
|
|
10c622afa6 |
fix(providers): default missing cache_control.ttl to 1h on the native Claude OAuth path (#10221)
Real Claude Code (and CC-protocol-compatible clients) commonly send
`cache_control: { type: "ephemeral" }` with no `ttl`. On the native
Claude OAuth path (provider `claude`/`cc`) the outbound anthropic-beta
set always includes extended-cache-ttl-2025-04-11, so requesting the 1h
TTL is always valid here — but Anthropic only honors it when `ttl` is
explicit; an absent `ttl` silently falls back to the platform default of
5 minutes even though the 1h beta was negotiated.
Practical effect: any pause longer than 5 minutes between turns forces a
full prefix rewrite (tens of thousands of tokens for a typical Claude
Code system+tools prefix) instead of a cache hit, burning through the
subscription's rate limit far faster than native (direct-to-Anthropic)
usage for the same workload.
Adds `normalizeCacheControlTtl()` to claudeCodeConstraints.ts (same
module as the sibling cache_control helpers enforceCacheControlLimit /
ensureCacheControlOnLastUserMessage) and calls it right after the
billing-header system-block manipulation in base.ts, immediately before
the request is signed and sent. Never touches a cache_control that
already specifies a ttl.
Measured before/after with a real Claude Code CLI session through this
path (system + tools prefix ~46k tokens):
before: cache writes always land in ephemeral_5m_input_tokens; a >5min
gap between turns forces a full rewrite (cache_read resets to 0)
after: cache writes land in ephemeral_1h_input_tokens; a >6min gap
survives (cache_read stays intact)
--no-verify note: local pre-commit's check:docs-sync fails on this branch
tip ("CHANGELOG.md first section must be Unreleased") for reasons
unrelated to this diff (pre-existing state of release/v3.8.50 mid-cycle,
CHANGELOG.md untouched by this change). Added the required changelog.d
fragment per CONTRIBUTING.md regardless.
Co-authored-by: Jefferson Alves <jefferson@rastrosystem.com.br>
|
||
|
|
d259d9fcba |
fix(ci): clear base-reds on release/v3.8.50 (round 3) (#10213)
* fix(ci): clear base-reds on release/v3.8.50 (round 3) - CHANGELOG.md: restore the top [Unreleased] section dropped by the #10189 reconcile (docs-sync gate: first section must be Unreleased) - env-doc-sync: document CONDUCTOR_ORCHESTRATOR_TOKEN + CONDUCTOR_SPOKESPERSON_URL in .env.example/ENVIRONMENT.md; allowlist the CI-only GITHUB_STEP_SUMMARY and TS7_BASE_REF (ts7 ratchet signals); drop a stray merge artifact line - providers: restore the audited chatanywhere metadata entry that base-reds round 2 dropped together with its duplicate — the provider was half-wired (registry+endpoint without APIKEY metadata), which is what the wave3 test catches; re-pin providers-constants-split at the measured 228 - docs counts: 338 -> 339 (today's +2 void-ai/helixmind, -1 Puter) via gen:provider-reference + README/AGENTS/llm.txt/package.json/diagrams/i18n mirrors - file-size ratchet: annotated rebaseline for the two pre-existing drifts (ModelSelectModal 1138, gateways 1250) following the 2026-08-11 precedent Refs #9985 * fix(ci): base-reds round 3b — stale sibling tests + mode-pack weight contract - check-docs-counts-sync.test.ts: drop the imports/subtests of the four helpers #10196 removed from the gate script (readMcpFactsFromSource, listLocalizedDocs, makeRequiredCountsValidator, checkFreeTierInventory) — the new-API tests that #10196 added stay; the file now loads again under the node runner - quota-connection-recovery.test.ts: convert from vitest APIs to node:test — the file lives in tests/unit/*.test.ts (node-runner glob) and the vitest runtime crashes when imported outside vitest, killing the whole shard entry - modePacks.ts: re-normalize all six mode packs to sum 1.0 — #8940 added sessionAvailability: 0.05 to every pack without rebalancing (1.05 total); ratios preserved exactly (÷1.05), so post-normalizeScoringWeights behavior is unchanged; restores the declared sum-to-1.0 contract the 4235 test pins Refs #9985 * fix(ci): base-reds round 3c — vitest siblings, weights default, secrets FP, mutation tap - DistributeProxiesButton.test.tsx: wrap renders in NextIntlClientProvider — #9245 localized the component (useTranslations) and left the test without the intl context, failing all 14 cases - scoring.ts: re-normalize DEFAULT_WEIGHTS to sum 1.0 (same #8940 class as the mode packs — sessionAvailability added without rebalancing; ratios preserved) - .gitleaks.toml: generalize the kimi sponsor-banner localStorage-key allowlist to -v\d+ — #10200 bumped v1→v2 and the stale regex regressed the secrets ratchet with a false positive - stryker.conf.json: register 6 covering unit tests in tap.testFiles (4 modules) so their mutant kills count — unblocks check:mutation-test-coverage --strict Refs #9985 * fix(ci): base-reds round 3d — inspector factor gap, stale registry/gap tests, i18n key sync - comboScoringInspector: add cacheAffinity/sessionAvailability/connectionDensity to FACTOR_KEYS + the factor-key type — calculateScore() weighs them but the breakdown omitted them, so the explained contributions never summed to the reported score (inspector bug, red on the pure tip) - combo-scoring-inspector.test: make the explicit-weights override sum-neutral (±0.05 shift) so it stays valid for any DEFAULT_WEIGHTS values — the hardcoded override only summed to 1.0 against the pre-#8940 defaults, which is also why explicit weights silently fell back to 'default' on the tip - unorouter-registry.test: align to the canonical .com host (api.unorouter.ai 301-redirects there, verified live) and to wave4's live model discovery (passthrough, no static seed) — the .ai/auto-model expectations were stale - check-migration-numbering.test: 147 left KNOWN_GAPS when 147_api_keys_model_access_mode.sql landed — assert absent (same as 143) - i18n: sync-ui pass — 35,914 missing UI keys stamped as __MISSING__ placeholders across 42 locales (mechanical; greens the pt-BR key-presence integrity test; coverage pct unchanged by design — translation is a separate workstream) Refs #9985 * fix(ci): base-reds round 3e — 2 real defects + 14 stale sibling tests (waves A-E) Real defects fixed: - src/lib/db/apiKeys.ts: #9313's empty-allowlist early return bypassed the group permission check, silently disabling group deny rules (#8817) for every key without a per-key allowlist; fall-through restored, restricted+[] deny-all kept - open-sse/utils/proxyFetch.ts: #10032 re-appended the raw transport error to the propagated message, reintroducing the proxy user:password leak #9837 closed; new redactProxyDetailsInMessage() keeps the reason, redacts URL/credentials - .github/workflows/quality.yml: #10134 added the TS7 ratchet as a separate blocking step AFTER the aggregated gates — the exact #8542 masking mechanism; folded into the non-fail-fast loop (still blocking, still PR-only) ⚠️ CI edit, gate-strengthening — explicit owner sign-off requested on the PR - src/i18n/messages/ko.json: 3 machine-mistranslation regressions caught by the #8244 glossary checker (장애인→비활성화됨, 양말5://→socks5://, 비클로드→Claude가 아닌) Stale sibling tests aligned to deliberately-moved contracts (each cites its mover): request-log-detail-layout + -stream (#9245 intl provider), repro-8542 pin update, quality-rail-gate-membership (#10134 shape), agentSkills-routes 45→46 (#9058), cloudflare-ai-catalog-8717 (#8804 supersedes #8808), executor-xai (#9994), vision-bridge-claude-wire (#9463 minimax→openai), sse-auth forced-pin (#8893), tls-proxy-context (strengthened leak guards), rate-limit-local-error-classification (#9164/#9342), minimax-thinking-signature (#9463), codebuddy-cn (#9723 +1 test), github-copilot-custom-model (#9050), providers-g4f-batch3 (#9584), synced-capability-warmup (#9199, stricter), sidebar-tools-group (#8221), oauth-modal-grok-cli-paste (#9245); agentSkills/catalog.ts comment 45→46; file-size rebaseline for proxyFetch (+19, annotated) Refs #9985 * fix(ci): base-reds round 3f — waves F-J: 9 more real defects + stale sibling sweep Real production defects fixed (all red on the pure tip, each with its origin): - routeGuard.ts: #8949 accidentally DELETED the /api/providers/[id]/login local-only pattern — the route spawns a browser, so the loopback gate for a process-spawning route was gone (Hard Rules #15/#17); restored (314 guard tests green) - agentSkills generator: #9058's category dispatch gave the config category an empty body, wiping skills/config-codex-cli/SKILL.md at the #10131 sync; fixed + SKILL.md regenerated via the official generator - imageRegistry: #9982 broke same-provider bare aliasing (antigravity preview id sent upstream unresolved); new resolveSameProviderBareAlias() keeps the fal cross-provider fix intact - imageRegistry: #9982's prefix strip handed the bare nano-banana ids to fal-ai, violating the pinned 2026-07-31 operator decision (adobe-firefly owns them); fal entries made prefix-only (dispatch already re-prefixes) - mediaGeneration/fal.ts: the missing-credential 401 guard was lost when #10198 deleted the superseded falHandler — tests were hitting the live network - bottleneckPatch/rateLimitManager: #9041's merge clobbered #9604, resurrecting the Bottleneck v2.19.5 heartbeat bug (reservoir never refills); patched the library defect at the root and re-aligned chat-rate-limit-body-lock to the working reservoir contract - processSupervisor.mjs: #9761 regressed the Node spawn to bare "node" (the #9156 launchd bug) and dropped #9209's ipv4first args; both restored - openai-responses/pureHelpers: #9423's Agent null-sentinel was unreachable on the schemaless JSON-string path; gate extended - i18n en.json: #8222's regen reverted the #9976 unclosed-tag fix and #8559's combo-cooldown copy; #9038 shipped 40 t() calls with no messages (runtime MISSING_MESSAGE); all restored/added + official sync-ui stamps, and vi's zero-marker policy re-established via the sanctioned translation backend Stale sibling tests aligned (movers cited inline): chat-helpers (#9447), executor-antigravity (#9351), video-fal-grok (#9982), visionBridge (#9759), web-session-credentials (#8974), production-build-module-integrity (positive anchor added), agentSkills-generator/skillManifestsLint/skills-injection/ agentSkillTools-mcp/listCapabilities-a2a (#9058), memory-settings (#10010), model-catalog-policy-invalidation (#8906), model-alias-seed (#9485), reactive-context-compaction (#8949), combo-provider-wildcard (broken upsert helper), oauth-google-loopback (43-locale resurrected-key removal) Validation: 501/501 across the 47 touched test files; typecheck:core, lint, file-size, docs-sync all green. Refs #9985 * fix(ci): base-reds round 3g — wave K/L: 4 more real defects + stale alignments Real defects: - base/reasoningEffort.ts: the stale duplicate cherry-pick #9612 re-added the codex minimal→low rewrite that #9883 had deliberately removed (OMP minimal passthrough); block removed again - cursorImages.ts: #9840 wired prepareCursorImageForWire (sharp re-encode, fail-closed) into the SHARED resolveCursorImages, breaking zai-web and conol-web image uploads (HTTP 400 'undecodable'); new prepareForWire opt-out, Cursor default path unchanged (8 cursor suites green) - modelCapabilities/snapshot: catalog prepare still issued 323 per-model reads of model_context_overrides + max_input_tokens overrides, violating #9199's bulk-load contract; both now resolve from the snapshot single pass - v1-models-discovery-conformance: re-pinned to the bounded 30s SWR window (#9199/#10198) — the old 'stale-first regardless of age' contract is gone Stale tests aligned (movers cited inline): codex-tools-strict-default (#9828 redundant-oneOf strip), devin-providers (#9245 i18n), db-migrationrunner- constants-split (147→151 renumber #8228), gitlab-duo-oauth-setup (#9245), chatcore-extracted-modules (#9161 outbound-protocol keying) compression-api CI failures were cascade artifacts of codex-tools-strict-default failing in the same force-exit shard process — no own defect (171/171 local). Refs #9985 * fix(test): compression-api — register both describes before the runner starts The DATA_DIR setup + route/db top-level awaits sat BETWEEN the two describes; under --test-force-exit (the CI unit-runner flag) the process exits once the already-registered tests finish, so on slow CI machines the whole second describe died as 'Promise resolution is still pending' — the recurring CI-only shard-2 failure that never reproduced locally without the flag. Moved to the top of the file; 10/10 under --test-force-exit locally. Refs #9985 * fix(quality): freeze modelCapabilities.ts at 1006 (annotated) — snapshot routing growth Refs #9985 * fix(quality): move the modelCapabilities freeze into the frozen map (nested schema) Refs #9985 * fix(i18n): translate all 39,718 pending UI keys across 42 locales (owner-approved) Mass-translated every __MISSING__ placeholder via the official i18n:sync-ui --translate-markers pipeline (operator backend), restoring i18nUiCoverage to the 100 baseline (was 89.9 after the merge-storm UI landings + the 42 keys #9038 never shipped). Post-pass repairs, all caught by the existing gates: - glossary: retired renderings the machine reintroduced normalized again (提供商→提供者 zh-CN/zh-TW, 鏈接→連結, 文檔→文件, 調用→呼叫, 供應商→提供者, 響應→回應, 不活躍→未啟用 zh-TW; 클로드→Claude, 옴니루트→OmniRoute ko); DATA_DIR forbidden rendering avoided via 数据文件夹 rephrase - ICU integrity: 120 values with renamed/dropped {params} repaired (39 positional renames, 81 reset to the en source — functional over fluent) Validation: glossary/pt-BR/vi/deno-relay/settings-keys/value-drift/google- loopback suites 76/76; placeholder diff en×42 locales = 0; worst-locale coverage = 100.0%. Refs #9985 --------- Co-authored-by: backryun <bakryun0718@proton.me> |
||
|
|
f1eb0b8357 |
refactor(providers): remove the Puter provider at its owner's request (#10210)
Remove the Puter provider (id `puter`, alias `pu`) entirely, at the request of Puter's owner, Nariman Jelveh: - registry entry (open-sse/config/providers/registry/puter/) and PuterExecutor (open-sse/executors/puter.ts), with their registrations - API-key preset card (gateways.ts), provider icon and public SVG asset - 33 free-model catalog entries (pool `puter`) - authHint i18n key across all 43 UI locales - credential-requirement frozen-list entry and related comments - docs: ARCHITECTURE, CODEBASE_DOCUMENTATION, FREE_TIERS (removal note), PROVIDER_REFERENCE regenerated (337 providers), translated doc mirrors, llm.txt + its 42 i18n mirrors, README/AGENTS/package.json counts (338→337 providers, 144→145 migrations) and the 5 canonical SVGs - migration 152 cleans up stored puter connections/keys/custom models; historical usage records are preserved (same principle as migration 151) - regression guard: tests/unit/puter-provider-removed.test.ts; puter fixtures in shared tests swapped for neutral providers; translate-path golden snapshot regenerated Historical CHANGELOG mentions are intentionally preserved; the removal carries its own CHANGELOG entry. Co-authored-by: backryun <bakryun0718@proton.me> |
||
|
|
f6ccd3cf9f |
fix(quality): green release/v3.8.50 base-reds round 2 (#9985) (#10131)
* fix(quality): green release/v3.8.50 base-reds round 2 — gateways/conol/deepai corruption, migrations, docs, ratchets, dashboard-typecheck Base-red fix for issue #9985 after the 2026-08-11 merge storm (99 PRs). Real defects fixed: - gateways.ts: close regolo entry (was swallowing naga-ac + chatanywhere from #9421), drop stale duplicate chatanywhere entry (#9594) - conol-web + deepai registry: correct ../shared import depth + deepai executor:default - modelSelectModalHelpers: close isProviderModelHidden (#9011) - driverFactory.test.ts: restore eaten test-closing brace (#9173) - usageTracking: remove duplicate cache_* props - modelCapability{Overrides,ResolutionSnapshot,Capabilities}: max_token -> max_output_tokens (#9199 vs #8908) + test align - videoGeneration: drop duplicate handleFalVideoGeneration import (mediaGeneration/fal canonical, #9982) - responseSanitizer: cast input_tokens_details before .cached_tokens access - EditConnectionModal: missing alibaba code fields, hoist validationPsd, providerPageHelpers Badge variant union - FreeBudgetCard: t() -> labels.noApiKey - peerRouting + cliRuntime: ProcessEnv typing - image-combo.test.ts: type any -> unknown - fal.test.ts: moved to tests/unit/services (collected path) 14 tests green - remove duplicate 143_job_registry.sql (146 canonical), KNOWN_GAPS fix Docs/ratchets (owner-authorized rebaselines, annotated): - CHANGELOG 3.8.50 living section restored + 42 i18n mirrors - MCP-SERVER.md 104->105 tools + i18n - ENVIRONMENT.md/.env.example: ADOBE_FIREFLY_CHROME_HEADED + DEBUG_CLAUDE_NONSTREAM - fabricated-docs allowlist: TELEGRAM proposal env vars - file-size: 5 grown files + proxyFetch 1207->1220 - dead-code 230->248, codeql 2->9 (drift from merged PRs, not this PR) - untrack _tasks symlink; agent-skills-sync --apply (config-codex-cli) * fix(changelog): reformat two feature fragments to the bullet convention (#9239, #9490) * fix(quality): prune stale ESLint suppressions (base-red) * fix(quality): resolve open-sse type errors + catalog/build regressions (base-red round 3) Storm-merge splices repaired in the base-fix PR #10131: - doctor.ts: AppConfig missing brokerSocketPath - conol-web.ts: Buffer not assignable to BodyInit (Uint8Array) - tinycms.ts: TinyCmsExecutor.execute return matches BaseExecutor (response/url/transformedBody) - tinycmsSigner.ts: encodeInto never-narrowing guard + dead wasm URL fallback (Turbopack) - virtualFactory.ts: options slot for resolutionSnapshot - bottleneckPatch.ts: insufficient-overlap casts (as unknown as) - imageCombo.ts: narrow handleImageGeneration union result - browser-worker.ts: AppConfig + turn.capabilities splice - conolDiscovery.ts: getProviderOutboundGuard from Policy module - catalog.ts: drop removed SWR hooks (getCatalogStaleWhileRevalidateMs + accessors), CatalogCachePolicy -> inline settings, resolve 4-arg call - catalogCache.ts: remove dead inFlight/promise refs - chat.ts: add isProviderBreakerFailureStatus import - model-catalog-cache-swr-8728.test.ts: align to #9199 new API (policy injection removed) * fix(quality): align UI test fixtures to current component contracts (base-red vitest) - setup-wizard: provide required serverState prop (component gained it in a merged PR) - grok-device-oauth-modal: next-intl stub resolves grok flow keys to EN labels - provider-quota-widget: label now inline (PR #8916 removed AutoRefreshButtonLabel extraction) — test the widget - use-provider-connections-cursor-refresh + phase1f: match /api/providers?provider=<id> query form; hoist heavy dynamic imports to module scope (timeout flake) - home-topology: mock next/navigation useRouter (component added node-click navigation) - cooling/lobe/AutoComboCatalog: raise cold-import describe timeouts to 30-60s - request-logger-*: align to current detail-view contract * fix(search): guard params.token undefined in serper headers (typecheck base-red) * fix(search): guard token headers + non-null providerConfig (typecheck base-red) * fix(changelog): restore base CHANGELOGs eaten by merge auto-resolve (43 files) --------- Co-authored-by: diegosouzapw <diegosouzapw@users.noreply.github.com> Co-authored-by: backryun <bakryun0718@proton.me> |
||
|
|
37d70785c7 |
fix(build): repair broken production build, red lint gate and SWR crash (#10198)
The Build CI job is advisory, so eight module-level defects from eight different PRs accumulated on release/v3.8.50 until `npm run build` failed with 7 Turbopack errors and `npm run lint` with 14. Build (link-time): - modelSelectModalHelpers.ts: a lost `}` swallowed PROVIDER_TEST_CHUNK_SIZE into isProviderModelHidden's body (#9011). - videoGeneration.ts: handleFalVideoGeneration imported twice; the standalone falHandler.ts is superseded by the provider-neutral mediaGeneration/fal.ts and is removed here (#9982 over #9969). - catalog.ts: re-exported and called the injectable SWR policy that #9199 deliberately replaced with a fixed 30s bound. Fixed on the consumer side — restoring the accessor would resurrect the unbounded window #9199 removed after measuring a 41s catalog build in production. - tinycmsSigner.ts: generated wasm-bindgen glue kept a sidecar `new URL('wasm_signer_bg.wasm', import.meta.url)` that no file backs; Turbopack resolves it statically. The module ships inlined as WASM_BASE64 and the only caller always passes it explicitly (#8736/#10087). - conolDiscovery.ts: imported getProviderOutboundGuard from outboundUrlGuard, which does not export it. Fixed on the consumer side: outboundUrlGuard.ts is loaded by the packaged CLI without a tsconfig, so it must stay free of `@/`-aliased imports (#7682). Runtime (the build never caught this one): - catalogCache.ts::scheduleBackgroundRefresh had two dangling statements referencing undeclared `inFlight`/`promise`, so EVERY stale-while-revalidate read threw a ReferenceError. Surfaced by realigning the #8728 suite, which #9199 left asserting a removed contract. Lint: - driverFactory.test.ts: a case inserted between the preceding test's `finally` and its `});` left the file unparseable, so the SQLite driver-cascade suite (26 tests) had not run since 2026-08-11 (#9173). - providerModelsConfig.ts: imported an executor directly, crossing the G14 boundary; routed through a new open-sse/services/zaiWebCredentials.ts (#8451). - image-combo.test.ts: 11 `any` violations, now typed (#9499). Validation: npm run build exit 0, npm run lint clean, typecheck:core clean, 41/41 tests green across the affected suites. Refs #9011 #9982 #9199 #8728 #8736 #10087 #8974 #9173 #8451 #9499 Co-authored-by: backryun <bakryun0718@proton.me> |