Skip to content

feat(delegate): run cursor_delegate as a navigable child session#70

Open
justin-carper wants to merge 2 commits into
mainfrom
cursor-subagent-ui-output
Open

feat(delegate): run cursor_delegate as a navigable child session#70
justin-carper wants to merge 2 commits into
mainfrom
cursor-subagent-ui-output

Conversation

@justin-carper

Copy link
Copy Markdown
Collaborator

Problem

opencode lets you navigate into and follow subagent calls from a parent session. cursor_delegate did not participate in that UI: it ran the Cursor SDK directly inside the tool and collapsed the whole turn into a single result block, so Cursor's tool calls, reasoning, and text were invisible.

Why this approach

opencode's SDK has no API to inject externally-produced message parts into a session (/global/event is subscribe-only; there is no part-write endpoint). The only way to surface a live, navigable subagent is to let opencode itself drive the turn on a child session via session.create({ parentID }) + session.prompt(...).

What changed

cursor_delegate now (keeping its API-key check and context.ask permission gate up front):

  1. Creates a parent-linked child session (parentID = context.sessionID).
  2. Runs the delegated prompt through session.prompt against the cursor provider.
  3. opencode's existing provider pipeline (language-model.tsstream-map.ts) streams the child turn, so Cursor's live activity renders in the navigable subagent UI; the child session persists for later inspection.
  4. Returns the final text plus metadata { childSessionID, model, status, usage }.

Supporting changes:

  • Per-turn control bridge: mode/thinking/sandbox/agentId/cwd are forwarded to only the matching child session via a session-keyed map consumed by the existing chat.params hook, then cleared after the turn (siblings never inherit).
  • Provider: reads per-turn sandbox and cwd from providerOptions.cursor; cwd now reaches acquireAgent (not just the session query).
  • Abort: aborts the in-flight child on tool abort, and deletes a child that finishes creation after abort won the race so no empty orphan session lingers.
  • Removed the now-dead runDelegate runtime (src/provider/delegate.ts) and its tests.

Testing

  • npm test — 299 passing (added child-session creation/prompt shape, control forwarding + sibling isolation, per-turn cwd/sandbox, abort + orphan-delete, restored cloud-agent regressions).
  • npm run typecheck — clean.
  • npm run build — success.

Reviews

Two code-reviewer passes. First surfaced a cwd-propagation gap and an abort race (both fixed); second returned ready-to-merge with one Important item — abort-race child should be deleted, not aborted (a no-op on an unprompted session) — now fixed and covered by a test.

Notes

  • Same sandbox posture as before (provider path); the delegated Cursor execution is unchanged, only opencode now owns the turn.
  • Host dependency: the child is always reachable via parentID/session.children; whether the parent's tool block is click-through depends on the opencode TUI version. childSessionID is exposed in metadata to support click-through where available. No private TUI APIs used.

cursor_delegate previously ran the Cursor SDK directly inside the tool and
collapsed the turn into a single result block, so its tool calls, reasoning,
and text were invisible in opencode. opencode exposes no API to inject
externally-produced message parts into a session, so the only way to surface
that activity is to let opencode drive the turn.

The tool now creates a parent-linked child session (session.create with
parentID = the calling session) and runs the delegated prompt through
session.prompt against the cursor provider. opencode's existing provider
pipeline streams the child turn, so Cursor's live activity renders in the
navigable subagent UI and the child session persists for later inspection.

- Forward per-turn controls (mode/thinking/sandbox/agentId/cwd) to only the
  matching child session via a session-keyed control map consumed by the
  chat.params hook; controls are cleared after the turn so siblings never
  inherit them.
- Read per-turn sandbox and cwd from providerOptions.cursor so the child
  executes in the requested directory and sandbox (cwd now reaches
  acquireAgent, not just the session query).
- Abort the in-flight child on tool abort and delete a child that finishes
  creation after abort won the race, so no empty orphan session lingers.
- Remove the now-dead direct runDelegate runtime and its tests.

299 tests, typecheck, and build pass.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant