Repository navigation
[Compatibility] Cursor Computer Use / node_repl tool results come back empty or truncated #1866
Description
Client or integration
Codex App
Provider or upstream service
cursor
OpenCodex version
2.18.0 (local proxy on localhost:10100; worktree HEAD e2ef24a)
Endpoint or capability
Cursor adapter tool-result replay for Codex Desktop Computer Use / node_repl. Observed on /v1/responses turns routed to cursor/grok-4.6.
Current behaviour
A Codex App session routed through the OpenCodex Cursor adapter can call every Computer Use API successfully, then lose the result on the next turn.
Live probe on 2026-08-17 (macOS, Chrome already open):
list_apps,get_app_state, screenshot emit,click,set_value,type_text,select_text,press_key,scroll,drag, andperform_secondary_actionall succeeded when invoked throughmcp__node_repl__js.- The next outer
execwrapper often returned empty output even though the nested Computer Use call completed. - Full AX trees and JPEG screenshots truncated in the model-visible tool result (
original token count: 43857/36963, then…8039 tokens truncated…). - After Chrome's focused window/tab changed, later actions failed with:
SkyComputerUseError: The user changed '/Applications/Google Chrome.app'. Re-query the latest state with `get_app_state` before sending more actions.
- After a node_repl reset or a failed nested call, the same session then reported
sky is not definedandIdentifier 'x' has already been declared.
OpenCodex itself stayed healthy: dashboard localhost:10100/#logs showed HTTP 200 for the Cursor-routed turns. The break is in how large Computer Use / node_repl results come back through the Cursor adapter, not in the proxy being down.
The Cursor adapter already bounds replayed history to CURSOR_EXTERNAL_ROOT_BYTE_LIMIT = 512 * 1024 and can truncate or drop tool-result blobs (src/adapters/cursor/protobuf-request.ts, truncateToolResultBlob). A Computer Use get_app_state payload is a full accessibility tree plus a JPEG screenshot; that is enough to hit the budget and leave the next turn with an empty or head-only result.
Expected behaviour
When Codex Desktop Computer Use / node_repl is used on a Cursor-routed turn:
- A completed nested
mcp__node_repl__jscall must surface a non-empty tool result to the model. Empty outer-execwrappers should not be treated as success. - Oversized Computer Use payloads (AX tree + screenshot) should be reduced to a usable summary that still names the focused window, URL, and the action error, instead of disappearing or arriving as a truncated binary dump.
- If Chrome state changed, the adapter/tool result should include the re-query instruction and the new focused element, not an empty cell that causes the model to retry with stale element indexes.
- node_repl declaration collisions and lost
skybindings should be reported as a recoverable tool error, not as a blank result.
Minimal redacted request or reproduction
1. Run OpenCodex 2.18.0 locally (`ocx` on port 10100).
2. Point Codex App at the local proxy and route the turn to cursor/grok-4.6.
3. Ask the model to inspect Chrome via Computer Use, then exercise every Computer Use API (list_apps, get_app_state, click, set_value, type_text, select_text, press_key, scroll, drag, perform_secondary_action, screenshot).
4. Observe:
- first successful get_app_state / screenshot tool result is truncated
- a later nested node_repl call returns empty outer-exec output
- changing the Chrome tab/window produces SkyComputerUseError: The user changed '/Applications/Google Chrome.app'
- a subsequent call in the same node_repl kernel fails with Identifier already declared or sky is not defined
5. Confirm the OpenCodex dashboard still logs those turns as HTTP 200.
Do not navigate the address bar, click bookmarks, stop the proxy, or star/unstar GitHub while reproducing. The failures above happen on read and low-impact UI actions.
Actual response or error
# empty outer exec after a completed nested Computer Use call
Script completed
Wall time 7.9 seconds
Output:
<empty>
# truncated Computer Use screenshot / AX tree
Warning: truncated output (original token count: 43857)
…8039 tokens truncated…
# Chrome state changed
SkyComputerUseError: The user changed '/Applications/Google Chrome.app'. Re-query the latest state with `get_app_state` before sending more actions.
# node_repl kernel collisions after reset / nested wrap
Identifier 'state' has already been declared
Identifier 'fs' has already been declared
sky is not defined
unsupported import in exec
Dashboard rows for the same window were HTTP 200 on cursor/grok-4.6, estimated usage, Cursor cache detail unreported.
Upstream documentation
No public Cursor Computer Use / root-replay specification. The OpenCodex Cursor adapter documents the replay budget and truncation marker in src/adapters/cursor/protobuf-request.ts:
CURSOR_EXTERNAL_ROOT_BLOB_LIMIT = 192CURSOR_EXTERNAL_ROOT_BYTE_LIMIT = 512 * 1024- truncation marker:
…[truncated for Cursor external replay budget]
Codex Desktop Computer Use is invoked only through mcp__node_repl__js importing @oai/sky. Outer code-mode exec cannot import @oai/sky (unsupported import in exec).
Related but not the same:
- [Bug]: Cursor adapter large-context turns collapse or rate-limit while direct Cursor remains healthy #1527 Cursor large-context collapse / rate-limit
- Tool-heavy Codex App sessions can enter an unbounded continuation loop with WebSocket forwarding #326 tool-heavy continuation loops
- [architecture][memory] Make 32 concurrent tool-recall sessions protocol-safe and memory-bounded #820 concurrent tool-recall memory bounds
Suggested mapping or implementation notes
Likely OpenCodex-side fixes, in order:
- Detect Computer Use / node_repl results that are empty after a successful nested call and surface a structured error instead of replaying a blank tool result.
- Before Cursor root replay, compact Computer Use payloads: keep AX text / error / focused element, drop or downscale the screenshot when over budget, and keep the truncation marker visible to the model.
- Do not drop the active trailing Computer Use tool result when it is the only evidence the model has for the next action.
- Treat
The user changed '/Applications/Google Chrome.app'as a recoverable Computer Use error that forcesget_app_staterather than as a silent empty cell.
Headless provider.desktopExecutor.computerUseCommand is a separate path (src/adapters/cursor/native-exec-desktop.ts) and was not used here. This report is the Codex Desktop Computer Use plugin flowing through the Cursor adapter.
Additional context and attachments
Worktree: /Users/jun/.codex/worktrees/7f9e/opencodex
Session: Codex App, Computer Use plugin, Chrome already running.
Proxy: OpenCodex 2.18.0 on localhost:10100, Cursor-routed grok-4.6.
I am not attaching screenshots or AX dumps because they contain local Chrome UI. The errors and dashboard HTTP 200s above are the reproducible signal.
Checks
- I searched existing provider and compatibility issues.
- The request and response were redacted.
- The expected behaviour is based on an upstream specification or a concrete client requirement.
Activity
coderabbitai commentedon Aug 16, 2026
🔗 Related PRs
#318 - fix(cursor): replay external model tool continuations with fresh conversation ids [merged]
#366 - fix(cursor): stable conversation continuity for store:false turns [merged]
#367 - fix(cursor): pin native composer conversation id to prompt_cache_key [open]
#387 - feat: ship packaged macOS menu bar companion [open]
#740 - perf(gui): progressive dashboard paint and Startup safety CLS [merged]
#757 - feat: route GPT-5.6 Pro through standard ChatGPT [open]
#770 - fix: research-batch reliability and security-boundary issues [closed]
#1106 - feat(toggles): Codex restore truth, Claude Desktop switch, composed acceptance (WP13 resume) [merged]
#1228 - fix(cursor): Add native image support for Cursor [open]
#1673 - fix(cursor): preserve unified exec in generic tool filtering [open]
📝 Issue Planner
Check the box below or use the
@coderabbitai plancommand to generate an implementation plan and prompts that you can use with your favorite coding assistant.🧪 Issue enrichment is currently in open beta.
To disable automatic issue enrichment, add the following to your
.coderabbit.yaml:💬 Have feedback or questions? Drop into our discord!
docs(devlog): adopt close-on-dev-merge for this run only
lidge-jun commentedon Aug 18, 2026
Closing — the decode campaign for this landed on
devin #1993 / #1994 / #1995 (merge4f72d6f75).What was actually wrong, in the order it bites a Computer Use session:
The turn could end with your tool call erased. A framed Cursor stream that ended at the HTTP/2 layer with no turn terminal, while a client tool call was still open, settled as success. Because a client tool call defers its
tool_call_startto completion, that open call emitted nothing at all — streaming degraded toresponse.incomplete, and the non-streaming path returnedcompletedfor a turn whose tool call had silently vanished. That is the emptyexecwrapper you saw.devhad independently fixed the same defect with a fail-closed error event; the merged result keeps that shape and adds theemittedTerminalflag so a second terminal cannot be appended after the turn already failed.A cancel nobody requested looked like a clean finish. An unexpected
NGHTTP2_CANCELproduced a turn with zero adapter events, because the adapter re-decided "benign?" from the error code alone with no provenance in scope — a cancel Cursor sent us was indistinguishable from one we sent. It is now a typed failure when it is not ours and no terminal was emitted (#1994).And the bridge reported all of this as success.
buildResponseJSONdefaulted tocompletedwith no adapter terminal, so even a truncated tool call came back marked complete. That default is gone, along with six related routes that installed compaction history from turns that never finished (#1995).One thing this does NOT fix, stated plainly: tool-result images. The Cursor encoder now emits real
McpImageContentinstead of placeholder text, but every Cursor model sits innoVisionModels, so the vision sidecar describes or strips images before the adapter ever runs. Screenshots still do not reach the model as images. Closing that needs role-aware vision preprocessing and is tracked separately — I would rather say so than let this issue close on a capability that does not reach production.Verified on Linux at
ff4b0bb4e: typecheck,privacy:scan,audit:high,build:gui, and the full isolated suite all green.lidge-jun commentedon Aug 18, 2026
Reopening with narrowed residual scope. Landed via #1993/#1994/#1995: the lifecycle half (erased trailing tool calls, unrequested-cancel provenance, terminal-less turns defaulting to completed). Still open from the original report: empty outer
execresults, screenshot/base64 stripping, AX-tree compaction, active trailing tool-result preservation, and recoverable-error normalization (SkyComputerUseError, redeclaration,sky is not defined). That residual work lives in draft #1920, which must NOT merge as-is: (a) it grants the full 512 KiB root budget per tool result instead of sharing it, (b) it matches generic names (click/js/scroll/screenshot) as Computer Use without namespace qualification, (c) it fabricates a user-switched-apps cause for everySkyComputerUseError, (d) request-builder size caps and regression tests are missing. Ordering constraint: merge #1997 first (assistant-role tool-result replay), then rebase #1920 on top — the stale #1920 diff still contains user-role replay and would regress the #1992 fix if merged first.lidge-jun commentedon Aug 18, 2026
Fixed on dev by #2038 (c42d1eb): empty or whitespace-only Computer Use / node_repl results now come back as an actionable error ([empty output: ... verify application state with get_app_state]) instead of silent emptiness, and plain-text runtime failure states (SkyComputerUseError, sky is not defined, redeclared identifier, unsupported import in exec) are marked as tool errors with recovery guidance — on the native wire AND the external replay path (cursor/grok-4.6, the repro here). Explicitly deferred from this fix: summarizing oversized AX-tree/screenshot TEXT payloads (the truncation half of this issue). The native path bounds oversized steps by dropping images oldest-first; text summarization would need its own design. If truncated-text cases persist after this lands, please open a follow-up scoped to that.
fix(cursor): normalize empty and failure-state Computer Use tool resu…
docs(devlog): adopt close-on-dev-merge for this run only
fix(cursor): normalize empty and failure-state Computer Use tool resu…