Skip to content

fix(cursor): replay external model tool continuations with fresh conversation ids - #318

Merged
Ingwannu merged 2 commits into
lidge-jun:devfrom
Wibias:codex/fix-cursor-sol-continuation
Jul 23, 2026
Merged

fix(cursor): replay external model tool continuations with fresh conversation ids#318
Ingwannu merged 2 commits into
lidge-jun:devfrom
Wibias:codex/fix-cursor-sol-continuation

Conversation

@Wibias

@Wibias Wibias commented Jul 23, 2026

Copy link
Copy Markdown
Collaborator

Summary

Cursor Connect tool-result continuations for external models like cursor/gpt-5.6-sol were reusing the same conversation id via resumeAction. That works for Cursor-native models (composer-*, auto/default), but external models often lose server-side conversation state and then fail after stepCompleted with:

connect-end-stream invalid_argument
followed by reconnect + getBlobArgs burst + another failed follow-up turn.

Native openai gpt-5.6-sol and Cursor composer-2.5 stayed stable. The bug is specific to external Cursor Connect continuation.

Root cause

For tool-result-only turns, OpenCodex correctly uses resumeAction and structured conversation history. It previously kept the remembered _cursorConversationId for every model.

For external Cursor models, that resume path is brittle: Cursor hydrates blob/checkpoint state, then rejects the next step with a generic invalid_argument instead of emitting turnEnded. OpenCodex then reconnects and can loop.

Fix

  1. Detect Cursor-native vs external wire models (composer-*, auto/default = native).
  2. For external-model tool-result continuations, mint a fresh conversationId while still sending full replay history (conversationTurns + rootPromptMessagesJson) under resumeAction.
  3. Keep native Cursor models on the remembered conversation id path.
  4. One-shot adapter retry: if an external model still hits invalid_argument, rebuild once with forceFreshConversation: true.
  5. Sync _cursorConversationId to the conversation id actually used for the turn.
  6. Add debug diagnostics for wireModel, action, conversationId, turn type, and externalModel.

Why this is not "use openai instead"

Sol remains on the Cursor provider. Only the continuation strategy changes for external Cursor models.

Tests

  • bun run typecheck
  • bun test tests/cursor-adapter.test.ts tests/cursor-request-builder.test.ts tests/cursor-discovery.test.ts tests/cursor-errors.test.ts tests/cursor-tool-continuation.test.ts

Added coverage for:

  • external tool-result continuation forces a fresh conversation id
  • native tool-result continuation keeps the remembered id
  • forceFreshConversation always mints a new id
  • one-shot invalid_argument retry with a second conversation id
  • native/external wire-model classification
  • invalid_argument detector

Files

  • src/adapters/cursor.ts
  • src/adapters/cursor/discovery.ts
  • src/adapters/cursor/request-builder.ts
  • src/adapters/cursor/protobuf-request.ts
  • src/adapters/cursor/cursor-errors.ts
  • matching Cursor tests

Summary by CodeRabbit

  • Bug Fixes
    • Improved Cursor conversation handling for tool-result continuations, preventing conversation-state corruption across external-model replay.
    • Added a one-time retry for specific Cursor invalid_argument failures that can occur during resume, using a fresh conversation identifier when safe.
    • Preserves existing conversation continuity when continuation is supported.
  • Diagnostics
    • Enhanced Cursor run request diagnostics with action type, model type, and conversation identifiers.
  • Tests
    • Added unit tests covering retry logic, conversation refresh behavior, model classification, and invalid-argument detection.

…ersation ids

External Cursor models like gpt-5.6-sol can reject resumeAction on a stale
server-side conversation with Connect invalid_argument after stepCompleted.
Mint a fresh conversation id for external tool-result continuations, keep
native composer resume unchanged, and retry once with full history replay.
@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 3d836f4d-f9dc-44eb-919e-a4aaf1a80ef8

📥 Commits

Reviewing files that changed from the base of the PR and between 29cb2dc and 9cf0abd.

📒 Files selected for processing (5)
  • src/adapters/cursor.ts
  • src/adapters/cursor/live-transport.ts
  • src/adapters/cursor/protobuf-events.ts
  • tests/cursor-adapter.test.ts
  • tests/cursor-protobuf-events.test.ts

📝 Walkthrough

Walkthrough

Changes

The Cursor adapter now distinguishes native and external wire models, selects fresh conversation IDs for external tool-result continuations, and retries qualifying invalid_argument failures once with regenerated conversation state. Usage totals are rekeyed, request diagnostics are emitted, and tests cover classification, construction, and recovery.

Cursor conversation classification and request options

Layer / File(s) Summary
Model and request classification contracts
src/adapters/cursor/cursor-errors.ts, src/adapters/cursor/discovery.ts, src/adapters/cursor/request-builder.ts
Adds invalid-argument and native/external wire-model classifiers plus the forceFreshConversation request option.
Conversation request construction
src/adapters/cursor/request-builder.ts, src/adapters/cursor/protobuf-request.ts, tests/cursor-discovery.test.ts, tests/cursor-request-builder.test.ts
External tool-result continuations generate new conversation IDs, native continuations preserve remembered IDs, and request action diagnostics identify the selected action and model category.
Runtime invalid-argument recovery
src/adapters/cursor.ts, src/adapters/cursor/live-transport.ts, src/adapters/cursor/protobuf-events.ts, tests/cursor-adapter.test.ts, tests/cursor-errors.test.ts, tests/cursor-protobuf-events.test.ts
runTurn retries qualifying failures once, updates the parsed conversation ID, and transfers context-usage totals to the regenerated conversation ID. Tests cover successful retry, suppression after output, error classification, and usage rekeying.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant runTurn
  participant createCursorRequest
  participant CursorTransport
  participant cursorContextUsageTracker
  runTurn->>createCursorRequest: Build continuation request
  createCursorRequest-->>runTurn: Return conversationId and request
  runTurn->>CursorTransport: Execute Cursor turn
  CursorTransport-->>runTurn: Return invalid_argument failure
  runTurn->>createCursorRequest: Force fresh conversation
  createCursorRequest-->>runTurn: Return regenerated conversationId and request
  runTurn->>cursorContextUsageTracker: Rekey usage totals
  runTurn->>CursorTransport: Retry turn once
Loading

Suggested reviewers: lidge-j, ingwannu

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: fixing external Cursor tool-result continuations by replaying them with fresh conversation IDs.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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 unit tests (beta)
  • Create PR with unit tests

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: 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 `@src/adapters/cursor.ts`:
- Around line 79-121: Update the runOnce/fallback flow in the cursor adapter to
track whether any non-heartbeat event was forwarded through emit, and allow the
fresh-conversation retry only when no output has been committed. Preserve the
existing invalid-argument, external-model, and abort checks, but rethrow after
forwarded text or tool-call events. Add a regression test covering an event
emitted before invalid_argument and verify the turn is not replayed.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: d9a316a7-4a99-42df-9750-8788b0b05cfe

📥 Commits

Reviewing files that changed from the base of the PR and between af973e5 and 29cb2dc.

📒 Files selected for processing (9)
  • src/adapters/cursor.ts
  • src/adapters/cursor/cursor-errors.ts
  • src/adapters/cursor/discovery.ts
  • src/adapters/cursor/protobuf-request.ts
  • src/adapters/cursor/request-builder.ts
  • tests/cursor-adapter.test.ts
  • tests/cursor-discovery.test.ts
  • tests/cursor-errors.test.ts
  • tests/cursor-request-builder.test.ts

Comment thread src/adapters/cursor.ts

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 29cb2dcd0f

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/adapters/cursor.ts
Comment thread src/adapters/cursor/request-builder.ts
…forward

Address PR lidge-jun#318 review feedback:
- retry invalid_argument only for external tool-result continuations with no committed output
- track emitted non-heartbeat events to avoid duplicate text/tool replay
- rekey Cursor context-usage carry-forward when conversation ids rotate
- add regression coverage for both behaviors

@Ingwannu Ingwannu left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

The fix is valuable, but the current one-shot fallback can replay already-committed output. runOnce() forwards every mapped event to emit immediately, while the catch block retries any qualifying external-model invalid_argument without checking whether text, thinking, or tool-call events were already emitted. If Cursor yields visible output and then throws, the second request replays the turn and duplicates/corrupts the client stream. The added test throws before yielding anything, so it does not cover this case.

Please track whether any non-heartbeat/output event has been forwarded and permit the fresh-conversation retry only before output is committed. Add a regression where attempt 1 emits text or a tool-call event and then throws invalid_argument; assert there is no second attempt and no duplicate output. Re-request review after that. I am leaving the PR open because the underlying external-model continuation fix is meaningful.

@Wibias Wibias added the bug Something isn't working label Jul 23, 2026

@Ingwannu Ingwannu left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Re-reviewed the updated head. The retry is now gated to an external-model tool-result continuation, invalid_argument, no abort, and no emitted non-heartbeat event. The new regression proves that text followed by invalid_argument does not trigger a second attempt, eliminating the duplicate-output blocker. Conversation-id rotation also rekeys context-usage carry-forward instead of dropping it. Integrated on current dev: 81 focused Cursor tests passed, typecheck and privacy scan passed; the PR head is green on Linux/macOS/Windows. Approved.

@Ingwannu
Ingwannu merged commit a0b9688 into lidge-jun:dev Jul 23, 2026
9 of 11 checks passed
eachann1024 pushed a commit to eachann1024/opencodex that referenced this pull request Jul 23, 2026
@Wibias
Wibias deleted the codex/fix-cursor-sol-continuation branch July 25, 2026 07:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants