Skip to content

feat(v0.7.6): Dollar Savings Tracker, Provider Leaderboard, SSE Live …#9

Open
rsalmn wants to merge 1 commit into
mainfrom
v0.7.6
Open

feat(v0.7.6): Dollar Savings Tracker, Provider Leaderboard, SSE Live …#9
rsalmn wants to merge 1 commit into
mainfrom
v0.7.6

Conversation

@rsalmn

@rsalmn rsalmn commented Jul 25, 2026

Copy link
Copy Markdown
Owner

Features

  • Cline + ClinePass Quota Tracker: both providers now report plan usage limits (5-hour / weekly / monthly) as percentUsed in the Quota Tracker dashboard. Shared getClineUsage handler with OAuth/API-key auth fallback.
  • Breaker-Aware Combo Pre-Filter: combos now proactively skip models whose provider circuit breaker is OPEN before attempting them — saving a wasted credential-selection round-trip per broken model. New read-only isBreakerBlocking() check (does not consume the single HALF_OPEN probe slot) + filterBreakerOpenModels() helper. Falls back to the original model list if ALL are blocked (probe window may open during attempt).
  • Providers Page Redesign (list): full redesign — 5 collapsible sections replaced with a unified flat grid + filter chips (All / Connected / Errors / OAuth / API Key / Free / Cookie / Custom) + sort dropdown. New rich tiles (ProviderTile) with larger icons, connection counts, status badges, and an action bar (test + settings + toggle). KPI row (ProviderKpis) with interactive Errors tile. Toolbar (ProviderToolbar) with live count badges.
  • Provider Detail Page Redesign: God Component (1831 lines) split into 4 extracted components: ProviderDetailHeader (branded header), ConnectionsCard (toolbar + rows + bulk proxy modal), ModelsCard (toolbar + grid), CollapsibleSection (reusable wrapper). page.js reduced to a lean orchestrator (~960 lines). Visual polish: branded header with category summary, collapsible sections (Health default-collapsed), responsive model grid (grid-cols-3), progress-bar-style one-by-one test summary, compact pill toolbars.

Fixes — Combo Engine (28 bug fixes across 4 audit rounds)

  • C1 Critical — body_global race condition (swarm.js): removed module-level mutable state; body threaded explicitly to all stage runners. Concurrent swarm requests no longer clobber each other's prompt (cross-request leak).
  • C2 Critical — releaseBreakerProbe ReferenceError (circuitBreaker.js): monitorsbreakers — half-open probe slot now releases correctly; breaker no longer stuck open forever.
  • C1+H2 Critical — orphaned comboStrategies on rename/delete ([id]/route.js): patchComboStrategies now sends partial patches with { [key]: null } delete-signals compatible with the deep-merge in updateSettings.
  • H1 — Wrong key comboStickyLimit (chat.js): comboStickyLimitcomboStickyRoundRobinLimit. Round-robin fast-path now respects sticky limit config.
  • H2 — Lost-update race in handleSetComboStrategy (settingsRepo.js + CombosPageInner.js): backend deep-merges comboStrategies at combo-name level; UI sends only the changed entry.
  • H3 — ComboFormModal stale create state: reset local draft via useEffect watch on isOpen transition.
  • H4 — ComboFormModal closure-models bug: 3 handlers now use functional updates setModels(prev => ...).
  • H5 — PUT empty-string name bypass validation: if (body.name)if (body.name !== undefined) + non-empty check.
  • M1 — Non-array models crash: Array.isArray(models) validation in POST + PUT.
  • M2 — Case-sensitive strategy compare: normalizeStrategy() helper (trim+lowercase+whitelist).
  • M3 — Fusion single-survivor stream downgrade: re-run with stream flag preserved when body.stream === true.
  • M4 — ComboCard null-guard: combo.models normalized to [] defensively.
  • M5 — handleDelete silent failure: error feedback via alert().
  • M6 — Cross-strategy breaker pollution: skipBreaker opt for panel calls; fusion/swarm failures no longer trip shared per-provider breaker.
  • feat(v0.7.0): Combos redesign, v0.app rewrite, FreeBuff/v0 profiles, … #1 — Fusion single-answer re-run: return existing panel response instead of re-invoking model.
  • feat(v0.7.2): token saver full coverage + breakdown UI, Kiro 403 fix,… #3parseStrategy truncated JSON: lenient recovery salvages complete subtask objects via regex.
  • feat(v0.7.4): C1 auth bypass fix, Forge/TokenRouter/Qwen/Alibaba/hcns… #5workerCount config ignored: workerCount from UI now honored via workerCap.
  • L1-L9: getStrategyDistribution whitelist, PUT response canonical shape, stale role-field cleanup, ModelSelectModal highlight, VALID_NAME_REGEX dedup, ComboFormModal fetch cleanup.

Fixes — Code Review Feedback

  • GitHub /responses proactive routing (github.js): buildUrl() + execute() route targetFormat:"openai-responses" models to /responses proactively.
  • xAI unused U import (xai.js): dead code removed.
  • sanitize-html test (sanitize-html.test.js): rewritten as idiomatic vitest with it.each/expect + standalone-script fallback.

Improvements

  • Settings deep-merge (settingsRepo.js): comboStrategies merged at combo-name level (not replaced), with null as the delete-signal.
  • Swarm ALIAS_TO_ID map (combo.js): built from REGISTRY for breaker lookups without crossing the open-sse→src layer boundary.
  • Provider list unified entry array (providers/page.js): ONE flat array with category tags replaces 5 separate section arrays.

Summary by CodeRabbit

  • New Features

    • Added Dollar Savings tracking and a Provider Performance Leaderboard to the dashboard.
    • Added live dashboard updates with automatic reconnection.
    • Added persistent in-app notifications with unread counts and history.
    • Added Bynara, Infron, AgentRouter, and InxoraStudio providers.
    • Added new model discovery, tier options, usage reporting, and multi-format provider support.
  • Bug Fixes

    • Improved streaming reliability, fallback handling, validation, notifications, and provider-specific request processing.
    • Improved Responses API event, tool-call, and terminal-state handling.

…Dashboard, Notification System, Cross-Transport Fallback, 5 new providers, Kiro fix, Responses accumulator, dead code cleanup

Features:
- Dollar Savings Tracker (hero card + per-mechanism breakdown)
- Provider Performance Leaderboard (sortable, TTFT/P95/cost/success)
- SSE Live Dashboard (unified stream + auto-reconnect hook)
- In-App Notification System (bell + unread badge + history)
- Cross-Transport Fallback (OpenAI timeout -> Anthropic auto-retry)
- New providers: Bynara, InxoraStudio (API + Web), Infron AI, AgentRouter
- Updated: Command Code (standard API), Grok Web quota, Infron quota
- Quota trackers: Grok Web rate-limits, Infron credit balance

Fixes:
- Kiro REQUEST_BODY_INVALID (level suffix normalization)
- Responses API accumulator (alias-safe + exactly-once terminal)
- Antigravity model turn 400 + tier routing + Cloud Code endpoints
- Circuit breaker releaseBreakerProbe ReferenceError
- Combo stickyLimit key + comboStrategies deep-merge
- Notification spam (health_update filtered) + raw JSON + duplicates
- hcnsec model discovery + WEB_COOKIE_PROVIDERS modelsFetcher

Improvements:
- Dead code cleanup (12 files, 1,705 LOC)
- Junk dependency removed (fs placeholder)
- Authenticated model discovery proxy

Bumps package.json + cli/package.json to 0.7.6
@gemini-code-assist

Copy link
Copy Markdown

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@coderabbitai

coderabbitai Bot commented Jul 25, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Version 0.7.6 adds provider integrations, cross-transport fallback, Responses stream accumulation, Kiro thinking normalization, dashboard metrics and SSE updates, persistent notifications, authenticated model discovery, and release cleanup.

Changes

Core streaming and routing

Layer / File(s) Summary
Responses accumulation and Kiro model intent
open-sse/translator/..., open-sse/config/kiroConstants.js, open-sse/handlers/chatCore/streamingHandler.js
Responses events now share accumulated tool, message, usage, terminal, and abort state. Kiro model suffixes now control normalized effort and thinking overrides.
Provider transports and custom execution
open-sse/handlers/chatCore.js, open-sse/executors/..., open-sse/providers/registry/*
Providers gain OpenAI/Claude transport definitions, alternate transport retry, new provider registrations, and an InxoraStudio web SSE executor.

Dashboard and provider UI

Layer / File(s) Summary
Usage metrics and dashboard views
src/lib/db/repos/usageRepo.js, src/app/api/usage/*, src/app/(dashboard)/dashboard/overview/*
Usage persistence records dollar savings and latency data, while the dashboard displays savings and provider leaderboard metrics.
Live notifications
src/app/api/dashboard/stream/route.js, src/shared/hooks/useDashboardStream.js, src/shared/components/NotificationBell.js, src/store/notificationStore.js
Dashboard events stream over SSE, reconnecting clients refresh metrics, and server events become persisted notification history.
Authenticated discovery and profile support
src/app/api/providers/*, src/shared/utils/providerModelsFetcher.js, src/app/(dashboard)/dashboard/providers/*
Suggested-model requests can use connection credentials, InxoraStudio profiles are displayed and validated, and provider-specific discovery and icons are expanded.

Release maintenance

Layer / File(s) Summary
Release configuration and removed legacy UI
package.json, cli/package.json, CHANGELOG.md, src/app/(dashboard)/dashboard/providers/[id]/PassthroughModelsSection.js
Versions and development ports are updated, the obsolete fs dependency and legacy passthrough UI are removed, and the changelog records v0.7.6.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 56.86% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the main release features: savings tracking, provider leaderboard, and SSE live dashboard updates.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch v0.7.6

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 20

🧹 Nitpick comments (9)
src/lib/db/repos/usageRepo.js (1)

329-362: 🗄️ Data Integrity & Integration | 🔵 Trivial | 💤 Low value

Reuse the pricing lookup oncegetPricingForModel() is synchronous here, so the unawaited calls inside the transaction are fine. Resolve it once and reuse it in both savings blocks.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/db/repos/usageRepo.js` around lines 329 - 362, Resolve
getPricingForModel(entry.provider, entry.model) once before the overall savings
processing, then reuse that pricing value in both the total costSavedLifetime
block and the per-mechanism cost calculation. Remove the duplicate lookup
assigned to mechPricing while preserving the existing pricing checks and savings
updates.
open-sse/config/kiroConstants.js (2)

1-1: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Unclear/untested handling of the "none" thinking override. applyKiroThinkingOverride sets output_config.effort to the literal "none" for a (none)/(off) suffix, a value outside the documented effort enum, and no test confirms how this round-trips through extractThinking/resolveKiroThinkingBudget.

  • open-sse/config/kiroConstants.js#L296-313: confirm (or special-case, similar to the "budget" branch) how override.mode === "none" is meant to disable thinking without emitting a non-enum effort value.
  • tests/unit/kiro-thinking-normalization.test.js#L67-96: once the intended behavior is confirmed, add a test asserting applyKiroThinkingOverride(body, { mode: "none" }) produces the expected, budget-resolvable result.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@open-sse/config/kiroConstants.js` at line 1, Clarify and implement the
mode-none behavior in applyKiroThinkingOverride so disabling thinking does not
emit the unsupported effort value "none"; align it with the existing budget
handling and ensure extractThinking/resolveKiroThinkingBudget can resolve the
result correctly. Add coverage in the Kiro thinking normalization tests
asserting applyKiroThinkingOverride with mode "none" produces the intended
budget-resolvable body.

1-1: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Family classification duplicated in two places using two different mechanisms. resolveKiroEffortPath (regex) and PATTERN_THINKING (glob) both classify Claude-5/GPT-5.6 vs. everything else; keeping them in sync manually risks drift as new model families are added.

  • open-sse/config/kiroConstants.js#L254-263: keep as the single source of truth for family classification (and tighten the \b boundary per the earlier comment).
  • open-sse/providers/thinkingLevels.js#L29-38,54-69: drop PATTERN_THINKING's redundant glob re-match and return L.KIRO_NATIVE directly once resolveKiroEffortPath confirms the family, instead of re-deriving it via a second pattern list.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@open-sse/config/kiroConstants.js` at line 1, Use resolveKiroEffortPath as the
sole source of truth for model-family classification, tightening its
word-boundary matching as required. In the thinking-level resolution flow,
remove PATTERN_THINKING and its redundant glob matching; once
resolveKiroEffortPath confirms the supported family, return the KIRO_NATIVE
constant directly. Keep existing behavior for models that do not resolve to that
family.
open-sse/providers/thinkingLevels.js (1)

29-38: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicate family classification vs. resolveKiroEffortPath.

PATTERN_THINKING's glob patterns re-encode the same Claude-5/GPT-5.6 family classification that resolveKiroEffortPath (in kiroConstants.js) already computes via regex. getThinkingLevels calls both: first resolveKiroEffortPath to gate, then PATTERN_THINKING.find to pick levels — two independent lists that must stay in sync manually.

♻️ Proposed consolidation
 export function getThinkingLevels(provider, model) {
-  // Kiro gate FIRST: only Claude 5 / GPT-5.6 families advertise native levels.
-  // resolveKiroEffortPath returns null for everything else → hide the picker.
-  if (provider === "kiro" && resolveKiroEffortPath(model) === null) return null;
-
-  const caps = getCapabilitiesForModel(provider, model);
-  if (!caps.reasoning) return null;
-
-  // Pattern match for Kiro native families.
-  const hit = PATTERN_THINKING.find((p) => matchPattern(p.pattern, model));
-  if (hit) return hit.levels;
+  if (provider === "kiro") {
+    const path = resolveKiroEffortPath(model);
+    if (path === null) return null;
+    return L.KIRO_NATIVE;
+  }
+
+  const caps = getCapabilitiesForModel(provider, model);
+  if (!caps.reasoning) return null;

Also applies to: 54-69

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@open-sse/providers/thinkingLevels.js` around lines 29 - 38, Remove the
duplicated Claude-5/GPT-5.6 family patterns from PATTERN_THINKING and update
getThinkingLevels to derive the supported thinking levels from the result of
resolveKiroEffortPath. Preserve the existing gating and KIRO_NATIVE level
behavior while ensuring family classification has a single source of truth.
open-sse/handlers/chatCore.js (2)

440-447: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Dead AbortError branches.

AbortError already returned at line 430-433, so both error.name === "AbortError" ternaries here always evaluate to the BAD_GATEWAY/502 arm. Simplify to constants to avoid implying a reachable 499 path.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@open-sse/handlers/chatCore.js` around lines 440 - 447, In the failure logging
block after the early AbortError return, update the status in appendRequestLog
and the response.status in buildRequestDetail to use the constant
BAD_GATEWAY/502 values directly. Remove both unreachable error.name ===
"AbortError" ternaries while preserving the existing error message and
request-detail fields.

486-528: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Both branches duplicate the same ~15-line error-return block.

Lines 493-508 and 512-527 are byte-identical apart from trackPendingRequest ordering. Extract one local returnUpstreamError(statusCode, message, resetsAtMs) closure and call it from both branches so future changes to error logging/detail persistence can't diverge.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@open-sse/handlers/chatCore.js` around lines 486 - 528, Extract the duplicated
upstream error handling in the surrounding request flow into a local
returnUpstreamError(statusCode, message, resetsAtMs) closure, including
tracking, request logging, detail persistence, formatting, console logging, and
createErrorResult. Replace both the recovered=false 5xx/transport-failure path
and the 4xx path with calls to this closure, preserving their existing control
flow and parameters.
open-sse/utils/responsesStreamHelpers.js (1)

34-41: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Extract the stream_disconnected error payload into a shared constant.

The same { type, code, message } triple is now literal in both buildAbortedResponsesTerminalBytes and formatIncompleteOpenAIResponsesStreamFailure (lines 53-57); they can silently drift so the SSE payload and the accumulator's recorded error disagree.

♻️ Suggested consolidation
+const STREAM_DISCONNECTED_ERROR = {
+  type: "stream_error",
+  code: "stream_disconnected",
+  message: "stream closed before response.completed",
+};
+
 export function buildAbortedResponsesTerminalBytes(accumulator = null) {
   if (accumulator) {
     accumulator.finalize({
-      error: { type: "stream_error", code: "stream_disconnected", message: "stream closed before response.completed" },
+      error: { ...STREAM_DISCONNECTED_ERROR },
       status: "failed",
     });
   }

Then reuse STREAM_DISCONNECTED_ERROR inside formatIncompleteOpenAIResponsesStreamFailure.

As per coding guidelines, "never hardcode values" and keep the code DRY using constants from config/.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@open-sse/utils/responsesStreamHelpers.js` around lines 34 - 41, Extract the
shared `{ type: "stream_error", code: "stream_disconnected", message: "stream
closed before response.completed" }` payload into a named constant in the
appropriate config/constants module, then reuse that constant in both
`buildAbortedResponsesTerminalBytes` and
`formatIncompleteOpenAIResponsesStreamFailure` so the accumulator error and SSE
response remain identical.

Source: Coding guidelines

open-sse/executors/inxorastudio-web.js (1)

58-131: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Add a request timeout to both upstream calls.

Only signal is forwarded; a hung labs.inxorastudio.com response blocks the request path indefinitely. Compose the caller signal with a timeout (AbortSignal.any([signal, AbortSignal.timeout(ms)])) using the shared timeout constant.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@open-sse/executors/inxorastudio-web.js` around lines 58 - 131, Update
execute’s upstream fetch calls, including the conversation creation request and
the later generation request, to compose the caller’s signal with
AbortSignal.timeout using the shared timeout constant. Pass the composed signal
to proxyAwareFetch while preserving caller cancellation and existing AbortError
handling.
tests/unit/responses-accumulator.test.js (1)

118-132: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add coverage for the terminal-snapshot merge path.

Current suite never exercises response.completed carrying an output array after deltas, nor a function_call item without an arguments field — both are where _ingestFullItem misbehaves today (clobbered argsBuffer, '""' arguments). Also worth covering finalize() exactly-once and preferComplete disagreement between deltas and snapshot.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/unit/responses-accumulator.test.js` around lines 118 - 132, Extend the
response.completed tests around ResponsesAccumulator to cover merging a terminal
output snapshot after prior deltas, including function_call items without
arguments, preserving buffered arguments and avoiding synthesized empty-string
arguments. Add assertions that finalize() is idempotent and that preferComplete
resolves disagreements between delta data and the terminal snapshot as intended.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@open-sse/executors/inxorastudio-web.js`:
- Around line 24-26: Update the inxorastudio-web executor to remove hardcoded
API_BASE, USER_AGENT, default model, and request mode values. Source the host
from the inxorastudio-web registry transport.baseUrl, the model from
config/providerModels.js, and the user-agent and mode from the existing
config/schema constants; reuse those values at the affected request and payload
construction points while preserving current behavior.
- Around line 245-251: Update the usage guard in the assistant token handling
block to use explicit presence checks for inputTokens and outputTokens rather
than truthiness, so zero-valued counts still populate finalChunk.usage. Preserve
the existing prompt_tokens, completion_tokens, and total_tokens assignments.
- Around line 258-263: Update the stream lifecycle around the catch/finally
handling in the executor’s start flow: do not enqueue SSE_DONE or close the
controller after controller.error(err), and ensure the upstream reader is
released or cancelled when the abort path breaks. Preserve normal completion
behavior by sending SSE_DONE and closing only while the controller remains
usable, without masking the original error.

In `@open-sse/handlers/chatCore.js`:
- Around line 375-393: Restore credentials.runtimeTransport to originalTransport
in the alternate non-OK branch before returning false, matching the catch-path
cleanup. Also consume or cancel altResult.response.body there so the failed
alternate response does not retain its socket.

In `@open-sse/providers/registry/agentrouter.js`:
- Line 43: Update the comment above the multi-endpoint configuration to
accurately describe the Claude base URL as the full /v1/messages endpoint, and
remove the claim that the SDK appends /v1/messages. Leave the endpoint
configuration unchanged.

In `@open-sse/providers/registry/bynara.js`:
- Around line 26-30: Remove the referral query parameter from both signupUrl and
apiKeyUrl in the notice configuration, leaving the links pointed at the plain
Bynara registration URL. Keep the existing notice text and other provider
settings unchanged.

In `@open-sse/providers/registry/glm.js`:
- Around line 36-39: Restore the GLM Coding Plan endpoint in the registry
entry’s baseUrl to /api/coding/paas/v4/chat/completions. Do not route
coding-plan keys through the standard /api/paas/v4 endpoint; if supporting both
key types, branch explicitly by key type while preserving the coding-plan URL.

In `@open-sse/translator/concerns/responsesAccumulator.js`:
- Around line 437-442: Update toolCallIndexFor to deduplicate aliases by their
underlying tool-entry identity before calculating the index, so equivalent
item_id and output_index references resolve to the same position. Preserve the
existing fallback of toolKeys.length when resolution fails, and ensure the index
used by openai-responses.js remains stable across header and delta events.
- Around line 277-299: Update _ingestFullItem to preserve an existing streamed
tool entry instead of overwriting it: reuse the entry, apply the existing
preferComplete behavior, and ensure _toolsByItemId and _toolsByCallId aliases
are registered for late deltas and lookups. When initializing arguments from a
full item with no arguments, use an empty string rather than JSON-stringifying a
fallback that produces "\"\""; retain valid streamed arguments and existing
message handling.

In `@open-sse/translator/response/openai-responses.js`:
- Around line 524-541: Update the terminal-event handling around
computeFinishReason so response.incomplete produces a normal terminal chunk
without injected error content, using computeFinishReason to preserve the LENGTH
mapping and including available usage. Restrict the existing error chunk with
STOP and error text to error, response.failed, and response.cancelled, while
preserving the exactly-once finish guard.
- Around line 487-494: Update the response.output_item.done handling in the
function-call branch to emit the complete arguments from data.item when no
argument deltas were streamed for that item, using state.argsStreamed as the
guard. Preserve exactly-once behavior by checking acc.isToolCallEmitted and
marking the item emitted after emitting or confirming the existing streamed
path; do not return null before the done-only arguments can reach the client.

In `@src/app/`(dashboard)/dashboard/overview/components/LeaderboardTable.js:
- Around line 49-56: Update the leaderboard fetch effect keyed by period to
avoid calling setLoading(true) directly in the effect body, while preserving
loading during the request and the existing setLoading(false) cleanup behavior.
Use an established lint-compliant state or fetch pattern without changing the
data/error handling flow.

In `@src/app/`(dashboard)/dashboard/providers/[id]/InxoraProfile.js:
- Line 17: Update the response returned by the inxora-profile API route to omit
the apiKey field before serialization, while preserving the profile data
consumed by InxoraProfile. Locate the response construction in the route handler
for /api/providers/[id]/inxora-profile and remove only the credential from its
returned payload.
- Around line 18-24: Update the profile-loading flow around the response check
so loading always finishes when the fetch response is unsuccessful, including
401/404/500 responses. Ensure the existing cancellation guard is preserved and
the setLoading(false) call in the InxoraProfile effect remains reachable before
returning from the failed-response path.

In `@src/app/`(dashboard)/dashboard/providers/[id]/page.js:
- Around line 436-441: Update the useEffect that invokes fetchSuggestedModels to
clear suggestedModels when no modelsFetcher exists, and guard asynchronous
results with an invalidation or cancellation mechanism so only the latest
provider/connection request can call setSuggestedModels. Ensure cleanup
invalidates the previous request when the effect reruns or unmounts.

In `@src/app/api/providers/`[id]/inxora-profile/route.js:
- Around line 9-48: Update GET to authenticate the caller and retrieve the
provider connection through an owner-scoped lookup, rejecting unauthenticated or
unauthorized requests before contacting the upstream API. In the response
construction around data.user, remove u.apiKey and return only a masked or
non-secret indicator if required by the UI; preserve the existing profile fields
and status handling.
- Around line 19-26: Update the upstream fetch in the inxora profile route to
use an AbortController with a configurable, bounded timeout, ensuring the timer
is cleaned up after completion. Catch aborts caused by this deadline and return
the route’s controlled upstream-timeout error while preserving existing handling
for other fetch failures.

In `@src/app/api/providers/suggested-models/route.js`:
- Around line 40-65: Update the suggested-models route’s connectionId/url
handling to avoid sending stored credentials to caller-controlled destinations:
authenticate and authorize the connection owner, resolve the models URL and
filter type from the trusted provider configuration, and validate the resolved
destination against its configured allowlist before fetching. Reject or omit
caller-supplied destinations that do not match the authorized configuration,
while preserving unauthenticated behavior only where no connection is selected.

In `@src/app/api/providers/validate/route.js`:
- Around line 642-649: Add a timeout to the upstream fetch in the Inxora
validation branch, using the handler’s existing bounded-probe pattern and
timeout configuration where available. Update the fetch options around the
Inxora validation request while preserving its method and headers, ensuring
stalled requests abort rather than holding validation indefinitely.

In `@src/shared/components/NotificationBell.js`:
- Around line 33-53: Align NOTIFIABLE_TYPES in the NotificationBell effect with
the event types emitted by /api/dashboard/stream: remove rate_limited,
budget_exceeded, and health_degraded unless the stream is updated to forward
them. Ensure the set and related filtering reflect the actual SSE types,
preserving provider_down and provider_recovered handling.

---

Nitpick comments:
In `@open-sse/config/kiroConstants.js`:
- Line 1: Clarify and implement the mode-none behavior in
applyKiroThinkingOverride so disabling thinking does not emit the unsupported
effort value "none"; align it with the existing budget handling and ensure
extractThinking/resolveKiroThinkingBudget can resolve the result correctly. Add
coverage in the Kiro thinking normalization tests asserting
applyKiroThinkingOverride with mode "none" produces the intended
budget-resolvable body.
- Line 1: Use resolveKiroEffortPath as the sole source of truth for model-family
classification, tightening its word-boundary matching as required. In the
thinking-level resolution flow, remove PATTERN_THINKING and its redundant glob
matching; once resolveKiroEffortPath confirms the supported family, return the
KIRO_NATIVE constant directly. Keep existing behavior for models that do not
resolve to that family.

In `@open-sse/executors/inxorastudio-web.js`:
- Around line 58-131: Update execute’s upstream fetch calls, including the
conversation creation request and the later generation request, to compose the
caller’s signal with AbortSignal.timeout using the shared timeout constant. Pass
the composed signal to proxyAwareFetch while preserving caller cancellation and
existing AbortError handling.

In `@open-sse/handlers/chatCore.js`:
- Around line 440-447: In the failure logging block after the early AbortError
return, update the status in appendRequestLog and the response.status in
buildRequestDetail to use the constant BAD_GATEWAY/502 values directly. Remove
both unreachable error.name === "AbortError" ternaries while preserving the
existing error message and request-detail fields.
- Around line 486-528: Extract the duplicated upstream error handling in the
surrounding request flow into a local returnUpstreamError(statusCode, message,
resetsAtMs) closure, including tracking, request logging, detail persistence,
formatting, console logging, and createErrorResult. Replace both the
recovered=false 5xx/transport-failure path and the 4xx path with calls to this
closure, preserving their existing control flow and parameters.

In `@open-sse/providers/thinkingLevels.js`:
- Around line 29-38: Remove the duplicated Claude-5/GPT-5.6 family patterns from
PATTERN_THINKING and update getThinkingLevels to derive the supported thinking
levels from the result of resolveKiroEffortPath. Preserve the existing gating
and KIRO_NATIVE level behavior while ensuring family classification has a single
source of truth.

In `@open-sse/utils/responsesStreamHelpers.js`:
- Around line 34-41: Extract the shared `{ type: "stream_error", code:
"stream_disconnected", message: "stream closed before response.completed" }`
payload into a named constant in the appropriate config/constants module, then
reuse that constant in both `buildAbortedResponsesTerminalBytes` and
`formatIncompleteOpenAIResponsesStreamFailure` so the accumulator error and SSE
response remain identical.

In `@src/lib/db/repos/usageRepo.js`:
- Around line 329-362: Resolve getPricingForModel(entry.provider, entry.model)
once before the overall savings processing, then reuse that pricing value in
both the total costSavedLifetime block and the per-mechanism cost calculation.
Remove the duplicate lookup assigned to mechPricing while preserving the
existing pricing checks and savings updates.

In `@tests/unit/responses-accumulator.test.js`:
- Around line 118-132: Extend the response.completed tests around
ResponsesAccumulator to cover merging a terminal output snapshot after prior
deltas, including function_call items without arguments, preserving buffered
arguments and avoiding synthesized empty-string arguments. Add assertions that
finalize() is idempotent and that preferComplete resolves disagreements between
delta data and the terminal snapshot as intended.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 4ce95746-e821-4bba-85a3-0b1584b95964

📥 Commits

Reviewing files that changed from the base of the PR and between 5c02220 and a06a4e6.

⛔ Files ignored due to path filters (5)
  • public/providers/agentrouter.png is excluded by !**/*.png
  • public/providers/bynara.svg is excluded by !**/*.svg
  • public/providers/infron.svg is excluded by !**/*.svg
  • public/providers/inxorastudio-web.svg is excluded by !**/*.svg
  • public/providers/inxorastudio.svg is excluded by !**/*.svg
📒 Files selected for processing (76)
  • CHANGELOG.md
  • cli/package.json
  • open-sse/config/appConstants.js
  • open-sse/config/kiroConstants.js
  • open-sse/config/providerModels.js
  • open-sse/executors/antigravity.js
  • open-sse/executors/index.js
  • open-sse/executors/inxorastudio-web.js
  • open-sse/handlers/chatCore.js
  • open-sse/handlers/chatCore/streamingHandler.js
  • open-sse/handlers/responsesHandler.js
  • open-sse/providers/registry/agentrouter.js
  • open-sse/providers/registry/antigravity.js
  • open-sse/providers/registry/bynara.js
  • open-sse/providers/registry/commandcode.js
  • open-sse/providers/registry/glm.js
  • open-sse/providers/registry/grok-web.js
  • open-sse/providers/registry/hcnsec.js
  • open-sse/providers/registry/index.js
  • open-sse/providers/registry/infron.js
  • open-sse/providers/registry/inxorastudio-web.js
  • open-sse/providers/registry/inxorastudio.js
  • open-sse/providers/thinkingLevels.js
  • open-sse/services/projectId.js
  • open-sse/services/provider.js
  • open-sse/services/usage.js
  • open-sse/services/usage/grok-web.js
  • open-sse/services/usage/infron.js
  • open-sse/transformer/responsesTransformer.js
  • open-sse/transformer/streamToJsonConverter.js
  • open-sse/translator/concerns/responsesAccumulator.js
  • open-sse/translator/formats/responsesApi.js
  • open-sse/translator/request/claude-to-kiro.js
  • open-sse/translator/request/openai-to-kiro.js
  • open-sse/translator/response/openai-responses.js
  • open-sse/utils/responsesStreamHelpers.js
  • open-sse/utils/stream.js
  • open-sse/utils/tlsImpersonate.js
  • package.json
  • scripts/test-combo-autoswitch.mjs
  • src/app/(dashboard)/dashboard/combos/components/ComboTemplates.js
  • src/app/(dashboard)/dashboard/overview/OverviewClient.js
  • src/app/(dashboard)/dashboard/overview/components/LeaderboardTable.js
  • src/app/(dashboard)/dashboard/overview/components/SavingsCard.js
  • src/app/(dashboard)/dashboard/providers/[id]/InxoraProfile.js
  • src/app/(dashboard)/dashboard/providers/[id]/ModelRow.js
  • src/app/(dashboard)/dashboard/providers/[id]/PassthroughModelsSection.js
  • src/app/(dashboard)/dashboard/providers/[id]/components/ConnectionsCard.js
  • src/app/(dashboard)/dashboard/providers/[id]/page.js
  • src/app/(dashboard)/dashboard/providers/components/ProviderCardV2.js
  • src/app/(dashboard)/dashboard/providers/components/ProviderSection.js
  • src/app/(dashboard)/dashboard/providers/components/ProviderSummary.js
  • src/app/(dashboard)/dashboard/quota/components/ProviderLimits/ProviderLimitCard.js
  • src/app/(dashboard)/dashboard/quota/components/ProviderLimits/utils.js
  • src/app/api/dashboard/stream/route.js
  • src/app/api/providers/[id]/inxora-profile/route.js
  • src/app/api/providers/client/route.js
  • src/app/api/providers/suggested-models/filters.js
  • src/app/api/providers/suggested-models/route.js
  • src/app/api/providers/validate/route.js
  • src/app/api/usage/leaderboard/route.js
  • src/app/api/usage/meta/route.js
  • src/lib/db/repos/usageRepo.js
  • src/lib/oauth/utils/banner.js
  • src/shared/components/Header.js
  • src/shared/components/NotificationBell.js
  • src/shared/hooks/useDashboardStream.js
  • src/shared/utils/providerIcon.js
  • src/shared/utils/providerModelsFetcher.js
  • src/sse/handlers/chat.js
  • src/sse/services/tokenRefresh.js
  • src/store/notificationStore.js
  • src/store/settingsStore.js
  • tests/translator/real/nvidia-thinking.e2e.test.js
  • tests/unit/kiro-thinking-normalization.test.js
  • tests/unit/responses-accumulator.test.js
💤 Files with no reviewable changes (11)
  • src/app/(dashboard)/dashboard/quota/components/ProviderLimits/ProviderLimitCard.js
  • open-sse/utils/tlsImpersonate.js
  • src/app/(dashboard)/dashboard/providers/components/ProviderSummary.js
  • src/lib/oauth/utils/banner.js
  • src/app/(dashboard)/dashboard/providers/components/ProviderSection.js
  • src/store/settingsStore.js
  • open-sse/handlers/responsesHandler.js
  • src/app/(dashboard)/dashboard/combos/components/ComboTemplates.js
  • src/app/(dashboard)/dashboard/providers/components/ProviderCardV2.js
  • src/app/(dashboard)/dashboard/providers/[id]/PassthroughModelsSection.js
  • open-sse/transformer/responsesTransformer.js

Comment on lines +24 to +26
const API_BASE = "https://labs.inxorastudio.com";
const USER_AGENT =
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/150.0.0.0 Safari/537.36";

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Move host, User-Agent, default model, and request mode into config/ and the provider registry.

API_BASE, USER_AGENT, the default model "ixlabs/gpt-5.5", and mode: "deep" are hardcoded in the executor. Source them from config/ (and the inxorastudio-web registry transport.baseUrl / config/providerModels.js) so the endpoint and catalog stay config-driven like the other providers.

As per coding guidelines, "Keep the code config-driven and DRY; use camelCase, and never hardcode values, models, or block/role strings. Use constants and schemas from config/ and translator/schema/."

Also applies to: 89-89, 132-140

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@open-sse/executors/inxorastudio-web.js` around lines 24 - 26, Update the
inxorastudio-web executor to remove hardcoded API_BASE, USER_AGENT, default
model, and request mode values. Source the host from the inxorastudio-web
registry transport.baseUrl, the model from config/providerModels.js, and the
user-agent and mode from the existing config/schema constants; reuse those
values at the affected request and payload construction points while preserving
current behavior.

Source: Coding guidelines

Comment on lines +245 to +251
if (assistant?.inputTokens && assistant?.outputTokens) {
finalChunk.usage = {
prompt_tokens: assistant.inputTokens,
completion_tokens: assistant.outputTokens,
total_tokens: assistant.tokens || (assistant.inputTokens + assistant.outputTokens),
};
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Usage is dropped when either token count is 0.

Truthiness check hides legitimate zero values and skips usage entirely. Prefer explicit presence checks.

🐛 Proposed fix
-                  if (assistant?.inputTokens && assistant?.outputTokens) {
+                  if (Number.isFinite(assistant?.inputTokens) && Number.isFinite(assistant?.outputTokens)) {
                     finalChunk.usage = {
                       prompt_tokens: assistant.inputTokens,
                       completion_tokens: assistant.outputTokens,
-                      total_tokens: assistant.tokens || (assistant.inputTokens + assistant.outputTokens),
+                      total_tokens: assistant.tokens ?? (assistant.inputTokens + assistant.outputTokens),
                     };
                   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (assistant?.inputTokens && assistant?.outputTokens) {
finalChunk.usage = {
prompt_tokens: assistant.inputTokens,
completion_tokens: assistant.outputTokens,
total_tokens: assistant.tokens || (assistant.inputTokens + assistant.outputTokens),
};
}
if (Number.isFinite(assistant?.inputTokens) && Number.isFinite(assistant?.outputTokens)) {
finalChunk.usage = {
prompt_tokens: assistant.inputTokens,
completion_tokens: assistant.outputTokens,
total_tokens: assistant.tokens ?? (assistant.inputTokens + assistant.outputTokens),
};
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@open-sse/executors/inxorastudio-web.js` around lines 245 - 251, Update the
usage guard in the assistant token handling block to use explicit presence
checks for inputTokens and outputTokens rather than truthiness, so zero-valued
counts still populate finalChunk.usage. Preserve the existing prompt_tokens,
completion_tokens, and total_tokens assignments.

Comment on lines +258 to +263
} catch (err) {
if (!signal?.aborted) controller.error(err);
} finally {
controller.enqueue(encoder.encode(SSE_DONE));
controller.close();
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win

finally block enqueues/closes after controller.error() → throws inside start().

Once controller.error(err) runs, the controller is errored: the subsequent controller.enqueue(SSE_DONE) and controller.close() both throw TypeError, producing an unhandled rejection from start() and masking the original error. The upstream reader is also never released/cancelled on the abort break path.

🐛 Proposed fix
-        } catch (err) {
-          if (!signal?.aborted) controller.error(err);
-        } finally {
-          controller.enqueue(encoder.encode(SSE_DONE));
-          controller.close();
-        }
+        } catch (err) {
+          if (!signal?.aborted) {
+            try { reader.cancel(); } catch { /* noop */ }
+            reader.releaseLock();
+            controller.error(err);
+            return;
+          }
+        }
+        try { await reader.cancel(); } catch { /* noop */ }
+        reader.releaseLock();
+        try {
+          controller.enqueue(encoder.encode(SSE_DONE));
+          controller.close();
+        } catch { /* controller already closed */ }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
} catch (err) {
if (!signal?.aborted) controller.error(err);
} finally {
controller.enqueue(encoder.encode(SSE_DONE));
controller.close();
}
} catch (err) {
if (!signal?.aborted) {
try { reader.cancel(); } catch { /* noop */ }
reader.releaseLock();
controller.error(err);
return;
}
}
try { await reader.cancel(); } catch { /* noop */ }
reader.releaseLock();
try {
controller.enqueue(encoder.encode(SSE_DONE));
controller.close();
} catch { /* controller already closed */ }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@open-sse/executors/inxorastudio-web.js` around lines 258 - 263, Update the
stream lifecycle around the catch/finally handling in the executor’s start flow:
do not enqueue SSE_DONE or close the controller after controller.error(err), and
ensure the upstream reader is released or cancelled when the abort path breaks.
Preserve normal completion behavior by sending SSE_DONE and closing only while
the controller remains usable, without masking the original error.

Comment on lines +375 to +393
if (altResult.response?.ok) {
providerResponse = altResult.response;
providerUrl = altResult.url;
providerHeaders = altResult.headers;
finalBody = altResult.transformedBody;
executorRetryCount += altResult.retryCount || 0;
return true;
} else {
// M1 FIX: Log the alternate's failure status so operators can debug
// "both endpoints down" scenarios instead of seeing only the primary error.
log?.warn?.("TRANSPORT", `${provider.toUpperCase()} | alternate ${altTransport.format} also failed (HTTP ${altResult.response.status})`);
}
} catch (altErr) {
// M1 FIX: Log the alternate's exception instead of swallowing it silently.
log?.warn?.("TRANSPORT", `${provider.toUpperCase()} | alternate ${altTransport.format} threw: ${altErr?.message || altErr}`);
// Restore original transport so downstream retry paths use the correct endpoint.
credentials.runtimeTransport = originalTransport;
}
return false;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

credentials.runtimeTransport is not restored when the alternate returns a non-OK response.

The catch branch restores originalTransport, but the non-OK branch (lines 382-386) returns false with credentials.runtimeTransport still pointing at the alternate. Since credentials is a caller-owned object (refreshedCredentials in src/sse/handlers/chat.js), any later use of it executes against the alternate endpoint while the body is still in primary format — exactly the hazard the comment on lines 353-356 describes. Also, the alternate's non-OK body is never consumed or cancelled, which holds the socket until GC.

🛠️ Proposed fix
     try {
       const altResult = await executor.execute({ model, body: altTranslatedBody, stream, credentials, signal: altController.signal, log, proxyOptions });
       if (altResult.response?.ok) {
         providerResponse = altResult.response;
         providerUrl = altResult.url;
         providerHeaders = altResult.headers;
         finalBody = altResult.transformedBody;
         executorRetryCount += altResult.retryCount || 0;
         return true;
-      } else {
-        // M1 FIX: Log the alternate's failure status so operators can debug
-        // "both endpoints down" scenarios instead of seeing only the primary error.
-        log?.warn?.("TRANSPORT", `${provider.toUpperCase()} | alternate ${altTransport.format} also failed (HTTP ${altResult.response.status})`);
       }
+      log?.warn?.("TRANSPORT", `${provider.toUpperCase()} | alternate ${altTransport.format} also failed (HTTP ${altResult.response?.status ?? "no response"})`);
+      try { await altResult.response?.body?.cancel?.(); } catch { /* ignore */ }
+      credentials.runtimeTransport = originalTransport;
     } catch (altErr) {
-      // M1 FIX: Log the alternate's exception instead of swallowing it silently.
       log?.warn?.("TRANSPORT", `${provider.toUpperCase()} | alternate ${altTransport.format} threw: ${altErr?.message || altErr}`);
-      // Restore original transport so downstream retry paths use the correct endpoint.
       credentials.runtimeTransport = originalTransport;
     }
     return false;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (altResult.response?.ok) {
providerResponse = altResult.response;
providerUrl = altResult.url;
providerHeaders = altResult.headers;
finalBody = altResult.transformedBody;
executorRetryCount += altResult.retryCount || 0;
return true;
} else {
// M1 FIX: Log the alternate's failure status so operators can debug
// "both endpoints down" scenarios instead of seeing only the primary error.
log?.warn?.("TRANSPORT", `${provider.toUpperCase()} | alternate ${altTransport.format} also failed (HTTP ${altResult.response.status})`);
}
} catch (altErr) {
// M1 FIX: Log the alternate's exception instead of swallowing it silently.
log?.warn?.("TRANSPORT", `${provider.toUpperCase()} | alternate ${altTransport.format} threw: ${altErr?.message || altErr}`);
// Restore original transport so downstream retry paths use the correct endpoint.
credentials.runtimeTransport = originalTransport;
}
return false;
if (altResult.response?.ok) {
providerResponse = altResult.response;
providerUrl = altResult.url;
providerHeaders = altResult.headers;
finalBody = altResult.transformedBody;
executorRetryCount += altResult.retryCount || 0;
return true;
}
log?.warn?.("TRANSPORT", `${provider.toUpperCase()} | alternate ${altTransport.format} also failed (HTTP ${altResult.response?.status ?? "no response"})`);
try { await altResult.response?.body?.cancel?.(); } catch { /* ignore */ }
credentials.runtimeTransport = originalTransport;
} catch (altErr) {
log?.warn?.("TRANSPORT", `${provider.toUpperCase()} | alternate ${altTransport.format} threw: ${altErr?.message || altErr}`);
credentials.runtimeTransport = originalTransport;
}
return false;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@open-sse/handlers/chatCore.js` around lines 375 - 393, Restore
credentials.runtimeTransport to originalTransport in the alternate non-OK branch
before returning false, matching the catch-path cleanup. Also consume or cancel
altResult.response.body there so the failed alternate response does not retain
its socket.

scheme: "bearer",
},
},
// Multi-endpoint: OpenAI (/v1) + Anthropic (root — SDK appends /v1/messages).

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Stale comment: the Claude baseUrl is not a root URL.

Line 52 already carries the full /v1/messages path, so "root — SDK appends /v1/messages" is misleading.

📝 Proposed fix
-  // Multi-endpoint: OpenAI (/v1) + Anthropic (root — SDK appends /v1/messages).
+  // Multi-endpoint: OpenAI (/v1/chat/completions) + Anthropic (/v1/messages).
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Multi-endpoint: OpenAI (/v1) + Anthropic (root — SDK appends /v1/messages).
// Multi-endpoint: OpenAI (/v1/chat/completions) + Anthropic (/v1/messages).
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@open-sse/providers/registry/agentrouter.js` at line 43, Update the comment
above the multi-endpoint configuration to accurately describe the Claude base
URL as the full /v1/messages endpoint, and remove the claim that the SDK appends
/v1/messages. Leave the endpoint configuration unchanged.

Comment on lines +9 to +48
export async function GET(_request, { params }) {
try {
const { id } = await params;
const connection = await getProviderConnectionById(id);
if (!connection) {
return NextResponse.json({ error: "Connection not found" }, { status: 404 });
}

let token = (connection.apiKey || "").replace(/^Bearer\s+/i, "").replace(/^cookie:\s*/i, "").trim();

const res = await fetch("https://labs.inxorastudio.com/api/auth/me", {
method: "GET",
headers: {
Authorization: `Bearer ${token}`,
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/150.0.0.0 Safari/537.36",
Referer: "https://labs.inxorastudio.com/dashboard",
},
});

if (res.status === 401 || res.status === 403) {
return NextResponse.json({ error: "Session expired" }, { status: 401 });
}

if (!res.ok) {
return NextResponse.json({ error: `InxoraStudio returned ${res.status}` }, { status: res.status });
}

const data = await res.json();
if (!data?.user) {
return NextResponse.json({ error: "No user in response" }, { status: 401 });
}

const u = data.user;
return NextResponse.json({
name: u.name || u.email?.split("@")[0] || "User",
email: u.email || "",
plan: u.plan || "FREE",
apiKey: u.apiKey || "",
isActive: u.isActive !== false,
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🔴 Critical | ⚡ Quick win

Enforce connection ownership and do not return u.apiKey.

This handler has no authentication/ownership check and serializes the upstream API key to the client. A caller with another connection ID can obtain that connection’s profile PII and credential. Authenticate the caller, scope the connection lookup to its owner, and return only a masked/non-secret key indicator if the UI needs one.

As per coding guidelines: “Protect API keys and secrets; implement authentication, authorization, input validation, rate limiting, secure headers, environment-variable usage, and secrets management. Never expose secrets.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/app/api/providers/`[id]/inxora-profile/route.js around lines 9 - 48,
Update GET to authenticate the caller and retrieve the provider connection
through an owner-scoped lookup, rejecting unauthenticated or unauthorized
requests before contacting the upstream API. In the response construction around
data.user, remove u.apiKey and return only a masked or non-secret indicator if
required by the UI; preserve the existing profile fields and status handling.

Source: Coding guidelines

Comment on lines +19 to +26
const res = await fetch("https://labs.inxorastudio.com/api/auth/me", {
method: "GET",
headers: {
Authorization: `Bearer ${token}`,
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/150.0.0.0 Safari/537.36",
Referer: "https://labs.inxorastudio.com/dashboard",
},
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Add a bounded timeout to the upstream profile request.

A stalled InxoraStudio response can hold this route open indefinitely. Attach a configurable abort deadline and return a controlled upstream-timeout error.

As per coding guidelines: “Consider response speed, memory usage, bundle size, lazy loading, caching, parallel requests, and streaming; identify bottlenecks before implementation.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/app/api/providers/`[id]/inxora-profile/route.js around lines 19 - 26,
Update the upstream fetch in the inxora profile route to use an AbortController
with a configurable, bounded timeout, ensuring the timer is cleaned up after
completion. Catch aborts caused by this deadline and return the route’s
controlled upstream-timeout error while preserving existing handling for other
fetch failures.

Source: Coding guidelines

Comment on lines +40 to +65
if (connectionId) {
try {
const connection = await getProviderConnectionById(connectionId);
if (!connection) {
return NextResponse.json({ error: "Connection not found" }, { status: 404 });
}
const token = connection.apiKey || connection.accessToken;
if (token) {
authHeader = `Bearer ${token}`;
}
} catch {
// Fall through — try unauthenticated fetch
}
}

if (!url) {
return NextResponse.json({ error: "Missing url" }, { status: 400 });
}

try {
const res = await fetch(url);
const headers = { Accept: "application/json" };
if (authHeader) headers.Authorization = authHeader;

const res = await fetch(url, {
headers,
signal: AbortSignal.timeout(12000),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🔴 Critical | 🏗️ Heavy lift

Do not forward connection credentials to a caller-controlled URL.

connectionId selects a stored token while url selects its destination. A request with an attacker URL can exfiltrate that token in Authorization and can also probe internal URLs. Resolve the models URL and filter type server-side from trusted provider configuration after loading the connection; enforce connection ownership and reject destinations outside the configured allowlist.

As per coding guidelines, “Protect API keys and secrets; implement authentication, authorization, input validation…”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/app/api/providers/suggested-models/route.js` around lines 40 - 65, Update
the suggested-models route’s connectionId/url handling to avoid sending stored
credentials to caller-controlled destinations: authenticate and authorize the
connection owner, resolve the models URL and filter type from the trusted
provider configuration, and validate the resolved destination against its
configured allowlist before fetching. Reject or omit caller-supplied
destinations that do not match the authorized configuration, while preserving
unauthenticated behavior only where no connection is selected.

Source: Coding guidelines

Comment on lines +642 to +649
const res = await fetch("https://labs.inxorastudio.com/api/auth/me", {
method: "GET",
headers: {
Authorization: `Bearer ${token}`,
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/150.0.0.0 Safari/537.36",
Referer: "https://labs.inxorastudio.com/dashboard",
},
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Bound the Inxora validation request.

This new upstream call has no timeout, so a stalled provider can indefinitely hold validation work. Match the bounded probes elsewhere in this handler.

Proposed fix
             const res = await fetch("https://labs.inxorastudio.com/api/auth/me", {
               method: "GET",
+              signal: AbortSignal.timeout(8000),
               headers: {

As per coding guidelines, external calls should be resilient and safe.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const res = await fetch("https://labs.inxorastudio.com/api/auth/me", {
method: "GET",
headers: {
Authorization: `Bearer ${token}`,
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/150.0.0.0 Safari/537.36",
Referer: "https://labs.inxorastudio.com/dashboard",
},
});
const res = await fetch("https://labs.inxorastudio.com/api/auth/me", {
method: "GET",
signal: AbortSignal.timeout(8000),
headers: {
Authorization: `Bearer ${token}`,
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/150.0.0.0 Safari/537.36",
Referer: "https://labs.inxorastudio.com/dashboard",
},
});
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/app/api/providers/validate/route.js` around lines 642 - 649, Add a
timeout to the upstream fetch in the Inxora validation branch, using the
handler’s existing bounded-probe pattern and timeout configuration where
available. Update the fetch options around the Inxora validation request while
preserving its method and headers, ensuring stalled requests abort rather than
holding validation indefinitely.

Source: Coding guidelines

Comment on lines +33 to +53
const NOTIFIABLE_TYPES = new Set([
"provider_down",
"provider_recovered",
"health_degraded",
"rate_limited",
"budget_exceeded",
]);

useEffect(() => {
if (events.length === 0) return;
const latest = events[0];
// Only notify on significant events — skip health_update (too spammy)
// and usage_update (not actionable). health_update fires every 500ms per
// provider on any metric change; only health_degraded (success < 70%)
// is worth surfacing as a notification.
if (!NOTIFIABLE_TYPES.has(latest.type)) return;
if (latest.ts && latest.ts > lastProcessedTs.current) {
lastProcessedTs.current = latest.ts;
useNotificationStore.getState().addServerEvent(latest);
}
}, [events]);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -n "rate_limited|budget_exceeded|health_degraded" --type=js

Repository: rsalmn/ExtremeRouter

Length of output: 158


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- matches for target event types ---'
rg -n --hidden --glob '!node_modules' --glob '!dist' --glob '!build' \
  'rate_limited|budget_exceeded|health_degraded|provider_down|provider_recovered|health_update|usage_update|snapshot' .

printf '\n%s\n' '--- files mentioning NotificationBell or notification store ---'
rg -n --hidden --glob '!node_modules' --glob '!dist' --glob '!build' \
  'NotificationBell|formatEventTitle|formatEventMessage|addServerEvent|useNotificationStore' .

Repository: rsalmn/ExtremeRouter

Length of output: 50377


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- dashboard stream route and notification store ---'
fd -a 'route.js|notificationStore.js|NotificationBell.js' src

printf '\n%s\n' '--- outline NotificationBell.js ---'
ast-grep outline src/shared/components/NotificationBell.js --view expanded

printf '\n%s\n' '--- outline notificationStore.js ---'
ast-grep outline src/shared/stores/notificationStore.js --view expanded

Repository: rsalmn/ExtremeRouter

Length of output: 9532


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- dashboard stream route ---'
sed -n '1,260p' src/app/api/dashboard/stream/route.js

printf '\n%s\n' '--- notification store ---'
sed -n '1,260p' src/store/notificationStore.js

printf '\n%s\n' '--- notification bell ---'
sed -n '1,220p' src/shared/components/NotificationBell.js

Repository: rsalmn/ExtremeRouter

Length of output: 15063


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- event type matches ---'
rg -n --hidden --glob '!node_modules' --glob '!dist' --glob '!build' \
  'rate_limited|budget_exceeded|health_degraded' src

printf '\n%s\n' '--- notification formatting matches ---'
rg -n --hidden --glob '!node_modules' --glob '!dist' --glob '!build' \
  'formatEventTitle|formatEventMessage|addServerEvent|NOTIFIABLE_TYPES|health_update|provider_down|provider_recovered|usage_update|snapshot' \
  src/store src/shared src/app/api/dashboard/stream

Repository: rsalmn/ExtremeRouter

Length of output: 6491


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- alert service ---'
sed -n '1,220p' src/shared/services/alertService.js

printf '\n%s\n' '--- dispatchAlert call sites for the extra types ---'
rg -n --hidden --glob '!node_modules' --glob '!dist' --glob '!build' \
  'dispatchAlert\("(health_degraded|budget_exceeded|rate_limited)"|type:\s*"(health_degraded|budget_exceeded|rate_limited)"' \
  src/sse src/shared src/app

Repository: rsalmn/ExtremeRouter

Length of output: 6277


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- useDashboardStream ---'
sed -n '1,220p' src/shared/hooks/useDashboardStream.js

printf '\n%s\n' '--- NotificationBell line numbers ---'
cat -n src/shared/components/NotificationBell.js | sed -n '1,120p'

Repository: rsalmn/ExtremeRouter

Length of output: 8493


Keep the notification types aligned with the dashboard stream NOTIFIABLE_TYPES includes health_degraded, rate_limited, and budget_exceeded, but /api/dashboard/stream only emits provider_down, provider_recovered, and health_update. rate_limited is used by the webhook alert service, not this SSE path, so this bell never sees it; the health_update branch in src/store/notificationStore.js is also unreachable from here. Either forward those events into the stream or trim the unused cases.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/shared/components/NotificationBell.js` around lines 33 - 53, Align
NOTIFIABLE_TYPES in the NotificationBell effect with the event types emitted by
/api/dashboard/stream: remove rate_limited, budget_exceeded, and health_degraded
unless the stream is updated to forward them. Ensure the set and related
filtering reflect the actual SSE types, preserving provider_down and
provider_recovered handling.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant