pi-cursor-sdk 0.1.15 → 0.1.17

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.
Files changed (46) hide show
  1. package/CHANGELOG.md +56 -1
  2. package/README.md +20 -8
  3. package/docs/cursor-live-smoke-checklist.md +267 -0
  4. package/docs/cursor-model-ux-spec.md +15 -5
  5. package/docs/cursor-native-tool-replay.md +16 -5
  6. package/package.json +12 -5
  7. package/scripts/steering-rpc-smoke.mjs +238 -0
  8. package/scripts/tmux-live-smoke.sh +418 -0
  9. package/scripts/validate-smoke-jsonl.mjs +152 -0
  10. package/src/context.ts +180 -5
  11. package/src/cursor-bridge-contract.ts +27 -0
  12. package/src/cursor-edit-diff.ts +11 -0
  13. package/src/cursor-env-boolean.ts +22 -0
  14. package/src/cursor-live-run-accounting.ts +65 -0
  15. package/src/cursor-live-run-coordinator.ts +483 -0
  16. package/src/cursor-native-tool-display-registration.ts +93 -0
  17. package/src/cursor-native-tool-display-replay.ts +465 -0
  18. package/src/cursor-native-tool-display-state.ts +78 -0
  19. package/src/cursor-native-tool-display-tools.ts +102 -0
  20. package/src/cursor-native-tool-display.ts +10 -639
  21. package/src/cursor-partial-content-emitter.ts +121 -0
  22. package/src/cursor-pi-tool-bridge-abort.ts +133 -0
  23. package/src/cursor-pi-tool-bridge-diagnostics.ts +179 -0
  24. package/src/cursor-pi-tool-bridge-mcp.ts +118 -0
  25. package/src/cursor-pi-tool-bridge-run.ts +384 -0
  26. package/src/cursor-pi-tool-bridge-server.ts +182 -0
  27. package/src/cursor-pi-tool-bridge-snapshot.ts +88 -0
  28. package/src/cursor-pi-tool-bridge-types.ts +80 -0
  29. package/src/cursor-pi-tool-bridge.ts +77 -602
  30. package/src/cursor-provider-live-run-drain.ts +379 -0
  31. package/src/cursor-provider-turn-coordinator.ts +456 -0
  32. package/src/cursor-provider.ts +133 -1092
  33. package/src/cursor-question-tool.ts +7 -2
  34. package/src/cursor-record-utils.ts +26 -0
  35. package/src/cursor-sdk-output-filter.ts +100 -0
  36. package/src/cursor-sensitive-text.ts +37 -0
  37. package/src/cursor-session-agent.ts +372 -0
  38. package/src/cursor-session-cwd.ts +14 -19
  39. package/src/cursor-session-scope.ts +65 -0
  40. package/src/cursor-state.ts +38 -10
  41. package/src/cursor-tool-transcript.ts +28 -1229
  42. package/src/cursor-transcript-tool-formatters.ts +641 -0
  43. package/src/cursor-transcript-tool-specs.ts +441 -0
  44. package/src/cursor-transcript-utils.ts +276 -0
  45. package/src/cursor-usage-accounting.ts +71 -0
  46. package/src/index.ts +20 -3
package/CHANGELOG.md CHANGED
@@ -1,6 +1,61 @@
1
1
  # Changelog
2
2
 
3
- ## Unreleased
3
+ ## 0.1.17 - 2026-05-23
4
+
5
+ ### Added
6
+
7
+ - Surface in-progress Cursor SDK `task` activity in the TUI from SDK-provided `args.description`, with one deduped line such as `Cursor task: Explore AI/automation projects` and no generic heartbeat or per-tool start spam.
8
+
9
+ ### Changed
10
+
11
+ - Bump pi dev dependency baseline to `0.75.5` for read-tool collapsed-card rendering, package update fixes, and other upstream pi changes. Cursor edit replay remains display-only via `diffString`; pi's new SDK `details.patch` field is not required because Cursor agents do not execute pi's edit tool.
12
+ - Rework live-run internals into dedicated coordination/drain/turn/partial-content modules (`cursor-live-run-coordinator.ts`, `cursor-provider-live-run-drain.ts`, `cursor-provider-turn-coordinator.ts`, `cursor-partial-content-emitter.ts`) while preserving the provider's external contract.
13
+ - Complete phase-2 remediation for #23/#24/#25 by splitting bridge ownership across snapshot/server/run/abort/diagnostics/MCP/types modules, splitting native replay ownership across state/registration/replay/tools modules, and unifying tool completion routing through `resolveToolCompletion`.
14
+ - Replace monolithic provider test coverage with focused stream/bridge/replay/live-run suites plus shared harness helpers.
15
+ - Promote smoke automation into packaged entrypoints (`npm run smoke:live`, `npm run smoke:steering`, `npm run smoke:jsonl`) and make helper retry/polling behavior explicit (TUI answer/footer polling plus deterministic tmux cleanup).
16
+ - Document the hard maintainer rule that Cursor SDK behavior must be verified against the installed `@cursor/sdk` package and/or official TypeScript SDK docs before implementation or release claims.
17
+ - Bump package metadata to `0.1.17` so the dry-run tarball no longer collides with the existing `v0.1.16` tag.
18
+
19
+ ### Fixed
20
+
21
+ - Resolve startup noise issue #17 by extending Cursor SDK bootstrap filtering to late hook compatibility warnings and ripgrep/ignore-mapping output while preserving non-startup logs.
22
+ - Fix steering/follow-up delivery for active pooled Cursor runs by resuming/waiting on the in-flight run and sending incremental follow-up text after pending tool/result flow completes instead of issuing a second concurrent `Agent.send()`; additional stale tool batches from the old run are cancelled so the new user input is not lost.
23
+ - Resolve issue #19 with a canonical edit-diff fallback resolver (`diffString → diff → unifiedDiff → patch`) shared by replay and transcript formatting paths.
24
+ - Resolve issue #20 by updating the token-tracking investigation note to mark the `0.75.3` observation as point-in-time and call out the current `0.75.5` development baseline.
25
+ - Resolve issue #21 by decomposing prior 1k+ provider/transcript/bridge/test monoliths into ownership-scoped modules.
26
+ - Harden bridge diagnostics and secret scrubbing so debug JSONL stays run-safe and allowlisted without endpoint path material, raw args/results, or credential payloads.
27
+ - Make Cursor SDK output filtering safe for overlapping provider streams by restoring the global stdout/stderr/console patch only after the last active install.
28
+ - Reject bridge MCP calls cleanly when tool-dispatch handlers throw, and avoid suppressing unrelated MCP replay solely because an external payload reuses a known bridge request ID.
29
+ - Bound native replay diff/write previews by both lines and characters, summarize non-text MCP content without dumping raw payload JSON, and make expanded-diff truncation copy truthful.
30
+ - Change smoke forbidden-material scans to report only matching file names, not secret-bearing matched lines.
31
+ - Harden live-smoke direct-output checks so a step logs `PASS` only after both command exit and expected stdout assertion succeed, with the basic prompt retrying once on empty output even when the first command exits zero.
32
+
33
+ ## 0.1.16 - 2026-05-22
34
+
35
+ ### Added
36
+
37
+ - Reuse Cursor SDK agents within the same pi session when model, API key, cwd, bridge surface, and pi context remain compatible, sending incremental follow-up prompts instead of re-bootstrapping full history on every turn.
38
+ - Add context fingerprinting to choose bootstrap vs incremental `Agent.send()` prompts, including branch and compaction summary detection after `/tree` navigation and session compaction.
39
+ - Add a manual [Cursor live smoke checklist](docs/cursor-live-smoke-checklist.md) for release validation with real `pi -e . --cursor-no-fast --model cursor/composer-2.5` runs, diagnostics safety scans, TUI observation, bridge/replay checks, abort/cancel coverage, and an assume-everything-is-in-scope no-optional/no-deferred release rule.
40
+ - Share the Cursor pi bridge contract through provider prompts and bridged MCP tool descriptions via `src/cursor-bridge-contract.ts`.
41
+ - Isolate Cursor usage and live-run accounting in `src/cursor-usage-accounting.ts` and `src/cursor-live-run-accounting.ts`.
42
+
43
+ ### Changed
44
+
45
+ - Clarify the Cursor provider tool contract in README and replay docs: separate Cursor-native surface, pi bridge surface, and display-only replay.
46
+ - Document bridge debug diagnostics (`PI_CURSOR_PI_TOOL_BRIDGE_DEBUG=1`) and the scrubbed JSONL allowlist behavior.
47
+ - Refresh Cursor fast footer status on `turn_start` and treat models with the `cursor-sdk` API as Cursor models for status updates.
48
+
49
+ ### Fixed
50
+
51
+ - Harden Cursor pi tool bridge diagnostics so debug JSONL uses run-safe IDs separate from tokenized loopback routes and an allowlisted serializer that omits endpoint path material, raw args/results, and secrets.
52
+ - Improve Cursor SDK token accounting for `/session` and compaction by keeping raw Cursor internal usage diagnostic-only, counting split-run tool-call activity/tool-result consumption in approximate pi session usage, using `usage.totalTokens` for the replayable Cursor prompt/context estimate, and sharing the same matched tool-result boundary between provider usage and bridge result resolution.
53
+ - Fix duplicated final assistant text when Cursor streams partial post-tool text that prefixes the eventual final answer.
54
+ - Preserve the latest user request in budgeted incremental Cursor session-agent prompts.
55
+ - Invalidate and recreate session agents on compaction, API key changes, send errors, session shutdown, and `/tree` navigation so reused agents stay aligned with the active branch.
56
+ - Treat `/reload` session shutdown as non-terminal for the session-agent pool so the same session can acquire a fresh Cursor SDK agent after reload.
57
+ - Bootstrap prompts now include branch summaries after `/tree` navigation.
58
+ - Harden Cursor pi tool bridge validation and contract boundaries.
4
59
 
5
60
  ## 0.1.15 - 2026-05-21
6
61
 
package/README.md CHANGED
@@ -198,11 +198,18 @@ If you do not see `cursor fast`, fast mode is off.
198
198
  Images from the latest user message are forwarded to Cursor. Historical images are kept out of the transcript and appear only as `[image omitted from transcript]` placeholders, so follow-up questions about an earlier image should reattach the image or include a textual description. The extension advertises `text` and `image` input for Cursor models because Cursor's SDK accepts image messages and Cursor models are expected to support them.
199
199
 
200
200
 
201
- ## Tools and local pi bridge
201
+ ## Cursor provider tool contract
202
202
 
203
- Cursor runs use local Cursor SDK agents. Cursor's own local-agent tools, Cursor settings, plugins, and configured Cursor MCP servers remain available through the SDK.
203
+ Cursor runs use local Cursor SDK agents with two separate tool surfaces:
204
204
 
205
- In addition, pi-cursor-sdk exposes the current pi session's bridgeable active tools to Cursor through a local loopback MCP bridge by default. The bridge snapshots `pi.getActiveTools()` and `pi.getAllTools()` for each Cursor run, excludes internal Cursor replay activity names, and hides overlapping built-in pi tools (`read`, `bash`, `write`, `edit`, `grep`, `find`, `ls`) by default because Cursor local agents already have native equivalents. Non-overlapping active tools and extension/custom tools present in pi's active tool registry are exposed as MCP tools with collision-safe names such as `pi__sem_reindex`, and calls map back to the real pi tool names. When Cursor calls a bridged tool, pi executes it through the normal pi tool path, so confirmations, tool hooks, renderers, session history, and abort behavior stay pi-native. The bridge also exposes `cursor_ask_question` as `pi__cursor_ask_question` when enabled, allowing Cursor to ask the user through pi UI instead of silently choosing a default. The bridge does not call tool `execute()` handlers directly.
205
+ - **Cursor-native surface:** Cursor local-agent tools, Cursor settings, plugins, and configured Cursor MCP servers. These remain owned by the Cursor SDK local agent path.
206
+ - **pi bridge surface:** pi-cursor-sdk exposes bridgeable active pi tools through a per-run local loopback MCP bridge when the bridge is enabled and the current pi tool registry has exposed tools.
207
+
208
+ Bridge capabilities are snapshotted from `pi.getActiveTools()` and `pi.getAllTools()` for each Cursor run. Cursor sees active bridgeable pi tools as collision-safe MCP names such as `pi__sem_reindex` only when they are exposed in that current run. Pi session output, tool cards, confirmations, hooks, renderers, history, and abort behavior use the real pi tool name, such as `sem_reindex`. The bridge queues Cursor's MCP call, emits a normal pi `toolCall`, waits for the matching pi `toolResult`, and resolves that result back into the same live Cursor SDK run without creating a new `Agent`, unless the run was disposed, aborted, or cancelled. The bridge does not call pi tool `execute()` handlers directly.
209
+
210
+ Overlapping built-in pi tools (`read`, `bash`, `write`, `edit`, `grep`, `find`, `ls`) are hidden by default because Cursor local agents already have native equivalents. Extension/custom tools and non-overlapping active tools present in pi's active tool registry normally remain exposed. The bridge also exposes `cursor_ask_question` as `pi__cursor_ask_question` when enabled, allowing Cursor to ask the user through pi UI instead of silently choosing a default.
211
+
212
+ Cursor-native tool replay is separate from the bridge. Replay cards are display-only recorded Cursor SDK activity. They never re-run Cursor-side commands, reapply Cursor edits, call MCP servers, or mutate pi state. See [Cursor native tool replay](docs/cursor-native-tool-replay.md).
206
213
 
207
214
  Bridge controls:
208
215
 
@@ -216,11 +223,16 @@ PI_CURSOR_EXPOSE_BUILTIN_TOOLS=1 pi --model cursor/composer-2.5
216
223
  # Override Cursor SDK MCP tool-call timeout, including bridged pi tools and configured Cursor MCP servers.
217
224
  PI_CURSOR_MCP_TOOL_TIMEOUT_SECONDS=7200 pi --model cursor/composer-2.5
218
225
  PI_CURSOR_MCP_TOOL_TIMEOUT_MS=7200000 pi --model cursor/composer-2.5
226
+
227
+ # Emit scrubbed bridge diagnostics as JSONL to stderr with prefix [pi-cursor-sdk:bridge].
228
+ PI_CURSOR_PI_TOOL_BRIDGE_DEBUG=1 pi --model cursor/composer-2.5
219
229
  ```
220
230
 
221
- `PI_CURSOR_PI_TOOL_BRIDGE=0` is the supported rollback flag and disables the bridge entirely. The bridge also treats `false`, `off`, `none`, `no`, and `disabled` as off; `1`, `true`, `on`, `yes`, and `enabled` as on. `PI_CURSOR_EXPOSE_BUILTIN_TOOLS=1` opts in to exposing overlapping pi tool names that Cursor already has native equivalents for (`read`, `bash`, `write`, `edit`, `grep`, `find`, and `ls`). By default those names are hidden even when pi's Cursor replay wrapper has registered them as extension tools. The Cursor MCP timeout override defaults to 3600 seconds because the installed Cursor SDK has a 60-second MCP request default that is too short for some local MCP tools, including bridged pi tools and configured Cursor MCP servers.
231
+ `PI_CURSOR_PI_TOOL_BRIDGE=0` is the supported rollback flag and disables the bridge entirely. The bridge also treats `false`, `off`, `none`, `no`, and `disabled` as off; `1`, `true`, `on`, `yes`, and `enabled` as on. `PI_CURSOR_EXPOSE_BUILTIN_TOOLS=1` opts in to exposing overlapping pi tool names that Cursor already has native equivalents for. The Cursor MCP timeout override defaults to 3600 seconds because the installed Cursor SDK has a 60-second MCP request default that is too short for some local MCP tools, including bridged pi tools and configured Cursor MCP servers. `PI_CURSOR_PI_TOOL_BRIDGE_DEBUG=1` is off by default and emits typed, allowlisted, scrubbed single-line JSONL records to `process.stderr`. These records are operational diagnostics, not anonymous telemetry: they intentionally include tool names, safe correlation IDs, bridge run state, exposed pi↔MCP name pairs, queued requests, result resolution, rejection, cancellation, and pending counts. They must not include endpoint URLs, endpoint path components, endpoint tokens, raw args/results, stdout/stderr payloads, file contents, Cursor settings output, API keys, bearer tokens, cookies, session credentials, or secrets. Do not enable or share bridge debug logs where tool names themselves are sensitive.
232
+
233
+ ### Maintainer live smoke release gate
222
234
 
223
- Cursor-native tool replay is separate from the bridge. Replay only displays completed Cursor SDK tool activity as pi-native-looking cards with recorded results, using pi's normal success/error card shell for neutral Cursor activity too. It never re-runs Cursor-side commands, reapplies Cursor edits, calls MCP servers, or mutates pi state. See [Cursor native tool replay](docs/cursor-native-tool-replay.md).
235
+ For Cursor provider/runtime changes, follow the manual [Cursor live smoke checklist](docs/cursor-live-smoke-checklist.md) before release. Assume every runtime surface is in scope. The checklist uses real `pi -e . --cursor-no-fast --model cursor/composer-2.5` runs with temporary session dirs and requires the visible TUI/output, scrubbed diagnostics, and persisted JSONL to agree. Do not mark a release ready with optional, deferred, mostly-passing, or unobserved smoke checks outstanding.
224
236
 
225
237
  ## Fallback models
226
238
 
@@ -235,11 +247,11 @@ Actual Cursor runs still need a key from `/login`, `CURSOR_API_KEY`, or `--api-k
235
247
  - **Local Cursor SDK agents only.** This extension does not use Cursor cloud agents. Cloud pi tool bridging is out of scope because it needs a separate auth, transport, lifetime, and remote trust design.
