Skip to content

[Compatibility] Cursor Computer Use / node_repl tool results come back empty or truncated #1866

Closed

Description

@lidge-jun

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):

  1. list_apps, get_app_state, screenshot emit, click, set_value, type_text, select_text, press_key, scroll, drag, and perform_secondary_action all succeeded when invoked through mcp__node_repl__js.
  2. The next outer exec wrapper often returned empty output even though the nested Computer Use call completed.
  3. Full AX trees and JPEG screenshots truncated in the model-visible tool result (original token count: 43857 / 36963, then …8039 tokens truncated…).
  4. 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.
  1. After a node_repl reset or a failed nested call, the same session then reported sky is not defined and Identifier '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__js call must surface a non-empty tool result to the model. Empty outer-exec wrappers 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 sky bindings 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 = 192
  • CURSOR_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:

Suggested mapping or implementation notes

Likely OpenCodex-side fixes, in order:

  1. 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.
  2. 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.
  3. Do not drop the active trailing Computer Use tool result when it is the only evidence the model has for the next action.
  4. Treat The user changed '/Applications/Google Chrome.app' as a recoverable Computer Use error that forces get_app_state rather 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

  1. added
    providerProvider adapters, OpenAI-compat presets, upstream API quirks
    toolstool_calls, MCP, web-search / sidecar tools
    on Aug 16, 2026
  2. coderabbitai commented on Aug 16, 2026

    @coderabbitai
    Contributor
    🔗 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 plan command to generate an implementation plan and prompts that you can use with your favorite coding assistant.

    • Create Plan

    🧪 Issue enrichment is currently in open beta.

    To disable automatic issue enrichment, add the following to your .coderabbit.yaml:

    issue_enrichment:
      auto_enrich:
        enabled: false

    💬 Have feedback or questions? Drop into our discord!

  3. lidge-jun commented on Aug 18, 2026

    @lidge-jun
    OwnerAuthor

    Closing — the decode campaign for this landed on dev in #1993 / #1994 / #1995 (merge 4f72d6f75).

    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_start to completion, that open call emitted nothing at all — streaming degraded to response.incomplete, and the non-streaming path returned completed for a turn whose tool call had silently vanished. That is the empty exec wrapper you saw. dev had independently fixed the same defect with a fail-closed error event; the merged result keeps that shape and adds the emittedTerminal flag so a second terminal cannot be appended after the turn already failed.

    A cancel nobody requested looked like a clean finish. An unexpected NGHTTP2_CANCEL produced 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. buildResponseJSON defaulted to completed with 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 McpImageContent instead of placeholder text, but every Cursor model sits in noVisionModels, 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.

  4. lidge-jun commented on Aug 18, 2026

    @lidge-jun
    OwnerAuthor

    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 exec results, 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 every SkyComputerUseError, (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.

  5. lidge-jun commented on Aug 18, 2026

    @lidge-jun
    OwnerAuthor

    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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingproviderProvider adapters, OpenAI-compat presets, upstream API quirksprovider-compatibilityProvider compatibility reportstoolstool_calls, MCP, web-search / sidecar tools

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions