mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-14 03:02:14 +03:00
84e83e2f19360b279ec65e6880792fc87c6d4450
975 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
84e83e2f19 |
feat(providers): add DeepSeek V4 thinking effort aliases (#9485)
* feat(providers): add DeepSeek V4 thinking effort aliases * docs(changelog): add DeepSeek effort alias entry * fix(catalog): scope effort-tier fallback to declared models and harden resolver Addresses reviewer findings on #9485: - CRITICAL #1: catalog no longer synthesizes unresolvable effort aliases for static reasoning models without declared tiers (cheaperinference, cline, etc.) - CRITICAL #2: tiered static models survive synced-coverage suppression so normal installs with synced DeepSeek base models still expose aliases - WARNING #3: registry suffix resolution short-circuits when the raw id matches a direct custom or synced model, preserving custom apiFormat/targetFormat - WARNING #4: empty synced effort array no longer erases the registry fallback - WARNING #5: isFlash check is robust to suffixed/prefixed model ids - Added regression tests for blast radius, custom-model shadowing, none-path, and suffixed isFlash * fix(combos): expose static registry effort tiers in Combo Builder (#9485) Static provider registry models (e.g. DeepSeek V4 Flash/Pro) declare supportedThinkingEfforts, but buildModelOptions() only ran appendSyncedEffortVariants() over DB-synced rows. Synced metadata for a DeepSeek connection can omit supportedThinkingEfforts, so the catalog/ Playground surfaced the declared aliases while the Combo Builder picker showed only the bare base ids. Feed builtInModels with declared effort tiers through the same appendSyncedEffortVariants() utility used for synced rows, inheriting the base entry's contextLength/outputTokenLimit/supportedEndpoints/ supportsThinking and preserving its source. DeepSeek is not skipped by shouldExposeSyncedEffortVariants(), so Flash (none/low/high/max) and Pro (none/high/max) aliases now appear in the Combo Builder for any connection whose synced rows omit effort metadata. Regression test seeds a DeepSeek connection with effort-less synced rows and asserts the exact alias sets, source preservation, and metadata inheritance. |
||
|
|
a9d6fd3d9a |
feat(api): add per-key prompt compression bypass (#10001)
* feat(api): add per-key compression bypass * docs(changelog): note per-key compression bypass * chore(db): renumber API key compression migration * fix(compression): preserve hard kill during adaptive planning * chore(db): refresh migration gap allowlist |
||
|
|
aa4e72097a |
fix(bun): make server child and outbound fetch Bun-safe (#9761)
* chore(changelog): v3.8.49 reconciliation — 200 missing bullets + 22 restored credits Phase 0a of /generate-release. Measured commit<->CHANGELOG coverage over the real cycle range (2c62333b0..HEAD, 933 non-merge commits) instead of the last tag: 180 merged PRs had no bullet at all (they landed without a changelog.d fragment) and a further 19 were invisible because the merge-train landed them under a generic 'Train 1D: merge via --admin' subject that carries no PR reference. - +200 bullets, all with PR back-reference and author attribution (1179 -> 1379) - 🙌 Contributors 156 -> 178; credits @terrafirmbot-source for #7904, which shipped through the conflict-resolved #8685 without any attribution - closed-PR credit audit over the 32 human PRs closed unmerged this cycle: 12 had already landed under the author's own follow-up PR and were verified credited - rollup bullet for the direct release-branch maintenance (merge-train landings, ratchet re-pins, base-red sweeps) that carries no PR of its own - [3.8.49] header dated 2026-07-28 (was TBD) in the root file and the 42 i18n mirrors Coverage after: 0 commits uncovered. * chore(quality): v3.8.49 pre-flight — clear 4 base-reds, absorb cycle drift Pre-flight sweep (Phase 0). Test suites ran on the dedicated 32-core box so the self-inflicted load of `node --test` could not fabricate timing flakes. Base-reds fixed (all real, all from merged cycle PRs that did not update their characterization tests): - providers-constants-split / quota-plan-registry / provider-translate-path GOLDEN: #8861 added the Xiaomi MiMo Token Plan provider, so APIKEY_PROVIDERS is 195 (was 194), knownProviders() is 12 (was 11) and the translate-path snapshot gains one purely additive entry. Counts aligned to the shipped catalog, never relaxed. - agent-skills-content: skills/config-codex-cli/ was added by #8709 with a custom block, so the custom-block set is 13, not 12. - chatcore-compression-integration: #8595/#8560 deliberately decoupled REACTIVE context compaction from the `enabled` master switch, so a body above 70% of the window is pruned even with compression off. The test was sized above that threshold, which made it assert against intended behavior; it now stays below it and keeps testing the invariant it was written for (resolveBasePlan short-circuits to "off" before reading comboOverrides). Static gates: - 3 shellcheck directives were malformed (`# shellcheck disable=SC2086 — text`; the em-dash makes shellcheck reject the whole directive as SC1125) in ci.yml and nightly-release-green.yml — the comment now sits on its own line. - gitleaks: 2 new generic-api-key false positives allowlisted with justification — a localStorage key for the sponsor banner (#8723) and the PUBLIC Adobe Firefly web x-api-key, whose only literals are in JSDoc (the runtime reads it through resolvePublicCred, per Hard Rule #11). secretFindings back to 0. - zizmor 176 -> 189 and bundleSize 6762 -> 7666 rebaselined with the measurement and the reason; both are ordinary cycle drift absorbed at release. Environment-dependent failures classified out, not silenced: the two tproxy tests assert the native addon is unavailable/unprivileged and therefore fail when the suite runs as root on the build box (they pass as a normal user), and the consoleInterceptor rate-limit test is a 4s-timing flake under load (6/6 isolated). * test(codex): align the Responses HTTP e2e to the #8507 input-item contract Fifth and last base-red of the v3.8.49 pre-flight. #8507 (#8083) deliberately sets `status: "completed"` on Responses input items so strict upstream validators accept them; codex-chat-reasoning-http-e2e still asserted the pre-#8507 shape, so it failed against intended behavior. Expectation updated with the reason inline — the assertion is not relaxed, it now pins the current contract. The test was never reached in the first pre-flight sweep (the run was interrupted during the integration phase, and this file sorts after the one that failed). * docs(release): v3.8.49 feature-documentation sync Phase 1 step 6b. Swept the cycle's 284 New Features bullets against the existing docs before writing anything: nearly every large theme (Kimi, xAI OAuth, session affinity, bun:sqlite, Firecrawl, Opus 5, omniglyph, GCF v3.2, homologation suite) was already covered. Six real gaps were left undocumented by the PRs that shipped them, each verified in source before being written up: - CredentialMaskerGuardrail (#7683) is registered in guardrails/registry.ts but the GUARDRAILS table listed only 3 of the 4 guardrails - the cacheAffinity scoring factor and the cache-optimized combo strategy (#8008): the docs still said 12 factors / 18 strategies, the code has 13 / 19 - the optional dashboard OIDC login gate (#6973) — /api/auth/oidc/{login,callback} had no mention in AUTHZ_GUIDE - GET /api/usage/cache-health (#8827) and GET /api/usage/model-latency-stats (#6873) were missing from the API reference README "What's New" gains one bullet (routing transparency) and merges two others rather than growing a second changelog. PROVIDER_REFERENCE regenerated with the generator (Firecrawl reclassified to Search, Xiaomi MiMo added by #8861). check:docs-all green: 134 docs, 813 internal links, no fabricated API/env/CLI references. Known pre-existing drift left alone and reported: stale nominal counts in ARCHITECTURE/CODEBASE_DOCUMENTATION (soft), the 9-factor mentions scattered in AUTO-COMBO, and the auto-combo diagram SVG (the renderer needs a browser this environment does not have — the .mmd source is updated and the .md says so). * chore(release): v3.8.49 — clear the release-PR CI in one pass Every finding from the first full ci.yml run on the release PR, fixed or justified together so a single re-push clears the board. Lint / check:route-validation:t06 — three routes read request.json() with no visible Zod validation. The two proxy-subscriptions routes validated with a hand-rolled parsePayload(); they now use real Zod schemas (src/lib/proxySubscription/schema.ts) reproducing the same acceptance rules, error strings and status codes. chat/completions is the proxy's hottest path and parses the body ONCE on purpose (#4380 OOM crash-loop), so it now safeParses the ALREADY-PARSED object against a deliberately permissive structural schema — proven not to change behavior: absent model and model:null still pass through, role "developer" still reaches 200, a ~300 KB payload is accepted, and the body is still read exactly once. 25 new tests. i18n UI value drift — 13 English strings rewritten during the cycle left stale translations in up to 41 locales (317 pairs). Eleven are genuine rewrites and now carry the pipeline's __MISSING__:<english> marker so the runtime serves corrected English until translation catches up; vi forbids that marker by test, so it got a real translation. PR Test Policy — 33 files flagged. Each was verified against the SOURCE, not the diff: 26 assert reductions are legitimate (mostly the #7866 Qwen OAuth provider removal and the #8013 Antigravity refactor deleting the surface under test) and are allowlisted with the PR and the evidence; 5 deleted files have verified replacements. One was NOT legitimate: #7528's GraphQL->WebSocket migration dropped four muse-spark continuation scenarios whose logic is still live — connection isolation, cache eviction after a failed turn (the commit itself says "was missing"), parallel-chat cache collision, and the empty-content guard. All four are restored against the new transport and each was verified to fail when the corresponding production mechanism is broken. Quality Ratchet / openapiCoverage — 36.6% against a baseline of 38: the cycle added routes faster than the spec. Eight real endpoints are now documented from their route.ts (usage cache-health and model-latency-stats, the two OIDC endpoints, and the five proxy-subscriptions paths), bringing it to 38.1%. Quality Gates (Extended) / zizmor — the runner measures 190 where the devbox measures 189 on the same commit, a delta already recorded in this baseline's history. Baselined to the runner's number. Also: the driverFactory better-sqlite3 guard moved from a mid-body t.skip() to a declared { skip: <condition> } test option. Same behavior for the optional native dependency, but the skip now shows up in the report and is distinguishable from a test.skip() that silences a test outright. Verified under both runners: 15/15 on Node, 14/14 on Bun. SonarCloud Code Analysis stays red and is not a blocker: sonar.qualitygate.wait=false since #7038 makes the job informative, the built-in gate cannot be swapped on the FREE plan, and main has no branch protection. * chore(quality): close the last two release-PR reds test-masking — I had missed one of the 34 flagged files: my first pass grepped only paths under tests/, so open-sse/services/__tests__/tierResolver.test.ts was invisible. Same #7866 cause as the other eight qwen-driven reductions: the "classifies Qwen as free" case and qwen's entry in the batch list went with the removed provider, and the batch indices dropped from 10 to 9 (61→59). Allowlisted with that evidence. dast-smoke — all four Schemathesis findings are on the two OIDC endpoints documented in the previous commit, and none is a defect. /api/auth/oidc/* is a BROWSER redirect flow: it answers 302 to the IdP and 302 back to /login?oidc_error=... on every failure, which Schemathesis reads as "accepted a schema-violating request", and it answers 400 when OIDC is not configured, which it reads as "rejected a schema-compliant request". Keeping the endpoints in the spec is right — operators need them, and they are what brought openapi coverage back over the baseline — so the flow is excluded from the fuzz instead, with the reason inline in the workflow. The rest of /api/auth and /api/keys stays in scope. * test(db): reword the driverFactory skip comment so the gate stops counting it The anti-test-masking gate greps text, not code: my explanation of WHY the better-sqlite3 guard moved out of the test body spelled the runner API out literally, and those two mentions inside a comment were counted as two new skip markers — the exact signal the previous commit set out to clear. Same explanation, phrased without the call syntax. Verified with the gate's own exported helpers against the merge-base: 0 modified-file violations, 0 deletion violations. Test still 15/15. * fix(dashboard): unbreak the vitest:ui gate — 2 real production bugs + the i18n test seam The Vitest job is a BLOCKING gate that had not run to completion once in this whole release: rounds 1-3 cancelled it via cancel-in-progress on each successive fix push, so its red was indistinguishable from green. Round 4 finally ran it and the suite was broken cycle-wide. Root cause of the suite: #7935 instrumented ~180 shared/dashboard components with next-intl's useTranslations/useLocale without updating the tests that mount them, so every one of them threw "context from NextIntlClientProvider was not found". Fixed at the shared seam (tests/_setup/vitestUiPolyfills.ts) rather than per file: a translator built from the REAL en.json via next-intl's own createTranslator, memoized per namespace — the naive version returns a fresh function each call and any component whose useCallback/useEffect depends on t spins forever, which reads as a hang, not a failure. A local mock still wins over the default. 22 files fixed by the seam alone, 15 realigned to the real strings; no assert removed or weakened. Two production bugs the suite was hiding, both pre-existing and both with a failing regression test already in the tree: - RequestLoggerDetail crashed on a structured error object. #7920 gave the component formatErrorForDisplay for exactly this case, then #8213's combo-503 / cooldown checks went to the raw field and called .toLowerCase() on it. Both paths now use the helper. - The logs detail modal reopened on first close again. #6830 fixed that by reading the deep-link id ONCE; the #8354 page rewrite regressed it by reading the live searchParams every render, so the prop flips mid-session and re-fires the child's deep-link effect exactly as the modal closes. Frozen at mount again. Also tightens i18nUiCoverage 75.5 -> 99, which the ratchet demanded under --require-tighten: the metric genuinely improved as the async translation workflow paid off the debt that the v3.8.39/.44/.47 rebaselines had been recording. The collector subtracts placeholders, so this release's 317 __MISSING__ markers are already netted out of the 99. Two UI files still fail locally under 20-worker concurrency (combos-page-smoke, evals-tab-smoke) — cold-import flakes that pass isolated and with a larger timeout. * test(e2e): repair the four shards the first green Build finally exercised test-e2e has `needs: [build]`, and the release PR's Build died on every round until now — so the 9-shard matrix produced ZERO signal for this whole cycle while ~200 PRs merged. The first successful Build surfaced four independent breakages, each traced to the commit that caused it: - providers-management (#7361): the single-connection delete moved from window.confirm() to a ConfirmModal, so page.once("dialog") never fired and the DELETE was never sent (deleteCalls stayed 0). Click the modal instead. - providers-bailian-coding-plan (#7882): the free-text Base URL field was deliberately replaced by a region step whose choice resolves the endpoint (global-sg -> coding-intl.dashscope, china-beijing -> coding.dashscope). Both cases rewritten against the region step; the invalid-URL case is unreachable from this modal now, so it covers the CN choice instead. - group-b-activity-feed: the stack-trace guard ran against page.content(), which embeds the serialized i18n payload — zenmux's "endpoint at /api/v1/chat/completions" is prose, not a leak. Assert on rendered innerText and require the :line:col every real stack frame carries. - navigation (#8292): APP_ROUTE_PATTERN accepted only /login and /dashboard, but the new prefetch spec is the sole caller passing /home, so waitForURL never resolved and the retry loop burned the full 180s timeout. E2E is green on main (9/9 on 07-22 and 07-23), so all four are cycle regressions, not pre-existing debt. Tests only — no production code touched. * fix(dashboard): stop the /home quick-start cards from prefetching too #8292 fixed half the RSC prefetch storm: it added prefetch={false} to the sidebar's navigation and logo links, but /home — the landing route, and the one its own e2e guard visits — renders five more internal Links in the quick-start cards. First paint still fired 12 speculative RSC requests for /dashboard/{analytics,logs,providers,api-manager} and /docs. That PR shipped the test that would have caught this, but the test never got to its assertion: gotoDashboardRoute("/home") hung because APP_ROUTE_PATTERN accepted only /login and /dashboard, so the retry loop burned the whole 180s timeout with no assertion error. With that helper repaired in the previous commit, navigation.spec.ts finally ran and reported the 12 requests. Validated both ways, per Hard Rule #18: - tests/unit/sidebar-prefetch-policy-8281.test.ts extended to /home — red on the parent commit (5 internal Links, 5 without prefetch={false}), green here. - the e2e assertion expect(speculativeRequests).toEqual([]) is the end-to-end guard; it is what surfaced the defect in the first place. * refactor(dashboard): shrink HomePageClient back under the size gate The prefetch fix in the parent commit tripped check:file-size — the frozen budget for this file is 1377 lines and a naive fix measured 1391, because `href` + `prefetch={false}` + `className` no longer fits Prettier's 100-column budget, so three one-line <Link> elements each expanded to five. Followed the gate's own first suggestion (extract/DRY) before touching the baseline: the quick-start links repeated the same className literal four times, and the docs link carried a 180-char one inline. Hoisting both into INLINE_LINK / DOCS_LINK collapses five wrapped <Link> blocks back to a single line each and removes the duplication — 1391 -> 1381. The remaining +4 over the frozen budget is the five prefetch attributes themselves, which cannot be expressed in fewer lines. Rebaselined to 1381 with the rationale recorded in file-size-baseline.json under _rebaseline_2026_07_29_8281_home_quickstart_prefetch. tests/unit/sidebar-prefetch-policy-8281.test.ts still passes (2/2): it matches whole <Link ...> blocks, so it is indifferent to the wrapping and only checks that every internal link opts out of prefetch. * fix(bun): use native fetch for direct outbound requests * test(bun): cover native direct fetch path * fix(bun): preload polyfill for next build workers * fix(bun): expose AsyncLocalStorage globally * fix(bun): filter non-page Fumadocs metadata * fix(bun): defer docs-only route dependencies * chore(skills): sync generated OmniRoute agent skill docs --------- Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> Co-authored-by: diegosouzapw <diegosouzapw@users.noreply.github.com> |
||
|
|
f5ce51a9ff |
feat(providers): add Conol (conol.ai) web session provider (#8974)
* feat(providers): add Conol web support
* fix(conol): preserve sessions and image turns
* fix(conol): pin session model and effort via /model endpoint
Conol ignores agentModel/agentEffort on POST /api/sessions, so every
session silently ran on the downgraded account default (the create
response reports modelDowngraded: true / effectiveModel).
Sessions are now created empty and configured out-of-band against
POST /api/sessions/{id}/model before the first turn is submitted, in the
order the web client uses: modelPreset, then agentModel, then agentEffort.
The ordering is load-bearing because the model call resets agentEffort to
null server-side.
Effort now defaults to xhigh when the caller does not pin one via the
-<effort> model suffix, and is clamped onto the ladder each model actually
advertises, so xhigh degrades to high on claude-sonnet-5 and is skipped
entirely for models without an effort ladder such as openrouter/fusion.
Model and effort are also dropped from the session binding key so switching
models re-pins the existing session instead of stranding it and losing the
conversation history. Re-pinning only happens on an actual change, so
steady-state follow-ups cost no extra round trips.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
|
||
|
|
16c566d146 | fix(copilot-web): restore browser authentication (#9984) | ||
|
|
3e2b166869 |
feat(combo): add quota-only priority fallback (#9983)
Add a per-target priority option that advances only after trusted quota exhaustion while preserving retry, nested Combo, quality, and Global Fallback semantics. |
||
|
|
029a43359c |
fix(command-code): include tool call arguments (#9821)
* fix(command-code): normalize malformed tool call arguments and fix test assertion handling * fix(command-code): resolve toolName from assistant calls and update version header to 1.15.1 * refactor(command-code): consolidate pre-pass message tool metadata extraction and add unknown fallback test * fix(command-code): fallback unnamed tool calls to unknown to satisfy upstream name validation * fix(db): rename 139_job_registry -> 143 to avoid collision with 139_ccr_blocks release/v3.8.50 owns version 139 (ccr_blocks, #9061). The #9631 job registry cherry-pick ( |
||
|
|
668ed4813f | refactor(providers): remove retired GitHub Models (#9023) | ||
|
|
b4fc835d25 |
[v3.8.50] feat(providers): add support for TinyCMS Web (#8736)
* feat(providers): add support for TinyCMS Web including WASM-based cryptographic signing and Proof-of-Work emulation * feat(providers): add unit tests, ESLint suppressions, and fix hardcoded userid for TinyCMS Web - Add unit tests for WASM init, UUID validation, challenge flow (15 tests) - Add WASM source comment explaining binary origin - Replace hardcoded userid with dynamic provider-specific data - Add ESLint suppressions for no-explicit-any in WASM bridge code - Add explanatory comments for DOM shim (runtime WASM-bindgen, not test mocks) Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> * refactor(providers): extract TinyCMS DOM shims into an explicit setup function tinycmsSigner.ts installed its window/document/HTMLCanvasElement/ CanvasRenderingContext2D shims for the wasm-bindgen glue as a module-load side effect. That meant merely importing the module (even transitively, e.g. through the provider registry from an unrelated test) mutated global state for the rest of the test process. Extract the shim installation into setupDomMocks(), which returns a restore callback: - initTinyCmsWasm() calls it once before instantiating the WASM module (production path — unchanged behavior, still automatic). - tests/unit/provider-tinycms-web.test.ts now calls it explicitly in a `before` hook and restores the previous globals in `after`, so the shims never leak into other test files. As a side effect, replacing five separate `as any` casts with a single typed `global as Record<string, any>` handle drops the file's no-explicit-any count from 5 to 1; eslint-suppressions.json updated to match. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> * docs(providers): regenerate PROVIDER_REFERENCE.md for tinycms-web Mechanical `npm run gen:provider-reference` run after merging release/ v3.8.50 into this branch — the generated table was stale for both the new tinycms-web entry this PR adds and the release's own cheaperinference addition. Total providers 290 -> 292, Web Cookie Providers 31 -> 32. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> --------- Co-authored-by: diegosouzapw <diegosouzapw@users.noreply.github.com> Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> |
||
|
|
57744aeb14 |
feat(cursor): proactively renews Cursor sessions and fixes manual refresh (#9173)
* refactor(cursor): extracts token extraction into shared lib
Moves tryIdeAuth/tryAgentAuth and supporting helpers out of the
auto-import route into src/lib/cursor/tokenExtractor.ts, and adds
an agent-cli-state.json fallback candidate path to tryAgentAuth
(alongside the existing auth.json candidate) so the extraction
logic can be reused by the upcoming renewal orchestrator.
* feat(cursor): adds cursor-agent-backed token renewal orchestrator
Builds the renewal orchestrator in src/lib/cursor/renewal.ts: a
bounded, unattended-safe --list-models nudge, a side-effect-free
status availability check, an in-flight spawn lock keyed by
command, and renewCursorConnection() which nudges cursor-agent
then independently re-scrapes the IDE and cursor-agent credential
sources to detect whichever refreshed. Extends cursorAgent.ts's
binary resolution and spawn helper with fixed-paths-only mode and
a SIGKILL follow-up for background use. Adds a generic keyed-mutex
utility (src/shared/utils/keyedMutex.ts) for serializing a
connection's renew-then-persist cycle, and forwards a busy-timeout
through driverFactory's node:sqlite fallback path.
* feat(cursor): proactively renews Cursor sessions in the sweep
Adds src/lib/tokenHealthCheckCursor.ts, sweep-side glue that calls
the renewal orchestrator and persists the result, wired into
tokenHealthCheck.ts's checkConnection() via a new Cursor-specific
branch placed ahead of the generic no-refresh-token fallthrough.
Carves out a non-terminal exception for a Cursor connection that
already landed at testStatus "expired" via the request-time 401
path, excluding permanently-dead account_deactivated connections.
Extends buildRefreshFailureUpdate() with an overrides param so
Cursor's failure path can use a distinct, non-terminal errorCode
instead of the generic refresh_failed/expired taxonomy.
* feat(cursor): adds local-only manual refresh route
Adds POST /api/providers/[id]/refresh-cursor, a dedicated
loopback-only route that calls the renewal orchestrator on demand
for a single Cursor connection, bounded by a 30s per-connection
cooldown. Classifies the new route in LOCAL_ONLY_API_PATTERNS and
closes the manage-scope-bypass gap for dynamic-segment spawn-capable
routes under /api/providers/ via a new SPAWN_CAPABLE_PATTERNS /
SPAWN_CAPABLE_PATTERN_ANCESTORS mechanism, which also retroactively
covers the pre-existing /login route. The existing shared
/api/providers/[id]/refresh route is untouched and stays
remote-reachable for every other provider.
* feat(cursor): surfaces a dismissible cursor-agent nudge
Adds GET /api/providers/cursor/agent-availability, a credential-free
LOCAL_ONLY route returning only { cursorAgentAvailable: boolean },
backed by a 5-minute cached wrapper around the renewal orchestrator's
existing availability check. Surfaces a dismissible dashboard banner
on the Cursor provider page suggesting cursor-agent installation
when it isn't detected, following the existing dismissible-banner
convention. Also fixes a pre-existing bracket character in a
routeGuard.ts comment that was silently truncating
check-openapi-security-tiers.mjs's view of LOCAL_ONLY_API_PREFIXES.
* fix(cursor): wires manual refresh button to the new route
Branches handleRefreshToken to call the dedicated Cursor refresh
route instead of the generic /refresh route, which silently 502s
for Cursor connections today since they carry no refresh token.
Every other provider's refresh behavior is unaffected. Adds the
cursorSessionUnchanged i18n key and syncs it (plus a pre-existing,
unrelated 28-key backlog) across all 42 locale files.
* fix(cursor): addresses Phase 4/4.5 review findings
Restores the legacy stdout/stderr auth-pattern fallback in
checkCursorAgentAvailability() that the plan's Task 2 Step 4
required but the implementation had dropped. Threads an optional
deps parameter through checkCursorConnectionIfNeeded() so its
error branch is reachable in tests, and switches both it and the
manual-refresh route to exhaustive switch statements over the
renewal result. Adds a short-lived host-keyed dedup cache around
tryIdeAuth() so multiple due Cursor connections sharing a host
don't each open the same state.vscdb file in one sweep tick.
Adds opportunistic eviction to the manual-refresh cooldown map,
an outer try/catch to the availability route for defense-in-depth
consistency with the plan's other routes, and corrects a stale
JSDoc claim about the /login route's auth check. Documents the
now-empirically-confirmed agent-cli-state.json schema mismatch
found while validating against a real cursor-agent install.
* docs(cursor): adds changelog fragments for the renewal plan
Adds one fragment per user-facing outcome per changelog.d/README.md's
convention for a PR that both fixes and adds. PR number placeholder
to be filled in once the PR is opened.
* fix(i18n): translates the new Cursor keys into Vietnamese
The i18n:sync-ui run in an earlier commit left __MISSING__
sentinels for the 4 new Cursor keys in every locale, but
Vietnamese has a dedicated completeness test requiring zero
internal missing markers. Provides real translations for
cursorSessionUnchanged, cursorAgentNudgeTitle,
cursorAgentNudgeBody, and cursorAgentNudgeDismiss.
* fix(cursor): addresses quality-gate Layer 1.5 findings
Restores a comment that misrepresented execFile's actual argv shape
after an earlier bracket-removal fix, this time avoiding literal
closing-bracket characters entirely so the openapi checker's naive
array parser can't be broken by either version. Bounds the sweep-
and manual-route-triggered tryIdeAuth() busy-timeout to 250ms
(down from the interactive auto-import path's 2000ms), since both
share the main event loop with all other in-flight requests and
should fail fast on a WAL-lock collision rather than block the
whole instance for up to ~4s. Has the manual refresh route bypass
the sweep's IDE-auth dedup cache so a click always sees a fresh
read, consistent with this plan's existing "manual actions never
see stale cached data" convention. Documents the previously-missing
agent-availability route in ROUTE_GUARD_TIERS.md's spawn-capable
table.
* fix(cursor): adds SIGKILL follow-up to the status-check spawn
Matches the nudge spawn's existing SIGTERM+SIGKILL pattern so an
unresponsive cursor-agent status check can't leak a lingering
process if it ignores SIGTERM.
* docs(cursor): fills in the PR number for changelog fragments
Renames the 3 changelog.d fragments to their PR-numbered filenames and replaces the (#PR) placeholder with #9173, now that the PR exists.
* fix(cursor): corrects changelog fragments to reference PR #9173
The prior commit only staged the git mv rename — a git add invocation with a stale (pre-rename) pathspec aborted before the actual (#PR) -> (#9173) content edit was staged, so the rename landed without the fix it was meant to carry. This captures the actual content change.
* docs(cursor): regenerates the agent-skills catalog for the new route
check:agent-skills-sync (CI's Merge integrity gate) requires SKILL.md files to stay in sync with the live route catalog. Adding /api/providers/cursor/agent-availability in an earlier commit needed a regen this branch never ran.
* chore(quality): rebaselines file-size caps grown by agentrouter merges
Two already-merged agentrouter commits (
|
||
|
|
2e799b33a7 |
fix: skills & memory — tool-name encoding, schema normalization, warm-cache, combo id, Ponytail catalog (#9058)
* feat(skills): add Ponytail minimalism skill as external catalog entry
- Add 'external' SkillCategory + SkillArea
- Register ponytail (MIT, DietrichGebert/ponytail) in CURATED_SKILLS
- Generator: external skills carry content in custom block, no api/cli body
- Generate skills/ponytail/SKILL.md with original content preserved
- Update catalog test counts 45 -> 46
* fix(skills+memory): builtin handler fallback in executor, skip vector upsert for deleted memories
- skills: Next.js compiles SkillExecutor into multiple chunks (own singleton
each); route chunk lacked builtin handlers registered at startup via
instrumentation. execute() now falls back to builtinSkills registry, so
POST /api/skills/executions works for file_read/web_fetch/etc.
- memory: scheduleVectorUpsert is fire-and-forget and embeddings are slow;
health-check verify (create->delete test memory) left queued upserts
failing with 'memory not found' every 30s. Check existence before embedding
and skip quietly.
* fix(skills): encode tool names with @ and . for providers rejecting them
Skill tools were advertised as 'name@version' (e.g. test-fr2@1.0.0), but
DeepSeek/Groq/OpenAI reject function names not matching ^[a-zA-Z0-9_-]+$.
Names already valid are left untouched; invalid ones are reversibly encoded
as omr_skill_<base64url> and decoded in interception before registry lookup.
* fix(combos): include DB id column in combo records for dashboard links
getCombos() selected only data/sort_order/context_cache_protection, so
combos whose JSON blob lacked an id field returned id: undefined. The
dashboard then linked to /dashboard/combos/undefined and Combo Control
Center failed with 'Combo not found'. Merge the id column into parsed
rows (authoritative, only when the blob has no id).
* fix(skills): normalize flat skill schemas to object schema for Gemini/Claude
Stored skill schemas are flat property maps ({ text: { type: string } }),
which OpenAI-compatible providers tolerate but Gemini
(function_declarations[].parameters) rejects with 'Unknown name ... Cannot
find field'. Wrap bare maps into { type: 'object', properties: {...} } for
all three tool formats.
* fix(skills): warm registry cache before skill injection in chat path
injectSkills() lists the in-memory skillRegistry, which is empty after a
cold start until something calls loadFromDatabase(). The interception path
already warms the cache (#2815); the injection path did not, so skills
were silently skipped (no_enabled_skills) for the first requests after
restart. Warm the cache for the chat owner before injection.
---------
Co-authored-by: Egor <egorich-print@users.noreply.github.com>
|
||
|
|
8bdd29f835 |
fix(resilience): recover idle-capacity limiter wedges early (#9041)
* fix(resilience): recover idle-capacity limiter wedges early * docs(changelog): note limiter wedge recovery * fix(resilience): harden limiter wedge recovery * fix(resilience): close limiter recovery review gaps * test(resilience): preserve scoped exhaustion guards * docs(changelog): remove self-credit suffix * test: include limiter regressions in mutation coverage * chore(quality): reconcile v3.8.50 file-size baselines * fix(docs): add WAF MDX title frontmatter * fix(docs): complete WAF frontmatter metadata |
||
|
|
1f45cf885c |
fix(vision): preserve images for text-only routes (#9037)
* fix(vision): preserve images for text-only routes * fix(i18n): complete Vietnamese vision bridge copy * fix(ci): drain prerelease tag input --------- Co-authored-by: rinseaid <rinseaid@rinseaid.net> |
||
|
|
21fd0a94f8 |
feat(alibaba): free-tier routing with live quota sync (#8893)
* feat(alibaba): add free-tier routing with console quota and builtin allowlist Classify DashScope free vs paid models via console quota API, a hardcoded operator allowlist fallback, and per-connection drained tracking. Wire wildcard combo expansion, model refresh, combo exhaustion, and audit redaction for Alibaba console credentials. * fix(routing): reset forced connection pin and persist Alibaba free-tier drain Drop session affinity pins when a forced connection is excluded after 429, and record Alibaba free-tier exhaustion on upstream 403 so per-key drained lists stay accurate without blocking sibling keys. * fix(alibaba): prefer live quota sync over static free-tier allowlist Stop unioning the builtin text allowlist when a console quota snapshot exists, treat expired quotaValidityPeriod as not_capable, and add a dated JSON pack plus sync-alibaba-allowlist script for operator refresh without code edits. * docs(alibaba): document free-tier console path + allowlist env overrides Adds the 4 ALIBABA_FREE_TIER_*_FE_PATH / ALIBABA_FREE_TIER_ALLOWLIST_PATH env vars (referenced by alibabaFreeTierQuotaFetcher.ts and alibabaFreeTierAllowlist.ts) to .env.example and docs/reference/ENVIRONMENT.md so the env/docs contract check passes. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> * refactor(open-sse): split alibabaFreeTierQuotaFetcher.ts under file-size cap Extract pure parsing/classification/eligibility-filtering logic into alibabaFreeTierQuotaClassify.ts and shared types/primitives into alibabaFreeTierQuotaTypes.ts, leaving the HTTP/console-fetch flow in the original file. Public API is unchanged (re-exported), behavior is identical. Co-authored-by: AndrianBalanescu <AndrianBalanescu@users.noreply.github.com> * fix: resolve typecheck errors in alibaba-free-tier routing --------- Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> Co-authored-by: diegosouzapw <diegosouzapw@users.noreply.github.com> Co-authored-by: AndrianBalanescu <AndrianBalanescu@users.noreply.github.com> Co-authored-by: AndrianBalanescu <andrian@balanescu.dev> |
||
|
|
9a99a39b33 |
fix(antigravity): quota-aware account selection and projectId persistence (#8891)
* fix(antigravity): per-model quota + 30min credits_exhausted reprobe - accountFallback.ts: hasPerModelQuota() now treats antigravity/agy as per-model quota. A single-model 429 no longer cascades to all models in the provider. - connectionRecovery.ts: credits_exhausted removed from terminal set; isCreditsExhaustedReprobeCandidate() with 30min default. Loads active+inactive rows so inactive credits_exhausted accounts can recover. - tests/unit/quota-connection-recovery.test.ts: 6 cases covering pure helpers + tick wiring. * fix(antigravity): persist projectId and prefer healthy accounts Save Cloud Code projectId after runtime discovery, skip accounts missing projectId when alternatives exist, and mark missing_project_id on 422. * fix(antigravity): skip quota-exhausted models during account selection Avoid repeatedly dispatching to Antigravity models that already report exhausted quota, reducing wasted upstream calls and combo fallback latency. --------- Co-authored-by: hermes <hermes@nous.local> |
||
|
|
75c6a18a9b |
[v3.8.50] fix(api): serve stale model catalog during refresh (#8728)
* fix(api): make model catalog refresh response-safe * fix(api): invalidate model catalog mutation paths * fix(db): preserve aliases backup import after catalog rebase --------- Co-authored-by: Erick Kinnee <erick@ekinnee.dev> |
||
|
|
dce79714a8 |
[v3.8.50] feat(ui): add global model search to Combo builder (#8285)
* Feat: Busca Global de Modelos no Combo Builder * Fix: assembleStandalone src and dest equality check on Windows * fix(ui): i18n global model search + drop pnpm-lock + extract search panel - Drop pnpm-lock.yaml (repo is npm-workspaces; package-lock.json is canonical). - i18n: replace hardcoded Portuguese strings in the new global model search UI (Combo Builder) with getI18nOrFallback()/t() EN-fallback calls; add the 10 new keys (builderModeStep, builderModeGlobal, builderGlobal*) to en.json and propagate __MISSING__ placeholders to all 42 locales. - Extract the mode-toggle + global-search panel JSX into a new GlobalModelSearchPanel component, and the allGlobalModels/ filteredGlobalModels/add-step/add-all logic into pure, unit-tested helpers (buildGlobalModelList, filterGlobalModelList, addGlobalModelStep, addAllGlobalSearchMatches) in src/lib/combos/builderDraft.ts, keeping combos/page.tsx under its frozen file-size budget. - Revert the unrelated local-tooling .source/dynamic.ts one-liner to match origin/release/v3.8.49. - Add unit tests for the new builderDraft helpers. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> --------- Co-authored-by: Gleisson de Jesus Santos <T034183@embasanet.ba.gov.br> Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> Co-authored-by: ikelvingo <im.kelvinwong@gmail.com> Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com> Co-authored-by: diegosouzapw <diegosouzapw@users.noreply.github.com> |
||
|
|
5e02cf7623 |
fix(radar): refresh entitlement-sensitive state (#9776)
* fix(radar): refresh entitlement-sensitive state * chore(changelog): assign Radar fix to PR 9776 * test(radar): localize canonical feed fixture * test(radar): refresh canonical feed hash --------- Co-authored-by: diegosouzapw <diegosouzapw@users.noreply.github.com> |
||
|
|
4d0182daa0 |
feat: bridge audio inputs for text-only models (#9807)
* feat(modality-bridge): resolve audio input capability * feat(modality-bridge): resolve audio runtime settings * feat(modality-bridge): add audio transcription helpers * feat(modality-bridge): add Audio Bridge guardrail * feat(dashboard): make Modality Bridge audio tab functional * docs(guardrails): document Audio Bridge runtime * fix(modality-bridge): harden audio catalog and response header * chore(changelog): record audio modality bridge --------- Co-authored-by: diegosouzapw <diegosouzapw@users.noreply.github.com> |
||
|
|
1bf38e85d5 |
feat(onboarding): add one-click free provider setup (#10014)
* feat(onboarding): add one-click free provider setup (#9752) * fix(i18n): preserve existing provider URL validation labels * fix(i18n): restore provider URL validation labels --------- Co-authored-by: diegosouzapw <diegosouzapw@users.noreply.github.com> |
||
|
|
d3037d1fdc |
feat(resilience): abort persistently slow upstream streams (#9709) (#10012)
Co-authored-by: diegosouzapw <diegosouzapw@users.noreply.github.com> |
||
|
|
227795cd08 |
feat(memory): support custom remote embedding endpoints (#9622) (#10010)
Co-authored-by: diegosouzapw <diegosouzapw@users.noreply.github.com> |
||
|
|
344e4398c8 |
fix(quality): green release/v3.8.50 base-reds — env-doc sync + file-size freeze (#9985) (#10032)
* fix(quality): green release/v3.8.50 base-reds — env-doc sync + file-size freeze (#9985) Sweep base-reds from issue #9985 on release/v3.8.50: - env-doc-sync: add COMMANDCODE_API_URL + ANTIGRAVITY_ALLOW_SIGNATURE_BYPASS to .env.example and ENVIRONMENT.md (in code, missing from docs); add OMNIROUTE_STRICT_SYSTEM_PROVIDERS + TLS_FINGERPRINT_PROVIDERS to ENVIRONMENT.md (in .env.example, missing from doc). Restores the 3-way env contract. - file-size: freeze open-sse/utils/proxyFetch.ts at 1207 (new proxied-TLS fetch helper over the 1000 cap). Owner-authorized quick rebaseline; slim for v3.9.0. Co-authored-by: OmniRoute maintenance <maintainers@omniroute.local> * fix(quality): green open-sse+dashboard typecheck base-reds (#9985) Release-equivalent fast-gates surface 5 real TS regressions inherited by the base from merged Fal/guardrails/cursor work (fast-gates PR->release do not run these, so they accrued on release/v3.8.50): - open-sse/handlers/imageGeneration/providers/fal.ts: normalizeProviderImagePayload missing 4th 'b64_json' arg (TS2554). - open-sse/handlers/videoGeneration/falHandler.ts: narrow video to Record before .url. - src/app/api/v1/images/generations/route.ts: type the toJsonErrorPayload read. - src/lib/guardrails/visionBridgeHelpers.ts: cast through unknown for UA fetch. - src/lib/providers/mergeProviderModelListing.ts: drop index-signature requirement that made interface RegistryModel[] unassignable (TS2322, from #9911). All fixed in source (keeps the gates meaningful); each reproduces on the base tip. Co-authored-by: OmniRoute maintenance <maintainers@omniroute.local> * fix(quality): allowlist onnxruntime-node in dependency allowlist (#9985) check:deps base-red — onnxruntime-node is a real production dep (transformers embedding path) landed via the LLMLingua/transformers bump (#9962) without an allowlist entry. Legit package: microsoft onnxruntime, verified in registry. * fix(quality): rebaseline CodeQL ratchet 1->2 for #9940 fingerprint alerts (#9985) Base-red: 2nd js/insufficient-password-hash alert on chatBodyAdmission API-key fingerprints (sha256->16-hex admission-lane key), not password verification. Reproduces on release/v3.8.50 tip. Owner-authorized rebaseline (revisit v3.9.0). * fix(quality): green release/v3.8.50 unit base-reds (#9985) 8 unit-test base-reds reproducing on the pristine release tip, fixed in-source (fast-gates PR->release do not run the unit suite, so these accrued silently): - ServiceSupervisor: spawn-failure now resolves with error status (was throwing); health-probe-failure path still rejects. Distinct via spawnFailed flag. - stream + responseSanitizer: numeric passthrough id preserved as string (was regenerated chatcmpl-); finish chunk with empty delta no longer swallowed by the emptyChoices guard. - proxyFetch: genuine (non-abort) proxy transport failures keep the underlying reason in the surfaced error. - auto-combo builtinCatalog: advertised undefined-variant auto/* ids (auto/chat, auto/best-chat, auto/pro-chat) materialize instead of throwing 'Unknown'. - getTranslations en.json: add missing providers.iconUrlInvalid. - optional-transformers-dependency.test: reconcile to #9962's deliberate move of @huggingface/transformers to a regular dep (napi onnxruntime). Co-authored-by: OmniRoute maintenance <maintainers@omniroute.local> --------- Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouzapw@gmail.com> Co-authored-by: OmniRoute maintenance <maintainers@omniroute.local> Co-authored-by: diegosouzapw <diegosouzapw@users.noreply.github.com> |
||
|
|
8fc4023f94 |
fix(migrations): allow fresh install past mass-migration guard (#9934) (#10022)
Co-authored-by: diegosouzapw <diegosouzapw@users.noreply.github.com> |
||
|
|
fed0858f89 |
fix(encryption): identify failing credential in decrypt errors (#9927) (#10019)
Co-authored-by: diegosouzapw <diegosouzapw@users.noreply.github.com> |
||
|
|
0cb7410ca6 |
fix(services): stop embedded-service supervisor retry loop when binary cannot spawn (#9937)
* 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(services): stop embedded-service supervisor retry loop when binary cannot spawn A non-spawnable supervised binary (ENOENT/EACCES, or an ELF on Windows where spawn() throws EFTYPE synchronously) left the supervisor in 'starting' forever while the HealthChecker polled the dead port every healthIntervalMs. Each failed probe fired a full ProxyFetch dispatcher+native fetch pair, burning CPU and eventually collapsing the server (observed: 24 warns/min against 127.0.0.1:8317 for 2 days). - handle synchronous spawn() throws and the child 'error' event: stop the poller and transition to an explicit error state - transition to error and stop polling when FAILURE_THRESHOLD consecutive health probes fail, including during startup - waitForHealthy re-checks the state after its deadline so a mid-startup error surfaces as a rejection instead of being overwritten by 'running' --------- 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> |
||
|
|
0dbc34ea56 |
feat(cursor): exclusive live listing + verbatim AgentRun model ids (#9911)
* feat(cursor): prefer live synced catalog for listing and Test All When an active synced Cursor catalog exists, list only live models plus injected auto routers (and customs). Keep the static registry as offline fallback. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(cursor): send live-catalog model ids verbatim on AgentRun Skip #7289 effort/reasoning splits when the exact id is in the active synced Cursor catalog so AgentRun does not rewrite flattened live ids into missing bases that return AI Model Not Found. Also wires auto-cost/balance/intelligence to default + optimization for the injected routers. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: yansigit <yansigit@users.noreply.github.com> |
||
|
|
32935b04fa |
fix(antigravity): ban-safety hardening — bounded onboarding retries, gate thought-signature bypass sentinel (#9939)
* 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(antigravity): ban-safety hardening — bounded onboarding retries with jitter, gate the thought-signature bypass sentinel - onboardAntigravityUser: cap retries 10->3 and jitter the delay (3-7s) so a stuck loop cannot read as scripted automation to the upstream - openai-to-gemini: the skip_thought_signature_validator sentinel is an audit-trail risk; gate it behind ANTIGRAVITY_ALLOW_SIGNATURE_BYPASS (default enabled for compatibility, set 0 to disable). Real signatures always win. * test(antigravity): cover the signature-bypass sentinel gate (default on, env-disabled) Adds tests/unit/translator-antigravity-signature-bypass.test.ts (2 tests, verified locally with node --import tsx/esm) + CHANGELOG entry for the ban-safety hardening. --------- 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: benzntech <benzntech@users.noreply.github.com> Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> |
||
|
|
9105220242 |
fix: resolve hollow external package directory crashes and implement duckduckgo search fallback (#9913)
Co-authored-by: SupremeNexas <SupremeNexas@users.noreply.github.com> Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> |
||
|
|
d7d98fe356 |
fix(guardrails): vision bridge reroute/pool/self-loop fixes (#9946)
- auto/best-vision and auto/pro-vision now resolve to the vision CATEGORY (candidate filter by capability) instead of the flat smart variant, so the vision-bridge describe/reroute target can actually see images (resolveBuiltinAutoSpec in builtinCatalog). - vision candidate pool excludes registry entries whose catalog OVERSTATES vision support (opencode-go/opencode-zen/tokenrouter are forced through the vision bridge by isVisionBridgeForcedModel) in both the auto-combo candidate filter (suffixComposition) and the vision router (visionBridgeRouter). - reroute guard: an auto/* target is a virtual combo; a missing 'auto' provider row (hasUsableCredentials=false) must never block the reroute. - claude-wire backends (minimax, zai, ...) reject remote image URLs (MiniMax 403 2013): ensureBase64ImagesForClaudeWire resolves URLs to base64 before rerouting, and the describe self-loop normalizes to base64 for those targets (isClaudeWireFormatModel). - self-loop describe uses a real DB-backed key (resolveSelfLoopApiKey) instead of the sk_omniroute sentinel rejected by REQUIRE_API_KEY instances, and bypasses the runtime's hooked global fetch via undici (ProxyFetch with a dead local proxy would otherwise break every describe); compression is disabled on the self-loop sub-request so image payloads are never mangled. Tests: vision-bridge-auto-reroute (2), vision-bridge-selfloop-key (4), vision-bridge-claude-wire (6), builtin-vision-spec (4), vision-filter-excludes-forced (4). Co-authored-by: herjarsa <herjarsa@users.noreply.github.com> |
||
|
|
e4a7da7526 |
feat(usage): add Command Code quota tracking (#9921)
Wire Bearer /alpha billing credits and 5h/weekly windows into Provider Limits and genericQuotaFetcher so dashboard and preflight see live CC quotas. Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
ee0073dc4a | fix(db): invalidate stale LKGP pins on connection delete (#9936) | ||
|
|
9bcb48a2d2 | fix(db): avoid skipping pending job registry migration 146 (#9965) | ||
|
|
de2679f6d1 | fix(types): stabilize skill token extraction (#9920) | ||
|
|
f390327c88 |
fix(cleanup): prune mcp_tool_audit/a2a_task_events by created_at column (#9963)
Both tables (002_mcp_a2a_tables.sql) store their row timestamp in created_at; the cleanup queries used WHERE timestamp < ? which does not exist, so every boot-time cleanup logged: Error cleaning mcp_tool_audit: SqliteError: no such column: timestamp Error cleaning a2a_task_events: SqliteError: no such column: timestamp and retention pruning for these two tables never ran. Fix the DELETE columns and align the log labels/doc comments with the real table names. Adds source-level invariant tests (cleanup-column-fix.test.mjs) asserting the created_at column for both tables. |
||
|
|
4938a435ea |
fix(memory): env-configurable strict system-message-first providers (#9924)
* fix(memory): allow OMNIROUTE_STRICT_SYSTEM_PROVIDERS to extend the system-first provider list PROVIDERS_SYSTEM_MUST_BE_FIRST (added in #6225 for #6135) gates both the memory-injection placement fix and the #7293 hoistLeadingSystemMessage translator fix, but was hardcoded to xiaomi-mimo/mimo only. Self-hosted deployments routing other strict backends (e.g. a custom OpenAI-compatible connection in front of a self-hosted Qwen3.5+/3.6 model, whose chat template rejects any non-leading system message the same way) had no way to opt in without forking and rebuilding the image. Adds OMNIROUTE_STRICT_SYSTEM_PROVIDERS (comma-separated, case-insensitive provider ids) to extend the built-in set at read time, mirroring the injectable-env pattern already used in src/lib/memory/typedDecay.ts. No behavior change for anyone who doesn't set it. * chore: fix changelog fragment PR number |
||
|
|
792626c3c4 | Merge remote-tracking branch 'origin/release/v3.8.50' into codex/wave2b-9749 | ||
|
|
9f4186a2b2 |
Merge remote-tracking branch 'origin/release/v3.8.50' into merge-prs-9749-canonical
# Conflicts: # package.json |
||
|
|
0d36801f20 |
Merge remote-tracking branch 'origin/release/v3.8.50' into fix/release-v3.8.50-post-sweep-base-red
# Conflicts: # src/i18n/messages/vi.json |
||
|
|
754ba0fa86 | fix(release): repair post-sweep base regressions | ||
|
|
2afaab52a1 |
maint: final follow-up cherry-pick #9619 (#9901)
* fix(quality): clears two release/v3.8.50 base-red gates Unblocks Merge integrity and Docs Gates for every PR against release/v3.8.50, not just this branch: - changelog.d/features/9415-newapi-sub2api-aggregator-balance.md had a non-standard YAML frontmatter header that no other fragment in the tree uses. check-changelog-integrity.mjs reads a fragment's first non-blank line to validate it starts with a markdown bullet; the frontmatter's leading `---` made that check fail regardless of the actual bullet content further down. Removed the frontmatter and reformatted the body to match the documented changelog.d/README.md bullet convention. - docs/ops/VM_DEPLOYMENT_GUIDE.md documented OMNIROUTE_MAX_POOL_SIZE and OMNIROUTE_DB_POOL_SIZE as tunable env vars, but neither is read anywhere in the codebase (confirmed via full-repo grep) — this repo uses SQLite, which has no connection-pool concept these vars could plausibly control. check:fabricated-docs --strict correctly flags fabricated env-var claims; removed the bullet rather than implementing a feature to match invented documentation. * fix(i18n): completes Vietnamese parity, fixes empty migration query Two more release/v3.8.50 base-red items, both surfaced while chasing CI failures on unrelated PRs: - vi.json was missing 8 keys that #9539 (NewAPI/Sub2API aggregator balance) added to en.json without a matching i18n:sync-ui run — pt-BR.json already had all 8, only Vietnamese drifted. Added translations for the 6 provider-settings strings, the feature-flag description, and the quota tooltip; verified against tests/unit/i18n-vi-completeness.test.ts (parity, placeholder preservation, ICU parse — all 5 assertions pass). - src/lib/db/migrations/120_interception_rules.sql was pure comments documenting a no-schema-change key_value namespace, with no executable SQL statement — the migration runner logged "FAILED: 120_interception_rules — Query contained no valid SQL statement" on every fresh DB init. 118_provider_param_filters.sql (same pattern, two migrations earlier) already ends with a bare `SELECT 1;` no-op for exactly this reason; 120 was just missing it. Verified directly against better-sqlite3 that the file now executes without error. * fix(types): clears 6 pre-existing release/v3.8.50 typecheck errors typecheck:core is its own blocking CI job (quality.yml), separate from Docs Gates/Merge integrity. Confirmed pre-existing and unrelated to any current work by branching this worktree directly from upstream/release/v3.8.50 with no other merges applied. - accountSemaphore.ts: isBypassed() already excludes null/<=0 maxConcurrency before ensureGate() is called, but a boolean- returning helper isn't a type predicate TS can narrow through. Added a targeted `as number` at the one call site, with a comment explaining why it's safe. - combo/comboStructure.ts: two module-scope `const HARD_COMPAT_REASONS` declarations with different values — a genuine "can't redeclare" compile error, not a narrowing gap. The first (4-item set including "output_tokens") had zero usages between its own declaration and the second; the second (3-item set, matching the CompatFilterOptions doc comment exactly) is what hasHardCapabilityFailure/ describeCapabilityFilterExhaustion/the third call site all actually use. Removed the dead first declaration. - combo/comboStructure.ts + combo/fusionPanel.ts: both accessed `.prompt`/`.model` on a `ComboModelStep | ComboProviderWildcardStep` union after only excluding `combo-ref`, but `ComboProviderWildcardStep` has neither field — a real latent bug (fusionPanel would have pushed `undefined` into a fusion panel for a wildcard step). Narrowed to `step.kind === "model"` in comboStructure, and switched to the already-existing `getComboModelString()` helper in fusionPanel (which correctly resolves to null for unsupported step kinds, mirroring how combo-ref is already skipped there). Verified directly via a standalone script exercising both branches (wildcard vs. model step). - combo/quotaStrategies.ts: imported `preferAntigravityConnectionsWithStoredProject` from a module that never existed (`../antigravityProjectPersistence.ts`, distinct from the real `antigravityProjectPersist.ts`) — the function itself was referenced nowhere else in the codebase. Wrote the missing implementation: prefers Antigravity connections with a discovered `projectId` for reset-aware routing, failing open to the full list when none have one yet (per the file's own "Exclude... from reset-aware pool" changelog note, softened to a preference — strict exclusion would empty the pool entirely for a fleet of freshly-added accounts). Verified directly via a standalone script. - compression/engines/ccr/index.ts: `enforceGlobalBudget(owner, bytes)` was called with only `bytes` at one of its two call sites, missing the `owner` argument the other call site (and the function's own doc comment on preferring the calling principal's LRU eviction) already uses correctly. Added the missing `entry.principalId` argument. - firecrawlQuotaFetcher.ts: `fetchFirecrawlQuota` was annotated to return `Promise<QuotaInfo | null>` but every return path constructs a `FirecrawlQuota` (QuotaInfo extended with remainingCredits/planCredits/ extraCreditsInferred/overPlan) — the type the file already defines and the type `parseFirecrawlCreditUsage` already correctly returns. Widened the annotation to match; `FirecrawlQuota extends QuotaInfo` so this stays compatible with the `QuotaFetcher` contract. npm run typecheck:core and npm run check:dashboard-typecheck both pass cleanly. A subset of DB-backed tests in this area also fail, but 100% attributably to an already-tracked, unrelated migration version collision (134 -> [ccr_blocks, proxy_logs_egress_ip], see _tasks/features-v3.8.4/9route/POST-MERGE-AUDIT.md) — confirmed by every failure's stack trace bottoming out at that exact error, not at anything touched here. * fix(sse): update stale ALL_ACCOUNTS_INACTIVE test assertions to ALL_TARGETS_SKIPPED Two combo-routing-engine.test.ts cases assert the pre-dispatch-skip scenario (isModelAvailable always false, zero dispatch attempts) returns ALL_ACCOUNTS_INACTIVE. Production code already distinguishes this case via the recordedAttempts === 0 branch and returns the more precise ALL_TARGETS_SKIPPED -- the tests were never updated when that branch shipped upstream, so they fail on a clean release/v3.8.50 checkout independent of this PR's changes. * fix(sse): update second stale ALL_ACCOUNTS_INACTIVE assertion (T24) Same pre-existing upstream test-drift as |
||
|
|
54bba33e2f |
maint: follow-up cherry-pick fix-in-place #9629 (conflict-resolved fallback) (#9885)
* fix(compression): add Lite tool truncation toggle * fix(antigravity): add missing antigravityProjectPersistence.ts module The quota-strategy engine (quotaStrategies.ts) imports from antigravityProjectPersistence.ts, but only antigravityProjectPersist.ts existed in the tree. Add the missing module with the expected preferAntigravityConnectionsWithStoredProject() helper and re-export the existing persistDiscoveredAntigravityProjectId(). Co-authored-by: diegosouzapw <diegosouza.pw@outlook.com> * fix(file-size): rebaseline strategySelector.ts for Lite truncation toggle The PR adds one line to threading options?.config?.lite into applyLiteCompression. Update the frozen size from 1060 to 1061. Co-authored-by: diegosouzapw <diegosouzapw@users.noreply.github.com> Refs #9629 --------- Co-authored-by: Xiangzhe <xiangzhedev@gmail.com> Co-authored-by: xz-dev <xz-dev@users.noreply.github.com> Co-authored-by: diegosouzapw <diegosouzapw@users.noreply.github.com> |
||
|
|
a54c1f73af |
fix(db): resolve ccr migration version collision (#9884)
Renumber the CCR block-store migration from 134 to 139, reconcile databases that already applied the legacy slot, and add regression coverage for both upgrade paths. Co-authored-by: fenix007 <fenix007@users.noreply.github.com> |
||
|
|
5eba045175 |
maint: follow-up cherry-pick fix-in-place #9510 (fallback resolution) (#9880)
* feat(api): add GET /api/resilience/connections for per-account state The three temporary-failure mechanisms each have their own scope -- the provider circuit breaker covers a whole provider, connection cooldown covers one account, model lockout covers a provider/connection/model triple -- and until now nothing showed them side by side. Diagnosing "why is this key being skipped" meant reading three separate surfaces and correlating by hand, which is exactly what the docs' own debugging guidance asks an operator to do. The route returns all three keyed by connection, plus the breaker's transition history so a flapping provider is visible as a sequence rather than a single current state. getStatus() already assembled everything except that history; it now returns a copy of it and carries an explicit CircuitBreakerStatus type instead of an inferred one. Reading raw connection rows for this meant widening getRawProviderConnections' column projection, so the existing allowlist is exported and the route selects through it. A test asserts every column the route names is in that allowlist, which turns a future typo into a failure here rather than a silent empty field. Each of the three data sources is wrapped independently: one of them throwing degrades that section and sets meta.degraded rather than failing the whole response, since a partial view still answers most of the questions the page exists for. Loopback-gated. It spawns nothing, unlike every other entry on that list, but it exposes per-account operational state and the comment says so to keep it from being read as precedent for gating read-only routes generally. Tests are real isolated-DB integration tests rather than mocks -- ESM mocking is unavailable here (no mock.module, non-configurable exports) and the codebase already has the isolated-DB pattern, which exercises more than a mock would anyway. Signed-off-by: Minxi Hou <houminxi@gmail.com> * feat(dashboard): add the per-account resilience connections page Renders what the API added: every connection with its cooldown, its provider breaker, and its model lockouts in one table, with a detail view per connection and the breaker's transitions drawn as a timeline. The timeline is the part that is hard to get from the existing surfaces -- a breaker sitting at CLOSED right now looks healthy, and only the sequence shows it has opened four times in the last hour. Polls rather than streams. The state it displays changes on the order of seconds to minutes and the page is loopback-gated, so an SSE channel would buy nothing over an interval. ModelCooldownsCard had its own formatRemaining. The new table needs the same countdown format and two copies would drift, so it moves to shared/utils/formatRemaining.ts and both import it -- behaviour unchanged, the extracted version differs from the deleted one only in local variable names. DataTable's column and row interfaces are exported for the same reason: the new table types against them rather than restating their shape. Signed-off-by: Minxi Hou <houminxi@gmail.com> * fix(i18n): translate new resilience-connections screen strings PR #9510 added the "Connection Resilience" dashboard screen but the sync-added i18n keys (sidebar.resilienceConnections/Subtitle and the full resilienceConnections namespace) were left as __MISSING__: in every non-English locale, dropping i18nUiCoverage.pct below the 99 ratchet baseline. Translate all ~78 new leaf strings into all 41 non-English locales. Pre-existing unrelated __MISSING__ debt (hermesRole*, apiProtocol*, grokAutoTopUp*, featureFlagExposeFunctionalGatewayMirrorsDescription) is left untouched — out of scope for this fix. Co-authored-by: HouMinXi <HouMinXi@users.noreply.github.com> --------- Signed-off-by: Minxi Hou <houminxi@gmail.com> Co-authored-by: Minxi Hou <houminxi@gmail.com> Co-authored-by: diegosouzapw <diegosouzapw@users.noreply.github.com> Co-authored-by: HouMinXi <HouMinXi@users.noreply.github.com> |
||
|
|
5f75abe4a2 |
cherry-pick(pr-9634): fix(test): reconcile base-drifted test expectations on release/v3.8.50 (#9874)
* fix(combo): restore routing module load * fix(db): resolve ccr migration version collision Renumber the CCR block-store migration from 134 to 139, reconcile databases that already applied the legacy slot, and add regression coverage for both upgrade paths. Co-Authored-By: GPT-5 <noreply@openai.com> * fix(changelog): format the aggregator balance fragment as a bullet The fragment landed with YAML frontmatter rather than the bullet the aggregator reads, so check:changelog-integrity exits 1 on every branch and takes the merge-integrity job down with it regardless of what the branch changed. Only the format changes. The entry text is the author's, unedited, and now carries the link to the pull request that shipped it. * fix(test): update expected auth/vision/provider schema for base-drifted expectations * fix(test): narrow this branch to the drifted test expectations Three other PRs already cover what this one was carrying. #9618 renumbers the colliding ccr_blocks migration, #9632 repairs the malformed aggregator changelog fragment, and #9676 restores the combo module load by implementing the selection helper the import was reaching for, rather than deleting the caller the way this branch did. Keeping any of it here would put two files back on the same migration slot and overwrite a better fix with a worse one. What survives is the part none of them touch. Once the combo barrel loads again, three assertions in the context-window filter suite start failing: they demand that catalog-too-small targets be dropped, while the file's own header and its four neighbouring tests say those targets stay available as runtime fallback. The unresolved import was masking them. A new case pins the output-token limit as a genuine hard requirement so the relaxation cannot drift further. The provider count assertion kept one literal at the old value after the rest of the file moved to 198, so the partition check failed on a sum that was correct. * chore(quality): re-time migrationRunner for the 139 guard on the new tip --------- Co-authored-by: alexey.nazarov@softmg.ru <alexey.nazarov@softmg.ru> Co-authored-by: GPT-5 <noreply@openai.com> Co-authored-by: Minxi Hou <houminxi@gmail.com> |
||
|
|
431fc02e75 |
cherry-pick(pr-9569): fix(settings): use provider prefixes in model overrides (#9878)
* fix(settings): use provider prefixes in model overrides * refactor(settings): extract pricing tab helpers Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> --------- Co-authored-by: Xiangzhe <xiangzhedev@gmail.com> |
||
|
|
c1d951e5c5 |
cherry-pick(pr-9572): fix(providers): reject the dashboard password as a connection API key (#9877)
* fix(providers): refuse to store the dashboard password as a connection API key A browser autofilled the management password into a connection's API-key field. The resulting credential authenticates against nothing, so every request routed through that connection came back 401, and because the field looks like any other password input the same autofill fired again while the connection was being repaired by hand. The refusal belongs on the write path rather than in the form. Twenty routes create or update connections and all of them funnel through createProviderConnection and updateProviderConnection, so one check there covers every entry point including a future one. The two other places that write api_key are left alone on purpose: one re-encrypts rows that already exist and the other is the one-time db.json import, and neither takes a value an operator just typed. Update checks the incoming value, never the merged one. A connection that already holds the password has to stay editable or the operator cannot repair the exact state this prevents, and re-checking the merged value would spend a bcrypt round on every unrelated field edit. Only a real match blocks the write. An unreadable settings row or a throwing bcrypt call logs and allows, because a guard against one specific mistake must not turn into a way to lock out every connection write. Signed-off-by: Minxi Hou <houminxi@gmail.com> * fix(providers): compare the untrimmed credential, and cover the guard's branches The guard trimmed the incoming value before comparing it, which catches a paste carrying whitespace the password does not have. It missed the mirror case: neither the login route nor the set-password route trims, so a dashboard password may itself begin or end with a space, and an autofill reproducing it exactly was trimmed into a value that no longer matched the stored hash. The write then went through, which is the state this guard exists to prevent. Both forms are compared now, the second only when the first fails on a string that differs, so an ordinary key still costs a single bcrypt round. Two branches carried no coverage and both are load-bearing. The catch that logs and allows is the only path that lets a write through; a stored hash bcrypt cannot parse reaches it without needing a mock, since the shape check accepts an impossible cost factor that the comparison then rejects. The early return is what keeps a token renewal -- a write carrying tokens but no apiKey -- from paying for a settings read and a bcrypt round every time it fires, and the same unparseable hash makes that path observable, so an absent warning is proof the return happened. The narrower scope is deliberate and now says so in the code: the OAuth tokens arrive from a provider's token endpoint rather than from a form, so extending the comparison to them would charge every renewal for a field no autofill can reach. Signed-off-by: Minxi Hou <houminxi@gmail.com> --------- Signed-off-by: Minxi Hou <houminxi@gmail.com> Co-authored-by: Minxi Hou <houminxi@gmail.com> |
||
|
|
740e16c3e2 |
cherry-pick(pr-9601): feat(responses): add encrypted reasoning replay opt-in (#9876)
* feat(codex): add encrypted reasoning replay opt-in * feat(responses): generalize encrypted reasoning replay * docs: clarify encrypted reasoning provider scope * fix(ui): group reasoning replay with connection controls * fix(logs): omit encrypted reasoning payloads * fix(chat): reduce file size Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> * fix(chat): reduce combined file size Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> --------- Co-authored-by: jackjinke <jack.kejin@gmail.com> |
||
|
|
5e5919dcc0 |
maint: follow-up cherry-pick fix-in-place #9631 (conflict-resolved fallback) (#9886)
* feat(db): add a job registry for scheduled background work Background jobs each ship their own timer today, so there is no list of what is scheduled, no history of what ran, and no way to pause one without an environment variable and a restart. The registry gives them one home: a jobs table holding the schedule, a job_runs table holding the outcomes, and a loopback-only API to inspect and control both. Cron jobs read their expression through an optional cronGetter rather than the stored column, so an operator changing OMNIROUTE_WARMUP_CRON does not need the row rewritten. register() is an idempotent upsert that refreshes the schedule but never overwrites `enabled` or `created_at`, which is what lets a job be re-registered on every boot without discarding the operator's toggle. Run history is pruned per job rather than globally, and safeRun records a failure for a handler that throws as well as one that returns success:false, so a crashing job leaves a trail instead of a gap. The API is under /api/jobs and gated to loopback in the route guard. It can trigger a run and flip a job off, which is runtime administration and does not belong on a remotely reachable surface. Signed-off-by: Minxi Hou <houminxi@gmail.com> * feat(jobs): move the budget reset and token health check onto the registry Both jobs owned their own timer and started themselves as an import side effect, so nothing could report whether they were running, when they last ran, or why a run failed. They now register with the job registry and are started from it, which also means their schedule and run history are visible through /api/jobs. startAll() runs each interval job's first tick synchronously, so both entry points start the registry only after initializeCloudSync() has been awaited. The old wiring reached that ordering two different ways: the budget reset was started after the init call, and the health check's first sweep sat behind a 10s timer. Replacing both with one startAll() would otherwise have moved the two handlers in front of the initialisation they run against. Both entry points also register the same pair of jobs. Registering one and not the other is how a background job goes missing without anything failing. sweep() now returns how many connections it swept, so the health check can record a real records_affected the way the budget reset does. The migration documents that column as a per-job count, and hardcoding zero would have left one of the two jobs reporting a number the schema promises but the code never produces. A skipped or empty sweep reports zero. Every existing caller ignores the return value. The token health check keeps its own disable semantics: the handler still calls isHealthCheckDisabled() before sweeping, so OMNIROUTE_DISABLE_TOKEN_HEALTHCHECK, the production-build phase and the automated-test guard behave as before. Its registry adapter lives in src/lib/jobs/ next to the budget reset rather than in tokenHealthCheck.ts, which is already above its frozen size ceiling on the base branch and should not grow further. The adapter lets a failing sweep throw rather than reporting it itself, matching the budget reset: safeRun records a thrown error as a failure run with its message. The warmup job is seeded disabled. Its handler arrives with the warmup scheduler, and startAll() filters on enabled before it looks for a handler, so seeding it enabled here would warn about the missing handler on every boot. * fix: allowlist cron-parser dep and document OMNIROUTE_RUNNOW_TIMEOUT_MS env var Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com> --------- Signed-off-by: Minxi Hou <houminxi@gmail.com> Co-authored-by: Minxi Hou <houminxi@gmail.com> Co-authored-by: diegosouzapw <diegosouzapw@users.noreply.github.com> |
||
|
|
a39f78c6f8 |
maint: follow-up cherry-pick fix-in-place #9707 (conflict-resolved fallback) (#9890)
* fix(db): renumber ccr_blocks migration 134 -> 139 134 was taken by 134_proxy_logs_egress_ip, so two migrations shared the same numeric prefix and check-migration-numbering failed. Move ccr_blocks to the next free slot and add the retroactive isSchemaAlreadyApplied guard so a DB that already applied it under 134 skips the re-run. * fix(combo): restore missing preferAntigravityConnectionsWithStoredProject quotaStrategies imported the reset-aware pool filter from ../antigravityProjectPersistence.ts, a module that does not exist — the helper belongs in antigravityProjectPersist.ts and was never added there, breaking typecheck. Add the helper alongside the persist path, point the import at the real module, and cover the filter with unit tests. * chore: add Makefile wrapping the canonical npm scripts * fix(compression): remove duplicate Antigravity project helper The release branch already includes the generic project-aware connection selection helper. Keep that implementation and remove the duplicate introduced while cherry-picking #9707. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> --------- Co-authored-by: Matias Baglieri <168452313+matiasbaglieri@users.noreply.github.com> |