Scope
Lineage only: who a Cursor conversation's parent is, what its root is, and — for subagents — the parent-of-subagent chain. Operator/user identity is out of scope for agent-bundle by maintainer decision (2026-09-03, see #391), so this issue tracks no identity fields; the framework passes them through inside the native payload unread.
What the host exposes (2026-09-03, live capture)
Evidence: docs/audits/2026-09-03-host-lineage-matrix.md §1–§5 and fixtures/host-lineage/cursor-3.18.25.ndjson (PR #421). Cursor 3.18.25 desktop, isolated --user-data-dir on Xvfb, plugin installed under ~/.cursor/plugins/local/host-test, Agents pane driven over CDP, one Task subagent that spawned a nested Task (depth 2).
| Fact |
Observed |
| Root conversation on its own events |
conversation_id (= session_id) + generation_id, cursor_version, workspace_roots |
subagentStart / subagentStop |
carried by the parent conversation: subagent_id = tool_call_id = the parent's Task tool_use_id (a two-line composite call-<uuid>-<n>\nfc_<id>_<k>), parent_conversation_id (= conversation_id), subagent_type, subagent_model, is_parallel_worker, task; generation_id equals the conversation id here. The child's conversation id is not included. |
| A subagent's own hooks |
a fresh conversation_id and nothing else: no parent_conversation_id, no subagent_id, no root; transcript_path is empty on the child (the root's names its own transcript, …/agent-transcripts/<root>/<root>.jsonl). Nested subagents likewise. |
MCP tools/call _meta |
progressToken only; client cursor-vscode 1.0.0. preToolUse fires with tool_name: "MCP:<tool>" before the call and postToolUse after, so ordering is the only correlation. |
| Not delivered to plugin hooks in this run |
sessionStart (never fired; the sibling raw hooks probe on the same build saw none either). workspaceOpen fired to user-level hooks only and sessionEnd did not fire — but see the desktop evidence below: both do reach plugin-scoped hooks on the desktop. preToolUse was delivered twice for some Read/Grep calls with the same tool_use_id. |
| Environment |
hooks receive CURSOR_PLUGIN_ROOT, CURSOR_PROJECT_DIR, CURSOR_VERSION, CURSOR_WORKSPACE_LABEL, CURSOR_EXTENSION_HOST_ROLE, CLAUDE_PLUGIN_ROOT, CLAUDE_PROJECT_DIR; no conversation id (shell commands the agent runs do get CURSOR_CONVERSATION_ID/CURSOR_REQUEST_ID, hooks do not) |
Desktop hooks-service evidence (2026-09-03, maintainer machine)
Cursor desktop writes ~/.config/Cursor/logs/<launch>/<window>/output_*/cursor.hooks.workspaceId-*.log and logs Hook step requested: <event> for every step before matching declared hooks (58,717 preToolUse steps were requested with no hook declared for them), so a missing step is non-dispatch, not non-registration. Across the retained logs (cursor_version 3.14.7, 2026-08-14 → 2026-08-25; 3.18.25 on 2026-09-03; 89,219 steps, 35 conversations, a local plugin declaring sessionStart/sessionEnd/workspaceOpen/stop/postToolUse/…):
| Step |
Requested |
Delivered to the plugin-scoped hook |
workspaceOpen |
12 |
yes (from claude-plugin config, sessionless envelope) |
sessionEnd |
6 |
yes — reason: window_close, final_status: none, session_id = conversation_id, generation_id: "", transcript_path: null |
beforeSubmitPrompt |
7 |
yes |
stop |
8 |
yes |
preCompact |
4 |
yes |
sessionStart |
0 |
— |
Only 7 of the 35 conversations ever produced beforeSubmitPrompt; 28 were first seen on a tool hook (Cursor restarted mid-conversation, or the plugin loaded mid-conversation). That is why root establishment cannot depend on sessionStart and why a blind child binding needs a correction path (below).
What the framework does about it
request.lineage (#421) is the single surface for parent/root/subagent on every host. On Cursor it binds by elimination and refuses when elimination is not possible:
- A never-seen
conversation_id on a tool hook binds to the pending subagentStart only when exactly one is pending in the same workspace_roots (resolution: 'inferred'); with several pending — parallel workers — it stays id-not-resolvable until all but one have stopped.
- A blind binding is undone the moment the bound conversation carries any root-only event (
beforeSubmitPrompt, stop, sessionEnd, preCompact — a subagent never does): the conversation is materialized as the root it is, anything it started meanwhile is re-rooted beneath it, and the pending child waits for its real conversation again. The correction runs before the event acts, so a sessionEnd on a misbound chat retires that chat, never the parent it was filed under.
- Only root-shaped events (
beforeSubmitPrompt, stop, sessionEnd, preCompact, sessionStart when delivered) establish a root; MCP calls resolve through the open MCP:<tool> pre-tool hook and are refused when open windows for that tool span several conversations.
- Duplicate
preToolUse deliveries replay the same journal entries (keys derive from the caller key and the payload digest).
- Cursor cloud agents run no user hooks, so the axis reports
cloud-agent-no-user-hooks there.
The workspace scoping and the root-only-event correction landed in #444 (merged as 284141958).
Host-side gaps (recorded for reference; not submitted to Cursor — maintainer decision 2026-09-03)
The Cursor lineage.parent/lineage.depth/lineage.root/mcp-correlation rows stay degraded (inferred / hook-correlated) until a Cursor build delivers:
- the child
conversation_id on subagentStart/subagentStop;
parent_conversation_id (and ideally the root) on every hook payload inside a subagent conversation;
- the conversation id (and the pre-tool
tool_use_id) in tools/call _meta, as Claude and Codex do;
sessionStart on the desktop (3.14.7 and 3.18.25 requested it 0× in 89,219 hook steps while dispatching sessionEnd, workspaceOpen, beforeSubmitPrompt, and stop; the cursor-agent CLI with trusted project hooks does dispatch it). workspaceOpen and sessionEnd are confirmed delivered to plugin-scoped hooks and are not part of this gap.
No feedback is sent to Cursor; the per-host write-up was removed in #467 (merged as 9d4fbd867) per maintainer decision, and docs/audits/2026-09-03-host-lineage-matrix.md plus the capability tables are the record a newer build is re-checked against.
Scope
Lineage only: who a Cursor conversation's parent is, what its root is, and — for subagents — the parent-of-subagent chain. Operator/user identity is out of scope for agent-bundle by maintainer decision (2026-09-03, see #391), so this issue tracks no identity fields; the framework passes them through inside the native payload unread.
What the host exposes (2026-09-03, live capture)
Evidence:
docs/audits/2026-09-03-host-lineage-matrix.md§1–§5 andfixtures/host-lineage/cursor-3.18.25.ndjson(PR #421). Cursor 3.18.25 desktop, isolated--user-data-diron Xvfb, plugin installed under~/.cursor/plugins/local/host-test, Agents pane driven over CDP, oneTasksubagent that spawned a nestedTask(depth 2).conversation_id(=session_id) +generation_id,cursor_version,workspace_rootssubagentStart/subagentStopsubagent_id=tool_call_id= the parent'sTasktool_use_id(a two-line compositecall-<uuid>-<n>\nfc_<id>_<k>),parent_conversation_id(=conversation_id),subagent_type,subagent_model,is_parallel_worker,task;generation_idequals the conversation id here. The child's conversation id is not included.conversation_idand nothing else: noparent_conversation_id, nosubagent_id, no root;transcript_pathis empty on the child (the root's names its own transcript,…/agent-transcripts/<root>/<root>.jsonl). Nested subagents likewise.tools/call_metaprogressTokenonly; clientcursor-vscode1.0.0.preToolUsefires withtool_name: "MCP:<tool>"before the call andpostToolUseafter, so ordering is the only correlation.sessionStart(never fired; the sibling raw hooks probe on the same build saw none either).workspaceOpenfired to user-level hooks only andsessionEnddid not fire — but see the desktop evidence below: both do reach plugin-scoped hooks on the desktop.preToolUsewas delivered twice for someRead/Grepcalls with the sametool_use_id.CURSOR_PLUGIN_ROOT,CURSOR_PROJECT_DIR,CURSOR_VERSION,CURSOR_WORKSPACE_LABEL,CURSOR_EXTENSION_HOST_ROLE,CLAUDE_PLUGIN_ROOT,CLAUDE_PROJECT_DIR; no conversation id (shell commands the agent runs do getCURSOR_CONVERSATION_ID/CURSOR_REQUEST_ID, hooks do not)Desktop hooks-service evidence (2026-09-03, maintainer machine)
Cursor desktop writes
~/.config/Cursor/logs/<launch>/<window>/output_*/cursor.hooks.workspaceId-*.logand logsHook step requested: <event>for every step before matching declared hooks (58,717preToolUsesteps were requested with no hook declared for them), so a missing step is non-dispatch, not non-registration. Across the retained logs (cursor_version 3.14.7, 2026-08-14 → 2026-08-25; 3.18.25 on 2026-09-03; 89,219 steps, 35 conversations, a local plugin declaringsessionStart/sessionEnd/workspaceOpen/stop/postToolUse/…):workspaceOpenfrom claude-plugin config, sessionless envelope)sessionEndreason: window_close,final_status: none,session_id=conversation_id,generation_id: "",transcript_path: nullbeforeSubmitPromptstoppreCompactsessionStartOnly 7 of the 35 conversations ever produced
beforeSubmitPrompt; 28 were first seen on a tool hook (Cursor restarted mid-conversation, or the plugin loaded mid-conversation). That is why root establishment cannot depend onsessionStartand why a blind child binding needs a correction path (below).What the framework does about it
request.lineage(#421) is the single surface for parent/root/subagent on every host. On Cursor it binds by elimination and refuses when elimination is not possible:conversation_idon a tool hook binds to the pendingsubagentStartonly when exactly one is pending in the sameworkspace_roots(resolution: 'inferred'); with several pending — parallel workers — it staysid-not-resolvableuntil all but one have stopped.beforeSubmitPrompt,stop,sessionEnd,preCompact— a subagent never does): the conversation is materialized as the root it is, anything it started meanwhile is re-rooted beneath it, and the pending child waits for its real conversation again. The correction runs before the event acts, so asessionEndon a misbound chat retires that chat, never the parent it was filed under.beforeSubmitPrompt,stop,sessionEnd,preCompact,sessionStartwhen delivered) establish a root; MCP calls resolve through the openMCP:<tool>pre-tool hook and are refused when open windows for that tool span several conversations.preToolUsedeliveries replay the same journal entries (keys derive from the caller key and the payload digest).cloud-agent-no-user-hooksthere.The workspace scoping and the root-only-event correction landed in #444 (merged as
284141958).Host-side gaps (recorded for reference; not submitted to Cursor — maintainer decision 2026-09-03)
The Cursor
lineage.parent/lineage.depth/lineage.root/mcp-correlationrows staydegraded(inferred / hook-correlated) until a Cursor build delivers:conversation_idonsubagentStart/subagentStop;parent_conversation_id(and ideally the root) on every hook payload inside a subagent conversation;tool_use_id) intools/call_meta, as Claude and Codex do;sessionStarton the desktop (3.14.7 and 3.18.25 requested it 0× in 89,219 hook steps while dispatchingsessionEnd,workspaceOpen,beforeSubmitPrompt, andstop; the cursor-agent CLI with trusted project hooks does dispatch it).workspaceOpenandsessionEndare confirmed delivered to plugin-scoped hooks and are not part of this gap.No feedback is sent to Cursor; the per-host write-up was removed in #467 (merged as
9d4fbd867) per maintainer decision, anddocs/audits/2026-09-03-host-lineage-matrix.mdplus the capability tables are the record a newer build is re-checked against.