mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-31 04:12:10 +03:00
65fbba4893f23fd910adddee4e8306072a45c18f
5222 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
65fbba4893 |
fix(antigravity): wrap Pro fallback chain in try/catch for timeout resilience (#7290)
* fix(antigravity): wrap executeOnce in try/catch for Pro fallback chain When a Pro-tier candidate times out or throws a network error, the exception now continues to the next candidate instead of aborting the entire chain. Includes diagnostic logging and unit tests. Signed-off-by: Minxi Hou <houminxi@gmail.com> * fix(antigravity): propagate abort signal in Pro fallback catch block Re-throw AbortError and signal.aborted immediately instead of retrying the next candidate. Prevents wasted upstream requests after client disconnect. Signed-off-by: Minxi Hou <houminxi@gmail.com> * fix(mitm): skip DNS modification when sudo unavailable (container) In containers (USER node, no sudo, not root) provisionDnsEntries() now detects the condition up-front and logs a clear message instead of attempting sudo and silently swallowing the error. Adds canElevate() to the injectable deps interface for testability, and supports SKIP_ANTIGRAVITY_DNS=true for explicit opt-out. * fix(antigravity): improve abort detection and fallback error handling Check Error.name === 'AbortError' for non-DOMException environments (polyfills, test harnesses). Capture first 400 from any candidate (not just i===0) so mixed paths surface the 400 instead of a generic error. Return firstResult when last candidate throws, consistent with the all-400 case. * test(mitm): add coverage for container-skip DNS provisioning * test(mitm): harden container-skip DNS test assertions The SKIP_ANTIGRAVITY_DNS=true and canElevate()=false tests used empty agentStates/customHosts, so they could not distinguish 'all steps skipped' from 'only the default step skipped'. Provide non-empty mocks and assert addHostsDns was NOT called. Also add a SKIP_ANTIGRAVITY_DNS=false boundary test confirming the strict === "true" comparison does not block normal provisioning, and verify sudoPassword passthrough in the canElevate=true happy-path test. * refactor(mitm): split provisionDnsEntries below complexity gate provisionDnsEntries() (complexity ~18, this PR's try/catch/log additions pushed it over check-complexity.mjs's threshold of 15) and execute()'s Pro-fallback loop (complexity 27, from wrapping executeOnce() in try/catch for timeout resilience) were both over the gate. Decomposed each into small named helpers, no behavior change: - provision.ts: split into provisionDefaultDns/provisionAgentDns/ provisionCustomHostsDns, each wrapping one best-effort DNS step. - antigravity.ts: extracted the fallback-chain catch/400-handling decisions (handleAntigravityFallbackChainError, isAntigravityAbortError, handleAntigravityFallback400) into a new antigravity/proFallbackChain.ts submodule (pure, no executor instance state), mirroring the existing antigravity/sseCollect.ts submodule pattern. Also fixes the antigravity.ts file-size cap (was pushed to 1854 lines > 1813 frozen ceiling by this PR's own try/catch addition; now 1771). execute/provisionDnsEntries no longer appear with ruleId complexity or max-lines-per-function in the check-complexity.mjs report. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> * refactor(antigravity): drop redundant loop continue (cognitive-complexity gate) Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> * refactor(antigravity): fold fallback outcome dispatch into switch (cognitive gate) Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> --------- Signed-off-by: Minxi Hou <houminxi@gmail.com> Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com> Co-authored-by: HouMinXi <19586012+HouMinXi@users.noreply.github.com> Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> |
||
|
|
43eb470790 |
fix(models): update Anthropic model contextLength to 1M (#7129)
* chore(ci): add .mergify.yml to main — Mergify only reads config from the default branch (#7168)
* fix(ci): add the auto-enqueue pull_request_rule to the Mergify config (queue_conditions alone are eligibility-only) (#7179)
* fix(ci): migrate Mergify auto-enqueue to merge_protections_settings.auto_merge_conditions (rules-based path is EOL 2026-07-16) (#7216)
* fix(ci): drop Mergify batch settings (batching is a paid-tier feature; free plan queue is serial) (#7220)
* fix(ci): merge queue tolerates the advisory dast-smoke failure (its GH-hosted build hang dequeued every attempt) (#7225)
* test(ci): make the #6634 selfref guard hermetic — main's copy hard-fails every PR (#7341)
main's copy of this test still does git I/O inside a unit test:
const baseSrc = git(['show', 'origin/main:' + FILE]);
Runners check out a shallow single ref, so origin/main does not resolve and the
test dies with 'fatal: invalid object name origin/main'. Every PR into main
fails Unit Tests (7/8) on it — today that is #7313, #7315, #7316, #7334, #7336
and #7337, six PRs red on a defect none of them introduced. #7313 has no other
red at all.
release/v3.8.49 already carries a fix (
|
||
|
|
88c28428e1 |
feat(providers): add Agnes AI native provider support (#7035)
Add built-in provider registry entry for Agnes AI (agnes-ai.com), a permanently free OpenAI-compatible API by Sapiens AI. Models: - agnes-2.0-flash: 256K context, 64K output, thinking mode (reasoning_content), vision, tool calling - agnes-1.5-flash: 256K context, 64K output, vision The baseUrl uses the full /v1/chat/completions path. This is the standard pattern for registry entries (115 built-in providers use the same convention). The default executor's buildUrl() routes registry entries through normalizeOpenAIChatUrl(), which detects the existing /chat/completions suffix and returns the URL as-is without appending. Only openai-compatible-* connections (dashboard- added custom providers) unconditionally append the path. Specs verified against MODEL_CATALOG.md v2026.06.28 (github.com/AgnesAI-Labs/AgnesAI-Models) and live API testing at apihub.agnes-ai.com/v1 (2026-07-13). Fixes #5580 Signed-off-by: Minxi Hou <houminxi@gmail.com> |
||
|
|
653c1ec40a |
docs(perf): add per-endpoint p50/p95/p99 latency + cost budget reference (#7336)
* docs(perf): add per-endpoint p50/p95/p99 latency + cost budgets
Adds canonical performance budgets (latency, throughput, cost) for
the v1 client API + management + relay surface, with monthly
re-evaluation cadence.
### Files (1 changed, +222 / -0)
- docs/PERF_BUDGETS.md — 222-line per-endpoint budget matrix
### Why this matters
- diegosouzapw/OmniRoute has zero performance budget doc as of 2026-06-23
- The 71-pillar framework (Performance domain, L13–L19) flags
performance budgets as P0 for any production-serving surface
- Sets SLO targets that downstream dashboards can alert against
### Budgets
- p50 / p95 / p99 latency per endpoint
- Sustained throughput (req/s) per replica
- Cost ceiling per request (USD)
- 30-day rolling window for review
### Compatibility
- Pure documentation — no code change, zero behavior change
- Single file, lands in one commit
- No new dependencies
Refs: 71-pillar framework L13–L19 (Performance domain), upstream
audit 2026-06-23 — no performance budget exists in
diegosouzapw/OmniRoute
* chore(ci): add .mergify.yml to main — Mergify only reads config from the default branch (#7168)
* fix(ci): add the auto-enqueue pull_request_rule to the Mergify config (queue_conditions alone are eligibility-only) (#7179)
* fix(ci): migrate Mergify auto-enqueue to merge_protections_settings.auto_merge_conditions (rules-based path is EOL 2026-07-16) (#7216)
* fix(ci): drop Mergify batch settings (batching is a paid-tier feature; free plan queue is serial) (#7220)
* fix(ci): merge queue tolerates the advisory dast-smoke failure (its GH-hosted build hang dequeued every attempt) (#7225)
* test(ci): make the #6634 selfref guard hermetic — main's copy hard-fails every PR (#7341)
main's copy of this test still does git I/O inside a unit test:
const baseSrc = git(['show', 'origin/main:' + FILE]);
Runners check out a shallow single ref, so origin/main does not resolve and the
test dies with 'fatal: invalid object name origin/main'. Every PR into main
fails Unit Tests (7/8) on it — today that is #7313, #7315, #7316, #7334, #7336
and #7337, six PRs red on a defect none of them introduced. #7313 has no other
red at all.
release/v3.8.49 already carries a fix (
|
||
|
|
7fefc6782b |
feat(incident-response): structured incident response templates (#7334)
* docs(ops): add canonical incident response runbook
Adds a 5-level severity incident response runbook with role
assignments, communication templates, and post-mortem cadence.
### Files (1 changed, +X / -0)
- docs/INCIDENT_RESPONSE.md — incident classification, response
roles per severity (sev1/sev2/sev3/sev4/sev5), pager rotation,
status page templates, post-mortem schedule (within 5 business
days of sev1/sev2 resolution)
### Why this matters
- diegosouzapw/OmniRoute has no incident response runbook as of 2026-06-23
- The 71-pillar framework (Observability & Ops domain, L56–L63)
flags incident response as P0 for any production-serving surface
- Establishes the on-call rotation + escalation paths in writing
- Post-mortem template is the load-bearing artifact (no-blame
culture, 5-business-day deadline, action item tracking)
### Compatibility
- Pure documentation — no code change, zero behavior change
- Single file, lands in one commit
- No new dependencies
* chore(ci): add .mergify.yml to main — Mergify only reads config from the default branch (#7168)
* fix(ci): add the auto-enqueue pull_request_rule to the Mergify config (queue_conditions alone are eligibility-only) (#7179)
* fix(ci): migrate Mergify auto-enqueue to merge_protections_settings.auto_merge_conditions (rules-based path is EOL 2026-07-16) (#7216)
* fix(ci): drop Mergify batch settings (batching is a paid-tier feature; free plan queue is serial) (#7220)
* fix(ci): merge queue tolerates the advisory dast-smoke failure (its GH-hosted build hang dequeued every attempt) (#7225)
* test(ci): make the #6634 selfref guard hermetic — main's copy hard-fails every PR (#7341)
main's copy of this test still does git I/O inside a unit test:
const baseSrc = git(['show', 'origin/main:' + FILE]);
Runners check out a shallow single ref, so origin/main does not resolve and the
test dies with 'fatal: invalid object name origin/main'. Every PR into main
fails Unit Tests (7/8) on it — today that is #7313, #7315, #7316, #7334, #7336
and #7337, six PRs red on a defect none of them introduced. #7313 has no other
red at all.
release/v3.8.49 already carries a fix (
|
||
|
|
f8e56e4615 |
fix(router-eval): retained-optimization gate cleanup (#7318)
* feat(eval): add router-eval harness (AIQ scoring, regression gate, Pareto search)
Extracts a standalone router-eval evaluation tool that replays routing
decisions (from NDJSON corpora or the usage_history/call_logs SQLite
tables) into an AIQ (success/latency/cost) score, compares baseline vs.
candidate router configs with a retained-run regression gate, and ranks
Pareto-optimal candidates across a search space — a sibling to the
existing eval:compression harness.
New scripts: scripts/router-eval/{index,compare,patch-compare,search,
trends}.ts, scripts/check/check-router-eval-regression.ts, and
src/lib/routerEval/index.ts, wired via 6 new package.json entries
(eval:router, eval:router:compare, eval:router:patch-compare,
eval:router:search, eval:router:trends, check:router-eval).
Reconstructed onto current release/v3.8.49 from the original ~142-commit
stale PR branch: only the genuinely new router-eval payload (17 files)
was extracted — the other ~560 changed files in the original diff were
base-drift already present on release in newer form. The new package.json
scripts now invoke `node --import tsx` instead of `bun`, matching the
`eval:compression` precedent (Bun is reserved for a closed 5-script
allowlist). The runtime-detection shim (`"Bun" in globalThis`) already
present in the harness gracefully falls back to better-sqlite3 under
Node, so no logic changes were needed there; the retained-run manifest's
previously-hardcoded `runtime: "bun"` field and matching CLI help text
were corrected to reflect the actual invocation.
All 27 existing router-eval unit tests pass unchanged.
Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
* refactor(router-eval): decompose toRouterObservation below complexity gate
toRouterObservation had cyclomatic complexity 18 (gate max is 15). Extract
the per-field parsing/normalization into pure helpers (sampleId, model
fields, latency, cost derivation, success) so the entry point is a plain
sequential assembly of a RouterObservation. Behavior is unchanged — same
tests pass, same fallbacks, same precedence between input aliases.
Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
---------
Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
|
||
|
|
f657c7865a |
feat(issue-agent): surface RecordedTriageTimeoutError as 504 (#7315)
* chore(ci): add .mergify.yml to main — Mergify only reads config from the default branch (#7168)
* feat: scaffold issue agent and router eval provenance
* feat: wire recorded issue triage runner
* feat: ingest recorded issue context
* feat: persist issue agent audit log
* feat: import recorded github issue exports
* docs: document issue agent env toggle
* fix(issue-agent): validate run requests
* fix(ci): add the auto-enqueue pull_request_rule to the Mergify config (queue_conditions alone are eligibility-only) (#7179)
* fix: validate issue agent run requests
* docs: add issue agent execution traceability
* feat(issue-agent): route recorded triage through chat
* test(issue-agent): verify recorded triage through chat route
* docs(issue-agent): add executable triage session artifacts
* fix(ci): migrate Mergify auto-enqueue to merge_protections_settings.auto_merge_conditions (rules-based path is EOL 2026-07-16) (#7216)
* fix(ci): drop Mergify batch settings (batching is a paid-tier feature; free plan queue is serial) (#7220)
* feat(issue-agent): surface RecordedTriageTimeoutError as 504
When the recorded-triage chat completion times out, the AbortController
fires an AbortError that previously surfaced as a generic 400 to the
caller. This change:
* Adds a `RecordedTriageTimeoutError` that wraps the AbortError
with the timeoutMs context.
* Re-throws it from `executeRecordedTriageChatCompletion` so the
caller can distinguish timeouts from other failures.
* In the runs route, catches it and returns a 504 with code
`ISSUE_AGENT_TIMEOUT` so clients can render a useful error.
Tests:
* issue-agent-execution.test.ts — verifies the typed error
* issue-agent-route-execution.test.ts — covers timeout path
* issue-agent-runs-route.test.ts — verifies 504 mapping
* fix(ci): merge queue tolerates the advisory dast-smoke failure (its GH-hosted build hang dequeued every attempt) (#7225)
* test(ci): make the #6634 selfref guard hermetic — main's copy hard-fails every PR (#7341)
main's copy of this test still does git I/O inside a unit test:
const baseSrc = git(['show', 'origin/main:' + FILE]);
Runners check out a shallow single ref, so origin/main does not resolve and the
test dies with 'fatal: invalid object name origin/main'. Every PR into main
fails Unit Tests (7/8) on it — today that is #7313, #7315, #7316, #7334, #7336
and #7337, six PRs red on a defect none of them introduced. #7313 has no other
red at all.
release/v3.8.49 already carries a fix (
|
||
|
|
8febd55e44 |
feat(sidecar): support conditional provider manifest refresh (#7130)
* feat(sidecar): support conditional provider manifest refresh * fix(sidecar): accept weak manifest validators * perf(sidecar): cache provider manifest payload * docs(sidecar): describe manifest conditional refresh * test(sidecar): restore CORS preflight and manifest-content coverage The ETag/conditional-refresh rewrite of this test file dropped two pieces of coverage without replacing them: the CORS OPTIONS-preflight test, and the 200-response test's providers.length>100 / clientSecret-not-leaked assertions. This is the only test file for the provider-plugin-manifest route, so none of that was covered anywhere else afterward. Restore both: fold the providers.length/openai-presence/clientSecret assertions back into the "stable ETag" 200-response test alongside the new ETag checks, and add back a dedicated OPTIONS test asserting the CORS preflight headers. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> --------- Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> |
||
|
|
0bbdb839d9 |
Explain effective auto-combo scoring weights (#7087)
* fix(inspector): report effective auto scoring weights * fix(inspector): check options.combos before the health-signal short-circuit resolveConfiguredCombos() returned [] unconditionally whenever healthResponse, forecastResponse, and either skipAutopilot or autopilotReport were all supplied -- before it ever looked at options.combos. That is exactly the call shape comboHealthDashboard.ts::buildComboHealthDashboardResponse() always uses (it resolves combos/health/forecast/autopilot once, then passes all of them into buildComboScoringInspectorResponse together), so through the real dashboard integration the caller-supplied combos were silently discarded every time. Since combosById/combosByName (built from resolveConfiguredCombos()'s return value) are what resolveInspectorWeights() uses to report a combo's actual configured modePack/weights, this meant the dashboard's weightSource/modePack fields always came back "default", even for a combo with an explicit mode pack configured. Check options.combos first, unconditionally, and only fall back to the health-signals short-circuit (skip an unnecessary getCombos() DB round-trip) or a fresh getCombos() call when the caller didn't supply combos at all. Adds a regression test that drives the real buildComboHealthDashboardResponse() end-to-end with a combo configured for modePack "ship-fast", proving the inspector now reports the correct weightSource/modePack through that call path -- the exact scenario the PR's own tests didn't cover (they only exercised buildComboScoringInspectorResponse() directly with comboId+combos, never combos alongside a pre-resolved healthResponse+forecastResponse). Also reconciles the existing "skipAutopilot avoids rebuilding autopilot report" test: it asserted options.combos was never even read in this scenario via a throwing property getter, which pinned the exact short-circuit-wins-always bug this fix removes. The test's options object never supplied a real combos array in the first place, so the fixed code still takes the same DB-free path -- only the poison-pill mechanism (which trapped a mere property read rather than an actual getCombos() call) no longer applies. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> * refactor(usage): split resolveInspectorWeights below complexity gate resolveInspectorWeights had cyclomatic complexity 16 (gate max is 15). Extract the auto-config precedence resolution (autoConfig -> config.auto -> config -> {}), the mode-pack name lookup, and the explicit-weights validation into small pure helpers, each returning early instead of nesting ternaries. Behavior is unchanged, including the fallback warning that fires when a mode pack or explicit weights were configured but could not be resolved. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> --------- Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> |
||
|
|
c711ed257b |
Restore proxy navigation and sidebar accordion state (#7381)
* fix(sidebar): restore proxy navigation and accordion state * fix(proxy): prevent free pool translation crash * fix(settings): guard protected sidebar items and hydration state * test(sidebar): cover proxy visibility and expansion state * chore: restart pull request checks * refactor(sidebar): extract group item visibility control * refactor(sidebar): move group visibility control to module scope * fix(sidebar): preserve collapsed state on initial load * fix(proxy): collect free pool UI regression test * chore(test): unfreeze free-pool-tab.test.tsx from test-discovery baseline The move to tests/unit/ui/free-pool-tab.test.tsx (this branch) relinks it to the test:vitest:ui runner, so the tests/unit/free-pool-tab.test.tsx entry in the frozen orphan baseline is now stale and fails check:test-discovery. Removes the stale entry (60 -> 59 known orphans). Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> --------- Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> |
||
|
|
d470526031 |
Honor provider proxies for The Old LLM Vercel blocks (#7380)
* fix(theoldllm): honor provider proxy for Vercel blocks * fix(theoldllm): fail closed when assigned proxy is unavailable * refactor(theoldllm): extract proxy guards into dedicated module |
||
|
|
cb594ae370 |
Reject invalid output token budgets (#7379)
* fix(context): reject invalid output token budgets * fix(context): enforce output budgets across request formats * fix(context): enforce default Claude output budgets * fix(context): include Responses API input in token budget * ci: rerun pull request checks |
||
|
|
c6315f9067 |
Refresh NVIDIA free metadata and detect catalog drift (#7378)
* fix(nvidia): refresh free metadata and detect drift * fix(nvidia): handle catalog fetch and parse failures * fix(nvidia): refresh hosted model metadata snapshot |
||
|
|
78d2eee914 |
Add per-connection Provider Quota visibility (#7360)
* feat(dashboard): add Provider Quota visibility toggle per connection * refactor(dashboard): extract provider quota visibility controls Move quota visibility UI and update logic into reusable components, add Portuguese translations, and remove the stale migration gap allowlist entry. * Hide quota visibility controls for unsupported providers * chore(ci): retrigger GitHub checks * fix(db): renumber quota-visibility migration past release tip (121→125) 122_free_proxy_sync_errors.sql, 123_quota_auto_ping.sql, and 124_generic_session_affinity_ttl.sql have since landed on release/v3.8.49, so 121 is now out-of-sequence and would not apply on databases already past 122+. Renumbers to 125 (the next free slot past the current release tip) and restores "121" in check-migration-numbering's KNOWN_GAPS allowlist, since 121 remains a genuine unfilled gap once this migration moves off that number. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> * chore(quality): rebaseline file-size + complexity for resync merge The release-resync merge unions two already-compliant features in the same god-component (ConnectionRow.tsx/ConnectionsListPanel.tsx): this PR's per-connection quota-visibility wiring and release's confirm- delete-account wiring (#7361). Both were individually within budget (785/786 lines); combined they land at 791. Complexity count moves 2058->2059 for the same reason (2 previously-compliant .map() render callbacks in ConnectionsListPanel.tsx now marginally exceed the 80-line function cap). No new logic was written — see the _rebaseline_2026_07_18_pr7360_quota_visibility_resync justification entries in both baseline files for the full accounting. Verified via a byte-for-byte diff of the violation lists between origin/release/ v3.8.49 tip and this merge. Structural shrink stays tracked in #3501. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> * refactor(db): split _updateConnectionRow update assembly (complexity gate) _updateConnectionRow grew past the 80-line max-lines-per-function ceiling after this branch added quota_visible column handling. Extract the `.run()` params assembly (field mapping/normalization, unchanged) into a module-private `_buildUpdateConnectionRowParams` helper in the same file so the SQL statement + call site stay in `_updateConnectionRow` while the function itself drops back under the gate. No behavior change. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> --------- Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> |
||
|
|
5f04d5bcbd |
feat: add principal-scoped CCR MCP lifecycle (#7282)
* feat: add principal-scoped CCR MCP lifecycle * refactor: extract CCR MCP schemas * refactor: reduce CCR store complexity * fix: preserve CCR retrieval feedback * fix: keep CCR expiry/accounting scoped to accessed entries --------- Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> |
||
|
|
c78f150ac3 |
feat(compression): support RTK TOML schema v1 filters (#7281)
* feat(compression): support RTK TOML filters * chore(ci): sync RTK skill and dependency allowlist * refactor(compression): reduce RTK import complexity * fix(i18n): add Portuguese RTK import translations * fix(compression): improve RTK TOML import validation feedback |
||
|
|
dc0dec46c7 |
Add cache-aligned Live Zone compression (#7280)
* feat(compression): add cache-aligned live-zone processing * refactor(compression): satisfy complexity ratchets * fix(compression): handle tool_result outputs in live zone * fix(i18n): add live-zone pt-BR strings |
||
|
|
9088151043 |
Fix Codex Responses compression analytics (#7273)
* fix compression analytics for Codex responses * preserve Responses tool output fields * Test compression analytics cost failure isolation |
||
|
|
66142721ad | fix(codex): normalize nested Responses output content (#7269) | ||
|
|
c1a3d83c27 |
fix(combo): reject known context overflow without exhausting providers (#7177)
* Fix context-window exhaustion classification * fix(combo): keep chat.ts/comboStructure.ts under the file-size ratchet + fix context-overflow boundary bug - Extract getKnownContextOverflow (+ its KnownContextOverflow type) out of comboStructure.ts into a new open-sse/services/combo/knownContextOverflow.ts leaf, so the file-size ratchet (cap 800 for new files) passes. - Extract the skipConnectionDisable predicate out of handleSingleModelChat in chat.ts into open-sse/services/combo/comboPredicates.ts::shouldSkipConnDisable, and consolidate the new combo-failure-handling imports, to keep chat.ts under its frozen file-size baseline (1796) after the #7177 request-scoped-failure wiring. - Fix a real boundary bug in getKnownContextOverflow surfaced by the merge: estimateRequestInputTokens counted a caller-omitted `messages: []` (which some combo entrypoints default in) as real content, charging a few phantom "structural" JSON.stringify tokens toward the estimate. That was enough to falsely trip the new known-context-overflow rejection for a request with no real input when max_tokens exactly equals the target's context window (a common config where limit_input === limit_output === limit_context), regressing tests/unit/combo-routing-engine.test.ts's pre-existing #3587 "non-reasoning model does not get max_tokens buffer" case. Empty arrays/objects no longer count as estimable content. - Add a regression test for the exact-boundary empty-content case. Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com> * refactor(combo): move overflow logic into knownContextOverflow module (file-size cap) comboStructure.ts is not frozen in the file-size baseline but is capped at 800 lines; this PR's net +29 on that file alone would push it over once merged. knownContextOverflow.ts already exists in this PR as the dedicated home for "known context limit" logic, so move the genuinely new pieces there instead of leaving them in comboStructure.ts: - hasEstimableContent (new): its own doc comment already frames it purely in terms of the known-context-overflow boundary check, so it belongs next to that check, not in the general request-compatibility file. - getKnownContextLimit (new, requestedOutputTokens-aware): this *is* the "how big is a target's known context window" primitive knownContextOverflow already consumes; hosting it there is a better fit than comboStructure.ts. - getLegacyKnownContextLimit: kept alongside its sibling rather than split across two files, since both are alternate implementations of the same concept (used only by comboStructure.ts's hasKnownCompatibleContextLimit). comboStructure.ts now imports all three back for its own internal callers (estimateRequestInputTokens, getTargetCompatibilityFailures, hasKnownCompatibleContextLimit). deriveRequestCompatibilityRequirements and the RequestCompatibilityRequirements type stay in comboStructure.ts exactly as this PR already has them (still consumed internally there), so knownContextOverflow.ts keeps importing those two, same as before. No behavior change — pure relocation, verified by the existing PR test suite (combo-context-window-filter, combo-breaker-429, combo-failure-log-message, combo-target-exhaustion, diagnostics) plus the pre-existing combo-vision-aware-routing/combo-context-requirements/combo-roundrobin-compat-fallback-6238 suites, all green. Net effect on open-sse/services/combo/comboStructure.ts vs. this PR's merge base: -2 lines (was +29). typecheck:core, lint, and the complexity ratchets (check:complexity, check:cognitive-complexity) are unchanged from this PR's current HEAD — the 4 pre-existing complexity/max-lines findings in valueContainsImagePart/filterTargetsByRequestCompatibility are untouched by this move (same violations, same total ratchet counts, just shifted line numbers). Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> --------- Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com> Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> |
||
|
|
d760169d9a | feat(usage): add Codex reset credit picker (#7154) | ||
|
|
95e537307b | fix(models): preserve direct-model combo metadata (#6993) | ||
|
|
0730eee9f2 | fix(api): await params in Agent Bridge DNS route (Next.js 16) (#7271) (#7492) | ||
|
|
28879375b7 |
fix(build): align engines.node with SUPPORTED_NODE_RANGE (#7446) (#7490)
* fix(build): align engines.node with SUPPORTED_NODE_RANGE (#7446) * docs(changelog): add 7490 engines-node alignment fragment Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> --------- Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> |
||
|
|
0f55956808 |
fix(combo): derive session stickiness key from Responses API .input, not just .messages (#7270) (#7277)
* fix(combo): derive session stickiness key from Responses API .input, not just .messages (#7270) * fix(combo): map bare-string .input array items in stickiness key derivation (#7270) normalizeStickinessMessages()'s Array.isArray(input) branch cast the array straight through, so a Responses-API `.input` array of PLAIN STRINGS (each string shorthand for a user message) never matched deriveMessageHash's `role === "user"` lookup and the key stayed null — the same fail-open bug #7270 fixed, just for this narrower wire shape. Map bare-string items to {role: "user", content: item}, mirroring the string-item handling already established in responsesInputNormalization.ts's normalizeCodexResponsesInputItem. Adds a regression test case (unit-level normalizeStickinessMessages assertion + round-robin re-pin case) so the fix's own suite covers this shape. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> --------- Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> |
||
|
|
52b9ed4bc8 |
feat(api): route Google AI Studio Imagen through /v1/images/generations (#7656)
* feat(api): route Google AI Studio Imagen through /v1/images/generations
gemini/imagen-4.0-* models were advertised in /v1/models (surfaced live via
Google ListModels) but were unroutable on /v1/images/generations: `gemini` was
not in the image registry, so the route rejected them with "Invalid image
model". They also 404 on the chat route because Imagen uses the dedicated
:predict endpoint, not generateContent.
Wire a `gemini` image provider (format "google-imagen") that POSTs to
{baseUrl}/{model}:predict with x-goog-api-key, sends the instances/parameters
body, and normalizes predictions[].bytesBase64Encoded into the OpenAI image
shape. Only imagen-* models dispatch here (isImagenModel guard) — gemini
flash-image / nano-banana keep routing through /v1/chat/completions.
Note: Imagen requires a billing-enabled Google project; free-tier keys get
403 / quota 0. Request-builder and response-parser are pure and unit-tested;
the live Google call needs a paid key to exercise.
Tests: tests/unit/gemini-imagen-predict.test.ts (7 cases: registry wiring,
parseImageModel resolution, isImagenModel guard, predict body shape +
sampleCount clamp + aspectRatio, response normalization + empty tolerance).
* refactor(images): extract Google Imagen entries to registry module (file-size cap)
Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
---------
Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com>
|
||
|
|
858d762918 |
fix(oauth): repair qwen + codebuddy-cn device-code endpoints (#7517)
Both device-code providers failed at the upstream request (surfacing in the
companion extension as 'ошибка сервера OmniRoute'). Diagnosed by calling the
upstream endpoints directly:
- qwen: QWEN_CONFIG used the bare host qwen.ai, whose /api/v1/oauth2/device/code
and /token paths return 404 Not Found. The working qwen-code device flow lives
at chat.qwen.ai (verified: 200 + a valid device_code/user_code). Point both
URLs at chat.qwen.ai.
- codebuddy-cn: the Tencent state endpoint reads 'platform' from the QUERY
string, not the JSON body. Sending it only in the body returned
400 {"code":10001,"msg":"platform is empty"}. Passing ?platform=CLI
returned 200 with {code:0, data:{state, authUrl}}. Build the stateUrl with the
platform query param (body kept as-is).
Validation: live upstream calls returned 200 for both corrected requests (device
flow can't be hit from CI). Regression guard: tests/unit/oauth-device-code-endpoints.test.ts.
|
||
|
|
7ff2e5c0b5 |
fix(oauth): surface sanitized device-code error instead of a generic 500 (#7511)
The dynamic OAuth GET handler swallowed every thrown error into a generic
`{ error: "Internal server error" }` 500, so a device-code upstream failure
(qwen → qwen.ai, codebuddy-cn → copilot.tencent.com — geo-block / outage / bad
client) surfaced in the extension as an indistinguishable 'ошибка сервера
OmniRoute', hiding WHY it failed.
Route the caught error through sanitizeErrorMessage() (already imported, hard
rule #12) so the real reason ("Device code request failed: …", "CodeBuddy
state request failed (403)") reaches the client, falling back to the generic
only when the sanitizer yields nothing.
Regression guard: tests/unit/oauth-device-code-error-transparency.test.ts
(source-level — the route needs the full Next request/auth/upstream graph, so
behavioural validation belongs on a real build/VPS).
|
||
|
|
d7726ef80a |
fix(db): stop a 'latest' path segment from disabling backups and migrations (#7359)
Eleven subsystems answered "am I running under a test runner?" with
`process.argv.some((arg) => arg.includes("test"))`. JavaScript agrees that
'latest'.includes('test') is true, so ANY argv carrying a `latest` segment — a release symlink
like /opt/omniroute/latest/server.js, an npm/npx cache path, a `--model=latest` flag — silently
put the process into test mode.
The worst consequence is src/lib/db/backup.ts: isSqliteAutoBackupDisabled() returns true, so
SQLite auto-backup simply never runs — no warning, no log. The same substring decides whether
migrationRunner runs its pending-migration check, whether cloud sync initialises, and whether
the local/token health checks, quota recovery, model-lockout settings and the WS live server
consider themselves live. All of them fail silent, which is the dangerous kind.
Replaces the eleven copies with one helper, src/shared/utils/testProcess.ts:
- env first (NODE_ENV=test, VITEST) — unchanged;
- argv: `test`/`tests` only as a WHOLE token delimited by a path separator, dot or dash, so
`--test`, `tests/unit/x.test.ts` and `src/x.test.ts` still match, while `latest`, `protest`,
`contest` and `attestation` no longer do;
- argv: runner binaries (vitest/jest/mocha/ava/tap), which have no delimiter before "test";
- execArgv as well as argv — `node --test x.js` puts `--test` in execArgv, and
modelLockoutSettings was the only copy that remembered to look there.
argv/env are parameters rather than globals so the negative cases are testable: under a test
runner the globals always say "test", which is precisely why this bug could never be caught.
tests/unit/test-process-detection.test.ts guards the regression (a `latest` path is not a test
run) alongside the positives that must keep working.
|
||
|
|
b71790bb7e |
fix(providers): accept m365.cloud.microsoft for copilot-m365-web token (#7078) (#7166)
* fix(providers): accept m365.cloud.microsoft for copilot-m365-web token (#7078) * test: regression for #7078 m365.cloud.microsoft token extraction * fix(7078): match on url.hostname and anchor path with startsWith * test(7078): cover explicit :443 port via url.hostname |
||
|
|
40e097cc7a |
fix(translator): preserve thinking.budget_tokens: 0 in Claude->Gemini (#6813) (#7061)
* fix(translator): preserve thinking.budget_tokens: 0 in Claude->Gemini (#6813) * test: regression guard for budget_tokens: 0 in Claude->Gemini (#6813) |
||
|
|
178496fd92 |
fix(providers): AgentRouter model import applies Claude Code wire image to /v1/models (#7016) (#7060)
* fix(providers): AgentRouter model import applies Claude Code wire image to /v1/models (#7016) * test: regression guard for AgentRouter /v1/models discovery (#7016) * fix(7016): case-insensitive Authorization strip + bare-array parseResponse * test(7016): assert no Authorization variant + bare-array parse |
||
|
|
4a7e2e51a5 |
fix(combo): least-used sorts by per-account executionKey (#7015) (#7059)
* fix(combo): least-used sorts by per-account executionKey (#7015) * test(combo): add #7015 per-account least-used regression coverage * test(combo): build real ResolvedComboTarget in least-used test (#7015) |
||
|
|
ff89a3d6ee |
fix(responses): map mid-conversation system turns to developer role (#6954) (#7056)
* fix(responses): map mid-conversation system turns to developer role (#6954) * test(responses): add #6954 mid-conversation system -> developer regression * fix(6954): keep bare-string content parts in buildResponsesTextParts * test(6954): cover array-form system content with bare string |
||
|
|
c55de7ab57 |
fix(antigravity): collect native part.functionCall into tool calls (#7037) (#7053)
* fix(antigravity): collect native part.functionCall into tool calls (#7037) * test(antigravity): add #7037 native functionCall regression coverage * fix(antigravity): do not clobber tool_calls finish reason with candidate STOP (#7037) |
||
|
|
688ff9d378 |
fix(combo): treat maxInputTokens as an input-only cap in the context filter (#7039) (#7052)
* fix(combo): treat maxInputTokens as an input-only cap in the context filter (#7039) * test(combo): add #7039 input-only maxInputTokens regression coverage * fix(combo): apply combined contextWindow check when maxInputTokens present * test(combo): add shared-window rejection regression for #7039 * refactor(combo): collapse context-limit return to single expression (file-size cap) Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> --------- Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> |
||
|
|
8994d6266f |
fix(providers): sanitize Claude native output_config.effort (#7044) (#7050)
* fix(providers): sanitize Claude native output_config.effort (#7044) * test(providers): add #7044 output_config.effort sanitizer coverage |
||
|
|
5db2e20c3b |
feat(sse): allow disabling : comment heartbeats via OMNIROUTE_SSE_COMMENTS=off (#7036)
* feat(sse): allow disabling `:` comment heartbeats via OMNIROUTE_SSE_COMMENTS=off * fix(sse): guard process access for edge/Workers + export helper * test(sse): cover sseCommentsEnabled + heartbeat suppression (#7036) |
||
|
|
a02fa3818b |
fix: add static.cloudflareinsights.com to CSP script-src (#7178)
PR #7178 — The CSP was blocking the Cloudflare Web Analytics beacon (static.cloudflareinsights.com). Both dev and prod script-src directives need the domain for the analytics script to load. Co-authored-by: oyi77 <oyi77@users.noreply.github.com> |
||
|
|
f287f42f15 |
perf: wrap ComboCard, HeroSection in React.memo (#7070)
* perf: wrap ComboCard, HeroSection in React.memo * fix(#7070): add test coverage for React.memo changes; fix selfref test in fork CI - Add smoke tests for combos page and EvalsTab to satisfy PR Test Policy requiring tests for production code changes - Fix selfref test (check-test-masking-selfref-6634) to try upstream/main first, falling back to origin/main, since origin/main may not exist in fork CI environments * fix(#7070): bump frozen baseline for combos/page.tsx 4655->4656 after React.memo wrapping The file-size checker's split('\n').length convention now counts 4656 for src/app/(dashboard)/dashboard/combos/page.tsx after wrapping ComboCard in React.memo (+1 effective line). --------- Co-authored-by: oyi77 <oyi77@users.noreply.github.com> |
||
|
|
3b7090a1cc |
feat(perf): add performance.mark/measure to SSE pipeline + request-size metric (#7045)
* feat(perf): add performance.mark/measure to SSE pipeline + request-size metric
- streamingPipeline.ts: mark/measure around assembly of SSE transform
chain — 'omni-pipeline-start'/'omni-pipeline-end'/'omni-pipeline'
- stream.ts: compute JSON body byte count on stream creation, emit as
performance.mark('omni-request-body-size', { detail: bytes })
Marks are visible via performance.getEntriesByType('mark') and
performance.getEntriesByType('measure') for DevTools/monitoring.
* fix(perf): prevent memory leak and TextEncoder allocation on hot path
- Add performance.clearMarks/clearMeasures before creating new marks to
prevent timeline accumulation in long-lived processes.
- Replace new TextEncoder().encode(str).length with Buffer.byteLength to
avoid allocating a full Uint8Array just to measure byte length.
* test(perf): add performance instrumentation tests
* chore(ci): rebaseline stream.ts 2796->2805 for perf instrumentation
Add _rebaseline_ entry documenting the +9 line growth from:
-
|
||
|
|
6d9caa8943 |
fix(auggie): update model registry to match v0.32.0 CLI model IDs (#7032)
* fix(auggie): update model registry to match v0.32.0 CLI model IDs All previous model IDs (claude-sonnet-4.6, claude-opus-4.6, gpt-5.5-high, etc.) were synthetic — the actual IDs use a different naming scheme (sonnet4.6, opus4.6, gpt5.5, etc.). Replaced the static best-guess registry with the 31 real model IDs from on v0.32.0, including: - All Claude variants (fable-5, haiku4.5, sonnet4.x/5, opus4.x/5) - Gemini 3.1 Pro Preview - Full GPT-5.x family (gpt5 ~ gpt5.6-terra) - GLM 5.2, Kimi K2.6/K2.7 - Prism composite routers (prism-a, prism-b) Removed unused entries that don't exist in v0.32.0 (gemini-3.0-flash, thinking variants, high/medium split IDs). Updated unit tests to reference valid model IDs (haiku4.5, sonnet4.6, opus4.6). * feat(auggie): auto-fetch model IDs on first execute() * fix(auggie): move sonnet4.6 first in model list, remove duplicate * fix(tests): update old claude-sonnet-4.6 model ID to sonnet4.6 in auggie test The registry was updated to use sonnet4.6 but the test at line 352 still referenced the old model ID claude-sonnet-4.6, causing resolveAuggieModel to reject it. * test(autoCombo): account for auggie's new glm-5.2 model in auto/glm family test The v0.32.0 auggie registry update in this PR adds a literal "glm-5.2" model id. auggie is a no-auth candidate (always in the auto/<family> pool per open-sse/services/autoCombo/virtualFactory.ts), and the family filter matches by model-id pattern (open-sse/services/autoCombo/modelFamily.ts), so it now legitimately joins auto/glm alongside the glm/zai connections — same documented behavior the "degrades gracefully" test below already covers for opencode/minimax. Updates the strict-equality assertion to include it instead of narrowing the pool in production code. Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com> * fix(auggie): add backward-compat alias map for v0.32.0 model IDs Saved combos may reference old model IDs (claude-sonnet-4.6 → sonnet4.6, gemini-3.1-pro → gemini-3.1-pro-preview, gpt-5.5-high → gpt5.5, etc). The alias map in resolveAuggieModel() resolves these before the allowlist check so existing combos continue working after the registry rename. Refs: #7032 * fix(auggie): use Map.get() for the pre-v0.32.0 alias lookup + changelog resolveAuggieModel() indexed AUGGIE_MODEL_ALIASES (a Map) with bracket notation (AUGGIE_MODEL_ALIASES[requested]), which always returns undefined for a Map instance — the alias branch never actually fired, so every pre-v0.32.0 saved model id still hit "Unknown Auggie model" after the v0.32.0 registry rename. Switch to .get(requested), the Map accessor. Adds a red-first regression test (fails on the old bracket access, passes with .get()) covering every old->new id pair in the alias map, and a changelog.d fragment documenting the breaking model-id rename + the alias fallback that keeps existing combos working. Refs: #7032 Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> --------- Co-authored-by: oyi77 <oyi77@users.noreply.github.com> Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com> Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> |
||
|
|
afd696e6b2 |
perf(db): cap modelLockouts eviction at 1000 entries (#6923)
* perf(db): cap modelLockouts eviction at 1000 entries - Add MODEL_LOCKOUT_EVICTION_CAP constant set to 1000 - Evict oldest entries in insertion order when cap exceeded - modelFailureState eviction skips entries still in modelLockouts - Prevents unbounded memory growth under sustained load * test(db): add lockout eviction test, export helpers - Extract evictModelLockoutOverflow() from ensureCleanupTimer for testability - Add getModelLockoutSize() and export MODEL_LOCKOUT_EVICTION_CAP - 3 tests: overflow eviction, under-cap idempotent, keeps recent entries * fix(resilience): never evict a still-active model lockout in evictModelLockoutOverflow() evictModelLockoutOverflow() walked modelLockouts in raw insertion order and deleted the oldest N regardless of entry.until. If the map exceeded 1000 entries while some of the oldest were still well within their active cooldown window, eviction silently deleted them — isModelLocked() would then report the model as unlocked even though it was still rate-limited/quota-exhausted, undermining the Model Lockout resilience layer. Reproduced live: lock a "victim" model first, lock 1000 more distinct models, call evictModelLockoutOverflow(), and isModelLocked() on the victim flips from true to false despite ~60s of cooldown left. Fix: only entries whose `until` has already elapsed are eviction candidates. ensureCleanupTimer()'s tick already runs cleanupModelLockKey() on every key immediately before calling this function, which removes genuinely-expired entries — so anything active left over the cap is, by construction, a real in-progress cooldown and must never be silently dropped. If the map is still over cap purely from active entries, the cap becomes a (rare-case) soft bound rather than trading away correctness. The 3 existing tests only asserted Map.size shrank to the cap, which is exactly the buggy behavior being fixed (they created only active/never-expiring locks and expected mass eviction regardless). Rewrote them to use lockModel()'s cooldownMs sign to construct deterministic active vs. already-expired entries (no real sleeps needed), and added a direct regression test asserting a specific still-active key survives eviction via isModelLocked() while an overflow of expired fillers is correctly evicted down to the cap. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> * refactor(resilience): extract lockout eviction to module (file-size cap) Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> --------- Co-authored-by: oyi77 <oyi77@users.noreply.github.com> Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> |
||
|
|
a0cff84339 |
perf(db): add temp_store=MEMORY pragma to SQLite init (#6921)
* perf(db): add temp_store=MEMORY pragma to SQLite init Store temp tables/indices in memory instead of disk for faster query execution (GROUP BY, ORDER BY, subquery materialization). The two other optimized PRAGMAs (synchronous=NORMAL, cache_size=-16384) were already set. * test(db): add temp_store MEMORY pragma test Verifies PRAGMA temp_store = 2 (MEMORY) after initDb() runs. --------- Co-authored-by: oyi77 <oyi77@users.noreply.github.com> |
||
|
|
e79de5c294 |
perf(startup): warm model catalog cache at module init (#6920)
* perf(startup): warm model catalog cache at module init Fire-and-forget call to getUnifiedModelsResponse after DB ready so first GET /v1/models request doesn't pay cold-build cost (15-30s). Non-fatal — if warmup fails the next request builds fresh. * test: add warm catalog cache source-pattern test Verifies registerNodejs() includes the model catalog warmup import and call to getUnifiedModelsResponse. * fix(perf): warm the durable OpenRouter catalog cache, not just the 1.5s TTL Response cache The warmup called getUnifiedModelsResponse() with no Authorization header, so it only ever populated the top-level per-key Response cache (catalogCache in catalog.ts) at key "|0|" — a real client sending an apiKey gets a different key and misses that cache entry. But that cache also has only a 1.5s TTL (CATALOG_CACHE_TTL_MS, a #6408 burst-dedup window for concurrent requests, not a startup-warm cache), so even a perfectly key-matched entry would almost always have expired before real traffic arrives regardless. The one genuinely durable, apiKey-independent cost in the catalog build is getOpenRouterCatalog()'s 24h disk-cached network fetch (src/lib/catalog/openrouterCatalog.ts) — buildUnifiedModelsResponseCore() calls it unconditionally whenever an OpenRouter connection is configured, fully decoupled from the per-key Response cache. Extract the warmup into an exported warmModelCatalogCache() (testable in isolation, without exercising all of registerNodejs()) that explicitly warms this cache too, guarded on an OpenRouter connection actually existing so deployments that never use OpenRouter don't pay an unconditional third-party network call at every boot. Replace the source-text-grep test with a behavioral one: warm once with a mocked fetch, confirm exactly one network call, then confirm a real request using a DIFFERENT apiKey than the warmup reuses the cache instead of re-fetching — the actual, durable, apiKey-independent benefit. Also covers the no-connection-configured and fetch-failure-is-non-fatal cases. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> --------- Co-authored-by: oyi77 <oyi77@users.noreply.github.com> Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> |
||
|
|
6e71553797 |
perf(db): project columns + composite index in getProviderConnections (#6918)
* perf(db): project columns + composite index in getProviderConnections - Add param to avoid (scans ~200MB/query) - Add WHERE clause support (was silently ignored) - Add composite index on (auth_type, is_active, refresh_token) - Update health check caller to request only needed columns - Test: authType filter, column projection, default full fetch * fix(db): dedupe authType filter, allowlist columns projection in getProviderConnections The branch was rebased on top of #6946 (already merged, same author, same authType-filter fix), leaving a duplicate `if (filter.authType)` block in getProviderConnections. Harmless at runtime (SQLite tolerates the repeated named param) but dead code — remove the newer duplicate, keep the one already merged via #6946. The `columns` projection param is interpolated directly into the SQL SELECT clause via `.join(", ")` with no validation. No current caller passes untrusted input, but it's a live SQL-injection footgun for whichever future caller wires it up: reproduced a working exfiltration via a single-statement subquery column name (no semicolon/stacked-query needed, so better-sqlite3's single-statement restriction doesn't help) that leaked an unrelated connection's api_key through the response. Add an allowlist validated against the real provider_connections schema (core.ts's SCHEMA_SQL) — rejects any non-listed column, and re-quotes the reserved "group" keyword so it stays usable. Verified the fix blocks the exact reproduced exfiltration. Add a regression test asserting invalid/injection-shaped column names are rejected, a mixed valid+invalid list still rejects (fail-closed, not a silent partial projection), and the legitimate "group" column still round-trips correctly when requested. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> --------- Co-authored-by: oyi77 <oyi77@users.noreply.github.com> Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> |
||
|
|
a2ebc343d4 |
fix: add re-entrancy guard to token health check sweep (#6917)
* fix: add re-entrancy guard to token health check sweep Adds an in-flight guard to prevent overlapping sweep() executions. Uses global state sweeping flag that is set before the first await and cleared in a finally block. Subsequent calls while sweeping return early with a debug log line. Test coverage: - skips when a previous sweep is still in flight - resets sweeping flag after normal completion - resets sweeping flag on empty connections * fix(test): move sweep re-entrancy test to node:test, wire CI correctly tests/unit/token-health-check-sweep.test.ts used vitest syntax while living directly under tests/unit/, which is exactly the glob `npm run test:unit` (node's native runner) scans — running it there threw "Vitest mocker was not initialized" and failed the file outright. Separately, the vitest.config.ts include-array edit didn't wire the test into any CI-blocking script either: `npm run test:vitest` runs vitest.mcp.config.ts (a different config, not this path), and test:vitest:ui is scoped to tests/unit/ui only — so the 3 tests never ran in CI at all while node's runner actively failed on the file. Rewrite the test to node:test, matching the tests/unit/apikey-connection-health-check.test.ts / tests/unit/token-health-check.test.ts convention (real temp-dir SQLite DB rather than vi.mock, since mock.module() is unavailable in this tsx/ESM + Node native test-runner setup). The re-entrancy scenario now drives the real, unmocked sweep() with real OAuth connections (healthCheckInterval: 0 keeps checkConnection() a fast no-op) and asserts on wall-clock elapsed time + the shared sweeping flag instead of a mocked call count. Verified this fails without the guard (909ms, ~3x the stagger) and passes with it restored. Remove the now-unused vitest.config.ts include entry since the test no longer needs it. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> * refactor(health): compact sweep guard + restore one-line stagger delay (file-size cap) Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> --------- Co-authored-by: oyi77 <oyi77@users.noreply.github.com> Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> |
||
|
|
cfc1d79edd |
chore(deps): bump github/codeql-action/init from 4.37.0 to 4.37.1 (#7642)
Bumps [github/codeql-action/init](https://github.com/github/codeql-action) from 4.37.0 to 4.37.1.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](
|
||
|
|
fd28ab13df |
chore(deps): bump github/codeql-action/analyze from 4.37.0 to 4.37.1 (#7641)
Bumps [github/codeql-action/analyze](https://github.com/github/codeql-action) from 4.37.0 to 4.37.1.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](
|
||
|
|
f35cddee6f |
deps: bump the production group across 1 directory with 12 updates (#7352)
Bumps the production group with 11 updates in the / directory: | Package | From | To | | --- | --- | --- | | [@aws-sdk/client-bedrock-runtime](https://github.com/aws/aws-sdk-js-v3/tree/HEAD/clients/client-bedrock-runtime) | `3.1081.0` | `3.1088.0` | | [@lobehub/icons](https://github.com/lobehub/lobe-icons) | `5.10.1` | `5.13.0` | | [fumadocs-core](https://github.com/fuma-nama/fumadocs) | `16.11.1` | `16.11.5` | | [fumadocs-mdx](https://github.com/fuma-nama/fumadocs) | `15.1.0` | `15.2.0` | | [fumadocs-ui](https://github.com/fuma-nama/fumadocs) | `16.11.1` | `16.11.5` | | [marked](https://github.com/markedjs/marked) | `18.0.5` | `18.0.6` | | [material-symbols](https://github.com/marella/material-symbols/tree/HEAD/material-symbols) | `0.45.6` | `0.45.8` | | [next-intl](https://github.com/amannn/next-intl) | `4.13.1` | `4.13.2` | | [omniglyph](https://github.com/diegosouzapw/OmniGlyph) | `1.0.2` | `1.3.1` | | [tsx](https://github.com/privatenumber/tsx) | `4.23.0` | `4.23.1` | | [ws](https://github.com/websockets/ws) | `8.21.0` | `8.21.1` | Updates `@aws-sdk/client-bedrock-runtime` from 3.1081.0 to 3.1088.0 - [Release notes](https://github.com/aws/aws-sdk-js-v3/releases) - [Changelog](https://github.com/aws/aws-sdk-js-v3/blob/main/clients/client-bedrock-runtime/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-js-v3/commits/v3.1088.0/clients/client-bedrock-runtime) Updates `@lobehub/icons` from 5.10.1 to 5.13.0 - [Release notes](https://github.com/lobehub/lobe-icons/releases) - [Changelog](https://github.com/lobehub/lobe-icons/blob/master/CHANGELOG.md) - [Commits](https://github.com/lobehub/lobe-icons/compare/v5.10.1...v5.13.0) Updates `fumadocs-core` from 16.11.1 to 16.11.5 - [Release notes](https://github.com/fuma-nama/fumadocs/releases) - [Commits](https://github.com/fuma-nama/fumadocs/compare/fumadocs@16.11.1...fumadocs@16.11.5) Updates `fumadocs-mdx` from 15.1.0 to 15.2.0 - [Release notes](https://github.com/fuma-nama/fumadocs/releases) - [Commits](https://github.com/fuma-nama/fumadocs/compare/fumadocs-mdx@15.1.0...fumadocs-mdx@15.2.0) Updates `fumadocs-ui` from 16.11.1 to 16.11.5 - [Release notes](https://github.com/fuma-nama/fumadocs/releases) - [Commits](https://github.com/fuma-nama/fumadocs/compare/fumadocs@16.11.1...fumadocs@16.11.5) Updates `lucide-react` from 1.23.0 to 1.24.0 - [Release notes](https://github.com/lucide-icons/lucide/releases) - [Commits](https://github.com/lucide-icons/lucide/commits/1.24.0/packages/lucide-react) Updates `marked` from 18.0.5 to 18.0.6 - [Release notes](https://github.com/markedjs/marked/releases) - [Commits](https://github.com/markedjs/marked/compare/v18.0.5...v18.0.6) Updates `material-symbols` from 0.45.6 to 0.45.8 - [Release notes](https://github.com/marella/material-symbols/releases) - [Commits](https://github.com/marella/material-symbols/commits/v0.45.8/material-symbols) Updates `next-intl` from 4.13.1 to 4.13.2 - [Release notes](https://github.com/amannn/next-intl/releases) - [Changelog](https://github.com/amannn/next-intl/blob/main/CHANGELOG.md) - [Commits](https://github.com/amannn/next-intl/compare/v4.13.1...v4.13.2) Updates `omniglyph` from 1.0.2 to 1.3.1 - [Release notes](https://github.com/diegosouzapw/OmniGlyph/releases) - [Changelog](https://github.com/diegosouzapw/OmniGlyph/blob/main/CHANGELOG.md) - [Commits](https://github.com/diegosouzapw/OmniGlyph/compare/v1.0.2...v1.3.1) Updates `tsx` from 4.23.0 to 4.23.1 - [Release notes](https://github.com/privatenumber/tsx/releases) - [Changelog](https://github.com/privatenumber/tsx/blob/master/release.config.cjs) - [Commits](https://github.com/privatenumber/tsx/compare/v4.23.0...v4.23.1) Updates `ws` from 8.21.0 to 8.21.1 - [Release notes](https://github.com/websockets/ws/releases) - [Commits](https://github.com/websockets/ws/compare/8.21.0...8.21.1) --- updated-dependencies: - dependency-name: "@aws-sdk/client-bedrock-runtime" dependency-version: 3.1088.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: production - dependency-name: "@lobehub/icons" dependency-version: 5.13.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: production - dependency-name: fumadocs-core dependency-version: 16.11.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: production - dependency-name: fumadocs-mdx dependency-version: 15.2.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: production - dependency-name: fumadocs-ui dependency-version: 16.11.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: production - dependency-name: lucide-react dependency-version: 1.24.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: production - dependency-name: marked dependency-version: 18.0.6 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: production - dependency-name: material-symbols dependency-version: 0.45.8 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: production - dependency-name: next-intl dependency-version: 4.13.2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: production - dependency-name: omniglyph dependency-version: 1.3.1 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: production - dependency-name: tsx dependency-version: 4.23.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: production - dependency-name: ws dependency-version: 8.21.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: production ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |