pi-cursor-sdk 0.1.20 → 0.1.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +32 -0
- package/README.md +49 -9
- package/docs/cursor-dogfood-checklist.md +57 -0
- package/docs/cursor-live-smoke-checklist.md +115 -9
- package/docs/cursor-model-ux-spec.md +57 -17
- package/docs/cursor-native-tool-replay.md +15 -7
- package/docs/cursor-native-tool-visual-audit.md +104 -59
- package/docs/cursor-testing-lessons.md +8 -3
- package/docs/cursor-tool-surfaces.md +69 -0
- package/package.json +34 -10
- package/scripts/debug-provider-events.d.mts +59 -0
- package/scripts/debug-provider-events.mjs +70 -175
- package/scripts/debug-sdk-events.d.mts +90 -0
- package/scripts/debug-sdk-events.mjs +36 -98
- package/scripts/fixtures/plan-strip-shim/index.ts +12 -0
- package/scripts/isolated-cursor-smoke.sh +264 -102
- package/scripts/lib/cursor-child-process.d.mts +10 -0
- package/scripts/lib/cursor-child-process.mjs +50 -0
- package/scripts/lib/cursor-cli-args.d.mts +63 -0
- package/scripts/lib/cursor-cli-args.mjs +129 -0
- package/scripts/lib/cursor-script-fail.d.mts +1 -0
- package/scripts/lib/cursor-script-fail.mjs +13 -0
- package/scripts/lib/cursor-sdk-output-filter.d.mts +5 -0
- package/scripts/lib/cursor-smoke-env.d.mts +38 -0
- package/scripts/lib/cursor-smoke-env.mjs +81 -0
- package/scripts/lib/cursor-smoke-shell.sh +174 -0
- package/scripts/lib/cursor-visual-render.d.mts +15 -0
- package/scripts/lib/cursor-visual-render.mjs +131 -0
- package/scripts/probe-mcp-coldstart.mjs +20 -38
- package/scripts/refresh-cursor-model-snapshots.mjs +29 -65
- package/scripts/steering-rpc-smoke.mjs +170 -65
- package/scripts/tmux-live-smoke.sh +152 -98
- package/scripts/visual-tui-smoke.mjs +659 -0
- package/shared/cursor-sdk-event-debug-env.d.mts +12 -0
- package/shared/cursor-sdk-event-debug-env.mjs +13 -0
- package/shared/cursor-sensitive-text.d.mts +1 -0
- package/{scripts/lib/cursor-probe-utils.mjs → shared/cursor-sensitive-text.mjs} +1 -13
- package/shared/cursor-setting-sources.d.mts +5 -0
- package/shared/cursor-setting-sources.mjs +22 -0
- package/src/context.ts +21 -12
- package/src/cursor-bridge-contract.ts +1 -3
- package/src/cursor-incomplete-tool-visibility.ts +22 -5
- package/src/cursor-native-tool-display-registration.ts +63 -27
- package/src/cursor-native-tool-display-replay.ts +246 -144
- package/src/cursor-native-tool-display-state.ts +2 -0
- package/src/cursor-native-tool-display-tools.ts +149 -41
- package/src/cursor-provider-live-run-drain.ts +1 -52
- package/src/cursor-provider-run-finalizer.ts +235 -0
- package/src/cursor-provider-run-outcome.ts +149 -0
- package/src/cursor-provider-turn-api-key.ts +8 -0
- package/src/cursor-provider-turn-coordinator.ts +98 -446
- package/src/cursor-provider-turn-display-router.ts +216 -0
- package/src/cursor-provider-turn-emit.ts +59 -0
- package/src/cursor-provider-turn-finalize.ts +119 -0
- package/src/cursor-provider-turn-lifecycle-emitter.ts +97 -0
- package/src/cursor-provider-turn-message-offset.ts +15 -0
- package/src/cursor-provider-turn-prepare.ts +216 -0
- package/src/cursor-provider-turn-runner.ts +138 -0
- package/src/cursor-provider-turn-sdk-normalizer.ts +88 -0
- package/src/cursor-provider-turn-send.ts +103 -0
- package/src/cursor-provider-turn-shell-output.ts +107 -0
- package/src/cursor-provider-turn-tool-ledger.ts +126 -0
- package/src/cursor-provider-turn-types.ts +87 -0
- package/src/cursor-provider.ts +16 -504
- package/src/cursor-replay-activity-builders.ts +276 -0
- package/src/cursor-replay-source-names.ts +33 -0
- package/src/cursor-replay-summary-args.ts +191 -0
- package/src/cursor-replay-tool-details.ts +464 -0
- package/src/cursor-run-final-text.ts +56 -0
- package/src/cursor-sdk-abort-error-guard.ts +4 -0
- package/src/cursor-sdk-event-debug-constants.ts +14 -5
- package/src/cursor-sdk-event-debug.ts +2 -1
- package/src/cursor-sensitive-text.ts +3 -36
- package/src/cursor-session-agent.ts +3 -1
- package/src/cursor-setting-sources.ts +7 -10
- package/src/cursor-state.ts +232 -28
- package/src/cursor-tool-lifecycle.ts +9 -8
- package/src/cursor-tool-manifest.ts +41 -0
- package/src/cursor-tool-names.ts +18 -106
- package/src/cursor-tool-presentation-registry.ts +556 -0
- package/src/cursor-tool-transcript.ts +1 -1
- package/src/cursor-tool-visibility.ts +3 -27
- package/src/cursor-transcript-tool-formatters.ts +0 -59
- package/src/cursor-transcript-tool-specs.ts +158 -233
- package/src/cursor-transcript-utils.ts +0 -44
- package/src/cursor-web-tool-activity.ts +10 -60
- package/src/cursor-web-tool-args.ts +39 -0
- package/src/index.ts +4 -10
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
This document records maintainer testing lessons for `pi-cursor-sdk`. It complements unit tests and the [Cursor live smoke checklist](./cursor-live-smoke-checklist.md). Use it when adding regression coverage, debugging false-green releases, or building isolated smoke harnesses.
|
|
6
6
|
|
|
7
|
+
For a **minimal one-session dogfood pass** (baseline env, one native + one bridge call, JSONL ID patterns, bootstrap manifest, edit diff card), use the [Cursor dogfood checklist](./cursor-dogfood-checklist.md) before running the full live smoke matrix.
|
|
8
|
+
|
|
7
9
|
## Core lesson: integration-shaped bugs beat unit mocks
|
|
8
10
|
|
|
9
11
|
The native replay `Tool grep not found` failure was integration-shaped, not unit-shaped:
|
|
@@ -236,7 +238,7 @@ The script writes timestamped artifacts under `--out` (default `/tmp/pi-cursor-s
|
|
|
236
238
|
|
|
237
239
|
Stdout prints artifact paths and summary counts only. Raw payloads stay on disk and may contain local paths, project text, tool args/results, or secrets — do not commit or share them.
|
|
238
240
|
|
|
239
|
-
Hard repo rule: Cursor SDK behavior claims must come from the installed `@cursor/sdk` package and/or https://cursor.com/docs/sdk/typescript, not from memory or ad-hoc probes alone.
|
|
241
|
+
Hard repo rule: Cursor SDK behavior claims must come from the installed `@cursor/sdk` package and/or https://cursor.com/docs/sdk/typescript, not from memory or ad-hoc probes alone. Current cutover validation targets exact `@cursor/sdk@1.0.14` and pi 0.76.0 local packages.
|
|
240
242
|
|
|
241
243
|
## Pi provider SDK event capture
|
|
242
244
|
|
|
@@ -340,7 +342,7 @@ Ask the reporter (or capture yourself) for:
|
|
|
340
342
|
| `pi --version` and installed `pi-cursor-sdk` version | Confirms extension/runtime in use |
|
|
341
343
|
| Model ID (for example `cursor/composer-2.5`) | Routing/replay behavior is model-scoped |
|
|
342
344
|
| Exact repro prompt and prior turns | Multi-turn replay history affects prompt text |
|
|
343
|
-
| Flags: `--cursor-no-fast`, `PI_CURSOR_PI_TOOL_BRIDGE`, `PI_CURSOR_EXPOSE_BUILTIN_TOOLS`, `PI_CURSOR_SETTING_SOURCES` | Bridge vs native-only vs narrowed settings |
|
|
345
|
+
| Flags: `--cursor-no-fast`, `PI_CURSOR_PI_TOOL_BRIDGE`, `PI_CURSOR_EXPOSE_BUILTIN_TOOLS`, `PI_CURSOR_SETTING_SOURCES`, `PI_CURSOR_TOOL_MANIFEST` | Bridge vs native-only vs narrowed settings; bootstrap callable-surface manifest |
|
|
344
346
|
| Whether the listed names are `pi__*` bridge MCP, Cursor-native (`browser_navigate`, `WebSearch`), or `cursor-replay-*` replay IDs | Three different surfaces (see [Cursor native tool replay](./cursor-native-tool-replay.md#live-bridge-vs-replay)) |
|
|
345
347
|
| Red toast / `errorMessage` text, if any | Distinguishes #55 failure surfacing from silent text echo |
|
|
346
348
|
| Process exit / uncaught `ConnectError` / `ETIMEDOUT` stack trace, if any | Hard network crash (**#43**), not #40 model text echo |
|
|
@@ -425,4 +427,7 @@ rg '"type": "toolCall"|Tool call \(Cursor|cursor-replay-' "$SMOKE_DIR/session"/*
|
|
|
425
427
|
- `scripts/validate-smoke-jsonl.mjs`
|
|
426
428
|
- `scripts/debug-sdk-events.mjs`
|
|
427
429
|
- `scripts/debug-provider-events.mjs`
|
|
428
|
-
- `
|
|
430
|
+
- `shared/` — runtime-safe ESM helpers consumed by provider `src/` and maintainer scripts (`cursor-sensitive-text.mjs`, `cursor-setting-sources.mjs`).
|
|
431
|
+
- `scripts/lib/` — maintainer plumbing (CLI arg parsing, secret-aware `fail()`, child-process shutdown, shell timeout/auth helpers). Re-exports `shared/` helpers so published smoke/debug scripts stay aligned with provider runtime (`test/maintainer-scripts-lib.test.ts`).
|
|
432
|
+
- `test/helpers/pi-harness.ts` — canonical fake pi/extension harness (`createPiHarness`, shared model/context/event helpers)
|
|
433
|
+
- `test/helpers/cursor-provider-harness.ts` — Cursor SDK provider mocks and stream helpers (re-exports pi-harness fixtures; `createNativeToolDisplayPiForTest` for native replay)
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# Cursor tool surfaces in pi
|
|
2
|
+
|
|
3
|
+
pi-cursor-sdk runs Cursor models through the local `@cursor/sdk` agent runtime. A single pi session can expose **three related but different** tool namespaces. This page is the user-facing guide; maintainer replay details live in [Cursor native tool replay](./cursor-native-tool-replay.md).
|
|
4
|
+
|
|
5
|
+
## The three surfaces
|
|
6
|
+
|
|
7
|
+
| Surface | Who owns it | Callable by Cursor? | What pi shows |
|
|
8
|
+
| --- | --- | --- | --- |
|
|
9
|
+
| **Cursor SDK host tools** | Cursor local agent | Yes | Native replay cards (`read`, `bash`, …) or neutral Cursor activity. Representative ToolType list: [SDK ToolType replay matrix](./cursor-native-tool-replay.md#sdk-tooltype-replay-matrix). |
|
|
10
|
+
| **Configured Cursor MCP** | Cursor settings / `~/.cursor/mcp.json` | Yes (when loaded) | Neutral **Cursor MCP** activity cards on replay |
|
|
11
|
+
| **Pi bridge (`pi__*`)** | pi-cursor-sdk loopback MCP | Yes, when exposed | Real pi tool names (`cursor_ask_question`, extension tools, …) |
|
|
12
|
+
|
|
13
|
+
**Not callable:** `cursor-replay-*` IDs in JSONL, pi history tool names used only for display, and transcript labels. Cursor must call exposed `pi__*` MCP names for bridged pi tools, not the pi card name.
|
|
14
|
+
|
|
15
|
+
## Discoverability
|
|
16
|
+
|
|
17
|
+
- **MCP `listTools`** (and pi's MCP catalog when present) lists **MCP servers only** — for example `pi_tools` with `pi__cursor_ask_question`. It does **not** enumerate Cursor SDK host tools such as `Read` or `Shell`.
|
|
18
|
+
- **Bootstrap prompts** include a short **Cursor SDK tool boundary** block plus a compact **callable tool surfaces** manifest by default (disable manifest with `PI_CURSOR_TOOL_MANIFEST=0`). The manifest lists host-tool categories, bridge `pi__*` names for the current run, and a reminder that configured Cursor MCP servers appear at runtime via `listTools`. MCP `listTools` entries for bridged pi tools point back to the bootstrap prompt instead of repeating the full contract.
|
|
19
|
+
- **Incremental prompts** omit the full boundary block but keep a short tail guard (including an explicit shell `cd` hint); the session agent retains prior bootstrap context.
|
|
20
|
+
- **In-session debug:** `/cursor-tools` prints bridge enablement, manifest enablement, effective `PI_CURSOR_SETTING_SOURCES`, and the current callable-surface snapshot.
|
|
21
|
+
|
|
22
|
+
## Pi bridge vs Cursor native
|
|
23
|
+
|
|
24
|
+
Default behavior:
|
|
25
|
+
|
|
26
|
+
- Cursor host tools handle files, shell, grep, edits, tasks, and Cursor-native MCP/plugins.
|
|
27
|
+
- The pi bridge exposes **active pi tools** as `pi__*` MCP names when `PI_CURSOR_PI_TOOL_BRIDGE` is enabled (default on).
|
|
28
|
+
- Overlapping pi builtins (`read`, `bash`, `write`, `edit`, `grep`, `find`, `ls`) are **hidden** from the bridge unless `PI_CURSOR_EXPOSE_BUILTIN_TOOLS=1`.
|
|
29
|
+
|
|
30
|
+
`pi-cursor-sdk` always registers `cursor_ask_question` for Cursor models when the bridge is on; Cursor sees `pi__cursor_ask_question`.
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
# Disable pi bridge entirely
|
|
34
|
+
PI_CURSOR_PI_TOOL_BRIDGE=0 pi --model cursor/composer-2.5
|
|
35
|
+
|
|
36
|
+
# Expose overlapping pi builtins through the bridge
|
|
37
|
+
PI_CURSOR_EXPOSE_BUILTIN_TOOLS=1 pi --model cursor/composer-2.5
|
|
38
|
+
|
|
39
|
+
# Disable bootstrap tool manifest
|
|
40
|
+
PI_CURSOR_TOOL_MANIFEST=0 pi --model cursor/composer-2.5
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Cursor settings vs pi toggles
|
|
44
|
+
|
|
45
|
+
Disabling or removing an MCP server **only in pi** does not remove Cursor ambient MCP loaded from Cursor config.
|
|
46
|
+
|
|
47
|
+
| Control | Effect |
|
|
48
|
+
| --- | --- |
|
|
49
|
+
| `PI_CURSOR_SETTING_SOURCES=all` (default) | Loads user/project Cursor MCP, plugins, rules (`~/.cursor/mcp.json`, etc.) |
|
|
50
|
+
| `PI_CURSOR_SETTING_SOURCES=none` | Disables ambient Cursor setting sources for local agents |
|
|
51
|
+
| `PI_CURSOR_SETTING_SOURCES=project,plugins` | Narrows which layers load |
|
|
52
|
+
| Empty or edited `~/.cursor/mcp.json` | Changes which user MCP servers Cursor connects to |
|
|
53
|
+
|
|
54
|
+
To reproduce a **minimal** surface (pi-cursor-sdk + Cursor host only), use extension-only install, empty user MCP config, and `PI_CURSOR_SETTING_SOURCES=none` when you do not need Cursor rules/MCP from disk.
|
|
55
|
+
|
|
56
|
+
## JSONL ID patterns (debugging)
|
|
57
|
+
|
|
58
|
+
| ID prefix | Meaning |
|
|
59
|
+
| --- | --- |
|
|
60
|
+
| `cursor-replay-*` | Display-only replay of Cursor SDK activity |
|
|
61
|
+
| `cursor-pi-bridge-run-*` | Live pi execution via bridge |
|
|
62
|
+
|
|
63
|
+
Example mistake: treating `cursor-replay-…` as a tool to invoke. Replay never re-runs work.
|
|
64
|
+
|
|
65
|
+
## Related docs
|
|
66
|
+
|
|
67
|
+
- [README — Cursor provider tool contract](../README.md#cursor-provider-tool-contract)
|
|
68
|
+
- [Cursor native tool replay](./cursor-native-tool-replay.md)
|
|
69
|
+
- [Cursor model UX spec](./cursor-model-ux-spec.md)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-cursor-sdk",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.21",
|
|
4
4
|
"description": "pi provider extension backed by @cursor/sdk local agents",
|
|
5
5
|
"author": "Mitch Fultz (https://github.com/fitchmultz)",
|
|
6
6
|
"license": "MIT",
|
|
@@ -22,21 +22,39 @@
|
|
|
22
22
|
},
|
|
23
23
|
"homepage": "https://github.com/fitchmultz/pi-cursor-sdk#readme",
|
|
24
24
|
"files": [
|
|
25
|
+
"shared",
|
|
25
26
|
"src",
|
|
26
27
|
"scripts/refresh-cursor-model-snapshots.mjs",
|
|
27
28
|
"scripts/steering-rpc-smoke.mjs",
|
|
28
29
|
"scripts/tmux-live-smoke.sh",
|
|
30
|
+
"scripts/visual-tui-smoke.mjs",
|
|
29
31
|
"scripts/isolated-cursor-smoke.sh",
|
|
32
|
+
"scripts/fixtures/plan-strip-shim",
|
|
30
33
|
"scripts/validate-smoke-jsonl.mjs",
|
|
31
34
|
"scripts/probe-mcp-coldstart.mjs",
|
|
32
35
|
"scripts/debug-sdk-events.mjs",
|
|
36
|
+
"scripts/debug-sdk-events.d.mts",
|
|
33
37
|
"scripts/debug-provider-events.mjs",
|
|
34
|
-
"scripts/
|
|
38
|
+
"scripts/debug-provider-events.d.mts",
|
|
39
|
+
"scripts/lib/cursor-cli-args.mjs",
|
|
40
|
+
"scripts/lib/cursor-cli-args.d.mts",
|
|
41
|
+
"scripts/lib/cursor-child-process.mjs",
|
|
42
|
+
"scripts/lib/cursor-child-process.d.mts",
|
|
43
|
+
"scripts/lib/cursor-script-fail.mjs",
|
|
44
|
+
"scripts/lib/cursor-script-fail.d.mts",
|
|
45
|
+
"scripts/lib/cursor-smoke-env.mjs",
|
|
46
|
+
"scripts/lib/cursor-smoke-env.d.mts",
|
|
47
|
+
"scripts/lib/cursor-smoke-shell.sh",
|
|
48
|
+
"scripts/lib/cursor-visual-render.mjs",
|
|
49
|
+
"scripts/lib/cursor-visual-render.d.mts",
|
|
35
50
|
"scripts/lib/cursor-sdk-output-filter.mjs",
|
|
51
|
+
"scripts/lib/cursor-sdk-output-filter.d.mts",
|
|
36
52
|
"README.md",
|
|
37
53
|
"docs/cursor-model-ux-spec.md",
|
|
54
|
+
"docs/cursor-tool-surfaces.md",
|
|
38
55
|
"docs/cursor-live-smoke-checklist.md",
|
|
39
56
|
"docs/cursor-testing-lessons.md",
|
|
57
|
+
"docs/cursor-dogfood-checklist.md",
|
|
40
58
|
"docs/cursor-native-tool-replay.md",
|
|
41
59
|
"docs/cursor-native-tool-visual-audit.md",
|
|
42
60
|
"LICENSE",
|
|
@@ -47,11 +65,15 @@
|
|
|
47
65
|
"node": ">=22.19.0"
|
|
48
66
|
},
|
|
49
67
|
"scripts": {
|
|
50
|
-
"typecheck": "
|
|
68
|
+
"typecheck": "npm run typecheck:src && npm run typecheck:tests && npm run typecheck:replay-compile",
|
|
69
|
+
"typecheck:src": "tsc --noEmit",
|
|
70
|
+
"typecheck:tests": "tsc -p tsconfig.test.json --noEmit",
|
|
71
|
+
"typecheck:replay-compile": "tsc --noEmit -p test/tsconfig.json",
|
|
51
72
|
"test": "vitest run",
|
|
52
73
|
"test:watch": "vitest",
|
|
53
74
|
"refresh:cursor-snapshots": "node scripts/refresh-cursor-model-snapshots.mjs",
|
|
54
75
|
"smoke:live": "scripts/tmux-live-smoke.sh",
|
|
76
|
+
"smoke:visual": "node scripts/visual-tui-smoke.mjs",
|
|
55
77
|
"smoke:isolated": "scripts/isolated-cursor-smoke.sh",
|
|
56
78
|
"smoke:steering": "node scripts/steering-rpc-smoke.mjs",
|
|
57
79
|
"smoke:jsonl": "node scripts/validate-smoke-jsonl.mjs",
|
|
@@ -60,19 +82,21 @@
|
|
|
60
82
|
"debug:mcp-coldstart": "node scripts/probe-mcp-coldstart.mjs"
|
|
61
83
|
},
|
|
62
84
|
"dependencies": {
|
|
63
|
-
"@cursor/sdk": "
|
|
85
|
+
"@cursor/sdk": "1.0.14",
|
|
64
86
|
"@modelcontextprotocol/sdk": "^1.29.0"
|
|
65
87
|
},
|
|
66
88
|
"peerDependencies": {
|
|
67
|
-
"@earendil-works/pi-ai": "
|
|
68
|
-
"@earendil-works/pi-coding-agent": "
|
|
69
|
-
"@earendil-works/pi-tui": "
|
|
89
|
+
"@earendil-works/pi-ai": ">=0.76.0",
|
|
90
|
+
"@earendil-works/pi-coding-agent": ">=0.76.0",
|
|
91
|
+
"@earendil-works/pi-tui": ">=0.76.0",
|
|
70
92
|
"typebox": "*"
|
|
71
93
|
},
|
|
72
94
|
"devDependencies": {
|
|
73
|
-
"@earendil-works/pi-ai": "
|
|
74
|
-
"@earendil-works/pi-coding-agent": "
|
|
75
|
-
"@earendil-works/pi-tui": "
|
|
95
|
+
"@earendil-works/pi-ai": "0.76.0",
|
|
96
|
+
"@earendil-works/pi-coding-agent": "0.76.0",
|
|
97
|
+
"@earendil-works/pi-tui": "0.76.0",
|
|
98
|
+
"@xterm/xterm": "^6.0.0",
|
|
99
|
+
"playwright": "^1.60.0",
|
|
76
100
|
"typebox": "^1.1.38",
|
|
77
101
|
"typescript": "^6.0.3",
|
|
78
102
|
"vitest": "^4.1.6"
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
export interface CursorDebugProviderEventsArgs {
|
|
2
|
+
cwd: string;
|
|
3
|
+
model: string;
|
|
4
|
+
prompt?: string;
|
|
5
|
+
promptFile?: string;
|
|
6
|
+
out?: string;
|
|
7
|
+
settingSources?: string[] | undefined;
|
|
8
|
+
sessionDir?: string;
|
|
9
|
+
apiKey?: string;
|
|
10
|
+
help: boolean;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export declare function parseDebugProviderEventsArgs(
|
|
14
|
+
argv: string[],
|
|
15
|
+
env?: NodeJS.ProcessEnv,
|
|
16
|
+
): CursorDebugProviderEventsArgs;
|
|
17
|
+
|
|
18
|
+
export interface CursorPiSessionSnapshotState {
|
|
19
|
+
copied: boolean;
|
|
20
|
+
sessionFile?: string;
|
|
21
|
+
reason?: string;
|
|
22
|
+
recoveredAfterChildExit?: boolean;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export type CursorDebugCaptureCounts = Record<string, number | Record<string, number>>;
|
|
26
|
+
|
|
27
|
+
export interface CursorDebugCaptureSummary {
|
|
28
|
+
artifactDir: string;
|
|
29
|
+
sessionFile?: string;
|
|
30
|
+
counts: CursorDebugCaptureCounts;
|
|
31
|
+
piSessionSnapshot?: CursorPiSessionSnapshotState;
|
|
32
|
+
artifacts?: Record<string, string>;
|
|
33
|
+
elapsedMs?: number;
|
|
34
|
+
waitResultRecorded?: boolean;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface CursorDebugProviderEventsRunSummary {
|
|
38
|
+
artifactDir: string;
|
|
39
|
+
artifacts: Record<string, string>;
|
|
40
|
+
counts: CursorDebugCaptureCounts;
|
|
41
|
+
elapsedMs: number;
|
|
42
|
+
model: string;
|
|
43
|
+
cwd: string;
|
|
44
|
+
sessionDir: string;
|
|
45
|
+
extensionVersion: string;
|
|
46
|
+
sdkVersion: string;
|
|
47
|
+
waitResultRecorded: boolean;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export declare function backfillPiSessionSnapshot(
|
|
51
|
+
captureSummary: CursorDebugCaptureSummary | undefined,
|
|
52
|
+
artifactDir: string,
|
|
53
|
+
sessionDir: string,
|
|
54
|
+
): CursorDebugCaptureSummary | undefined;
|
|
55
|
+
|
|
56
|
+
export declare function runDebugProviderEvents(
|
|
57
|
+
args: CursorDebugProviderEventsArgs,
|
|
58
|
+
env?: NodeJS.ProcessEnv,
|
|
59
|
+
): Promise<CursorDebugProviderEventsRunSummary>;
|
|
@@ -5,20 +5,26 @@
|
|
|
5
5
|
import { copyFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
6
6
|
import { spawn } from "node:child_process";
|
|
7
7
|
import { createRequire } from "node:module";
|
|
8
|
-
import { dirname, join
|
|
8
|
+
import { dirname, join } from "node:path";
|
|
9
9
|
import { fileURLToPath } from "node:url";
|
|
10
10
|
import {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
apiKeySecretsFromProcess,
|
|
12
|
+
commonProbeFlags,
|
|
13
|
+
defaultApiKeyFromEnv,
|
|
14
|
+
defaultSettingSourcesFromEnv,
|
|
15
|
+
parseArgv,
|
|
16
|
+
requireApiKey,
|
|
17
|
+
} from "./lib/cursor-cli-args.mjs";
|
|
18
|
+
import { parseJsonLines, terminateChild, waitForChildClose } from "./lib/cursor-child-process.mjs";
|
|
19
|
+
import { scrubSensitiveText } from "../shared/cursor-sensitive-text.mjs";
|
|
20
|
+
import { createScriptFail } from "./lib/cursor-script-fail.mjs";
|
|
21
|
+
import { serializeCursorSettingSources } from "../shared/cursor-setting-sources.mjs";
|
|
15
22
|
|
|
16
23
|
const require = createRequire(import.meta.url);
|
|
17
24
|
const root = fileURLToPath(new URL("..", import.meta.url));
|
|
18
25
|
const packageJson = require("../package.json");
|
|
19
26
|
const DEFAULT_MODEL = "cursor/composer-2.5";
|
|
20
27
|
const DEFAULT_OUT_BASE = ".debug/cursor-sdk-events";
|
|
21
|
-
const CHILD_SHUTDOWN_GRACE_MS = 2_000;
|
|
22
28
|
const SDK_EVENT_DEBUG_LOG_PREFIX = "[pi-cursor-sdk:sdk-events]";
|
|
23
29
|
const PI_SESSION_SNAPSHOT_ARTIFACT = "pi-session-snapshot.jsonl";
|
|
24
30
|
const SESSION_PI_SESSION_SNAPSHOT = "pi-session.jsonl";
|
|
@@ -72,113 +78,32 @@ Safety:
|
|
|
72
78
|
- Raw artifact files may contain local paths, tool args/results, or secrets. Do not commit or share them.`);
|
|
73
79
|
}
|
|
74
80
|
|
|
75
|
-
|
|
76
|
-
const scrubbed = scrubSensitiveText(message, secrets[0]);
|
|
77
|
-
console.error(`debug-provider-events: ${scrubbed}`);
|
|
78
|
-
process.exit(1);
|
|
79
|
-
}
|
|
81
|
+
const fail = createScriptFail("debug-provider-events");
|
|
80
82
|
|
|
81
83
|
export function parseDebugProviderEventsArgs(argv, env = process.env) {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
if (arg.startsWith("--cwd=")) {
|
|
106
|
-
args.cwd = resolve(arg.slice("--cwd=".length));
|
|
107
|
-
continue;
|
|
108
|
-
}
|
|
109
|
-
if (arg === "--model") {
|
|
110
|
-
const value = argv[++index];
|
|
111
|
-
if (!value || value.startsWith("--")) fail("--model requires a value");
|
|
112
|
-
args.model = value.trim();
|
|
113
|
-
continue;
|
|
114
|
-
}
|
|
115
|
-
if (arg.startsWith("--model=")) {
|
|
116
|
-
args.model = arg.slice("--model=".length).trim();
|
|
117
|
-
continue;
|
|
118
|
-
}
|
|
119
|
-
if (arg === "--prompt") {
|
|
120
|
-
const value = argv[++index];
|
|
121
|
-
if (!value || value.startsWith("--")) fail("--prompt requires a value");
|
|
122
|
-
args.prompt = value;
|
|
123
|
-
continue;
|
|
124
|
-
}
|
|
125
|
-
if (arg.startsWith("--prompt=")) {
|
|
126
|
-
args.prompt = arg.slice("--prompt=".length);
|
|
127
|
-
continue;
|
|
128
|
-
}
|
|
129
|
-
if (arg === "--prompt-file") {
|
|
130
|
-
const value = argv[++index];
|
|
131
|
-
if (!value || value.startsWith("--")) fail("--prompt-file requires a path");
|
|
132
|
-
args.promptFile = resolve(value);
|
|
133
|
-
continue;
|
|
134
|
-
}
|
|
135
|
-
if (arg.startsWith("--prompt-file=")) {
|
|
136
|
-
args.promptFile = resolve(arg.slice("--prompt-file=".length));
|
|
137
|
-
continue;
|
|
138
|
-
}
|
|
139
|
-
if (arg === "--out") {
|
|
140
|
-
const value = argv[++index];
|
|
141
|
-
if (!value || value.startsWith("--")) fail("--out requires a directory path");
|
|
142
|
-
args.out = resolve(value);
|
|
143
|
-
continue;
|
|
144
|
-
}
|
|
145
|
-
if (arg.startsWith("--out=")) {
|
|
146
|
-
args.out = resolve(arg.slice("--out=".length));
|
|
147
|
-
continue;
|
|
148
|
-
}
|
|
149
|
-
if (arg === "--session-dir") {
|
|
150
|
-
const value = argv[++index];
|
|
151
|
-
if (!value || value.startsWith("--")) fail("--session-dir requires a path");
|
|
152
|
-
args.sessionDir = resolve(value);
|
|
153
|
-
continue;
|
|
154
|
-
}
|
|
155
|
-
if (arg.startsWith("--session-dir=")) {
|
|
156
|
-
args.sessionDir = resolve(arg.slice("--session-dir=".length));
|
|
157
|
-
continue;
|
|
158
|
-
}
|
|
159
|
-
if (arg === "--setting-sources") {
|
|
160
|
-
const value = argv[++index];
|
|
161
|
-
if (!value || value.startsWith("--")) fail("--setting-sources requires a value");
|
|
162
|
-
args.settingSources = resolveCursorSettingSources(value);
|
|
163
|
-
continue;
|
|
164
|
-
}
|
|
165
|
-
if (arg.startsWith("--setting-sources=")) {
|
|
166
|
-
args.settingSources = resolveCursorSettingSources(arg.slice("--setting-sources=".length));
|
|
167
|
-
continue;
|
|
168
|
-
}
|
|
169
|
-
if (arg === "--api-key") {
|
|
170
|
-
const value = argv[++index];
|
|
171
|
-
if (!value || value.startsWith("--")) fail("--api-key requires a value");
|
|
172
|
-
args.apiKey = value.trim();
|
|
173
|
-
continue;
|
|
174
|
-
}
|
|
175
|
-
if (arg.startsWith("--api-key=")) {
|
|
176
|
-
args.apiKey = arg.slice("--api-key=".length).trim();
|
|
177
|
-
continue;
|
|
178
|
-
}
|
|
179
|
-
fail(`unknown argument: ${arg}`);
|
|
180
|
-
}
|
|
181
|
-
return args;
|
|
84
|
+
return parseArgv(argv, {
|
|
85
|
+
defaults: {
|
|
86
|
+
cwd: root,
|
|
87
|
+
model: DEFAULT_MODEL,
|
|
88
|
+
prompt: undefined,
|
|
89
|
+
promptFile: undefined,
|
|
90
|
+
out: undefined,
|
|
91
|
+
settingSources: defaultSettingSourcesFromEnv(env),
|
|
92
|
+
sessionDir: undefined,
|
|
93
|
+
apiKey: defaultApiKeyFromEnv(env),
|
|
94
|
+
},
|
|
95
|
+
flags: {
|
|
96
|
+
cwd: commonProbeFlags.cwd,
|
|
97
|
+
model: commonProbeFlags.model,
|
|
98
|
+
prompt: commonProbeFlags.prompt,
|
|
99
|
+
promptFile: commonProbeFlags.promptFile,
|
|
100
|
+
out: commonProbeFlags.out,
|
|
101
|
+
sessionDir: commonProbeFlags.sessionDir,
|
|
102
|
+
apiKey: commonProbeFlags.apiKey,
|
|
103
|
+
settingSources: commonProbeFlags.settingSources,
|
|
104
|
+
},
|
|
105
|
+
fail,
|
|
106
|
+
});
|
|
182
107
|
}
|
|
183
108
|
|
|
184
109
|
function defaultOutDir(cwd) {
|
|
@@ -186,55 +111,6 @@ function defaultOutDir(cwd) {
|
|
|
186
111
|
return join(cwd, DEFAULT_OUT_BASE, stamp);
|
|
187
112
|
}
|
|
188
113
|
|
|
189
|
-
function parseEvents(stdout) {
|
|
190
|
-
const events = [];
|
|
191
|
-
for (const line of stdout.split("\n")) {
|
|
192
|
-
if (!line.trim()) continue;
|
|
193
|
-
try {
|
|
194
|
-
events.push(JSON.parse(line));
|
|
195
|
-
} catch {
|
|
196
|
-
// ignore partial lines
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
return events;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
function waitForChildClose(child) {
|
|
203
|
-
if (child.exitCode !== null || child.signalCode !== null) return Promise.resolve(child.exitCode ?? 1);
|
|
204
|
-
return new Promise((resolve) => {
|
|
205
|
-
child.once("close", (code) => resolve(code ?? 1));
|
|
206
|
-
});
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
function signalChild(child, signal) {
|
|
210
|
-
if (!child.pid) return;
|
|
211
|
-
try {
|
|
212
|
-
if (process.platform === "win32") {
|
|
213
|
-
child.kill(signal);
|
|
214
|
-
} else {
|
|
215
|
-
process.kill(-child.pid, signal);
|
|
216
|
-
}
|
|
217
|
-
} catch {
|
|
218
|
-
try {
|
|
219
|
-
child.kill(signal);
|
|
220
|
-
} catch {
|
|
221
|
-
// child already exited
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
async function terminateChild(child) {
|
|
227
|
-
child.stdin.destroy();
|
|
228
|
-
if (child.exitCode !== null || child.signalCode !== null) return;
|
|
229
|
-
signalChild(child, "SIGTERM");
|
|
230
|
-
const killTimer = setTimeout(() => signalChild(child, "SIGKILL"), CHILD_SHUTDOWN_GRACE_MS);
|
|
231
|
-
try {
|
|
232
|
-
await waitForChildClose(child);
|
|
233
|
-
} finally {
|
|
234
|
-
clearTimeout(killTimer);
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
|
|
238
114
|
function readCaptureSummary(artifactDir, stderr) {
|
|
239
115
|
const summaryPath = join(artifactDir, SUMMARY_ARTIFACT);
|
|
240
116
|
try {
|
|
@@ -254,6 +130,25 @@ function readCaptureSummary(artifactDir, stderr) {
|
|
|
254
130
|
return undefined;
|
|
255
131
|
}
|
|
256
132
|
|
|
133
|
+
function assertCompleteCaptureSummary(captureSummary, artifactDir, apiKey) {
|
|
134
|
+
if (!captureSummary?.artifactDir) {
|
|
135
|
+
fail(`missing summary.json in ${artifactDir}`, [apiKey]);
|
|
136
|
+
}
|
|
137
|
+
if (!captureSummary.artifacts || typeof captureSummary.artifacts !== "object") {
|
|
138
|
+
fail(`summary.json missing artifacts in ${artifactDir}`, [apiKey]);
|
|
139
|
+
}
|
|
140
|
+
if (!captureSummary.counts || typeof captureSummary.counts !== "object") {
|
|
141
|
+
fail(`summary.json missing counts in ${artifactDir}`, [apiKey]);
|
|
142
|
+
}
|
|
143
|
+
if (typeof captureSummary.elapsedMs !== "number") {
|
|
144
|
+
fail(`summary.json missing elapsedMs in ${artifactDir}`, [apiKey]);
|
|
145
|
+
}
|
|
146
|
+
if (typeof captureSummary.waitResultRecorded !== "boolean") {
|
|
147
|
+
fail(`summary.json missing waitResultRecorded in ${artifactDir}`, [apiKey]);
|
|
148
|
+
}
|
|
149
|
+
return captureSummary;
|
|
150
|
+
}
|
|
151
|
+
|
|
257
152
|
export function backfillPiSessionSnapshot(captureSummary, artifactDir, sessionDir) {
|
|
258
153
|
const sessionFile = captureSummary?.piSessionSnapshot?.sessionFile ?? captureSummary?.sessionFile;
|
|
259
154
|
if (!captureSummary || captureSummary.piSessionSnapshot?.copied || !sessionFile || !existsSync(sessionFile)) {
|
|
@@ -279,12 +174,12 @@ export function backfillPiSessionSnapshot(captureSummary, artifactDir, sessionDi
|
|
|
279
174
|
}
|
|
280
175
|
}
|
|
281
176
|
|
|
282
|
-
export async function runDebugProviderEvents(args) {
|
|
177
|
+
export async function runDebugProviderEvents(args, envInput = process.env) {
|
|
283
178
|
if (args.promptFile) {
|
|
284
179
|
args.prompt = readFileSync(args.promptFile, "utf8");
|
|
285
180
|
}
|
|
286
181
|
if (!args.prompt?.trim()) fail("--prompt or --prompt-file is required");
|
|
287
|
-
|
|
182
|
+
args.apiKey = requireApiKey(args, envInput, fail);
|
|
288
183
|
|
|
289
184
|
const artifactDir = args.out ?? defaultOutDir(args.cwd);
|
|
290
185
|
const sessionDir = args.sessionDir ?? join(artifactDir, "session");
|
|
@@ -303,13 +198,13 @@ export async function runDebugProviderEvents(args) {
|
|
|
303
198
|
sessionDir,
|
|
304
199
|
];
|
|
305
200
|
const env = {
|
|
306
|
-
...
|
|
201
|
+
...envInput,
|
|
307
202
|
CURSOR_API_KEY: args.apiKey,
|
|
308
203
|
PI_CURSOR_SDK_EVENT_DEBUG: "1",
|
|
309
204
|
PI_CURSOR_SDK_EVENT_DEBUG_RUN_DIR: artifactDir,
|
|
310
|
-
PI_CURSOR_SETTING_SOURCES: args.settingSources
|
|
311
|
-
PI_CURSOR_NATIVE_TOOL_DISPLAY: envFlag(
|
|
312
|
-
PI_CURSOR_PI_TOOL_BRIDGE: envFlag(
|
|
205
|
+
PI_CURSOR_SETTING_SOURCES: serializeCursorSettingSources(args.settingSources),
|
|
206
|
+
PI_CURSOR_NATIVE_TOOL_DISPLAY: envFlag(envInput.PI_CURSOR_NATIVE_TOOL_DISPLAY, "1"),
|
|
207
|
+
PI_CURSOR_PI_TOOL_BRIDGE: envFlag(envInput.PI_CURSOR_PI_TOOL_BRIDGE, "1"),
|
|
313
208
|
};
|
|
314
209
|
|
|
315
210
|
const child = spawn("pi", piArgs, {
|
|
@@ -336,10 +231,10 @@ export async function runDebugProviderEvents(args) {
|
|
|
336
231
|
try {
|
|
337
232
|
send({ type: "prompt", message: args.prompt });
|
|
338
233
|
await new Promise((resolve, reject) => {
|
|
339
|
-
const timeoutMs = Number(
|
|
234
|
+
const timeoutMs = Number(envInput.PI_PROVIDER_EVENT_DEBUG_TIMEOUT_MS ?? 600_000);
|
|
340
235
|
const start = Date.now();
|
|
341
236
|
const tick = () => {
|
|
342
|
-
const events =
|
|
237
|
+
const events = parseJsonLines(stdout);
|
|
343
238
|
if (events.some((event) => event.type === "agent_end")) {
|
|
344
239
|
resolve(events);
|
|
345
240
|
return;
|
|
@@ -359,10 +254,11 @@ export async function runDebugProviderEvents(args) {
|
|
|
359
254
|
fail(`pi exited ${exitCode}\nstderr=${scrubSensitiveText(stderr.slice(-2000), args.apiKey)}`, [args.apiKey]);
|
|
360
255
|
}
|
|
361
256
|
|
|
362
|
-
const captureSummary =
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
257
|
+
const captureSummary = assertCompleteCaptureSummary(
|
|
258
|
+
backfillPiSessionSnapshot(readCaptureSummary(artifactDir, stderr), artifactDir, sessionDir),
|
|
259
|
+
artifactDir,
|
|
260
|
+
args.apiKey,
|
|
261
|
+
);
|
|
366
262
|
|
|
367
263
|
return {
|
|
368
264
|
artifactDir: captureSummary.artifactDir,
|
|
@@ -392,12 +288,11 @@ async function main(argv = process.argv.slice(2), env = process.env) {
|
|
|
392
288
|
printHelp();
|
|
393
289
|
return;
|
|
394
290
|
}
|
|
395
|
-
console.log(JSON.stringify(await runDebugProviderEvents(args)));
|
|
291
|
+
console.log(JSON.stringify(await runDebugProviderEvents(args, env)));
|
|
396
292
|
}
|
|
397
293
|
|
|
398
294
|
if (import.meta.url === new URL(process.argv[1], "file:").href) {
|
|
399
295
|
main().catch((error) => {
|
|
400
|
-
|
|
401
|
-
process.exitCode = 1;
|
|
296
|
+
fail(error instanceof Error ? error.message : String(error), apiKeySecretsFromProcess());
|
|
402
297
|
});
|
|
403
298
|
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
export interface CursorDebugSdkEventsArgs {
|
|
2
|
+
cwd: string;
|
|
3
|
+
model: string;
|
|
4
|
+
prompt?: string;
|
|
5
|
+
out?: string;
|
|
6
|
+
settingSources?: string[] | undefined;
|
|
7
|
+
includeConversation: boolean;
|
|
8
|
+
apiKey?: string;
|
|
9
|
+
help: boolean;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface CursorSdkEventDebugSummary {
|
|
13
|
+
artifactDir: string;
|
|
14
|
+
files: {
|
|
15
|
+
metadata: string;
|
|
16
|
+
streamEvents: string;
|
|
17
|
+
onDelta: string;
|
|
18
|
+
onStep: string;
|
|
19
|
+
waitResult: string;
|
|
20
|
+
conversation?: string;
|
|
21
|
+
};
|
|
22
|
+
counts: {
|
|
23
|
+
stream: Record<string, number>;
|
|
24
|
+
onDelta: Record<string, number>;
|
|
25
|
+
onStep: Record<string, number>;
|
|
26
|
+
};
|
|
27
|
+
timing: {
|
|
28
|
+
stream: CursorSdkEventTimingSnapshot;
|
|
29
|
+
onDelta: CursorSdkEventTimingSnapshot;
|
|
30
|
+
onStep: CursorSdkEventTimingSnapshot;
|
|
31
|
+
};
|
|
32
|
+
wait?: {
|
|
33
|
+
status: string;
|
|
34
|
+
durationMs: number;
|
|
35
|
+
hasResultText: boolean;
|
|
36
|
+
};
|
|
37
|
+
conversation?: { turnCount: number } | Record<string, unknown>;
|
|
38
|
+
warnings: string[];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface CursorSdkEventTimingSnapshot {
|
|
42
|
+
eventCount: number;
|
|
43
|
+
firstMs?: number;
|
|
44
|
+
lastMs?: number;
|
|
45
|
+
maxGapMs?: number;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export declare function parseDebugSdkEventsArgs(
|
|
49
|
+
argv: string[],
|
|
50
|
+
env?: NodeJS.ProcessEnv,
|
|
51
|
+
): CursorDebugSdkEventsArgs;
|
|
52
|
+
|
|
53
|
+
export declare function createTimingTracker(): {
|
|
54
|
+
eventCount: number;
|
|
55
|
+
firstMs?: number;
|
|
56
|
+
lastMs?: number;
|
|
57
|
+
maxGapMs?: number;
|
|
58
|
+
record(elapsedMs: number): void;
|
|
59
|
+
snapshot(): CursorSdkEventTimingSnapshot;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
export interface CursorSdkEventJsonlSink {
|
|
63
|
+
appendStream(event: unknown): void;
|
|
64
|
+
appendDelta(update: unknown): void;
|
|
65
|
+
appendStep(step: unknown): void;
|
|
66
|
+
getSummaryState(): {
|
|
67
|
+
counts: {
|
|
68
|
+
stream: Record<string, number>;
|
|
69
|
+
onDelta: Record<string, number>;
|
|
70
|
+
onStep: Record<string, number>;
|
|
71
|
+
};
|
|
72
|
+
timing: {
|
|
73
|
+
stream: CursorSdkEventTimingSnapshot;
|
|
74
|
+
onDelta: CursorSdkEventTimingSnapshot;
|
|
75
|
+
onStep: CursorSdkEventTimingSnapshot;
|
|
76
|
+
};
|
|
77
|
+
};
|
|
78
|
+
close(): Promise<void>;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export declare function createEventJsonlSink(artifactDir: string, startedAt: number): CursorSdkEventJsonlSink;
|
|
82
|
+
|
|
83
|
+
export declare function buildSummary(input: {
|
|
84
|
+
artifactDir: string;
|
|
85
|
+
counts: CursorSdkEventDebugSummary["counts"];
|
|
86
|
+
timing: CursorSdkEventDebugSummary["timing"];
|
|
87
|
+
waitResult?: { status: string; durationMs: number; result?: string };
|
|
88
|
+
conversation?: unknown;
|
|
89
|
+
includeConversation: boolean;
|
|
90
|
+
}): CursorSdkEventDebugSummary;
|