236
248
  - **The pi tool bridge is local and MCP-backed.** Bridgeable active pi tools are exposed to local Cursor agents through a tokenized `127.0.0.1` MCP endpoint; internal Cursor replay activity names are excluded, and overlapping built-in pi tools are hidden by default. Set `PI_CURSOR_PI_TOOL_BRIDGE=0` to disable it or `PI_CURSOR_EXPOSE_BUILTIN_TOOLS=1` to expose overlapping built-ins too.
237
249
  - **Cursor native tool replay is display-only.** Replay renders recorded Cursor SDK activity and never re-runs Cursor-side commands, reapplies Cursor edits, calls MCP servers, or mutates pi state. Workflow tools such as Cursor `SwitchMode` and Cursor todo state are not pi workflow controls. See [Cursor native tool replay](docs/cursor-native-tool-replay.md) for supported replay cards, ordering, conflict handling, and opt-out flags.
238
- - **Cursor run state can span tool-use turns.** A new Cursor SDK agent starts for a new provider run. When Cursor calls bridged pi tools or emits replayed Cursor tool activity, the same Cursor SDK run can stay alive across pi `toolUse` turns so results resume in the original Cursor run.
239
- - **Cursor setting sources default to all.** The extension passes `local.settingSources: ["all"]` by default so configured Cursor MCP servers, plugin tools, project/user settings, and related Cursor-native capabilities are available like they are in Cursor. To narrow loading, set a comma-separated list such as `PI_CURSOR_SETTING_SOURCES=project,user,plugins`. To disable ambient setting sources, set `PI_CURSOR_SETTING_SOURCES=none`. Direct Cursor SDK startup logs are suppressed so setting/skill loading messages do not pollute the TUI.
250
+ - **Cursor run state can span tool-use turns.** Within a pi session, the extension reuses one Cursor SDK agent across compatible follow-up turns and sends incremental prompts when context still matches. It recreates the agent when context diverges, after compaction or `/tree` navigation, on API key changes, after send errors, or on session shutdown. For bridged pi tools, the matching pi `toolResult` resolves into the same live Cursor SDK run without creating a new `Agent`, unless the run was disposed, aborted, or cancelled. Replay can also split one live Cursor SDK run across pi `toolUse` turns for display.
251
+ - **Cursor setting sources default to all.** The extension passes `local.settingSources: ["all"]` by default so configured Cursor MCP servers, plugin tools, project/user settings, and related Cursor-native capabilities are available like they are in Cursor. To narrow loading, set a comma-separated list such as `PI_CURSOR_SETTING_SOURCES=project,user,plugins`. To disable ambient setting sources, set `PI_CURSOR_SETTING_SOURCES=none`. Direct Cursor SDK bootstrap logs (settings, skills, hook-load compatibility warnings, and similar) are suppressed so they do not pollute the TUI.
240
252
  - **Max Mode is not a manual pi variant.** Cursor's SDK may enable Max Mode automatically for models that require it. This extension only advertises exact context-window variants that the SDK catalog exposes and otherwise uses conservative SDK-derived default/non-Max context windows.
241
253
  - **Output token limits are conservative.** Cursor SDK model metadata does not currently expose output token limits directly.
242
- - **Token usage is approximate in pi.** Cursor SDK usage events include internal agent/tool/cache work, so the extension reports an approximate replayable pi prompt/output size for context display and compaction decisions.
254
+ - **Token usage is approximate in pi.** Cursor SDK usage events include cumulative internal agent/tool/cache work, so raw Cursor SDK counters are not copied into pi usage. The extension reports approximate pi session activity in `input`/`output`, including split-run tool calls and consumed tool results, while `totalTokens` tracks the replayable Cursor prompt/context estimate used for context display and compaction.
243
255
 
244
256
  ## Troubleshooting
245
257
 
@@ -0,0 +1,267 @@
1
+ # Cursor Live Smoke Checklist
2
+
3
+ ## Purpose
4
+
5
+ Use this manual checklist before releasing Cursor provider/runtime changes. Unit tests and mocks are necessary, but they are not enough for this extension. Always assume every runtime surface is in scope. A release is not ready until every live check below has been observed with `cursor/composer-2.5` through the local working tree.
6
+
7
+ ## Release rule
8
+
9
+ - Run from a clean working tree except for the intended branch diff.
10
+ - Use the local extension under test: `pi -e . --cursor-no-fast --model cursor/composer-2.5`.
11
+ - Use a temporary `--session-dir` for every run.
12
+ - Do not paste or commit Cursor API keys, raw session contents with secrets, endpoint URLs, or local private paths.
13
+ - If a check fails, stop and fix or explicitly mark the release blocked. Do not ship with "optional," "deferred," "mostly," or "probably" checks outstanding.
14
+ - Do not narrow the smoke scope to the apparent code diff. Treat provider reality, TUI behavior, bridge behavior, replay behavior, diagnostics safety, abort/cancel cleanup, usage accounting, packaging, and cleanup as in scope for every Cursor provider/runtime release.
15
+ - A check is passed only when the visible TUI/output, stderr diagnostics, and persisted JSONL agree with the expected behavior.
16
+
17
+ ## Prerequisites
18
+
19
+ ```bash
20
+ export SMOKE_DIR="/tmp/pi-cursor-sdk-live-smoke-$(date +%Y%m%dT%H%M%S)"
21
+ mkdir -p "$SMOKE_DIR"
22
+ pi -e . --list-models cursor
23
+ ```
24
+
25
+ The repo also ships partial automation for the prerequisite/basic/default-settings/non-interactive math/TUI output polling/steering/diagnostic/JSONL subset:
26
+
27
+ ```bash
28
+ npm run smoke:live
29
+ ```
30
+
31
+ The script is a helper only; it polls the section 3 TUI for answer/footer evidence and then cleans up the tmux session, but it does not replace manual visual review of the full TUI checklist. Release readiness still requires the manual checks below for detailed TUI behavior, bridge, standalone native replay, abort/cancel, packaging, cleanup, and any touched runtime surface not covered by the helper.
32
+
33
+ Pass criteria:
34
+
35
+ - `cursor/composer-2.5` appears in the model list.
36
+ - No Cursor key or auth token is printed.
37
+ - If `CURSOR_API_KEY` is unavailable and `/login` is not configured, stop and report the live smoke as blocked.
38
+
39
+ ## 1. Basic provider reality check
40
+
41
+ ```bash
42
+ PI_CURSOR_SETTING_SOURCES=none \
43
+ pi -e . --cursor-no-fast --model cursor/composer-2.5 \
44
+ --session-dir "$SMOKE_DIR/basic" \
45
+ --no-tools \
46
+ -p 'Live smoke. Reply exactly: PI_CURSOR_SMOKE_OK' \
47
+ > "$SMOKE_DIR/basic.stdout.txt" \
48
+ 2> "$SMOKE_DIR/basic.stderr.txt"
49
+ ```
50
+
51
+ Pass criteria:
52
+
53
+ - Exit code is `0`.
54
+ - stdout contains `PI_CURSOR_SMOKE_OK`.
55
+ - stderr is empty or contains only expected non-secret diagnostics for the specific test.
56
+ - The persisted JSONL has exactly one assistant message with non-negative usage fields and `cacheRead/cacheWrite` equal to `0`.
57
+
58
+ ## 2. Default setting-source startup noise check
59
+
60
+ ```bash
61
+ pi -e . --cursor-no-fast --model cursor/composer-2.5 \
62
+ --session-dir "$SMOKE_DIR/default-settings" \
63
+ --no-tools \
64
+ -p 'Default settings smoke. Include PRODUCT=42 in the final answer.' \
65
+ > "$SMOKE_DIR/default-settings.stdout.txt" \
66
+ 2> "$SMOKE_DIR/default-settings.stderr.txt"
67
+ ```
68
+
69
+ Pass criteria:
70
+
71
+ - Exit code is `0`.
72
+ - stdout includes `PRODUCT=42`.
73
+ - stderr is empty.
74
+ - No Cursor SDK settings/skills startup logs corrupt stdout or the TUI.
75
+
76
+ ## 3. TUI observation check
77
+
78
+ Run a real interactive session under tmux:
79
+
80
+ ```bash
81
+ SESSION="pi-cursor-sdk-smoke-$(date +%s)"
82
+ tmux new-session -d -s "$SESSION" -x 120 -y 40 -- zsh -lc \
83
+ "cd '$PWD' && PI_CURSOR_SETTING_SOURCES=none pi -e . --cursor-no-fast --model cursor/composer-2.5 --session-dir '$SMOKE_DIR/tui' --no-tools 'TUI smoke. Compute 19 + 23. Reply only with SUM=<number>.'"
84
+ ```
85
+
86
+ Observe with `tmux capture-pane -pt "$SESSION"` or attach manually.
87
+
88
+ Pass criteria:
89
+
90
+ - Footer shows `(cursor) composer-2.5`. With `--cursor-no-fast`, Cursor fast mode is off and the Cursor extension status should not show `cursor fast`; ignore unrelated status text from other extensions.
91
+ - Assistant answer appears correctly.
92
+ - `/session` shows one user and one assistant message for the simple run.
93
+ - Persisted JSONL has one assistant message. If the screen appears duplicated, inspect JSONL before deciding whether it is a rendering bug.
94
+ - Kill the tmux session after the check and verify no smoke tmux sessions remain.
95
+
96
+ ## 4. Bridge multi-tool success and failure
97
+
98
+ ```bash
99
+ PI_CURSOR_SETTING_SOURCES=none \
100
+ PI_CURSOR_EXPOSE_BUILTIN_TOOLS=1 \
101
+ PI_CURSOR_PI_TOOL_BRIDGE_DEBUG=1 \
102
+ pi -e . --cursor-no-fast --model cursor/composer-2.5 \
103
+ --session-dir "$SMOKE_DIR/bridge" \
104
+ -p 'Bridge smoke. Do exactly two tool calls before answering: first call pi__read on ./package.json; second call pi__read on ./definitely-missing-pi-cursor-sdk-smoke-file.txt. Then answer: OK_NAME=<package name>; MISSING_RESULT=<error or success>. Do not use shell.' \
105
+ > "$SMOKE_DIR/bridge.stdout.txt" \
106
+ 2> "$SMOKE_DIR/bridge.stderr.txt"
107
+ ```
108
+
109
+ Pass criteria:
110
+
111
+ - stdout includes `OK_NAME=pi-cursor-sdk`.
112
+ - Diagnostics include `run_created`, `tools_exposed`, two `request_queued`, two `request_resolved`, and `run_disposed`.
113
+ - The missing-file request has `isError: true`.
114
+ - Persisted JSONL contains real pi tool calls named `read`, matching `toolResult` messages, and final assistant output.
115
+ - Later assistant usage counts consumed tool-result input; no assistant usage has negative values or nonzero cache fields.
116
+
117
+ ## 5. Native replay cards without the pi bridge
118
+
119
+ ```bash
120
+ PI_CURSOR_SETTING_SOURCES=none \
121
+ PI_CURSOR_PI_TOOL_BRIDGE=0 \
122
+ PI_CURSOR_NATIVE_TOOL_DISPLAY=1 \
123
+ pi -e . --cursor-no-fast --model cursor/composer-2.5 \
124
+ --session-dir "$SMOKE_DIR/native-replay" \
125
+ -p 'Native replay smoke. Use your Cursor file-reading capability to read ./README.md, then answer README_SEEN=yes if it contains pi-cursor-sdk.' \
126
+ > "$SMOKE_DIR/native-replay.stdout.txt" \
127
+ 2> "$SMOKE_DIR/native-replay.stderr.txt"
128
+ ```
129
+
130
+ Pass criteria:
131
+
132
+ - stdout includes `README_SEEN=yes`.
133
+ - Persisted JSONL shows an assistant `toolUse` turn with a replayed `read` tool call, a pi `read` `toolResult`, and a final assistant turn.
134
+ - Native replay is display-only: it must not re-run Cursor-side mutations or create duplicate pi mutations.
135
+
136
+ ## 6. Diagnostics safety contract
137
+
138
+ Bridge diagnostics are scrubbed operational logs, not anonymous telemetry.
139
+
140
+ Allowed fields:
141
+
142
+ - event name
143
+ - run-safe correlation IDs that are not endpoint path components
144
+ - bridge/pi tool call IDs derived from the run-safe ID
145
+ - hashed Cursor MCP call correlation IDs of the form `cursor-mcp-call-<8 hex chars>`
146
+ - exposed pi/MCP tool name pairs
147
+ - pending/queued/cancelled counts
148
+ - success/error booleans
149
+ - rejection kind
150
+
151
+ Forbidden fields:
152
+
153
+ - Cursor API keys or auth headers
154
+ - bearer tokens, cookies, sessions, or raw credential material
155
+ - endpoint URLs, endpoint path components, endpoint tokens, or loopback URLs
156
+ - raw tool args
157
+ - raw tool results
158
+ - stdout/stderr payloads
159
+ - file contents
160
+ - Cursor settings/skills startup output
161
+ - local private session paths in tracked docs
162
+
163
+ Run a forbidden-material scan over smoke stderr/captures:
164
+
165
+ ```bash
166
+ forbidden_files="$(find "$SMOKE_DIR" -type f \( -name '*stderr.txt' -o -name '*capture*.txt' \) -print0 |
167
+ xargs -0 grep -IlE 'CURSOR_API_KEY|Bearer [A-Za-z0-9._-]+|/cursor-pi-tool-bridge/[^ ]+/mcp|127\.0\.0\.1:[0-9]+/cursor-pi-tool-bridge|apiKey|cookie|session-cookie|secret-token' || true)"
168
+ if [[ -n "$forbidden_files" ]]; then
169
+ printf 'Forbidden material matched in smoke files; inspect locally without pasting matched lines.\n' >&2
170
+ while IFS= read -r file; do
171
+ [[ -z "$file" ]] && continue
172
+ if [[ "$file" == "$SMOKE_DIR/"* ]]; then
173
+ printf ' %s\n' "${file#"$SMOKE_DIR/"}" >&2
174
+ else
175
+ printf ' %s\n' "$file" >&2
176
+ fi
177
+ done <<<"$forbidden_files"
178
+ exit 1
179
+ fi
180
+ ```
181
+
182
+ Pass criteria:
183
+
184
+ - The scan returns no matching files except deliberately planted test strings that are asserted not to appear in serialized diagnostics, and it does not print matched secret-bearing lines.
185
+ - If tool names themselves are considered sensitive for a release target, do not enable `PI_CURSOR_PI_TOOL_BRIDGE_DEBUG=1` for shared logs. The diagnostics contract intentionally allows tool names.
186
+
187
+ ## 7. Long-running bridge and abort/cancel
188
+
189
+ This check is release-blocking for every Cursor provider/runtime release.
190
+
191
+ Use a harmless long-running command and interrupt it after the bridge request is queued:
192
+
193
+ ```bash
194
+ PI_CURSOR_SETTING_SOURCES=none \
195
+ PI_CURSOR_EXPOSE_BUILTIN_TOOLS=1 \
196
+ PI_CURSOR_PI_TOOL_BRIDGE_DEBUG=1 \
197
+ pi -e . --cursor-no-fast --model cursor/composer-2.5 \
198
+ --session-dir "$SMOKE_DIR/abort" \
199
+ -p 'Abort smoke. Call pi__bash with command: sleep 30 && echo SHOULD_NOT_PRINT. Do not answer until the tool completes.'
200
+ ```
201
+
202
+ Pass criteria:
203
+
204
+ - Interrupting the run does not leave `sleep 30`, `SHOULD_NOT_PRINT`, `pi`, or bridge-related child processes running.
205
+ - Diagnostics either show clean cancellation/disposal or the process exits cleanly without orphaning children.
206
+ - Persisted JSONL does not contain a false successful final answer.
207
+
208
+ ## 8. Final structural session scan
209
+
210
+ After all live runs, scan JSONL structurally instead of reading raw content into a report:
211
+
212
+ ```bash
213
+ node scripts/validate-smoke-jsonl.mjs "$SMOKE_DIR"
214
+ ```
215
+
216
+ Script-enforced pass criteria:
217
+
218
+ - Every scanned JSONL file is parseable and non-empty.
219
+ - Every scanned JSONL file contains at least one assistant message.
220
+ - Every assistant message has usage metadata.
221
+ - Assistant usage `input`, `output`, and `totalTokens` are non-negative numbers.
222
+ - Assistant usage `cacheRead` and `cacheWrite` are exactly `0`.
223
+
224
+ Additional manual usage checks for provider/accounting changes:
225
+
226
+ - Tool-heavy runs should show nonzero output for visible assistant/tool-call activity.
227
+ - Split runs should count consumed tool-result input once on the following assistant turn.
228
+
229
+ ## 9. Standard local gates
230
+
231
+ ```bash
232
+ git diff --check
233
+ npm test
234
+ npm run typecheck
235
+ npm pack --dry-run
236
+ ```
237
+
238
+ Pass criteria:
239
+
240
+ - All commands exit `0`.
241
+ - `npm pack --dry-run` includes all new runtime source files and excludes local smoke artifacts, sessions, package tarballs, `.env*`, `.pi/`, `dist/`, and `coverage/`.
242
+
243
+ ## 10. Cleanup
244
+
245
+ ```bash
246
+ tmux list-sessions | grep 'pi-cursor-sdk-smoke' || true
247
+ rm -rf "$SMOKE_DIR"
248
+ ```
249
+
250
+ Pass criteria:
251
+
252
+ - No smoke tmux sessions remain.
253
+ - No smoke child processes remain.
254
+ - No smoke artifacts are committed.
255
+
256
+ ## Coverage gaps this checklist makes explicit
257
+
258
+ Everything in this section is in scope for Cursor provider/runtime releases. These are not accepted as "done" unless the matching live check passes:
259
+
260
+ - Long-running bridged tool abort/cancel cleanup.
261
+ - Native replay cards beyond read, especially shell/edit/write cards, when those renderers change.
262
+ - Bridge question UI when `cursor_ask_question` changes.
263
+ - MCP timeout override behavior when timeout code changes.
264
+ - Ambient Cursor setting-source behavior when startup filtering or local Cursor settings handling changes.
265
+ - Model discovery aliases/context variants when model-discovery code or Cursor SDK versions change.
266
+
267
+ If any surface has no adequate live check, add that check before release instead of assuming mocks cover reality.
@@ -16,19 +16,28 @@ Current implementation notes:
16
16
  - Image payload forwarding sends images only from the latest user message. If the latest user turn is plain text after an earlier image turn, the transcript keeps an `[image omitted from transcript]` placeholder but no image bytes are sent to Cursor. The prompt explicitly tells Cursor that prior image bytes are unavailable and to ask the user to reattach or describe a prior image when needed. Carrying images forward across turns remains a future product decision because it affects token cost, privacy, stale visual context, and expected multimodal follow-up behavior.
17
17
  - `@cursor/sdk` is a package dependency of this extension; users should not need a global SDK install.
18
18
  - Cursor auth uses pi-native API-key resolution for provider `cursor`: CLI `--api-key`, stored `~/.pi/agent/auth.json` API key from `/login`, then `CURSOR_API_KEY`. The extension config file stores only non-secret Cursor-only state such as fast defaults.
19
- - Local agents pass `settingSources: ["all"]` by default so Cursor MCP servers, plugin tools, project/user settings, and related Cursor-native capabilities are available. Users can narrow loading with a comma-separated list such as `PI_CURSOR_SETTING_SOURCES=project,user,plugins`, or disable ambient setting sources with `PI_CURSOR_SETTING_SOURCES=none`. The provider suppresses direct Cursor SDK startup writes around agent creation so setting/skill loading logs do not pollute pi's TUI.
19
+ - Local agents pass `settingSources: ["all"]` by default so Cursor MCP servers, plugin tools, project/user settings, and related Cursor-native capabilities are available. Users can narrow loading with a comma-separated list such as `PI_CURSOR_SETTING_SOURCES=project,user,plugins`, or disable ambient setting sources with `PI_CURSOR_SETTING_SOURCES=none`. The provider suppresses direct Cursor SDK bootstrap stdout/stderr/console noise (including late first-send workspace loading such as hook compatibility warnings) so it does not pollute pi's TUI.
20
20
  - Cursor SDK models are treated as thinking-capable even when pi reports `thinking=no`; that pi column only means the SDK did not expose a pi-controllable thinking parameter for that model.
21
21
  - Cursor-side thinking remains visible through pi's native thinking rendering when the Cursor SDK emits thinking or summary deltas.
22
- - Local Cursor agents get two tool paths. First, Cursor keeps the Cursor SDK local-agent tool surface plus configured Cursor settings, plugins, and Cursor MCP servers. Second, pi-cursor-sdk exposes active pi tools through a default-on, tokenized loopback MCP bridge when `pi.getActiveTools()` and `pi.getAllTools()` contain exposable tools. Cursor sees collision-safe MCP names such as `pi__sem_reindex`, while pi emits and executes the real pi tool name. Overlapping built-in pi tools (`read`, `bash`, `write`, `edit`, `grep`, `find`, `ls`) are hidden by default because Cursor local agents already have native equivalents; `PI_CURSOR_EXPOSE_BUILTIN_TOOLS=1` opts into exposing them too. The provider also registers `cursor_ask_question` for Cursor models when the bridge is enabled; Cursor sees it as `pi__cursor_ask_question`, and pi executes it through the normal tool path so interactive users can choose options from pi UI. In non-UI modes it reports that UI is unavailable so Cursor can state a default assumption instead. The bridge queues MCP calls, emits provider `toolcall_*` events, waits for matching pi `toolResult` messages by `toolCallId`, resolves the result back to the same Cursor SDK run, and never calls tool `execute()` handlers directly. `PI_CURSOR_PI_TOOL_BRIDGE=0` disables this local bridge, including question bridging. Cloud Cursor agents remain out of scope for the bridge.
22
+ - Local Cursor agents get two tool surfaces. First, Cursor keeps the Cursor SDK local-agent tool surface plus configured Cursor settings, plugins, and Cursor MCP servers. Second, pi-cursor-sdk exposes active pi tools through a default-on, tokenized loopback MCP bridge when bridgeable tools exist.
23
+ - `buildCursorPiToolBridgeSnapshot()` is the runtime capability source for pi bridge tools. It snapshots `pi.getActiveTools()` and `pi.getAllTools()`, filters internal replay names, hides overlapping built-in pi tools (`read`, `bash`, `write`, `edit`, `grep`, `find`, `ls`) unless `PI_CURSOR_EXPOSE_BUILTIN_TOOLS=1`, and creates collision-safe MCP names such as `pi__sem_reindex`. Cursor discovers the current run's exposed bridge tools through MCP `listTools`; there is no default run-start manifest, per-turn visible tool list, status manifest, or footer manifest.
24
+ - Prompt text is the primary provider/bridge contract. MCP tool descriptions repeat the same contract to reinforce discovery, but do not replace the prompt boundary. Cursor must call the exposed `pi__*` MCP name, not the real pi tool name shown in pi history or transcripts. Pi emits and executes the real pi tool name.
25
+ - The provider also registers `cursor_ask_question` for Cursor models when the bridge is enabled. Cursor sees it as `pi__cursor_ask_question`, and pi executes it through the normal tool path so interactive users can choose options from pi UI. In non-UI modes it reports that UI is unavailable so Cursor can state a default assumption instead. `PI_CURSOR_PI_TOOL_BRIDGE=0` disables the local bridge, including question bridging. Cloud Cursor agents remain out of scope for the bridge.
26
+ - The bridge queues MCP calls, emits provider `toolcall_*` events, waits for matching pi `toolResult` messages by `toolCallId`, resolves the result back into the same live Cursor SDK run without creating a new `Agent`, and never calls tool `execute()` handlers directly. The same-run resume invariant holds unless the run was disposed, aborted, or cancelled.
23
27
  - Cursor SDK MCP tool calls use a guarded timeout override because installed `@cursor/sdk` 1.0.13 has a 60-second MCP request default with no public per-server timeout option. The extension extends that Cursor SDK MCP `callTool` timeout path to 3600 seconds by default. Users can override it with `PI_CURSOR_MCP_TOOL_TIMEOUT_MS` or `PI_CURSOR_MCP_TOOL_TIMEOUT_SECONDS`.
24
- - Cursor internal tool activity is recorded from SDK events and scrubbed. In interactive TTY sessions, supported completed `read`, `bash`, `grep`, `find`, `ls`, `edit`, `write`, diagnostics, delete, todo/plan, task, image generation, and MCP activity is replayed through pi's native tool-call rendering path with recorded Cursor results, so the TUI can show native-looking cards without rerunning Cursor's reads/shell commands/file edits. Cursor `glob` activity is replayed through native `find` cards. Cursor write activity is replayed through native-looking `write` cards, and Cursor StrReplace/edit activity uses native-looking `edit` only when recorded arguments truthfully satisfy pi's `edit` schema; path-only Cursor edit and notebook edit replay falls back to neutral Cursor activity before pi validation. Diagnostics, delete, todos/plans, task, image, and MCP activity use neutral Cursor activity cards with pi's default success/error shell. Neutral Cursor activity calls include `activityTitle` and, when available, `activitySummary` so partial/collapsed cards preserve identity such as `Cursor plan`, `Cursor todos`, `Cursor MCP`, or `Cursor edit`. Replay-only tools display recorded Cursor results, normalize workspace-local paths/diff headers for display, use pi diff colors for edit previews and path-inferred syntax highlighting for write previews, and fail closed if called without a recorded result. Native replay wrappers are registered only for tool names not already owned by another extension; conflicting tools use the bounded scrubbed transcript fallback. Cursor workflow tools such as `SwitchMode` and Cursor todo state are not pi workflow controls; reported todo/plan events are displayed as Cursor activity only. Plan/todo replay cards can be followed by Cursor's final plan text, selected from `run.wait().result` when Cursor provides one and trimmed against already-emitted text. Started Cursor SDK tool calls that never receive a completion event are discarded without synthetic replay errors; explicit failures remain visible when Cursor reports them through completed tool calls or step results. `PI_CURSOR_NATIVE_TOOL_DISPLAY=0` disables native replay, and `PI_CURSOR_REGISTER_NATIVE_TOOLS=0` is a registration-only opt-out that keeps the transcript fallback without shadowing pi tool names. When bridge or native replay cards are emitted, the provider mirrors Codex's turn shape as Cursor SDK activity arrives: assistant `toolUse`, pi `toolResult`s, live post-tool Cursor thinking/text, any later tool batches as further `toolUse` turns, then Cursor's final assistant answer. For shell replay, completed `stdout` / `stderr` are primary; unambiguous `shell-output-delta` data is used only as display-only fallback for empty successful shell completions, and overlapping shell calls drop ambiguous deltas instead of guessing. Non-interactive runs keep bounded scrubbed transcript output instead, preserving `pi -p` assistant text output. Cursor text deltas stream live when no live-run turn split is active.
28
+ - Bridge diagnostics are opt-in only: `PI_CURSOR_PI_TOOL_BRIDGE_DEBUG=1` writes typed, allowlisted, scrubbed single-line JSONL records to `process.stderr` with prefix `[pi-cursor-sdk:bridge]`. Diagnostics are scrubbed operational logs, not anonymous telemetry. They intentionally include tool names, safe correlation IDs, run lifecycle, exposed pi↔MCP name pairs, queued requests, result resolution, rejection, cancellation, and pending counts. Correlation IDs are generated independently from the tokenized endpoint path, and Cursor MCP call IDs are hashed before serialization. Diagnostics must not include endpoint paths/URLs/path components/tokens, API keys, bearer tokens, cookies, session credentials, raw args/results, stdout/stderr payloads, file contents, Cursor settings output, or local private session paths in tracked docs, and they must not call pi UI status, notification, or footer APIs. If tool names themselves are unacceptable for a release target, bridge debug diagnostics are not safe for shared logs under the current contract.
29
+ - This repo does not provide a generic desktop-automation, browser-driver, or CDP recipe. Provider docs should describe pi-cursor-sdk's Cursor provider/bridge contract only.
30
+ - Cursor internal tool activity is recorded from SDK events and scrubbed. In interactive TTY sessions, supported completed `read`, `bash`, `grep`, `find`, `ls`, `edit`, `write`, diagnostics, delete, todo/plan, task, image generation, and MCP activity is replayed through pi's native tool-call rendering path with recorded Cursor results, so the TUI can show native-looking cards without rerunning Cursor's reads/shell commands/file edits. Cursor `glob` activity is replayed through native `find` cards. Cursor write activity is replayed through native-looking `write` cards, and Cursor StrReplace/edit activity uses native-looking `edit` only when recorded arguments truthfully satisfy pi's `edit` schema; path-only Cursor edit and notebook edit replay falls back to neutral Cursor activity before pi validation. Diagnostics, delete, todos/plans, task, image, and MCP activity use neutral Cursor activity cards with pi's default success/error shell. Neutral Cursor activity calls include `activityTitle` and, when available, `activitySummary` so partial/collapsed cards preserve identity such as `Cursor plan`, `Cursor todos`, `Cursor MCP`, or `Cursor edit`. When the Cursor SDK emits a running `task` tool call with a description, the provider surfaces one low-noise in-progress line such as `Cursor task: Explore AI/automation projects` from SDK args only; it does not emit generic heartbeat text or per-tool start cards for ordinary `read`, `bash`, or `grep` activity. Replay-only tools display recorded Cursor results, normalize workspace-local paths/diff headers for display, use pi diff colors for edit previews and path-inferred syntax highlighting for write previews, and fail closed if called without a recorded result. Native replay wrappers are registered only for tool names not already owned by another extension; conflicting tools use the bounded scrubbed transcript fallback. Cursor workflow tools such as `SwitchMode` and Cursor todo state are not pi workflow controls; reported todo/plan events are displayed as Cursor activity only. Plan/todo replay cards can be followed by Cursor's final plan text, selected from `run.wait().result` when Cursor provides one and trimmed against already-emitted text. Started Cursor SDK tool calls that never receive a completion event are discarded without synthetic replay errors; explicit failures remain visible when Cursor reports them through completed tool calls or step results. `PI_CURSOR_NATIVE_TOOL_DISPLAY=0` disables native replay, and `PI_CURSOR_REGISTER_NATIVE_TOOLS=0` is a registration-only opt-out that keeps the transcript fallback without shadowing pi tool names. When bridge or native replay cards are emitted, the provider mirrors Codex's turn shape as Cursor SDK activity arrives: assistant `toolUse`, pi `toolResult`s, live post-tool Cursor thinking/text, any later tool batches as further `toolUse` turns, then Cursor's final assistant answer. For shell replay, completed `stdout` / `stderr` are primary; unambiguous `shell-output-delta` data is used only as display-only fallback for empty successful shell completions, and overlapping shell calls drop ambiguous deltas instead of guessing. Non-interactive runs keep bounded scrubbed transcript output instead, preserving `pi -p` assistant text output. Cursor text deltas stream live when no live-run turn split is active.
25
31
  - Synthetic replay names are internal compatibility details. New model-facing prompt text and user-visible cards use native tool names when renderer-compatible, or neutral Cursor activity labels when not. Legacy sessions containing old internal replay names are sanitized before prompt/display. Bridge MCP names such as `pi__sem_reindex` are MCP-only; pi session output uses real pi tool names.
26
- - Cursor SDK usage events report cumulative internal agent/tool/cache work, not the replayable pi prompt context. The extension reports approximate prompt/output usage for pi context display and compaction decisions instead of copying raw Cursor SDK usage. When native replay splits one Cursor SDK run into multiple pi turns, prompt input is counted once for the run; later synthetic replay turns report `input: 0` and only their own output estimate.
32
+ - Cursor SDK usage events report cumulative internal agent/tool/cache work, not the replayable pi prompt context. The extension does not copy raw Cursor SDK usage into pi usage or compaction. For Cursor assistant messages, `usage.input`/`usage.output` are approximate pi session activity components: initial Cursor prompt input is counted once, consumed split-run tool results are counted as deduped input on the following assistant turn, and assistant output includes visible text/thinking/tool-call content. `usage.totalTokens` is the replayable Cursor prompt/context estimate derived from the same `buildCursorPrompt()` path used for `Agent.send`; it may differ from `input + output` and is the context-safe value for display/compaction. `src/cursor-usage-accounting.ts` owns this usage policy, and `src/cursor-live-run-accounting.ts` owns prompt-once and consumed-tool-result accounting so provider usage and bridge result resolution share the same matched tool-result boundary.
27
33
  - Audit observation, 2026-05-19, superseded by the 2026-05-21 replay pass: a missing-file read with Composer 2.5 emitted `tool-call-started` for Cursor `read`, then streamed final text `Error: File not found`, but did not emit `tool-call-completed` or an `onStep` `toolCall` error result. Leftover started calls are now discarded at run completion instead of becoming synthetic replay errors. Cursor-reported completed/step errors remain visible.
28
34
  - Maintainer visual verification for replay-card changes should follow [Cursor Native Tool Visual Audit Workflow](./cursor-native-tool-visual-audit.md): offscreen PTY-driven pi run, xterm.js/Playwright screenshot rendering, and JSONL inspection before accepting commits or PRs.
35
+ - Cursor provider/runtime releases should follow [Cursor Live Smoke Checklist](./cursor-live-smoke-checklist.md) with real `pi -e . --cursor-no-fast --model cursor/composer-2.5` invocations, manual observation, temporary session dirs, diagnostics scans, and persisted JSONL inspection. Assume every runtime surface is in scope. A release is not ready when any live check is optional, deferred, mostly passing, or unobserved.
29
36
  - For models without a catalog `context` parameter, context windows are not hardcoded. The extension ships a bundled SDK-derived default/non-Max cache generated from `createAgentPlatform().checkpointStore.loadLatest(agentId).tokenDetails.maxTokens`. Successful runs can update a local override cache, but model discovery does not probe models at startup.
30
37
  - Max Mode context windows are distinct from default/non-Max context windows. `@cursor/sdk` 1.0.13 documentation says the SDK may enable Max Mode automatically when a selected model requires it, but the public local-agent `ModelSelection` path still does not expose a manual Max Mode selector. Do not advertise Max Mode context windows unless the SDK catalog exposes an exact parameter/variant or the SDK public API adds a Max Mode selector that the extension actually sends.
31
38
  - `@cursor/sdk` 1.0.13 adds latest-style `ModelListItem.aliases`. The extension registers only unambiguous aliases as pi model IDs (with the same context suffixes when applicable) and sends the alias back in `ModelSelection.id`, while sharing Cursor-only state such as fast defaults with the underlying catalog `id`. Aliases shared by multiple base models, such as generic family aliases, are skipped because the pi row metadata would otherwise imply one base model while Cursor may resolve the alias to another.
39
+ - Session-scoped Cursor SDK agent pooling reuses one live `@cursor/sdk` agent across compatible follow-up turns within the same pi session scope. `computeCursorContextFingerprint()` and `shouldBootstrapCursorSend()` decide whether the next turn sends a full bootstrap prompt or an incremental follow-up. The pool recreates the agent when context diverges, when branch or compaction summaries appear after `/tree` navigation or compaction, when the API key identity changes, after send errors, on `session_shutdown`, and when `session_before_tree` / `session_tree` invalidate the active branch. Incremental sends omit the full Cursor SDK tool boundary block because the session agent retains prior bootstrap context.
40
+ - Pi steering/follow-up delivery can arrive while a split live Cursor SDK run is still active. The provider resolves pending live runs by scanning trailing `toolResult` messages while skipping trailing `user` messages, tracks the active live run per session scope, and resumes the in-flight run instead of calling `Agent.send()` again. When the context ends with steering user text after tool results, the provider releases the prior live run and chains an incremental `Agent.send()` for the latest user message in the same provider turn; if the prior run emits more text or tool requests after steering arrives, that stale activity is cancelled instead of surfacing another old-run tool turn and losing the new user input. A pre-send guard waits for or resumes any still-active scoped live run before starting a fresh send so `@cursor/sdk` `AgentBusyError` (`already has active run`) does not surface to pi users.
32
41
 
33
42
  ## Goal
34
43
 
@@ -665,7 +674,8 @@ Before calling done:
665
674
  4. Tool bridge and replay:
666
675
  - `npm test -- test/cursor-pi-tool-bridge.test.ts test/cursor-provider.test.ts test/cursor-mcp-timeout-override.test.ts`
667
676
  - confirm `Agent.create()` gets `mcpServers.pi_tools` when active pi tools exist and omits it when `PI_CURSOR_PI_TOOL_BRIDGE=0` or the active snapshot is empty
668
- - confirm bridged MCP requests emit real pi tool calls and resolve matching pi tool results back to the same Cursor SDK run
677
+ - confirm bridged MCP requests emit real pi tool calls and resolve matching pi tool results back to the same live Cursor SDK run without creating a new `Agent`, unless the run was disposed, aborted, or cancelled
669
678
  - confirm bridge MCP activity is suppressed from Cursor replay while non-bridge Cursor MCP activity remains visible
670
679
  - confirm `PI_CURSOR_MCP_TOOL_TIMEOUT_MS` and `PI_CURSOR_MCP_TOOL_TIMEOUT_SECONDS` override the Cursor SDK MCP callTool timeout seam
680
+ - confirm `PI_CURSOR_PI_TOOL_BRIDGE_DEBUG=1` emits typed, allowlisted, scrubbed JSONL to `process.stderr` with prefix `[pi-cursor-sdk:bridge]`, omits endpoint URLs/path components/tokens, and unset/false leaves output unchanged
671
681
  - run the visual audit workflow when replay card visuals or bridge card visuals change; JSONL should show real pi tool names for bridged calls and no duplicate MCP replay for bridge calls
@@ -1,26 +1,37 @@
1
1
  # Cursor native tool replay
2
2
 
3
- pi-cursor-sdk has two separate tool paths:
3
+ pi-cursor-sdk has two separate pi-facing paths plus Cursor's own local-agent tool surface:
4
4
 
5
5
  1. **Local pi MCP bridge:** default-on for local Cursor agents. It exposes the current pi session's bridgeable active tools to Cursor through a tokenized `127.0.0.1` MCP endpoint, excluding internal Cursor replay activity names and, by default, overlapping built-in pi tools (`read`, `bash`, `write`, `edit`, `grep`, `find`, `ls`). When Cursor calls one of those MCP tools, pi executes the real pi tool through the normal pi tool path.
6
6
  2. **Cursor native tool replay:** display-only. It renders completed Cursor SDK tool activity as pi-native-looking cards using recorded Cursor results.
7
7
 
8
8
  This document is about replay. Replay is not execution and is not the local pi bridge.
9
9
 
10
+ ## Live bridge vs replay
11
+
12
+ | Surface | Names Cursor can call | Names pi shows | IDs | Execution behavior |
13
+ | --- | --- | --- | --- | --- |
14
+ | Local pi MCP bridge | Live MCP names such as `pi__sem_reindex`, only when exposed in the current run | Real pi tool names such as `sem_reindex` | Bridge run and tool IDs begin with `cursor-pi-bridge-*` | Real pi execution through normal pi `toolCall` / `toolResult` flow |
15
+ | Cursor native tool replay | None; replay names are not callable tools | Native-compatible card names or neutral Cursor activity labels | Replay IDs begin with `cursor-replay-*` | Display-only recorded Cursor results; no re-run, file mutation, MCP call, or pi state mutation |
16
+ | Cursor-native host tools/settings/plugins/MCP | Cursor SDK local-agent tool names, as provided by Cursor | Only replay cards or transcript summaries when reported by the SDK | Cursor SDK-owned IDs | Neither pi bridge nor replay execution; owned by the Cursor SDK local agent path |
17
+
18
+ Replay labels, replay cards, and transcript tool names are display-only/context-only. Bridge MCP names are also not pi tool names: Cursor must call the exposed `pi__*` MCP name, while pi history and cards use the real pi tool name.
19
+
10
20
  ## Local pi bridge summary
11
21
 
12
- The bridge is enabled by default when bridgeable active pi tools exist. Cursor sees bridge-owned MCP names such as `pi__sem_reindex`, while pi history and tool cards use the real pi tool name such as `sem_reindex`. The bridge hides overlapping built-in pi tools by default because Cursor already has native equivalents; extension/custom tools and non-overlapping active tools present in pi's active tool registry normally remain exposed. pi-cursor-sdk also registers `cursor_ask_question` for Cursor models when the bridge is enabled, exposed to Cursor as `pi__cursor_ask_question`, so Cursor can ask the user to choose instead of silently defaulting when the pi UI is available. The bridge does not call pi tool `execute()` handlers directly; it queues the request, emits a real pi `toolCall`, waits for the matching pi `toolResult`, and resolves the Cursor MCP call back into the same Cursor SDK run.
22
+ The bridge is enabled by default when bridgeable active pi tools exist. Cursor sees bridge-owned MCP names such as `pi__sem_reindex`, while pi history and tool cards use the real pi tool name such as `sem_reindex`. The bridge hides overlapping built-in pi tools by default because Cursor already has native equivalents; extension/custom tools and non-overlapping active tools present in pi's active tool registry normally remain exposed. pi-cursor-sdk also registers `cursor_ask_question` for Cursor models when the bridge is enabled, exposed to Cursor as `pi__cursor_ask_question`, so Cursor can ask the user to choose instead of silently defaulting when the pi UI is available. The bridge does not call pi tool `execute()` handlers directly; it queues the request, emits a real pi `toolCall`, waits for the matching pi `toolResult`, and resolves the Cursor MCP call back into the same live Cursor SDK run without creating a new `Agent`, unless the run was disposed, aborted, or cancelled.
13
23
 
14
- Rollback and timeout controls:
24
+ Rollback, timeout, and diagnostics controls:
15
25
 
16
26
  ```bash
17
27
  PI_CURSOR_PI_TOOL_BRIDGE=0 pi --model cursor/composer-2.5
18
28
  PI_CURSOR_EXPOSE_BUILTIN_TOOLS=1 pi --model cursor/composer-2.5
19
29
  PI_CURSOR_MCP_TOOL_TIMEOUT_SECONDS=7200 pi --model cursor/composer-2.5
20
30
  PI_CURSOR_MCP_TOOL_TIMEOUT_MS=7200000 pi --model cursor/composer-2.5
31
+ PI_CURSOR_PI_TOOL_BRIDGE_DEBUG=1 pi --model cursor/composer-2.5
21
32
  ```
22
33
 
23
- `PI_CURSOR_PI_TOOL_BRIDGE=0` disables the bridge, including `pi__cursor_ask_question`. `PI_CURSOR_EXPOSE_BUILTIN_TOOLS=1` opts in to exposing overlapping pi tool names that Cursor already has native equivalents for (`read`, `bash`, `write`, `edit`, `grep`, `find`, and `ls`). By default those names are hidden even when pi's Cursor replay wrapper has registered them as extension tools; non-overlapping active built-ins remain bridgeable by default. Cursor-native tools, Cursor settings, plugins, and configured Cursor MCP servers still come from the Cursor SDK local agent path. Cloud Cursor agents are out of scope for this bridge.
34
+ `PI_CURSOR_PI_TOOL_BRIDGE=0` disables the bridge, including `pi__cursor_ask_question`. `PI_CURSOR_EXPOSE_BUILTIN_TOOLS=1` opts in to exposing overlapping pi tool names that Cursor already has native equivalents for (`read`, `bash`, `write`, `edit`, `grep`, `find`, and `ls`). By default those names are hidden even when pi's Cursor replay wrapper has registered them as extension tools; non-overlapping active built-ins remain bridgeable by default. `PI_CURSOR_PI_TOOL_BRIDGE_DEBUG=1` emits typed, allowlisted, scrubbed single-line JSONL bridge diagnostics to `process.stderr` with prefix `[pi-cursor-sdk:bridge]`; it is off by default, uses run-safe IDs that are not reused in endpoint paths, and does not print endpoint URLs/path components/tokens, raw args/results, file contents, or secrets. Cursor-native tools, Cursor settings, plugins, and configured Cursor MCP servers still come from the Cursor SDK local agent path. Cloud Cursor agents are out of scope for this bridge.
24
35
 
25
36
  ## What gets replayed
26
37
 
@@ -63,7 +74,7 @@ Other unsupported Cursor SDK tools may still be described through a bounded scru
63
74
 
64
75
  As Cursor SDK tool completions arrive, the extension mirrors native Codex ordering by ending a tool-use turn, letting pi render the recorded tool results, then continuing with live post-tool Cursor thinking/text, later Cursor tool batches, or Cursor's final answer as the next assistant turn. For plan-mode runs, neutral Cursor plan/todo cards can therefore appear before the final Cursor plan text.
65
76
 
66
- Bridged pi tool calls follow the same visible pi `toolUse` turn shape, but they are real pi tool executions rather than replayed Cursor results.
77
+ Bridged pi tool calls follow the same visible pi `toolUse` turn shape, but they are real pi tool executions rather than replayed Cursor results. Split-run usage accounting keeps Cursor SDK internal counters out of pi usage: each live Cursor prompt is counted once, replay/bridge tool-call turns include visible assistant activity in output estimates, consumed tool results are counted once as input on the following assistant turn, and `usage.totalTokens` remains the replayable Cursor prompt/context estimate.
67
78
 
68
79
  For shell replay, completed `stdout` / `stderr` remain the primary source. If a successful completed shell result is empty and Cursor emitted unambiguous `shell-output-delta` data while exactly one shell call was active, the replay card uses that delta as display-only fallback data. Overlapping shell calls make delta attribution ambiguous, so those fallback deltas are dropped rather than guessed. `(no output)` is kept only when no completed output or safe delta fallback is available.
69
80
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-cursor-sdk",
3
- "version": "0.1.15",
3
+ "version": "0.1.17",
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",
@@ -24,8 +24,12 @@
24
24
  "files": [
25
25
  "src",
26
26
  "scripts/refresh-cursor-model-snapshots.mjs",
27
+ "scripts/steering-rpc-smoke.mjs",
28
+ "scripts/tmux-live-smoke.sh",
29
+ "scripts/validate-smoke-jsonl.mjs",
27
30
  "README.md",
28
31
  "docs/cursor-model-ux-spec.md",
32
+ "docs/cursor-live-smoke-checklist.md",
29
33
  "docs/cursor-native-tool-replay.md",
30
34
  "docs/cursor-native-tool-visual-audit.md",
31
35
  "LICENSE",
@@ -39,7 +43,10 @@
39
43
  "typecheck": "tsc --noEmit",
40
44
  "test": "vitest run",
41
45
  "test:watch": "vitest",
42
- "refresh:cursor-snapshots": "node scripts/refresh-cursor-model-snapshots.mjs"
46
+ "refresh:cursor-snapshots": "node scripts/refresh-cursor-model-snapshots.mjs",
47
+ "smoke:live": "scripts/tmux-live-smoke.sh",
48
+ "smoke:steering": "node scripts/steering-rpc-smoke.mjs",
49
+ "smoke:jsonl": "node scripts/validate-smoke-jsonl.mjs"
43
50
  },
44
51
  "dependencies": {
45
52
  "@cursor/sdk": "^1.0.13",
@@ -52,9 +59,9 @@
52
59
  "typebox": "*"
53
60
  },
54
61
  "devDependencies": {
55
- "@earendil-works/pi-ai": "^0.75.3",
56
- "@earendil-works/pi-coding-agent": "^0.75.3",
57
- "@earendil-works/pi-tui": "^0.75.3",
62
+ "@earendil-works/pi-ai": "^0.75.5",
63
+ "@earendil-works/pi-coding-agent": "^0.75.5",
64
+ "@earendil-works/pi-tui": "^0.75.5",
58
65
  "typebox": "^1.1.38",
59
66
  "typescript": "^6.0.3",
60
67
  "vitest": "^4.1.6"