Client or integration
Codex App
Provider or upstream service
cursor
OpenCodex version
2.57.0
Endpoint or capability
/v1/responses Cursor protobuf tool calls
Current behaviour
On Codex Desktop through OpenCodex, Cursor models (including cursor/grok-4.6) talk Connect protobuf, not Responses. When a Cursor stream ends with a client tool still open, OpenCodex fail-closes:
Cursor stream ended with incomplete tool call(s)
That error is streamed. It is not thrown, so the overflow/invalid_argument remint ladder never runs. _cursorConversationId is assigned at the start of runTurn and persisted by continuationStateForResponse. The next turn reuses that id (or the deterministic thread hash). Cursor's side of the conversation is still waiting for mcpResult, so later turns in the same thread interrupt more often. Neighboring threads that never interrupted stay clean.
Native Composer replay also flushes unpaired toolCallStep history without a result, which looks like an interrupted attempt. External wire models such as grok-4.6 skip native mcpToolCall replay, so history repair alone cannot recover them; conversation remint is the path.
This is not a Cursor 429 / Ultra quota issue. The incomplete-tool event is a truncated stream, and CursorStreamTruncatedError is defined but never thrown.
Expected behaviour
Keep fail-closed on the current turn: do not emit a partial tool_call_start, and do not retry that send. After the incomplete-tool error is streamed, eligible non-isolated turns should remint the Cursor conversation id (same helper as overflow remint), persist the thread override for store:false clients, and invalidate the inherited checkpoint so the next turn does not resume a conversation left waiting for mcpResult. Isolated helper/compaction turns must not remint or donate that recovery to the parent.
Native Composer unpaired toolCallStep history should carry an honest [missing tool_result for this tool_use in history] placeholder (isError: true), matching Google #2199. External grok-4.6 stays on conversation remint.
Minimal redacted request or reproduction
Codex App -> local OpenCodex proxy -> cursor/grok-4.6 (or another Cursor model). Run a tool-using thread until the proxy reports Cursor stream ended with incomplete tool call(s), then continue the same thread. Later turns in that thread interrupt more often than a neighboring thread that never had a truncated tool stream.
Actual response or error
Cursor stream ended with incomplete tool call(s): <call id>. Arguments may be truncated; the call was not committed.
Subsequent turns in the same thread reuse the same Cursor conversation id and interrupt again.
Upstream documentation
No public Cursor Connect specification. The existing OpenCodex fail-closed path is finalizeTurnEvents in src/adapters/cursor/protobuf-events.ts. Overflow remint is the in-tree recovery helper this should reuse. Related repair: #2199 (Google missing tool_result). Distinct from xAI Responses adjacency (#4870 / #4871).
Suggested mapping or implementation notes
- Add
isCursorIncompleteToolCallMessage and flag that streamed error in src/adapters/cursor.ts.
- After
runOnce returns, remint via remintConversationId for non-isolated turns. Do not retry the current send.
- Native
conversationTurns flush: unpaired pendingToolCalls get toolCallStep(..., missingToolResult, codeMode).
- Do not classify this as 429. Quota/rate cues stay on the existing overflow/rate path.
Additional context and attachments
Related: #2199, #3506, #1527, #4245, #4870 (xAI Responses, different adapter). Cursor 429 / empty-spin is a different failure and is not requested here.
Checks
Client or integration
Codex App
Provider or upstream service
cursor
OpenCodex version
2.57.0
Endpoint or capability
/v1/responses Cursor protobuf tool calls
Current behaviour
On Codex Desktop through OpenCodex, Cursor models (including
cursor/grok-4.6) talk Connect protobuf, not Responses. When a Cursor stream ends with a client tool still open, OpenCodex fail-closes:Cursor stream ended with incomplete tool call(s)That error is streamed. It is not thrown, so the overflow/invalid_argument remint ladder never runs.
_cursorConversationIdis assigned at the start ofrunTurnand persisted bycontinuationStateForResponse. The next turn reuses that id (or the deterministic thread hash). Cursor's side of the conversation is still waiting formcpResult, so later turns in the same thread interrupt more often. Neighboring threads that never interrupted stay clean.Native Composer replay also flushes unpaired
toolCallStephistory without a result, which looks like an interrupted attempt. External wire models such as grok-4.6 skip nativemcpToolCallreplay, so history repair alone cannot recover them; conversation remint is the path.This is not a Cursor 429 / Ultra quota issue. The incomplete-tool event is a truncated stream, and
CursorStreamTruncatedErroris defined but never thrown.Expected behaviour
Keep fail-closed on the current turn: do not emit a partial
tool_call_start, and do not retry that send. After the incomplete-tool error is streamed, eligible non-isolated turns should remint the Cursor conversation id (same helper as overflow remint), persist the thread override for store:false clients, and invalidate the inherited checkpoint so the next turn does not resume a conversation left waiting formcpResult. Isolated helper/compaction turns must not remint or donate that recovery to the parent.Native Composer unpaired
toolCallStephistory should carry an honest[missing tool_result for this tool_use in history]placeholder (isError: true), matching Google #2199. External grok-4.6 stays on conversation remint.Minimal redacted request or reproduction
Codex App -> local OpenCodex proxy ->
cursor/grok-4.6(or another Cursor model). Run a tool-using thread until the proxy reportsCursor stream ended with incomplete tool call(s), then continue the same thread. Later turns in that thread interrupt more often than a neighboring thread that never had a truncated tool stream.Actual response or error
Upstream documentation
No public Cursor Connect specification. The existing OpenCodex fail-closed path is
finalizeTurnEventsinsrc/adapters/cursor/protobuf-events.ts. Overflow remint is the in-tree recovery helper this should reuse. Related repair: #2199 (Google missing tool_result). Distinct from xAI Responses adjacency (#4870 / #4871).Suggested mapping or implementation notes
isCursorIncompleteToolCallMessageand flag that streamed error insrc/adapters/cursor.ts.runOncereturns, remint viaremintConversationIdfor non-isolated turns. Do not retry the current send.conversationTurnsflush: unpairedpendingToolCallsgettoolCallStep(..., missingToolResult, codeMode).Additional context and attachments
Related: #2199, #3506, #1527, #4245, #4870 (xAI Responses, different adapter). Cursor 429 / empty-spin is a different failure and is not requested here.
Checks