Skip to content

feat: add Cursor as a cloud LLM provider via local cursor-bridge sidecar - #5504

Open
AnmolKamboj wants to merge 10 commits into
tinyhumansai:mainfrom
AnmolKamboj:feat/cursor-provider
Open

feat: add Cursor as a cloud LLM provider via local cursor-bridge sidecar#5504
AnmolKamboj wants to merge 10 commits into
tinyhumansai:mainfrom
AnmolKamboj:feat/cursor-provider

Conversation

@AnmolKamboj

@AnmolKamboj AnmolKamboj commented Aug 12, 2026

Copy link
Copy Markdown

Summary

  • Adds Cursor as a cloud LLM provider, so users with a Cursor subscription (including free EDU accounts) can use Cursor models (Composer, GPT-5.x, Claude, Kimi, etc.) for chat and routing workloads.
  • New scripts/cursor-bridge: a small local Node/TypeScript sidecar that adapts the agent-based Cursor SDK (Agent.prompt) to the OpenAI-compatible HTTP surface (/v1/models, /v1/chat/completions) OpenHuman already speaks. No core changes required.
  • Cursor chip added to the built-in cloud providers panel (default endpoint http://127.0.0.1:8790/v1, bearer = Cursor API key).
  • Cursor model parameters exposed by the SDK (reasoning effort, context window, fast mode) are encoded as model-ID variants (~p=param:value) and surfaced in Settings as a grouped selector: one sorted base-model dropdown plus per-parameter dropdowns below it, instead of a long flat variant list.

Problem

  • Cursor exposes models only through its agent SDK / CLI, not an OpenAI-compatible HTTP API, so it could not be configured as an OpenHuman provider at all.
  • Naively listing every parameter combination as a separate model produces an unusably long, unsorted dropdown.

Solution

  • Bridge sidecar pattern: scripts/cursor-bridge runs locally, translates OpenAI-style requests into Cursor SDK calls (dedicated workspace cwd, per-key model cache, optional SSE streaming), and advertises the base model plus each supported parameter value as distinct model IDs.
  • UI grouping: ModelEntryField parses the ~p= encoding for the cursor provider and renders a base-model select (sorted, showing display name + model ID) with separate selects for each available parameter; selections are serialized back into the encoded ID, so routing/storage are unchanged.
  • Tradeoff: the bridge must be running for the provider to respond (endpoint is configurable). A Rust-side builtin catalog entry was intentionally dropped because the catalog moved to the tinymemory repo — the UI preset alone is sufficient; happy to open a companion PR there if you want it first-class.

Submission Checklist

If a section does not apply to this change, mark the item as N/A with a one-line reason. Do not delete items.

  • Tests added or updated — ModelEntryField.test.tsx covers parse/serialize round-trip, malformed encoded IDs, and existing picker behavior (10 tests). Bridge HTTP paths stay outside the app harness.
  • N/A: Diff coverage ≥ 80% for scripts/cursor-bridge — that sidecar is not in Vitest/llvm-cov; UI changed lines are covered by the ModelEntryField tests above.
  • N/A: Coverage matrix — new opt-in provider; no existing feature rows added, removed, or renamed.
  • N/A: Feature IDs in Related — no matrix rows affected.
  • No new external network dependencies introduced — the bridge only talks to Cursor's API when the user configures the provider with their own key (BYOK); app tests unaffected.
  • N/A: Manual smoke checklist — not a release-cut surface.
  • N/A: Linked issue — no existing issue; Discord-first contribution.

Impact

  • Desktop settings UI + new opt-in local sidecar script. No impact on existing providers, routing, or users who don't configure Cursor.
  • Security: the bridge binds to 127.0.0.1 only and requires the user's own Cursor API key as bearer; no keys are logged.

Related

  • Closes: (none — no existing issue)
  • Follow-up PR(s)/TODOs: optional tinymemory catalog entry for a Rust-side builtin preset.

AI Authored PR Metadata (required for Codex/Linear PRs)

Linear Issue

  • Key: N/A
  • URL: N/A

Commit & Branch

  • Branch: AnmolKamboj:feat/cursor-provider
  • Commit SHA: e13cee83d

Validation Run

  • pnpm --filter openhuman-app format:check — passes
  • pnpm typecheck — passes
  • Focused tests: pnpm exec vitest run --config test/vitest.config.ts src/components/settings/panels/__tests__/ModelEntryField.test.tsx — 10 passed
  • N/A: Rust fmt/check — no Rust changes in this PR
  • N/A: Tauri fmt/check — no Tauri changes in this PR
  • ESLint on changed files: 0 errors (2 pre-existing warnings in AIPanel.tsx, untouched by this PR)

Validation Blocked

  • command: N/A
  • error: N/A
  • impact: N/A

Behavior Changes

  • Intended behavior change: users can select "Cursor" in Settings → AI → Cloud Providers, paste a Cursor API key, and pick Cursor models (with parameter controls) for any workload.
  • User-visible effect: new provider chip + grouped Cursor model selector. Nothing changes for users who don't configure it.

Parity Contract

  • Legacy behavior preserved: yes — additive only; no existing provider, routing, or storage code paths modified beyond the ModelEntryField/AIPanel prop additions.
  • Guard/fallback/dispatch parity checks: N/A

Duplicate / Superseded PR Handling

  • Duplicate PR(s): none known
  • Canonical PR: this one
  • Resolution (closed/superseded/updated): N/A

Adds scripts/cursor-bridge, a small Node service that adapts the Cursor
SDK to an OpenAI-compatible surface (/v1/models + /v1/chat/completions
with SSE). Cursor.models.list() entries are expanded into per-variant
model ids so reasoning effort / thinking / context presets are selectable
straight from the existing model dropdowns. Registers a cursor preset
in both builtin provider catalogs pointing at the sidecar.
Replaces the flat variant list with a Cursor-specific picker in the AI
settings model field: base models sorted alphabetically (display name +
model id), with separate dropdowns below for each supported parameter
(reasoning effort / thinking, context window, fast mode). Selections
serialize to a composable '~p=param:value' model id grammar that the
cursor-bridge decodes back into SDK model params, so options combine
freely instead of exploding the list.
# Conflicts:
#	src/openhuman/config/schema/cloud_providers.rs
@AnmolKamboj
AnmolKamboj requested a review from a team August 12, 2026 02:12
@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This change adds Cursor as a built-in provider, adds Cursor-specific model selection and localization, and introduces a local bridge that exposes Cursor agents through an OpenAI-compatible API.

Changes

Cursor provider integration

Layer / File(s) Summary
Provider configuration and model selection
app/src/components/settings/panels/builtinCloudProviders.ts, app/src/components/settings/panels/ai/ModelEntryField.tsx, app/src/components/settings/panels/AIPanel.tsx, app/src/components/settings/panels/__tests__/ModelEntryField.test.tsx, app/src/lib/i18n/*
The settings UI configures Cursor and passes the provider slug to ModelEntryField. Cursor models use grouped base models and encoded parameters. Localization and round-trip tests cover the new parameters.
Bridge setup and model protocol
scripts/cursor-bridge/.gitignore, scripts/cursor-bridge/package.json, scripts/cursor-bridge/src/index.ts, .gitignore
The bridge package uses @cursor/sdk. It expands and caches Cursor models, normalizes chat messages, and parses parameterized model IDs.
Completion routing and server startup
scripts/cursor-bridge/src/index.ts, scripts/cursor-bridge/start.ps1, scripts/run-dev-win.sh
The bridge validates requests, enforces authentication and request limits, invokes Agent.prompt, returns JSON or SSE responses, handles routes and errors, and starts through the Windows development launcher.

Estimated code review effort: 4 (Complex) | ~45 minutes

Mergeability Score: 🟡 Moderate · up to f800e

The new local bridge can expose reusable Cursor API keys to another process that claims its configured port, and its Windows launcher can report success even when the bridge fails to start. These are bounded but concrete security and availability risks that should be fixed or explicitly accepted before merging.

Sequence Diagram(s)

sequenceDiagram
  participant OpenAIClient
  participant CursorBridge
  participant CursorAgent
  OpenAIClient->>CursorBridge: POST /v1/chat/completions
  CursorBridge->>CursorBridge: Validate request and resolve model
  CursorBridge->>CursorAgent: Agent.prompt with normalized prompt
  CursorAgent-->>CursorBridge: Completion text
  CursorBridge-->>OpenAIClient: JSON or SSE completion
Loading

Suggested labels: feature, agent

Suggested reviewers: m3ga-mind

Poem

I’m a rabbit with models in rows,
A local bridge where Cursor flow goes.
Parameters hop,
SSE streams pop,
And completion text gently flows.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 7.14% 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
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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes adding Cursor as a cloud LLM provider through the local cursor-bridge sidecar.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@coderabbitai coderabbitai Bot added agent Built-in agents, prompts, orchestration, and agent runtime in src/openhuman/agent/. feature Net-new user-facing capability or product behavior. labels Aug 12, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Nitpick comments (4)
app/src/components/settings/panels/ai/ModelEntryField.tsx (1)

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

Log the malformed-parameter branch.

The catch at Line 64 discards the decode error and returns the whole raw string as id, including the ~p= suffix. That value then becomes a catalog group key at Line 122 and a serialization input at Line 139, so a later serializeCursorSelection call re-encodes an id that already carries an encoded suffix. The fallback is safe, but it is silent, and the sibling code in AIPanel.tsx logs provider failures under an [ai-settings] prefix.

Add a grep-friendly diagnostic on this branch. Log the parameter key only, not the decoded value.

As per coding guidelines: "Add verbose, grep-friendly diagnostics for new or changed flows, including branches, external calls, retries, state transitions, and errors; never log secrets or full PII."

🔍 Proposed diagnostic
     } catch {
+      console.warn('[ai-settings][cursor] malformed encoded model parameters; ignoring parameters');
       return { id: value, parameters: new Map() };
     }
🤖 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 `@app/src/components/settings/panels/ai/ModelEntryField.tsx` around lines 49 -
69, Update the decode-error catch in parseCursorSelection to emit a
grep-friendly diagnostic using the [ai-settings] prefix and the malformed
parameter key only; do not log the decoded value or full raw input. Preserve the
existing safe fallback return behavior.

Source: Coding guidelines

scripts/cursor-bridge/src/index.ts (3)

285-288: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Guard the catch-all against a response that already started.

streamText calls res.writeHead at Line 172 and then writes SSE frames. If any error reaches this handler after that point, sendError calls sendJson, which calls res.writeHead a second time. Node then throws ERR_HTTP_HEADERS_SENT inside the catch block, and the client connection is left open.

Check res.headersSent before writing an error response.

🐛 Proposed guard
   } catch (err) {
     const message = err instanceof Error ? err.message : String(err);
+    if (res.headersSent) {
+      console.error("[cursor-bridge] error after response started:", message);
+      res.end();
+      return;
+    }
     sendError(res, 502, message);
   }
🤖 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 `@scripts/cursor-bridge/src/index.ts` around lines 285 - 288, Update the
catch-all handler in the request flow to check res.headersSent before calling
sendError. Only write the 502 response when headers have not been sent;
otherwise avoid sendError and ensure the already-started response is closed so
the client connection does not remain open.

192-196: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Bound the header phase and the request body.

Line 293 sets headersTimeout = 0. The comment at Line 291 justifies a long deadline for agent runs, which applies to requestTimeout, not to the header phase. With no header deadline, a client can open a socket, send partial headers, and hold the connection open without limit.

readBody at Lines 192-196 accumulates every chunk into memory with no size cap. Combined with requestTimeout = 0, a single request has neither a size bound nor a time bound.

Keep requestTimeout = 0 for the agent run. Restore a finite headersTimeout and reject oversized bodies.

🛡️ Proposed bounds
+const MAX_BODY_BYTES = 8 * 1024 * 1024;
+
 async function readBody(req: http.IncomingMessage): Promise<string> {
   const chunks: Buffer[] = [];
+  let total = 0;
-  for await (const chunk of req) chunks.push(chunk as Buffer);
+  for await (const chunk of req) {
+    const buf = chunk as Buffer;
+    total += buf.length;
+    if (total > MAX_BODY_BYTES) throw new Error("request body too large");
+    chunks.push(buf);
+  }
   return Buffer.concat(chunks).toString("utf8").replace(/^\ufeff/, "");
 }
-// Agent runs can take minutes; disable Node's default request timeouts.
+// Agent runs can take minutes, so the request deadline is disabled. The header
+// phase is unrelated to the run duration and keeps a finite deadline.
 server.requestTimeout = 0;
-server.headersTimeout = 0;
+server.headersTimeout = 60_000;

Also applies to: 291-293

🤖 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 `@scripts/cursor-bridge/src/index.ts` around lines 192 - 196, Update the server
configuration near requestTimeout and headersTimeout to restore a finite
headersTimeout while keeping requestTimeout at 0 for long agent runs. Modify
readBody to enforce a maximum request-body size and reject requests that exceed
it before accumulating unbounded data, preserving UTF-8 decoding and BOM removal
for accepted bodies.

243-246: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

State that streaming replays a completed run.

Agent.prompt at Line 230 resolves only when the whole run finishes. streamText then splits the finished text into 48-character frames. The client therefore receives nothing until the agent completes, and the SSE frames arrive in a burst afterwards.

This removes the latency benefit that a client expects from stream: true. A client or an intermediary with an idle-read deadline can also drop the connection during the silent wait, because the first byte arrives only at the end.

Send SSE keep-alive comment lines while the run is in progress, or document the limitation in the file header at Lines 5-8.

🤖 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 `@scripts/cursor-bridge/src/index.ts` around lines 243 - 246, Update the
streaming path around Agent.prompt and streamText so stream requests emit SSE
keep-alive comment lines while the run is still in progress, preventing an idle
connection before the completed result is replayed. Preserve the existing final
text framing, or document the completed-run replay limitation in the file header
if live keep-alives cannot be added.
🤖 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 `@app/src/components/settings/panels/ai/ModelEntryField.tsx`:
- Around line 168-176: Update the parameter field rendering in ModelEntryField
to compute the resolved label once from CURSOR_PARAMETER_LABELS[parameter] ??
parameter, then reuse it for both the visible label and SettingsSelect
aria-label. Associate the label and select with matching htmlFor and id values
so the accessible name matches the localized visible text.
- Around line 35-41: Update CursorModelSelector to call useT() and route all
newly introduced UI text through t(...), including CURSOR_PARAMETER_LABELS
values, option text, “Select a model” (reuse settings.ai.selectModel), and
“Default”. Add the corresponding locale keys for parameter labels, option text,
and the default option, preserving the existing rendered behavior.

In `@app/src/components/settings/panels/builtinCloudProviders.ts`:
- Around line 227-236: Add the `cursor` provider entry to the Rust
cloud-provider catalog consumed by
`src/openhuman/config/schema/cloud_providers.rs`, matching the JavaScript
definition’s slug and relevant metadata. Ensure the Rust catalog remains
synchronized with `builtinCloudProviders.ts`; do not remove the synchronization
claim.

In `@scripts/cursor-bridge/package.json`:
- Around line 8-12: Update scripts/cursor-bridge/package.json by pinning
`@cursor/sdk` to ^1.0.27, adding an engines.node requirement of >=22.18.0, and
adding `@types/node` under devDependencies for type-checking and editor support.

In `@scripts/cursor-bridge/src/index.ts`:
- Around line 262-289: Update the request handling in the http.createServer
callback to validate the Host header against the configured HOST authority and
return an error before apiKeyFrom or any agent handler runs when it does not
match. Also reject every request containing an Origin header before reaching
handleModels, handleCompletion, or Agent.prompt, while preserving the existing
health-route behavior only for validated requests.

---

Nitpick comments:
In `@app/src/components/settings/panels/ai/ModelEntryField.tsx`:
- Around line 49-69: Update the decode-error catch in parseCursorSelection to
emit a grep-friendly diagnostic using the [ai-settings] prefix and the malformed
parameter key only; do not log the decoded value or full raw input. Preserve the
existing safe fallback return behavior.

In `@scripts/cursor-bridge/src/index.ts`:
- Around line 285-288: Update the catch-all handler in the request flow to check
res.headersSent before calling sendError. Only write the 502 response when
headers have not been sent; otherwise avoid sendError and ensure the
already-started response is closed so the client connection does not remain
open.
- Around line 192-196: Update the server configuration near requestTimeout and
headersTimeout to restore a finite headersTimeout while keeping requestTimeout
at 0 for long agent runs. Modify readBody to enforce a maximum request-body size
and reject requests that exceed it before accumulating unbounded data,
preserving UTF-8 decoding and BOM removal for accepted bodies.
- Around line 243-246: Update the streaming path around Agent.prompt and
streamText so stream requests emit SSE keep-alive comment lines while the run is
still in progress, preventing an idle connection before the completed result is
replayed. Preserve the existing final text framing, or document the
completed-run replay limitation in the file header if live keep-alives cannot be
added.
🪄 Autofix

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: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 69bbabf3-81fc-4679-82af-0a8519b26b4a

📥 Commits

Reviewing files that changed from the base of the PR and between 2ad464b and 51bb508.

📒 Files selected for processing (6)
  • app/src/components/settings/panels/AIPanel.tsx
  • app/src/components/settings/panels/ai/ModelEntryField.tsx
  • app/src/components/settings/panels/builtinCloudProviders.ts
  • scripts/cursor-bridge/.gitignore
  • scripts/cursor-bridge/package.json
  • scripts/cursor-bridge/src/index.ts

Comment thread app/src/components/settings/panels/ai/ModelEntryField.tsx
Comment thread app/src/components/settings/panels/ai/ModelEntryField.tsx Outdated
Comment thread app/src/components/settings/panels/builtinCloudProviders.ts
Comment thread scripts/cursor-bridge/package.json Outdated
Comment thread scripts/cursor-bridge/src/index.ts

@tinysweeper tinysweeper 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.

Requesting changes: 1 lane(s) blocking, worst finding is high.

Fix or reply to the findings below and push. The next review clears this automatically once they are gone — you should not need to dismiss anything by hand.

             $0.0677 · 73,894 in / 19,072 out · 53,915 cached (73%) · z-ai/glm-5.2
critique:    $0.0301 · 21,890 in / 9,617 out  · 14,923 cached (68%) · z-ai/glm-5.2
security:    $0.0120 · 16,837 in / 2,880 out  · 12,302 cached (73%) · z-ai/glm-5.2
tests:       $0.0073 · 8,535 in  / 2,083 out  · 6,628 cached (78%)  · z-ai/glm-5.2
description: $0.0118 · 9,737 in  / 3,865 out  · 7,639 cached (78%)  · z-ai/glm-5.2

Comment thread scripts/cursor-bridge/package.json Outdated
Comment thread scripts/cursor-bridge/package.json Outdated
Comment thread app/src/components/settings/panels/ai/ModelEntryField.tsx Outdated
@tinysweeper

tinysweeper Bot commented Aug 12, 2026

Copy link
Copy Markdown

What this change touches

23 files, +920 -2 across 7 components. The code graph knows nothing about these files yet — normal for newly added files, and a cold index otherwise.

flowchart LR
  n0["scripts/cursor-bridge/src<br/>1 file +366 -0"]:::changed
  n1["scripts/cursor-bridge<br/>3 files +250 -0"]:::changed
  n2["app/src/components/settings/panels/ai<br/>1 file +188 -1"]:::changed
  n3["app/src/lib/i18n<br/>14 files +70 -0"]:::changed
  n4["app/src/components/settings/panels/__tests__<br/>1 file +31 -1"]:::changed
  n5["app/src/components/settings/panels<br/>2 files +14 -0"]:::changed
  n6["root<br/>1 file +1 -0<br/>1 finding"]:::flagged
  classDef changed fill:#0d4429,stroke:#238636,color:#e6edf3
  classDef impacted fill:#161b22,stroke:#6e7681,color:#c9d1d9
  classDef flagged fill:#5a1e02,stroke:#d93f0b,color:#ffffff
  classDef blocking fill:#67060c,stroke:#f85149,color:#ffffff
Loading

Green: changed. Grey: untouched, reached through an import or a call. Orange: has findings. Red: has a finding that blocks the merge.

Component Files Lines Findings
scripts/cursor-bridge/src changed 1 +366 -0
scripts/cursor-bridge changed 3 +250 -0
app/src/components/settings/panels/ai changed 1 +188 -1
app/src/lib/i18n changed 14 +70 -0
app/src/components/settings/panels/__tests__ changed 1 +31 -1
app/src/components/settings/panels changed 2 +14 -0
(root) changed 1 +1 -0 1 (medium)
Changed files

scripts/cursor-bridge/src

  • scripts/cursor-bridge/src/index.ts

scripts/cursor-bridge

  • scripts/cursor-bridge/.gitignore
  • scripts/cursor-bridge/package-lock.json
  • scripts/cursor-bridge/package.json

app/src/components/settings/panels/ai

  • app/src/components/settings/panels/ai/ModelEntryField.tsx

app/src/lib/i18n

  • app/src/lib/i18n/ar.ts
  • app/src/lib/i18n/bn.ts
  • app/src/lib/i18n/de.ts
  • app/src/lib/i18n/en.ts
  • app/src/lib/i18n/es.ts
  • app/src/lib/i18n/fr.ts
  • app/src/lib/i18n/hi.ts
  • app/src/lib/i18n/id.ts
  • app/src/lib/i18n/it.ts
  • app/src/lib/i18n/ko.ts
  • app/src/lib/i18n/pl.ts
  • app/src/lib/i18n/pt.ts
  • …and 2 more files

app/src/components/settings/panels/__tests__

  • app/src/components/settings/panels/__tests__/ModelEntryField.test.tsx

app/src/components/settings/panels

  • app/src/components/settings/panels/AIPanel.tsx
  • app/src/components/settings/panels/builtinCloudProviders.ts

(root)

  • .gitignore

tinysweeper 0.1.0

@tinysweeper tinysweeper Bot added the priority: p1 Next. Wrong behaviour a user will hit, or a security weakness behind a condition. label Aug 12, 2026
Harden the local bridge and make Cursor model selection accessible, localized, and regression-tested.

Co-authored-by: Cursor <cursoragent@cursor.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (5)
scripts/cursor-bridge/src/index.ts (5)

263-283: 🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift

Sensitive Data Exposure (CWE-319): Cleartext Transmission of Sensitive Information

Reachability: External

Do not send bearer credentials over plaintext HTTP.

The bridge accepts Authorization: Bearer <key> on http://127.0.0.1:8790. Host and Origin validation do not provide transport confidentiality. Keep the key in the bridge environment, or protect this hop with authenticated encryption.

🤖 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 `@scripts/cursor-bridge/src/index.ts` around lines 263 - 283, Update the
request handling in the server callback to stop accepting bearer credentials
over plaintext HTTP; remove the Authorization-header API-key path from
apiKeyFrom and require the bridge-environment key instead, unless this hop is
protected by authenticated encryption. Preserve the existing Host and Origin
validation and unauthenticated health endpoint behavior.

293-295: 🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

Information Disclosure (CWE-209): Generation of Error Message Containing Sensitive Information

Reachability: External

Return fixed public messages for backend failures.

The catch block and result.error?.message forward internal error details through sendError. Log sanitized diagnostics separately.

🤖 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 `@scripts/cursor-bridge/src/index.ts` around lines 293 - 295, Update the
backend failure handling in the catch block and the result.error path to stop
exposing internal error messages through sendError. Return fixed public-safe
messages for these failures, while logging the sanitized diagnostic details
separately for troubleshooting.

152-191: 🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

Information Disclosure (CWE-524)

Reachability: External

Mark identity-varying responses as no-store.

Add Cache-Control: no-store in sendJson and streamText. /v1/models and chat completions depend on the request API key.

🤖 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 `@scripts/cursor-bridge/src/index.ts` around lines 152 - 191, Update sendJson
and streamText to include Cache-Control: no-store in their response headers,
while preserving the existing content type and streaming headers.

152-191: 🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift

Broken Authentication (CWE-306): Missing Authentication for Critical Function

Reachability: External

Require bridge authentication separate from the Cursor API key.

When CURSOR_API_KEY is set, any local process that sends the expected Host header and omits Origin and Authorization can reach Agent.prompt with the environment key. Add a per-run bridge token or authenticated IPC channel.

🤖 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 `@scripts/cursor-bridge/src/index.ts` around lines 152 - 191, Update the bridge
authentication flow around apiKeyFrom and the request handler so access requires
a per-run bridge token or authenticated IPC channel independent of
CURSOR_API_KEY. Do not allow missing Authorization to fall back directly to
process.env.CURSOR_API_KEY; validate the bridge credential before invoking
Agent.prompt, while preserving CURSOR_API_KEY only for upstream Cursor API
authentication.

299-305: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Keep finite request deadlines and bound request bodies.

server.requestTimeout = 0 and server.headersTimeout = 0 remove Node’s request and header deadlines. Because readBody buffers all chunks until end-of-stream without a size or rate limit, a local client can hold connections open and grow memory. Keep finite timeout values and reject oversized request bodies. Agent execution begins after the request body is read, so it does not require disabling these deadlines.

🤖 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 `@scripts/cursor-bridge/src/index.ts` around lines 299 - 305, Update the server
configuration near server.listen to retain finite requestTimeout and
headersTimeout values instead of setting them to zero, and update readBody to
enforce a maximum request-body size while buffering chunks. Reject oversized
bodies promptly and preserve normal request handling for bodies within the
limit.
🧹 Nitpick comments (1)
app/src/components/settings/panels/ai/ModelEntryField.tsx (1)

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

Add diagnostics for Cursor selection changes.

updateParameter changes the persisted Cursor model selection through onModelChange. The new flow has no diagnostic event. Add privacy-safe, grep-friendly diagnostics for model and parameter selection transitions. Do not log credentials or user-authored text.

As per coding guidelines: “Add verbose, grep-friendly diagnostics for new or changed flows, including branches, external calls, retries, state transitions, and errors; never log secrets or full PII.”

🤖 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 `@app/src/components/settings/panels/ai/ModelEntryField.tsx` around lines 137 -
141, Add privacy-safe, grep-friendly diagnostics in updateParameter for Cursor
selection transitions, recording the model identifier and parameter name along
with the change direction or resulting selection state. Do not include parameter
values, credentials, or other user-authored text, and preserve the existing Map
update and onModelChange behavior.

Source: Coding guidelines

🤖 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 `@app/src/lib/i18n/de.ts`:
- Around line 4990-4991: Update the German translations for
settings.ai.cursorParameterReasoningEffort and
settings.ai.cursorParameterThinking to use distinct, idiomatic labels that
clearly differentiate reasoning intensity from thinking mode, such as
“Denkintensität” and “Denkmodus”.

In `@app/src/lib/i18n/id.ts`:
- Line 4878: Update the translation value for the
`settings.ai.cursorParameterDefault` key in the Indonesian locale from the
English label to the existing Indonesian term `Bawaan`, matching the value used
by the related translation at line 4788.

---

Outside diff comments:
In `@scripts/cursor-bridge/src/index.ts`:
- Around line 263-283: Update the request handling in the server callback to
stop accepting bearer credentials over plaintext HTTP; remove the
Authorization-header API-key path from apiKeyFrom and require the
bridge-environment key instead, unless this hop is protected by authenticated
encryption. Preserve the existing Host and Origin validation and unauthenticated
health endpoint behavior.
- Around line 293-295: Update the backend failure handling in the catch block
and the result.error path to stop exposing internal error messages through
sendError. Return fixed public-safe messages for these failures, while logging
the sanitized diagnostic details separately for troubleshooting.
- Around line 152-191: Update sendJson and streamText to include Cache-Control:
no-store in their response headers, while preserving the existing content type
and streaming headers.
- Around line 152-191: Update the bridge authentication flow around apiKeyFrom
and the request handler so access requires a per-run bridge token or
authenticated IPC channel independent of CURSOR_API_KEY. Do not allow missing
Authorization to fall back directly to process.env.CURSOR_API_KEY; validate the
bridge credential before invoking Agent.prompt, while preserving CURSOR_API_KEY
only for upstream Cursor API authentication.
- Around line 299-305: Update the server configuration near server.listen to
retain finite requestTimeout and headersTimeout values instead of setting them
to zero, and update readBody to enforce a maximum request-body size while
buffering chunks. Reject oversized bodies promptly and preserve normal request
handling for bodies within the limit.

---

Nitpick comments:
In `@app/src/components/settings/panels/ai/ModelEntryField.tsx`:
- Around line 137-141: Add privacy-safe, grep-friendly diagnostics in
updateParameter for Cursor selection transitions, recording the model identifier
and parameter name along with the change direction or resulting selection state.
Do not include parameter values, credentials, or other user-authored text, and
preserve the existing Map update and onModelChange behavior.
🪄 Autofix

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: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: fbfe50a1-40ad-4432-b22a-50ffc6a882f1

📥 Commits

Reviewing files that changed from the base of the PR and between 51bb508 and c32c029.

⛔ Files ignored due to path filters (1)
  • scripts/cursor-bridge/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (19)
  • .gitignore
  • app/src/components/settings/panels/__tests__/ModelEntryField.test.tsx
  • app/src/components/settings/panels/ai/ModelEntryField.tsx
  • app/src/lib/i18n/ar.ts
  • app/src/lib/i18n/bn.ts
  • app/src/lib/i18n/de.ts
  • app/src/lib/i18n/en.ts
  • app/src/lib/i18n/es.ts
  • app/src/lib/i18n/fr.ts
  • app/src/lib/i18n/hi.ts
  • app/src/lib/i18n/id.ts
  • app/src/lib/i18n/it.ts
  • app/src/lib/i18n/ko.ts
  • app/src/lib/i18n/pl.ts
  • app/src/lib/i18n/pt.ts
  • app/src/lib/i18n/ru.ts
  • app/src/lib/i18n/zh-CN.ts
  • scripts/cursor-bridge/package.json
  • scripts/cursor-bridge/src/index.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • scripts/cursor-bridge/package.json

Comment thread app/src/lib/i18n/de.ts Outdated
Comment thread app/src/lib/i18n/id.ts Outdated
- bridge: authenticate callers with a bridge-local token
  (CURSOR_BRIDGE_TOKEN or generated .bridge-token); CURSOR_API_KEY stays
  in the bridge environment and is used only for upstream Cursor auth,
  so client credentials never transit the plaintext HTTP hop
- bridge: public-safe error responses (diagnostics logged locally),
  Cache-Control: no-store, finite request/header timeouts, and a 10 MiB
  request-body limit with prompt 413 rejection
- i18n: idiomatic German (Denkintensitaet/Denkmodus) and Indonesian
  (Bawaan) labels for Cursor parameter controls
- ui: privacy-safe debug log on Cursor parameter changes (model id and
  parameter name only, never values)

Co-authored-by: Cursor <cursoragent@cursor.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
scripts/cursor-bridge/src/index.ts (1)

321-357: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy lift

Add automated route tests for this security boundary.

Add tests for Host rejection, Origin rejection, missing and invalid bearer tokens, oversized bodies, invalid JSON, and streamed responses. Manual end-to-end testing does not protect these branches from regressions.

🤖 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 `@scripts/cursor-bridge/src/index.ts` around lines 321 - 357, Add automated
route-level tests for the HTTP server created in the request handler, covering
Host rejection, Origin rejection, missing and invalid bearer tokens, oversized
request bodies, invalid JSON, and streamed responses. Exercise each branch
through actual HTTP requests and assert the status, response body, and streaming
behavior without changing the existing security handling.
🤖 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 `@scripts/cursor-bridge/src/index.ts`:
- Around line 58-61: Update the token-generation logging around the bridge
output in the main flow to remove the generated token from console output. Log
only TOKEN_FILE and instruct the user to read the saved file locally for the
provider API key, without exposing the bearer token in stdout or logs.
- Around line 284-289: Before the Agent.prompt call, validate or constrain
CURSOR_BACKEND_URL to a trusted HTTPS Cursor endpoint, rejecting arbitrary or
http:// values before CURSOR_API_KEY can be used. Preserve the existing
Agent.prompt options and ensure the validated backend configuration is what the
SDK uses.

---

Nitpick comments:
In `@scripts/cursor-bridge/src/index.ts`:
- Around line 321-357: Add automated route-level tests for the HTTP server
created in the request handler, covering Host rejection, Origin rejection,
missing and invalid bearer tokens, oversized request bodies, invalid JSON, and
streamed responses. Exercise each branch through actual HTTP requests and assert
the status, response body, and streaming behavior without changing the existing
security handling.
🪄 Autofix

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: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 3660cbd7-9a13-4cf5-b077-7afd2daee8a9

📥 Commits

Reviewing files that changed from the base of the PR and between c32c029 and c6c0037.

📒 Files selected for processing (6)
  • app/src/components/settings/panels/ai/ModelEntryField.tsx
  • app/src/components/settings/panels/builtinCloudProviders.ts
  • app/src/lib/i18n/de.ts
  • app/src/lib/i18n/id.ts
  • scripts/cursor-bridge/.gitignore
  • scripts/cursor-bridge/src/index.ts
🚧 Files skipped from review as they are similar to previous changes (5)
  • scripts/cursor-bridge/.gitignore
  • app/src/lib/i18n/de.ts
  • app/src/lib/i18n/id.ts
  • app/src/components/settings/panels/ai/ModelEntryField.tsx
  • app/src/components/settings/panels/builtinCloudProviders.ts

Comment thread scripts/cursor-bridge/src/index.ts Outdated
Comment thread scripts/cursor-bridge/src/index.ts

@tinysweeper tinysweeper 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.

The previously-blocking findings are resolved. Clearing the changes request.

             $0.0755 · 77,126 in / 23,065 out · 60,665 cached (79%) · z-ai/glm-5.2
critique:    $0.0316 · 19,697 in / 11,167 out · 15,834 cached (80%) · z-ai/glm-5.2
security:    $0.0160 · 19,571 in / 4,624 out  · 15,748 cached (80%) · z-ai/glm-5.2
tests:       $0.0152 · 18,329 in / 4,186 out  · 13,928 cached (76%) · z-ai/glm-5.2
description: $0.0127 · 19,529 in / 3,088 out  · 15,155 cached (78%) · z-ai/glm-5.2

@tinysweeper tinysweeper Bot added priority: p2 Soon. Real but survivable — a rough edge, a gap, a thing that will bite later. and removed priority: p1 Next. Wrong behaviour a user will hit, or a security weakness behind a condition. labels Aug 12, 2026
AnmolKamboj and others added 2 commits August 12, 2026 23:18
Drop the generated bridge-token split. OpenHuman's Cursor field takes
the user's crsr_... key (Bearer), matching OpenAI/Groq. The sidecar still
binds 127.0.0.1 and keeps Host/Origin checks, no-store, body limits, and
public-safe errors.

Also start the bridge from run-dev-win.sh / start.ps1 so Cursor models
work without a separate terminal.

Co-authored-by: Cursor <cursoragent@cursor.com>
The Cursor SDK reads CURSOR_BACKEND_URL from the environment. Allow only
https://api2.cursor.sh and https://api.cursor.com so a poisoned override
cannot send the caller's API key to an arbitrary host.

Co-authored-by: Cursor <cursoragent@cursor.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 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 `@scripts/cursor-bridge/src/index.ts`:
- Around line 153-157: Update the bridge health/authentication flow around
apiKeyFrom and the local /health endpoint so the local client is authenticated
before any Cursor API key is accepted or forwarded. Replace the unauthenticated
loopback HTTP trust with platform-authenticated IPC or another authenticated
local transport, and ensure start.ps1 validates that authentication rather than
accepting any { "ok": true } response.

In `@scripts/cursor-bridge/start.ps1`:
- Around line 7-10: Update Test-BridgeUp and the other bridge URL usages to
build the health-probe and startup requests with the configured
CURSOR_BRIDGE_PORT value instead of hardcoded port 8790, while preserving 8790
as the default when the environment variable is unset.
🪄 Autofix

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: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 490c6eda-2f9e-4dc4-a076-2a25a77bbd7a

📥 Commits

Reviewing files that changed from the base of the PR and between c6c0037 and eddb32a.

📒 Files selected for processing (5)
  • app/src/components/settings/panels/builtinCloudProviders.ts
  • scripts/cursor-bridge/.gitignore
  • scripts/cursor-bridge/src/index.ts
  • scripts/cursor-bridge/start.ps1
  • scripts/run-dev-win.sh
🚧 Files skipped from review as they are similar to previous changes (2)
  • scripts/cursor-bridge/.gitignore
  • app/src/components/settings/panels/builtinCloudProviders.ts

Comment thread scripts/cursor-bridge/src/index.ts
Comment thread scripts/cursor-bridge/start.ps1
Health probes and status messages used a hardcoded 8790, so a custom
CURSOR_BRIDGE_PORT would start a second process while thinking the default
port was already up. Default remains 8790 when the env var is unset.

Co-authored-by: Cursor <cursoragent@cursor.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 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 `@scripts/cursor-bridge/src/index.ts`:
- Line 349: Ensure backend validation failures from assertTrustedCursorBackend
reach the launcher with a non-zero status. Update the timeout/error path in
start.ps1 to exit 1, or propagate the cursor-bridge child process exit status,
while preserving normal successful startup behavior.
🪄 Autofix

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: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: cef71ed0-d155-4856-b8a3-06e685d6f234

📥 Commits

Reviewing files that changed from the base of the PR and between eddb32a and f800e38.

📒 Files selected for processing (1)
  • scripts/cursor-bridge/src/index.ts

Comment thread scripts/cursor-bridge/src/index.ts
coderabbitai[bot]
coderabbitai Bot previously approved these changes Aug 13, 2026
Start the node process with -PassThru and exit with its status if
assertTrustedCursorBackend (or any other boot error) kills the child
before /health is up. Successful startup and the 15s timeout path are
unchanged (timeout still exits 1).

Co-authored-by: Cursor <cursoragent@cursor.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agent Built-in agents, prompts, orchestration, and agent runtime in src/openhuman/agent/. feature Net-new user-facing capability or product behavior. priority: p2 Soon. Real but survivable — a rough edge, a gap, a thing that will bite later.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant