Feature: Addition of Cursor Support#1772
Conversation
The toLegacyProvider function only recognized 'codex' and 'claudeAgent', silently mapping 'cursor' (and any future providers) to 'codex'. This caused the provider picker to reset to OpenAI after sending a message with a cursor provider session. Use Schema.is(ProviderKind) to validate all provider kinds, matching the ace reference implementation.
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
ApprovabilityVerdict: Needs human review Diff is too large for automated approval analysis. A human reviewer should evaluate this PR. You can customize Macroscope's approvability policy. Learn more. |
| await context.client.close(); | ||
| }), | ||
| ).then(() => undefined), | ||
| ); |
There was a problem hiding this comment.
stopAll deletes sessions before close, skipping turn settlement
Medium Severity
In stopAll, sessions.delete(threadId) runs before context.client.close(), but in stopSession the order is reversed. The close handler (set during startSession) checks sessions.get(input.threadId) and returns early if the session is gone. This means stopAll silently skips settling any active turn (no turn.completed/turn.aborted event emitted) and never emits session.exited. Downstream consumers like ProviderRuntimeIngestion won't learn that turns ended or sessions closed, potentially leaving orchestration threads stuck in a stale "running" state.
Additional Locations (2)
Reviewed by Cursor Bugbot for commit 4cf2302. Configure here.
| return matched; | ||
| } | ||
| } | ||
| return preferredKinds.every((kind) => kind.startsWith("allow_")) ? options[0] : undefined; |
There was a problem hiding this comment.
🟢 Low Layers/CursorAdapterToolHelpers.ts:532
selectCursorPermissionOption returns options[0] when all preferredKinds start with "allow_" but no match exists. This returns a reject option (like "reject_once") when the caller explicitly requested an allow option. Consider returning undefined instead so the caller can handle the mismatch rather than receiving an unintended option.
+ return preferredKinds.every((kind) => kind.startsWith("allow_")) ? undefined : options[0];🤖 Copy this AI Prompt to have your agent fix this:
In file apps/server/src/provider/Layers/CursorAdapterToolHelpers.ts around line 532:
`selectCursorPermissionOption` returns `options[0]` when all `preferredKinds` start with `"allow_"` but no match exists. This returns a reject option (like `"reject_once"`) when the caller explicitly requested an allow option. Consider returning `undefined` instead so the caller can handle the mismatch rather than receiving an unintended option.
Evidence trail:
apps/server/src/provider/Layers/CursorAdapterToolHelpers.ts lines 522-534 (function definition and fallback logic at line 532); apps/server/src/provider/Layers/CursorAdapterToolHelpers.test.ts lines 119-125 (test showing undefined is returned when reject preferred but only allow options exist - reverse case is not tested)
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
There are 3 total unresolved issues (including 1 from previous review).
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 695b425. Configure here.
| settleTurn(activeContext, activeContext.activeTurn.id, { | ||
| type: "completed", | ||
| errorMessage: `Cursor ACP exited unexpectedly (code=${code ?? "null"}, signal=${signal ?? "null"}).`, | ||
| }); |
There was a problem hiding this comment.
Close handler reports "unexpectedly" for graceful stops
Medium Severity
The close handler always settles an active turn with errorMessage: "Cursor ACP exited unexpectedly..." regardless of whether context.stopping is true. The stopping flag is correctly checked for the session.exited event's reason and exitKind, but the settleTurn call ignores it. During a graceful stopSession, this causes the turn to emit a turn.completed with state: "failed" and a misleading error message, which the orchestration layer interprets as an error condition before the subsequent session.exited event arrives.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 695b425. Configure here.
| : status === "ready" | ||
| : isTerminalTurnLifecycleEvent(event) | ||
| ? lastErrorFromTerminalTurnEvent(event, thread.session?.lastError ?? null) | ||
| : status === "ready" || status === "interrupted" |
There was a problem hiding this comment.
Last error clearing now affects all providers' interrupted state
Medium Severity
Adding status === "interrupted" to the lastError clearing condition at line 997 changes behavior for all providers, not just Cursor. When a session.state.changed event transitions to "interrupted" state, lastError is now unconditionally cleared to null. Previously, the stale lastError was preserved. This can discard a meaningful error message if a provider session transitions to interrupted state after an error — the error context is lost before it can be surfaced.
Reviewed by Cursor Bugbot for commit 695b425. Configure here.
|
thanks but the cursor team has promised they'll fix the acp for us so i wanna use that (see #1355 ) |


What Changed
- Family: GPT 5.4,
- Effort High
- Fast: on.
Why
T3Code lacks the support for Cursor. During a recent live stream, Theo said that "Cursor ACP is not as reliable as Cursor Team claims and often provides outdated model lists." I had already implemented a solution addressing this limitation in another fork of the project, which I have been using in my daily workflow without issues. This PR ports that implementation into T3Code, enabling full Cursor support with proper handling of model families, variants, and performance modes.
UI Changes
N/A
Checklist
Note: Using the acpAdapter I introduced here, Gemini CLI can be easily be integrated.
Following are some samples of Cursors in Action:




Note
High Risk
Large change adding a new provider integration that spawns external binaries (
cursor-agent) and manages long-lived ACP sessions/events; errors or edge cases could impact orchestration session state, approvals, and runtime stability. Also tweaks attachment-path decoding and Claude thinking-token limits, which can affect request handling and model behavior.Overview
Adds Cursor as a first-class provider across server runtime: a new
CursorAdapterthat speaks Cursor ACP (session start/resume, streaming deltas, tool approvals, user-input prompts, interrupt/cancel, rollback-by-restarting sessions with transcript bootstrapping) plus supporting metadata/error-parsing helpers and extensive tests.Extends git-side text generation routing to support provider
"cursor"via a newCursorTextGenerationLiveimplementation that runscursor-agent --printwith JSON schema validation/timeouts and maps failures to typedTextGenerationError.Updates orchestration ingestion to treat
turn.abortedas a terminal lifecycle event (statusinterrupted), finalize partial assistant output, and clear buffered plans, and tightens attachments serving by decoding URL paths before normalization. Separately adds Claude thinking budget options and applies max-thinking-token limits when enabled, and adjusts integration harness layers to provideProjectionThreadMessageRepositoryLive.Reviewed by Cursor Bugbot for commit 695b425. Bugbot is set up for automated code reviews on this repo. Configure here.
Note
Add Cursor as a first-class provider with session management, model selection, and traits UI
CursorProvidershells out tocursor-agentto list models and auth status;CursorAdaptermanages ACP sessions over JSON-RPC stdin/stdout viaacpClient.cursorModelSelectorto group cursor models into families and resolve exact model slugs from traits (reasoningEffort, fastMode, thinking, maxMode); the provider model picker and traits picker use this to show family-level choices and emit concrete slugs.ProviderServiceto use a local transcript as the authoritative source for cursor sessions:sendTurnandstartSessionpassresumeCursorandreplayTurnsderived from persisted projection messages, falling back torebuild-local-transcriptwhen no resume cursor exists.CursorTextGenerationso title/commit generation spawnscursor-agent --print --output-format jsonwith a resolved model slug.low/medium/high) toClaudeAdapterand surfaces it as a selectable option in the traits picker.Macroscope summarized 695b425.