Skip to content

Commit a9f40a2

Browse files
committed
Add scenario for subagents
1 parent c22fad5 commit a9f40a2

4 files changed

Lines changed: 136 additions & 19 deletions

File tree

e2e/scenarios/cursor-sdk-instrumentation/__snapshots__/cursor-sdk-v1-auto-hook.span-events.json

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,38 @@
108108
],
109109
"type": null
110110
},
111-
"subagent_task": null,
111+
"subagent_task": {
112+
"has_input": true,
113+
"has_output": true,
114+
"metadata": {
115+
"cursor_sdk.tool.status": "completed",
116+
"gen_ai.tool.name": "task"
117+
},
118+
"metric_keys": [],
119+
"name": "Agent: <subagent>",
120+
"root_span_id": "<span:1>",
121+
"span_id": "<span:7>",
122+
"span_parents": [
123+
"<span:8>"
124+
],
125+
"type": "task"
126+
},
127+
"subagent_tool": {
128+
"has_input": true,
129+
"has_output": true,
130+
"metadata": {
131+
"cursor_sdk.tool.status": "completed",
132+
"gen_ai.tool.name": "task"
133+
},
134+
"metric_keys": [],
135+
"name": "tool: task",
136+
"root_span_id": "<span:1>",
137+
"span_id": "<span:8>",
138+
"span_parents": [
139+
"<span:9>"
140+
],
141+
"type": "tool"
142+
},
112143
"task": {
113144
"has_input": true,
114145
"has_output": true,
@@ -126,7 +157,7 @@
126157
],
127158
"name": "Cursor Agent",
128159
"root_span_id": "<span:1>",
129-
"span_id": "<span:7>",
160+
"span_id": "<span:9>",
130161
"span_parents": [
131162
"<span:6>"
132163
],
@@ -142,9 +173,9 @@
142173
"metric_keys": [],
143174
"name": "tool: shell",
144175
"root_span_id": "<span:1>",
145-
"span_id": "<span:8>",
176+
"span_id": "<span:10>",
146177
"span_parents": [
147-
"<span:7>"
178+
"<span:9>"
148179
],
149180
"type": "tool"
150181
}
@@ -159,7 +190,7 @@
159190
"metric_keys": [],
160191
"name": "cursor-sdk-wait-operation",
161192
"root_span_id": "<span:1>",
162-
"span_id": "<span:9>",
193+
"span_id": "<span:11>",
163194
"span_parents": [
164195
"<span:1>"
165196
],
@@ -193,9 +224,9 @@
193224
],
194225
"name": "Cursor Agent",
195226
"root_span_id": "<span:1>",
196-
"span_id": "<span:10>",
227+
"span_id": "<span:12>",
197228
"span_parents": [
198-
"<span:9>"
229+
"<span:11>"
199230
],
200231
"type": "task"
201232
}

e2e/scenarios/cursor-sdk-instrumentation/__snapshots__/cursor-sdk-v1-wrapped.span-events.json

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,38 @@
108108
],
109109
"type": null
110110
},
111-
"subagent_task": null,
111+
"subagent_task": {
112+
"has_input": true,
113+
"has_output": true,
114+
"metadata": {
115+
"cursor_sdk.tool.status": "completed",
116+
"gen_ai.tool.name": "task"
117+
},
118+
"metric_keys": [],
119+
"name": "Agent: <subagent>",
120+
"root_span_id": "<span:1>",
121+
"span_id": "<span:7>",
122+
"span_parents": [
123+
"<span:8>"
124+
],
125+
"type": "task"
126+
},
127+
"subagent_tool": {
128+
"has_input": true,
129+
"has_output": true,
130+
"metadata": {
131+
"cursor_sdk.tool.status": "completed",
132+
"gen_ai.tool.name": "task"
133+
},
134+
"metric_keys": [],
135+
"name": "tool: task",
136+
"root_span_id": "<span:1>",
137+
"span_id": "<span:8>",
138+
"span_parents": [
139+
"<span:9>"
140+
],
141+
"type": "tool"
142+
},
112143
"task": {
113144
"has_input": true,
114145
"has_output": true,
@@ -126,7 +157,7 @@
126157
],
127158
"name": "Cursor Agent",
128159
"root_span_id": "<span:1>",
129-
"span_id": "<span:7>",
160+
"span_id": "<span:9>",
130161
"span_parents": [
131162
"<span:6>"
132163
],
@@ -142,9 +173,9 @@
142173
"metric_keys": [],
143174
"name": "tool: shell",
144175
"root_span_id": "<span:1>",
145-
"span_id": "<span:8>",
176+
"span_id": "<span:10>",
146177
"span_parents": [
147-
"<span:7>"
178+
"<span:9>"
148179
],
149180
"type": "tool"
150181
}
@@ -159,7 +190,7 @@
159190
"metric_keys": [],
160191
"name": "cursor-sdk-wait-operation",
161192
"root_span_id": "<span:1>",
162-
"span_id": "<span:9>",
193+
"span_id": "<span:11>",
163194
"span_parents": [
164195
"<span:1>"
165196
],
@@ -193,9 +224,9 @@
193224
],
194225
"name": "Cursor Agent",
195226
"root_span_id": "<span:1>",
196-
"span_id": "<span:10>",
227+
"span_id": "<span:12>",
197228
"span_parents": [
198-
"<span:9>"
229+
"<span:11>"
199230
],
200231
"type": "task"
201232
}

e2e/scenarios/cursor-sdk-instrumentation/assertions.ts

Lines changed: 59 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,9 @@ function summarizeSpan(event: CapturedLogEvent | undefined): Json {
7070
if (typeof event.row.error === "string") {
7171
summary.error = event.row.error;
7272
}
73+
if (typeof summary.name === "string" && summary.name.startsWith("Agent:")) {
74+
summary.name = "Agent: <subagent>";
75+
}
7376
return summary;
7477
}
7578

@@ -82,6 +85,42 @@ function findCursorTask(events: CapturedLogEvent[], operationName: string) {
8285
return findChildSpans(events, "Cursor Agent", operation?.span.id).at(-1);
8386
}
8487

88+
function findSubagentTool(
89+
events: CapturedLogEvent[],
90+
parentId: string | undefined,
91+
) {
92+
if (!parentId) {
93+
return undefined;
94+
}
95+
return [...events]
96+
.reverse()
97+
.find(
98+
(event) =>
99+
event.span.type === "tool" &&
100+
event.span.parentIds.includes(parentId) &&
101+
["tool: Agent", "tool: Task", "tool: task"].includes(
102+
event.span.name ?? "",
103+
),
104+
);
105+
}
106+
107+
function findSubagentTask(
108+
events: CapturedLogEvent[],
109+
parentId: string | undefined,
110+
) {
111+
if (!parentId) {
112+
return undefined;
113+
}
114+
return [...events]
115+
.reverse()
116+
.find(
117+
(event) =>
118+
event.span.type === "task" &&
119+
event.span.parentIds.includes(parentId) &&
120+
event.span.name?.startsWith("Agent:"),
121+
);
122+
}
123+
85124
function outputText(event: CapturedLogEvent | undefined): string {
86125
return typeof event?.output === "string" ? event.output : "";
87126
}
@@ -95,10 +134,8 @@ function summarize(events: CapturedLogEvent[]): Json {
95134
"cursor-sdk-resume-conversation-operation",
96135
);
97136
const tool = findAllSpans(events, "tool: shell").at(-1);
98-
const subagentTask = events.find(
99-
(event) =>
100-
event.span.type === "task" && event.span.name?.startsWith("Agent:"),
101-
);
137+
const subagentTool = findSubagentTool(events, streamTask?.span.id);
138+
const subagentTask = findSubagentTask(events, subagentTool?.span.id);
102139

103140
return normalizeForSnapshot({
104141
conversation: {
@@ -119,6 +156,7 @@ function summarize(events: CapturedLogEvent[]): Json {
119156
findOperation(events, "cursor-sdk-stream-operation"),
120157
),
121158
subagent_task: summarizeSpan(subagentTask),
159+
subagent_tool: summarizeSpan(subagentTool),
122160
task: summarizeSpan(streamTask),
123161
tool: summarizeSpan(tool),
124162
},
@@ -213,6 +251,23 @@ export function defineCursorSDKInstrumentationAssertions(options: {
213251
},
214252
);
215253

254+
test("captures subagent spans when Cursor uses agents", testConfig, () => {
255+
const streamTask = findCursorTask(events, "cursor-sdk-stream-operation");
256+
const subagentTool = findSubagentTool(events, streamTask?.span.id);
257+
const subagentTask = findSubagentTask(events, subagentTool?.span.id);
258+
259+
expect(subagentTool).toBeDefined();
260+
expect(subagentTool?.metadata).toMatchObject({
261+
"cursor_sdk.tool.status": "completed",
262+
});
263+
expect(subagentTask).toBeDefined();
264+
expect(subagentTask?.span.rootId).toBe(streamTask?.span.rootId);
265+
expect(subagentTask?.metadata).toMatchObject({
266+
"cursor_sdk.tool.status": "completed",
267+
});
268+
expect(subagentTask?.output).toBeDefined();
269+
});
270+
216271
test("preserves user onDelta/onStep callbacks", testConfig, () => {
217272
expect(findLatestSpan(events, "cursor-sdk-user-on-delta")).toBeDefined();
218273
expect(findLatestSpan(events, "cursor-sdk-user-on-step")).toBeDefined();

e2e/scenarios/cursor-sdk-instrumentation/scenario.impl.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ async function runCursorSDKScenario({ decorateSDK, sdk }) {
6161
},
6262
});
6363
const run = await reusableAgent.send(
64-
"Run the shell command `printf cursor_tool_ok` and report the output. Do not edit files.",
64+
"First use the reviewer subagent to confirm exactly CURSOR_SUBAGENT_OK. Then run the shell command `printf cursor_tool_ok` and report the output. Do not edit files.",
6565
);
6666
await collectAsync(run.stream());
6767
});

0 commit comments

Comments
 (0)