pi-cursor-sdk 0.1.18 → 0.1.20
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 +58 -0
- package/README.md +59 -1
- package/docs/cursor-live-smoke-checklist.md +4 -1
- package/docs/cursor-model-ux-spec.md +7 -5
- package/docs/cursor-native-tool-replay.md +99 -3
- package/docs/cursor-testing-lessons.md +234 -5
- package/package.json +10 -2
- package/scripts/debug-provider-events.mjs +403 -0
- package/scripts/debug-sdk-events.mjs +413 -0
- package/scripts/lib/cursor-probe-utils.mjs +52 -0
- package/scripts/lib/cursor-sdk-output-filter.mjs +86 -0
- package/scripts/probe-mcp-coldstart.mjs +244 -0
- package/scripts/validate-smoke-jsonl.mjs +27 -3
- package/src/context.ts +45 -32
- package/src/cursor-agent-message-web-tools.ts +172 -0
- package/src/cursor-agents-context.ts +176 -0
- package/src/cursor-incomplete-tool-visibility.ts +124 -0
- package/src/cursor-live-run-coordinator.ts +18 -7
- package/src/cursor-mcp-timeout-override.ts +66 -11
- package/src/cursor-model.ts +12 -0
- package/src/cursor-native-tool-display-registration.ts +1 -4
- package/src/cursor-native-tool-display-replay.ts +65 -6
- package/src/cursor-native-tool-display-tools.ts +20 -0
- package/src/cursor-pi-tool-bridge-diagnostics.ts +11 -1
- package/src/cursor-pi-tool-bridge-run.ts +16 -1
- package/src/cursor-pi-tool-bridge-types.ts +3 -0
- package/src/cursor-provider-errors.ts +96 -0
- package/src/cursor-provider-live-run-drain.ts +181 -62
- package/src/cursor-provider-turn-coordinator.ts +220 -33
- package/src/cursor-provider.ts +302 -93
- package/src/cursor-question-tool.ts +1 -4
- package/src/cursor-sdk-abort-error-guard.ts +109 -0
- package/src/cursor-sdk-event-debug-constants.ts +40 -0
- package/src/cursor-sdk-event-debug-session.ts +163 -0
- package/src/cursor-sdk-event-debug.ts +602 -0
- package/src/cursor-sensitive-text.ts +27 -7
- package/src/cursor-session-agent.ts +279 -82
- package/src/cursor-session-send-policy.ts +43 -0
- package/src/cursor-setting-sources.ts +29 -0
- package/src/cursor-state.ts +1 -5
- package/src/cursor-tool-lifecycle.ts +85 -0
- package/src/cursor-tool-names.ts +39 -0
- package/src/cursor-tool-transcript.ts +4 -2
- package/src/cursor-tool-visibility.ts +63 -0
- package/src/cursor-transcript-tool-formatters.ts +228 -5
- package/src/cursor-transcript-tool-specs.ts +135 -24
- package/src/cursor-transcript-utils.ts +12 -0
- package/src/cursor-web-tool-activity.ts +84 -0
- package/src/index.ts +4 -1
|
@@ -20,6 +20,7 @@ Passing hundreds of unit tests did not prove that chain was safe. Regression cov
|
|
|
20
20
|
- `test/cursor-provider-replay-live-run.test.ts` — inactive replay tools emit trace instead of broken `toolUse`
|
|
21
21
|
- `test/cursor-native-replay-trace.test.ts` — shared inactive replay trace formatting
|
|
22
22
|
- `test/cursor-native-replay-routing.test.ts` — `resolveNativeReplayDisposition` and `partitionNativeToolsByActiveContext`
|
|
23
|
+
- `test/validate-smoke-jsonl.test.ts` — replay scan semantics (real errors vs doc mentions in successful reads)
|
|
23
24
|
|
|
24
25
|
When changing provider/runtime behavior, ask whether the bug spans **pi extension lifecycle**, **active tool state**, **provider streaming**, and **persisted JSONL**. If yes, add an integration-style unit test or live smoke coverage for that chain.
|
|
25
26
|
|
|
@@ -121,6 +122,7 @@ Every live check should use its own `--session-dir` under the isolated tree. Do
|
|
|
121
122
|
| Inherited shell env | mise/profile hooks hung or polluted runs | Use `env -i ... MISE_DISABLE=1` for isolated pi calls |
|
|
122
123
|
| No per-check timeout | One stuck prompt blocked entire harness | Wrap each live check with timeout/watchdog |
|
|
123
124
|
| stdout-only assertions | Missed replay failures persisted only in JSONL | Scan JSONL for `Tool grep/cursor/find/ls not found` |
|
|
125
|
+
| Naive JSONL substring scan | Successful `read` of docs mentioning replay errors looked like failures | `validate-smoke-jsonl.mjs` only flags error `toolResult` / error assistant messages |
|
|
124
126
|
| Plan strip only on first turn | Under-tested multi-turn resync | Shim strips on every `turn_start`; stress multi-turn separately |
|
|
125
127
|
| Assuming env auth equals pi auth | False "blocked" or false "pass" in CI-like shells | Check `auth.json` provider keys explicitly when needed |
|
|
126
128
|
|
|
@@ -140,12 +142,28 @@ Combined usage + replay scan after broader smoke:
|
|
|
140
142
|
node scripts/validate-smoke-jsonl.mjs --replay-errors "$SMOKE_DIR"
|
|
141
143
|
```
|
|
142
144
|
|
|
143
|
-
|
|
145
|
+
### What counts as a replay failure
|
|
144
146
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
- `
|
|
148
|
-
- `Tool
|
|
147
|
+
The scan fails only on **persisted error messages**, not arbitrary substring matches in session JSONL:
|
|
148
|
+
|
|
149
|
+
- error `toolResult` records (`isError: true`) whose text contains:
|
|
150
|
+
- `Tool grep not found`
|
|
151
|
+
- `Tool cursor not found`
|
|
152
|
+
- `Tool find not found`
|
|
153
|
+
- `Tool ls not found`
|
|
154
|
+
- error assistant messages (`stopReason: "error"` or `errorMessage`) containing those strings
|
|
155
|
+
|
|
156
|
+
Successful tool results are ignored even when file contents mention those strings (for example a `read` of `docs/cursor-testing-lessons.md` during plan-strip smoke).
|
|
157
|
+
|
|
158
|
+
### False-positive edge case (2026-05-23)
|
|
159
|
+
|
|
160
|
+
Plan-strip live smoke can make Cursor `read` testing docs that *document* replay failure strings. A naive whole-record JSON scan reported four failures from one successful `read` toolResult (`isError: false`).
|
|
161
|
+
|
|
162
|
+
When changing replay scan logic:
|
|
163
|
+
|
|
164
|
+
1. Update `scripts/validate-smoke-jsonl.mjs`
|
|
165
|
+
2. Add/adjust cases in `test/validate-smoke-jsonl.test.ts` (error toolResult must still fail; successful read of doc text must pass)
|
|
166
|
+
3. Re-run `npm run smoke:isolated` on a packed temp install before release
|
|
149
167
|
|
|
150
168
|
## Plan-mode regression scenario
|
|
151
169
|
|
|
@@ -180,6 +198,12 @@ npm run smoke:isolated # requires auth.json or CURSOR_API_KEY
|
|
|
180
198
|
npm run smoke:live # partial tmux checklist subset
|
|
181
199
|
```
|
|
182
200
|
|
|
201
|
+
After changing `scripts/validate-smoke-jsonl.mjs` or replay scan expectations, also run:
|
|
202
|
+
|
|
203
|
+
```bash
|
|
204
|
+
npm test -- test/validate-smoke-jsonl.test.ts
|
|
205
|
+
```
|
|
206
|
+
|
|
183
207
|
Then follow the full manual [Cursor live smoke checklist](./cursor-live-smoke-checklist.md) for surfaces the scripts do not cover (bridge MCP, abort/cancel, full TUI observation, packaging review, cleanup).
|
|
184
208
|
|
|
185
209
|
## What belongs in CI vs manual smoke
|
|
@@ -189,6 +213,209 @@ Then follow the full manual [Cursor live smoke checklist](./cursor-live-smoke-ch
|
|
|
189
213
|
|
|
190
214
|
If live smoke auth is unavailable, report the release as **blocked**, not skipped-ready.
|
|
191
215
|
|
|
216
|
+
## Cursor SDK event capture probe
|
|
217
|
+
|
|
218
|
+
When debugging TUI/progress/replay timing gaps, capture raw Cursor SDK surfaces side-by-side instead of writing a throwaway probe:
|
|
219
|
+
|
|
220
|
+
```bash
|
|
221
|
+
CURSOR_API_KEY=... npm run debug:sdk-events -- \
|
|
222
|
+
--cwd ~/Projects \
|
|
223
|
+
--model composer-2.5 \
|
|
224
|
+
--prompt 'Scan all of my projects and give me ideas that would be great to add the Cursor SDK to' \
|
|
225
|
+
--out /tmp/pi-cursor-sdk-sdk-events-manual
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
The script writes timestamped artifacts under `--out` (default `/tmp/pi-cursor-sdk-sdk-events-<timestamp>`):
|
|
229
|
+
|
|
230
|
+
- `stream-events.jsonl` — `run.stream()` messages
|
|
231
|
+
- `on-delta.jsonl` — `agent.send(..., { onDelta })` updates
|
|
232
|
+
- `on-step.jsonl` — `agent.send(..., { onStep })` steps
|
|
233
|
+
- `wait-result.json` — final `run.wait()` metadata
|
|
234
|
+
- optional `conversation.json` with `--include-conversation`
|
|
235
|
+
- `summary.json` — event counts and timing gaps
|
|
236
|
+
|
|
237
|
+
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
|
+
|
|
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.
|
|
240
|
+
|
|
241
|
+
## Pi provider SDK event capture
|
|
242
|
+
|
|
243
|
+
When debugging pi parsing, replay routing, bridge timing, or send-plan behavior, capture the raw `onDelta`/`onStep` payloads **as the Cursor provider receives them** instead of using the direct SDK probe above.
|
|
244
|
+
|
|
245
|
+
One-shot maintainer script (RPC pi run, gitignored artifacts by default):
|
|
246
|
+
|
|
247
|
+
```bash
|
|
248
|
+
CURSOR_API_KEY=... npm run debug:provider-events -- \
|
|
249
|
+
--cwd . \
|
|
250
|
+
--model cursor/composer-2.5 \
|
|
251
|
+
--prompt 'Repro prompt here' \
|
|
252
|
+
--out .debug/cursor-sdk-events/manual-repro
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
Or read a prompt from disk:
|
|
256
|
+
|
|
257
|
+
```bash
|
|
258
|
+
CURSOR_API_KEY=... npm run debug:provider-events -- \
|
|
259
|
+
--prompt-file .debug/repro-prompt.txt \
|
|
260
|
+
--out .debug/cursor-sdk-events/manual-repro
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
Artifacts under `--out` (default `.debug/cursor-sdk-events/<timestamp>/` under `--cwd`):
|
|
264
|
+
|
|
265
|
+
- `metadata.json` — model, cwd, send-plan/provider metadata
|
|
266
|
+
- `context-snapshot.json` — full pi `Context` passed into the provider turn
|
|
267
|
+
- `send-payload.json` — exact `agent.send()` input (text + images)
|
|
268
|
+
- `on-delta.jsonl` — raw `InteractionUpdate` objects passed to `turnCoordinator.handleDelta`
|
|
269
|
+
- `on-step.jsonl` — raw `onStep` payloads passed to `turnCoordinator.handleStep`
|
|
270
|
+
- `stream-events.jsonl` — raw `run.stream()` events when supported
|
|
271
|
+
- `pi-stream-events.jsonl` — exact pi stream events emitted to the TUI (`text_delta`, `thinking_delta`, replay cards, `done`, etc.)
|
|
272
|
+
- `provider-events.jsonl` — provider lifecycle markers (`agent_send_start`, `agent_send_returned`, …)
|
|
273
|
+
- `live-run-events.jsonl` — queued native replay / bridge live-run events
|
|
274
|
+
- `bridge-events.jsonl` — bridge lifecycle/request diagnostics (file-only; no stderr unless bridge debug is also enabled)
|
|
275
|
+
- `bridge-raw.jsonl` — raw bridged MCP args/results
|
|
276
|
+
- `display-decisions.jsonl` — per-tool native replay routing (`queue_replay`, `emit_trace`, `inactive_trace`, dedupe skips, bridge ignores) with transcript/trace text
|
|
277
|
+
- `coordinator-events.jsonl` — turn-coordinator side effects (task progress labels, discarded incomplete started tool calls, etc.)
|
|
278
|
+
- `drain-events.jsonl` — live-run pre-send drain and per-turn drain lifecycle (`turn_start`, `turn_end`, inactive replay traces, native display registration)
|
|
279
|
+
- `timeline.jsonl` — merged cross-layer timeline (one grep-friendly stream for the whole turn)
|
|
280
|
+
- `pi-session-snapshot.jsonl` — copy of pi session JSONL at turn finalize (session dir also gets latest `pi-session.jsonl`)
|
|
281
|
+
- `final-partial.json` — assistant partial emitted to pi at end of the provider turn
|
|
282
|
+
- `errors.jsonl` — provider/stream/conversation failures
|
|
283
|
+
- `wait-result.json` — `run.wait()` result
|
|
284
|
+
- `conversation.json` — `run.conversation()` when supported
|
|
285
|
+
- `summary.json` — counts and artifact paths
|
|
286
|
+
|
|
287
|
+
During any normal pi session you can also opt in with:
|
|
288
|
+
|
|
289
|
+
```bash
|
|
290
|
+
PI_CURSOR_SDK_EVENT_DEBUG=1 pi -e . --model cursor/composer-2.5
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
Multi-turn sessions group automatically by pi session file:
|
|
294
|
+
|
|
295
|
+
```text
|
|
296
|
+
.debug/cursor-sdk-events/sessions/<session-slug>/
|
|
297
|
+
session.json # index of all turns in this pi session
|
|
298
|
+
turn-001-<timestamp>/ # first provider turn
|
|
299
|
+
turn-002-<timestamp>/ # second provider turn
|
|
300
|
+
...
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
Each turn still gets the full per-turn artifact bundle above. Use `session.json` to jump between turns while debugging incremental send, bridge resolution, or native replay continuation across pi messages. For tool-heavy turns, trace/thinking replay often drains on the **next** pi message — check turn N+1 `drain-events.jsonl` and `pi-stream-events.jsonl` alongside turn N `display-decisions.jsonl`.
|
|
304
|
+
|
|
305
|
+
Optional env:
|
|
306
|
+
|
|
307
|
+
- `PI_CURSOR_SDK_EVENT_DEBUG_DIR` — base directory (default `.debug/cursor-sdk-events`)
|
|
308
|
+
- `PI_CURSOR_SDK_EVENT_DEBUG_SESSION_DIR` — exact session root for all turns in the current pi session
|
|
309
|
+
- `PI_CURSOR_SDK_EVENT_DEBUG_RUN_DIR` — exact artifact directory for one isolated turn (the maintainer script sets this via `--out`; bypasses session grouping)
|
|
310
|
+
- `PI_CURSOR_SDK_EVENT_DEBUG_STDERR=1` — also print the summary line to stderr (off by default so the pi TUI stays normal)
|
|
311
|
+
|
|
312
|
+
Capture is file-only by default: no stderr markers, and bridge diagnostics during SDK event debug go to `bridge-events.jsonl` instead of `[pi-cursor-sdk:bridge]` unless you separately set `PI_CURSOR_PI_TOOL_BRIDGE_DEBUG=1`. Raw payloads stay on disk and may contain secrets — do not commit or share them.
|
|
313
|
+
|
|
314
|
+
### Discarded incomplete SDK tool calls
|
|
315
|
+
|
|
316
|
+
When Cursor emits `tool-call-started` without a matching completion/step result, the provider surfaces a bounded neutral **Cursor … did not complete** activity card or thinking trace at run end for failed/aborted runs, runs with no assistant text, and external/side-effectful tools. Incomplete fast local discovery starts (`read`, `grep`, `glob`, `ls`) are debug-only after a successful text-producing run so stale SDK start events do not create red post-answer cards. pi bridge MCP calls (`pi__*`) are excluded because pi already shows the real pi tool execution path.
|
|
317
|
+
|
|
318
|
+
With `PI_CURSOR_SDK_EVENT_DEBUG=1`, each discarded started call is also recorded in `coordinator-events.jsonl` under phase `discarded-incomplete-started-tool-call` with:
|
|
319
|
+
|
|
320
|
+
- normalized SDK tool name
|
|
321
|
+
- scrubbed call-id hash (raw call IDs are not written)
|
|
322
|
+
- reason such as `no-completion-at-run-end`, `abort`, or `sdk-failure`
|
|
323
|
+
|
|
324
|
+
Stderr output for these records requires `PI_CURSOR_SDK_EVENT_DEBUG_STDERR=1`. This complements the standalone `npm run debug:sdk-events` probe by interpreting a specific provider discard path during normal pi runs. User-visible incomplete cards explain actionable gaps in the TUI; debug artifacts remain maintainer-only (**#52**) and are the source of truth for suppressed fast-local stale starts.
|
|
325
|
+
|
|
326
|
+
## Tool calls listed as plain text (#40 triage)
|
|
327
|
+
|
|
328
|
+
**Symptom:** Assistant output lists tool invocations (for example `Tool call`, `Cursor activity`, `call cursor-replay-…`, `toolName`, `browser_navigate`) instead of pi tool execution cards/results.
|
|
329
|
+
|
|
330
|
+
**What the screenshot in [#40](https://github.com/fitchmultz/pi-cursor-sdk/issues/40) shows:** Plain assistant text that mirrors pi's **prompt transcript format** for replay tool calls (`Tool call (Cursor activity, call cursor-replay-…): …` from `src/context.ts`) rather than a rendered pi `toolCall` card. That pattern usually means the Cursor model **narrated** a tool call as text; it is not proof that pi failed to emit `toolcall_start` / `toolUse`.
|
|
331
|
+
|
|
332
|
+
**Do not close #40 as duplicate of #55 without session JSONL.** #55 surfaces scrubbed SDK run failures and abort causes in the TUI. #40 can occur with no error toast when the model prints tool metadata as assistant text, when replay is display-only but the user expected real execution, when stale native replay routing or plan-strip resync gaps produce `Tool * not found` errors (see **#52**), or when started SDK tools were discarded at run end (see **#52** maintainer debug and [Discarded incomplete SDK tool calls](#discarded-incomplete-sdk-tool-calls) above). A hard **process exit** from uncaught `ConnectError` / `ETIMEDOUT` is **#43**, not #40 text echo.
|
|
333
|
+
|
|
334
|
+
### Reporter checklist (required before claiming a provider bug)
|
|
335
|
+
|
|
336
|
+
Ask the reporter (or capture yourself) for:
|
|
337
|
+
|
|
338
|
+
| Field | Why |
|
|
339
|
+
| --- | --- |
|
|
340
|
+
| `pi --version` and installed `pi-cursor-sdk` version | Confirms extension/runtime in use |
|
|
341
|
+
| Model ID (for example `cursor/composer-2.5`) | Routing/replay behavior is model-scoped |
|
|
342
|
+
| 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 |
|
|
344
|
+
| 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
|
+
| Red toast / `errorMessage` text, if any | Distinguishes #55 failure surfacing from silent text echo |
|
|
346
|
+
| Process exit / uncaught `ConnectError` / `ETIMEDOUT` stack trace, if any | Hard network crash (**#43**), not #40 model text echo |
|
|
347
|
+
| Session JSONL path (redact secrets before sharing) | Source of truth for `toolCall` vs plain `text` blocks; scan for replay `Tool * not found` (**#52**) |
|
|
348
|
+
|
|
349
|
+
### Capture steps (maintainers)
|
|
350
|
+
|
|
351
|
+
Use an isolated session dir and do not paste auth, tokens, or raw debug payloads into issues.
|
|
352
|
+
|
|
353
|
+
```bash
|
|
354
|
+
SMOKE_DIR="/tmp/pi-cursor-sdk-issue40-$(date +%s)"
|
|
355
|
+
mkdir -p "$SMOKE_DIR/home/.pi/agent"
|
|
356
|
+
cp "$HOME/.pi/agent/auth.json" "$SMOKE_DIR/home/.pi/agent/auth.json"
|
|
357
|
+
chmod 600 "$SMOKE_DIR/home/.pi/agent/auth.json"
|
|
358
|
+
|
|
359
|
+
env -i HOME="$SMOKE_DIR/home" PATH="/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin" \
|
|
360
|
+
MISE_DISABLE=1 \
|
|
361
|
+
PI_CURSOR_PI_TOOL_BRIDGE_DEBUG=1 \
|
|
362
|
+
pi -e . --cursor-no-fast --model cursor/composer-2.5 \
|
|
363
|
+
--session-dir "$SMOKE_DIR/session" \
|
|
364
|
+
-p '<exact reporter prompt>'
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
Optional provider/SDK timelines (separate from pi session JSONL; see [Pi provider SDK event capture](#pi-provider-sdk-event-capture) and [Cursor SDK event capture probe](#cursor-sdk-event-capture-probe)):
|
|
368
|
+
|
|
369
|
+
For pi parsing, replay routing, or bridge timing, prefer:
|
|
370
|
+
|
|
371
|
+
```bash
|
|
372
|
+
npm run debug:provider-events -- \
|
|
373
|
+
--cwd "$PWD" \
|
|
374
|
+
--model cursor/composer-2.5 \
|
|
375
|
+
--prompt '<exact reporter prompt>' \
|
|
376
|
+
--out "$SMOKE_DIR/provider-events"
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
Or add `PI_CURSOR_SDK_EVENT_DEBUG=1` to the pi run above (writes under `.debug/cursor-sdk-events/`).
|
|
380
|
+
|
|
381
|
+
For raw Cursor SDK surfaces only:
|
|
382
|
+
|
|
383
|
+
```bash
|
|
384
|
+
npm run debug:sdk-events -- \
|
|
385
|
+
--cwd "$PWD" \
|
|
386
|
+
--model composer-2.5 \
|
|
387
|
+
--prompt '<exact reporter prompt>' \
|
|
388
|
+
--out "$SMOKE_DIR/sdk-events"
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
### JSONL classification (decision tree)
|
|
392
|
+
|
|
393
|
+
Start with whether pi stayed alive:
|
|
394
|
+
|
|
395
|
+
0. **pi process exited / shell returned with uncaught `ConnectError` (`ETIMEDOUT`, code 14, `read ETIMEDOUT`)** — hard network crash bypassing provider error surfacing. Route to **#43** (coordinate with #55 for caught-failure messaging). If tools were mid-flight, note whether session JSONL ends abruptly; do not classify as #40 model text echo.
|
|
396
|
+
|
|
397
|
+
Then inspect the failing assistant turn in `$SMOKE_DIR/session/*.jsonl`:
|
|
398
|
+
|
|
399
|
+
1. **Error `toolResult` (`isError: true`) or error assistant message contains `Tool grep/cursor/find/ls not found`** — stale `context.tools` snapshot or plan-strip resync gap after plan-mode execute stripped active tools. Run `node scripts/validate-smoke-jsonl.mjs --replay-errors-only "$SMOKE_DIR/session"`. Optional: `display-decisions.jsonl` from `PI_CURSOR_SDK_EVENT_DEBUG=1` shows `inactive_trace` routing. Route to **#52** — not model text echo (those strings appear in persisted error records, not narrated `Tool call (` lines). See [Dual-check invariant](#dual-check-invariant-contexttools-vs-pi-active-tools).
|
|
400
|
+
2. **`content` has `type: "toolCall"` blocks and matching `toolResult` rows** — pi executed or replayed tools; if the TUI still looked like plain text, capture a screenshot and pi version (possible pi TUI/display issue, not provider dispatch).
|
|
401
|
+
3. **`content` is only `type: "text"` and text contains `Tool call (` / `cursor-replay-` / serialized arg keys** — model text echo of prompt transcript format; not #55, not #52 stale routing. Compare with `buildCursorPrompt()` output in the prior turn.
|
|
402
|
+
4. **No `toolCall` blocks, no error toast, user expected real execution** — check whether names are replay-only (`cursor-replay-*`) or Cursor-native MCP; replay never re-runs work ([replay doc](./cursor-native-tool-replay.md)).
|
|
403
|
+
5. **`stopReason: "error"` or scrubbed `errorMessage`** — classify under **#55**; check whether incomplete started tools were discarded (`discardIncompleteStartedToolCalls()`). Discarded starts with no completion and no model text echo: see `coordinator-events.jsonl` phase `discarded-incomplete-started-tool-call` ([Discarded incomplete SDK tool calls](#discarded-incomplete-sdk-tool-calls) above); route broader stale/inactive replay gaps to **#52**.
|
|
404
|
+
6. **Bridge expected (`pi__*` in Cursor MCP)** — inspect stderr `[pi-cursor-sdk:bridge]` JSONL with `PI_CURSOR_PI_TOOL_BRIDGE_DEBUG=1` for pending/unresolved bridge requests.
|
|
405
|
+
|
|
406
|
+
Quick structural scan (no secrets):
|
|
407
|
+
|
|
408
|
+
```bash
|
|
409
|
+
node scripts/validate-smoke-jsonl.mjs --replay-errors-only "$SMOKE_DIR/session"
|
|
410
|
+
rg '"type": "toolCall"|Tool call \(Cursor|cursor-replay-' "$SMOKE_DIR/session"/*.jsonl
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
### When to file follow-ups
|
|
414
|
+
|
|
415
|
+
- **#43** — pi exited from uncaught `ConnectError` / `ETIMEDOUT` during Cursor SDK HTTP traffic (hard crash, not a scrubbed #55 toast).
|
|
416
|
+
- **#55** — caught SDK run failure or abort with missing/opaque detail (already addressed on main for surfacing).
|
|
417
|
+
- **#52** — stale/inactive native replay routing after plan-strip or stale `context.tools` snapshot (`Tool * not found` in JSONL, `inactive_trace` in `display-decisions.jsonl`); or maintainer needs an explicit "started X, never completed" debug line when JSONL shows no completion and no model text echo.
|
|
418
|
+
- **New issue** — bridge dispatch failure with `[pi-cursor-sdk:bridge]` evidence, or proven provider bug with JSONL showing missing `toolCall` despite SDK `tool-call-completed` in `on-delta.jsonl` from `debug:provider-events` or `debug:sdk-events` artifacts.
|
|
192
419
|
## Related docs and scripts
|
|
193
420
|
|
|
194
421
|
- [Cursor live smoke checklist](./cursor-live-smoke-checklist.md)
|
|
@@ -196,4 +423,6 @@ If live smoke auth is unavailable, report the release as **blocked**, not skippe
|
|
|
196
423
|
- `scripts/isolated-cursor-smoke.sh`
|
|
197
424
|
- `scripts/tmux-live-smoke.sh`
|
|
198
425
|
- `scripts/validate-smoke-jsonl.mjs`
|
|
426
|
+
- `scripts/debug-sdk-events.mjs`
|
|
427
|
+
- `scripts/debug-provider-events.mjs`
|
|
199
428
|
- `test/helpers/cursor-provider-harness.ts` — controllable native replay pi mock (`createNativeToolDisplayPiForTest`)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-cursor-sdk",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.20",
|
|
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",
|
|
@@ -28,6 +28,11 @@
|
|
|
28
28
|
"scripts/tmux-live-smoke.sh",
|
|
29
29
|
"scripts/isolated-cursor-smoke.sh",
|
|
30
30
|
"scripts/validate-smoke-jsonl.mjs",
|
|
31
|
+
"scripts/probe-mcp-coldstart.mjs",
|
|
32
|
+
"scripts/debug-sdk-events.mjs",
|
|
33
|
+
"scripts/debug-provider-events.mjs",
|
|
34
|
+
"scripts/lib/cursor-probe-utils.mjs",
|
|
35
|
+
"scripts/lib/cursor-sdk-output-filter.mjs",
|
|
31
36
|
"README.md",
|
|
32
37
|
"docs/cursor-model-ux-spec.md",
|
|
33
38
|
"docs/cursor-live-smoke-checklist.md",
|
|
@@ -49,7 +54,10 @@
|
|
|
49
54
|
"smoke:live": "scripts/tmux-live-smoke.sh",
|
|
50
55
|
"smoke:isolated": "scripts/isolated-cursor-smoke.sh",
|
|
51
56
|
"smoke:steering": "node scripts/steering-rpc-smoke.mjs",
|
|
52
|
-
"smoke:jsonl": "node scripts/validate-smoke-jsonl.mjs"
|
|
57
|
+
"smoke:jsonl": "node scripts/validate-smoke-jsonl.mjs",
|
|
58
|
+
"debug:sdk-events": "node scripts/debug-sdk-events.mjs",
|
|
59
|
+
"debug:provider-events": "node scripts/debug-provider-events.mjs",
|
|
60
|
+
"debug:mcp-coldstart": "node scripts/probe-mcp-coldstart.mjs"
|
|
53
61
|
},
|
|
54
62
|
"dependencies": {
|
|
55
63
|
"@cursor/sdk": "^1.0.13",
|