pi-cursor-sdk 0.4.0 → 0.5.0

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 (125) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/README.md +65 -13
  3. package/dist/context-window-cache.js +0 -6
  4. package/dist/context.js +8 -0
  5. package/dist/cursor-cloud-lifecycle.js +122 -98
  6. package/dist/cursor-durable-fs.js +97 -3
  7. package/dist/cursor-fallback-models.generated.js +288 -1
  8. package/dist/cursor-fallback-warning.js +2 -2
  9. package/dist/cursor-http1.js +8 -8
  10. package/dist/cursor-live-run-coordinator.js +9 -13
  11. package/dist/cursor-native-replay-routing.js +1 -1
  12. package/dist/cursor-native-tool-display-registration.js +46 -22
  13. package/dist/cursor-native-tool-display-state.js +57 -22
  14. package/dist/cursor-native-tool-display-tools.js +5 -6
  15. package/dist/cursor-pi-tool-bridge-constants.js +23 -2
  16. package/dist/cursor-pi-tool-bridge-run.js +7 -3
  17. package/dist/cursor-pi-tool-bridge-snapshot.js +1 -1
  18. package/dist/cursor-pi-tool-bridge.js +31 -9
  19. package/dist/cursor-provider-binding.js +58 -0
  20. package/dist/cursor-provider-errors.js +28 -7
  21. package/dist/cursor-provider-lazy.js +23 -11
  22. package/dist/cursor-provider-live-run-drain.js +15 -12
  23. package/dist/cursor-provider-run-finalizer.js +19 -8
  24. package/dist/cursor-provider-turn-coordinator.js +1 -0
  25. package/dist/cursor-provider-turn-display-router.js +4 -0
  26. package/dist/cursor-provider-turn-emit.js +1 -0
  27. package/dist/cursor-provider-turn-finalize.js +29 -20
  28. package/dist/cursor-provider-turn-prepare.js +24 -11
  29. package/dist/cursor-provider-turn-runner.js +37 -11
  30. package/dist/cursor-provider-turn-send.js +22 -9
  31. package/dist/cursor-provider-turn-summary.js +89 -0
  32. package/dist/cursor-provider.js +9 -3
  33. package/dist/cursor-request-provenance.js +42 -0
  34. package/dist/cursor-ripgrep-path.js +14 -3
  35. package/dist/cursor-runtime-state.js +50 -43
  36. package/dist/cursor-sdk-billed-usage.js +54 -75
  37. package/dist/cursor-sdk-event-debug-session.js +5 -6
  38. package/dist/cursor-sdk-event-debug.js +9 -6
  39. package/dist/cursor-sdk-process-error-guard.js +8 -7
  40. package/dist/cursor-sdk-runtime.js +13 -0
  41. package/dist/cursor-session-agent-cleanup.js +2 -2
  42. package/dist/cursor-session-agent-lifecycle.js +18 -9
  43. package/dist/cursor-session-agent-lineage.js +28 -12
  44. package/dist/cursor-session-agent-resume.js +51 -35
  45. package/dist/cursor-session-agent.js +9 -9
  46. package/dist/cursor-session-compaction-prep.js +2 -7
  47. package/dist/cursor-session-scope.js +71 -39
  48. package/dist/cursor-session-settings.js +61 -0
  49. package/dist/cursor-skill-tool.js +32 -49
  50. package/dist/cursor-state.js +72 -62
  51. package/dist/cursor-usage-accounting.js +13 -70
  52. package/dist/cursor-usage-command.js +89 -0
  53. package/dist/cursor-usage-ledger.js +512 -0
  54. package/dist/index.js +11 -19
  55. package/dist/model-discovery.js +11 -19
  56. package/docs/cursor-dogfood-checklist.md +3 -3
  57. package/docs/cursor-live-smoke-checklist.md +15 -15
  58. package/docs/cursor-model-ux-spec.md +59 -18
  59. package/docs/cursor-native-tool-replay.md +5 -3
  60. package/docs/cursor-native-tool-visual-audit.md +8 -8
  61. package/docs/cursor-testing-lessons.md +47 -23
  62. package/docs/cursor-tool-surfaces.md +1 -1
  63. package/docs/platform-smoke-implementation.md +10 -6
  64. package/docs/platform-smoke.md +35 -46
  65. package/package.json +6 -6
  66. package/scripts/cloud-runtime-smoke.mjs +4 -2
  67. package/scripts/local-resume-smoke.mjs +1 -1
  68. package/scripts/platform-smoke/card-detect.mjs +2 -8
  69. package/scripts/platform-smoke/targets.mjs +8 -15
  70. package/scripts/platform-smoke/visual-evidence.mjs +7 -2
  71. package/scripts/tmux-live-smoke.sh +57 -90
  72. package/src/context-window-cache.ts +0 -6
  73. package/src/context.ts +8 -0
  74. package/src/cursor-cloud-lifecycle.ts +124 -98
  75. package/src/cursor-durable-fs.ts +65 -3
  76. package/src/cursor-fallback-models.generated.ts +288 -1
  77. package/src/cursor-fallback-warning.ts +2 -2
  78. package/src/cursor-http1.ts +8 -8
  79. package/src/cursor-live-run-coordinator.ts +12 -13
  80. package/src/cursor-native-replay-routing.ts +2 -1
  81. package/src/cursor-native-tool-display-registration.ts +48 -24
  82. package/src/cursor-native-tool-display-state.ts +65 -21
  83. package/src/cursor-native-tool-display-tools.ts +7 -4
  84. package/src/cursor-pi-tool-bridge-constants.ts +27 -2
  85. package/src/cursor-pi-tool-bridge-run.ts +11 -3
  86. package/src/cursor-pi-tool-bridge-snapshot.ts +1 -1
  87. package/src/cursor-pi-tool-bridge.ts +26 -9
  88. package/src/cursor-provider-binding.ts +62 -0
  89. package/src/cursor-provider-errors.ts +27 -7
  90. package/src/cursor-provider-lazy.ts +21 -15
  91. package/src/cursor-provider-live-run-drain.ts +20 -13
  92. package/src/cursor-provider-run-finalizer.ts +24 -15
  93. package/src/cursor-provider-turn-coordinator.ts +2 -0
  94. package/src/cursor-provider-turn-display-router.ts +5 -0
  95. package/src/cursor-provider-turn-emit.ts +3 -1
  96. package/src/cursor-provider-turn-finalize.ts +38 -23
  97. package/src/cursor-provider-turn-prepare.ts +23 -14
  98. package/src/cursor-provider-turn-runner.ts +40 -13
  99. package/src/cursor-provider-turn-send.ts +23 -15
  100. package/src/cursor-provider-turn-summary.ts +94 -0
  101. package/src/cursor-provider-turn-types.ts +37 -4
  102. package/src/cursor-provider.ts +25 -3
  103. package/src/cursor-request-provenance.ts +64 -0
  104. package/src/cursor-ripgrep-path.ts +15 -5
  105. package/src/cursor-runtime-state.ts +53 -48
  106. package/src/cursor-sdk-billed-usage.ts +54 -102
  107. package/src/cursor-sdk-event-debug-session.ts +5 -5
  108. package/src/cursor-sdk-event-debug.ts +10 -6
  109. package/src/cursor-sdk-process-error-guard.ts +7 -7
  110. package/src/cursor-sdk-runtime.ts +14 -0
  111. package/src/cursor-session-agent-cleanup.ts +2 -2
  112. package/src/cursor-session-agent-lifecycle.ts +14 -9
  113. package/src/cursor-session-agent-lineage.ts +26 -12
  114. package/src/cursor-session-agent-resume.ts +49 -36
  115. package/src/cursor-session-agent.ts +13 -10
  116. package/src/cursor-session-compaction-prep.ts +2 -7
  117. package/src/cursor-session-scope.ts +87 -39
  118. package/src/cursor-session-settings.ts +72 -0
  119. package/src/cursor-skill-tool.ts +37 -61
  120. package/src/cursor-state.ts +74 -67
  121. package/src/cursor-usage-accounting.ts +17 -82
  122. package/src/cursor-usage-command.ts +76 -0
  123. package/src/cursor-usage-ledger.ts +500 -0
  124. package/src/index.ts +14 -23
  125. package/src/model-discovery.ts +14 -22
@@ -1,6 +1,6 @@
1
1
  # Platform Smoke Implementation Reference
2
2
 
3
- Back to the canonical [Platform Smoke Gate runbook](./platform-smoke.md) for release commands, required targets and suites, artifacts, assertions, security, and the release bar.
3
+ Back to the canonical [Platform Smoke runbook](./platform-smoke.md) for cost-conscious verification, focused suite selection, optional comprehensive targets/suites, artifacts, assertions, security, and the release bar. Offline/faux checks and exact-input retained evidence come first; only necessary changed-behavior proof warrants the smallest live check on one representative environment. No paid Cloud testing for generic PRs/releases; only explicitly Cursor Cloud-focused PRs/issues may select a necessary focused Cloud check. Automated Cursor PR reviews continue unchanged.
4
4
 
5
5
  This document records detailed detector, registry, command-rendering, implementation-history, replacement, and portability material. The phase plan is retained as implementation history, not as active release instructions.
6
6
 
@@ -18,13 +18,15 @@ Required behavior:
18
18
  - fail when a card/evidence item has the wrong success/error state;
19
19
  - fail when footer/status is missing or unreadable.
20
20
 
21
- The plain-text card inventory uses Node's `stripVTControlCharacters` on the complete captured ANSI stream. OSC-8 hyperlinks may use either BEL or ST (ESC followed by a backslash) terminators, and a PTY chunk may split either sequence. Do not strip chunks independently or use a greedy OSC regex: that can consume the visible `package.json` label, successful read content, or subsequent cards even when xterm renders them correctly. Raw ANSI remains unchanged for independent xterm/PNG rendering and secret scanning; exact card patterns and JSONL success/error assertions still apply. `test/fixtures/platform-read-hyperlinks.json` retains minimal native/bridge headers from real captures with only the local hyperlink target anonymized.
21
+ The legacy card inventory and required-card assertions use the same rendered xterm buffer lines already collected for visual evidence and screenshots. Raw ANSI stripping cannot reconstruct cursor movement, line erasure, or redraws; `terminal.txt` remains raw diagnostic text, not card evidence. Raw ANSI remains unchanged for xterm/PNG rendering and secret scanning; exact card patterns, wrapped-line matching, and JSONL success/error assertions still apply. `test/fixtures/platform-read-hyperlinks.json` retains minimal native/bridge OSC-8 headers from real captures with only the local hyperlink target anonymized.
22
+
23
+ Run the host renderer regression with `node --test test/platform-smoke-rendered-cards.test.mjs`. It requires the host Playwright Chromium checked by platform doctor and fails, rather than skips, if rendering is unavailable. Default `npm test` does not require a guest/CI browser install; the full platform gate still requires doctor and live rendered-card evidence.
22
24
 
23
25
  Meaningful gap closed: earlier card assertions could pass when the prompt mentioned `pi__read` or a missing-file path even if the actual tool card/result never rendered. The gate now requires JSONL result evidence and per-evidence rendered screenshots for native read, native shell success/failure, native edit diffs, bridge read success/failure, and bridge shell success.
24
26
 
25
27
  ## Registry visual classification
26
28
 
27
- The implementation must classify every `CURSOR_TOOL_PRESENTATION_SPECS` entry from `src/cursor-tool-presentation-registry.ts` as required or excluded for the release visual gate. A validation check fails when a registry entry lacks classification.
29
+ The implementation must classify every `CURSOR_TOOL_PRESENTATION_SPECS` entry from `src/cursor-tool-presentation-registry.ts` as required or excluded for the optional comprehensive visual matrix. A validation check fails when a registry entry lacks classification.
28
30
 
29
31
  Required deterministic cards:
30
32
 
@@ -176,8 +178,10 @@ Update:
176
178
 
177
179
  They must state:
178
180
 
179
- - required local release gate is `npm run smoke:platform:all`;
180
- - cloud-runtime changes additionally require `npm run smoke:cloud`;
181
+ - offline/faux checks and exact-input retained proof come first; new live proof is limited to the smallest meaningful changed-behavior check on one representative environment;
182
+ - `npm run smoke:platform:all` is an optional comprehensive matrix, not an unconditional commit/release gate;
183
+ - no paid Cloud testing for generic PRs/releases; only explicitly Cursor Cloud-focused PRs/issues may select necessary focused Cloud proof, with unchanged run/evidence and cleanup contracts;
184
+ - no full paid campaign replay, matrix-only host coverage, or automatic paid retries; automated Cursor PR reviews continue unchanged;
181
185
  - legacy smoke scripts are inner-loop/debug helpers;
182
186
  - `tmux` visual smoke is not the canonical cross-platform gate.
183
187
 
@@ -195,7 +199,7 @@ Replace or redesign this platform runner if any of these become true:
195
199
  - The full gate exceeds the fixed Cursor invocation budget.
196
200
  - Node 24 + `node-pty` cannot be made reliable on Windows native.
197
201
 
198
- If the gate is replaced, document the new cross-platform release process before removing this one. Existing local smoke scripts remain inner-loop/debug helpers, not release gates.
202
+ If the runner is replaced, document the new verification process before removing it. Keep offline cross-platform contracts and selected-suite evidence fidelity; the comprehensive paid matrix is optional, not a default ship step.
199
203
 
200
204
  ## Portability to other pi extensions
201
205
 
@@ -1,6 +1,6 @@
1
- # Platform Smoke Gate
1
+ # Platform Smoke
2
2
 
3
- Status: current local-runtime release gate for Cursor provider/runtime changes. Cloud-runtime changes also require the separate paid `npm run smoke:cloud` gate. The Crabbox runner, packed-install platform-build suite, and real live PTY/ConPTY suite runner are implemented for macOS, Ubuntu, and Windows native targets with one-lease-per-target orchestration.
3
+ Status: optional comprehensive local-runtime matrix plus focused changed-behavior checks. Offline/faux verification and exact-input retained-evidence reuse come first. Paid checks are only the smallest meaningful proof needed for changed behavior on one representative environment; docs/metadata-only changes need none. No full paid campaign replay, matrix-only host coverage, or automatic paid retries. No paid Cloud testing for generic PRs/releases; only PRs/issues explicitly focused on Cursor Cloud may select a necessary focused Cloud check. Automated Cursor PR reviews continue unchanged. The Crabbox runner, packed-install platform-build suite, and real live PTY/ConPTY suite runner are implemented for macOS, Ubuntu, and Windows native targets with one-lease-per-target orchestration.
4
4
 
5
5
  Detailed detector, registry, command-rendering, implementation-history, replacement, and portability reference: [Platform Smoke Implementation Reference](./platform-smoke-implementation.md).
6
6
 
@@ -12,48 +12,47 @@ Crabbox best-practice baseline applied from `~/Projects/crabbox`: Crabbox owns l
12
12
 
13
13
  ## Decision
14
14
 
15
- Crabbox is the required local platform smoke runner for `pi-cursor-sdk` releases that touch Cursor provider/runtime behavior. PRs that touch actual cloud runtime execution must also run `npm run smoke:cloud`.
15
+ Keep offline cross-platform CI, build/type/package checks, and native/faux contracts. Reuse retained proof when its tested inputs are unchanged. If changed behavior needs new real-service proof, choose the smallest existing check and one representative environment. Source-file touch alone does not require a live run.
16
16
 
17
- Inner-loop checks remain useful, but they are not release gates:
17
+ Offline checks:
18
18
 
19
19
  ```bash
20
20
  npm run verify
21
21
  npm pack --dry-run
22
22
  ```
23
23
 
24
- The required local release gate is exactly:
24
+ Example focused live selection for changed restart behavior (choose the relevant suite/target, not this example by default):
25
25
 
26
26
  ```bash
27
- npm run smoke:platform:all
27
+ node scripts/platform-smoke.mjs run --target macos --suite cursor-local-resume-restart
28
28
  ```
29
29
 
30
- Cloud-runtime changes additionally require:
30
+ Optional comprehensive coverage, not an unconditional commit/release gate:
31
31
 
32
32
  ```bash
33
- npm run smoke:cloud
33
+ npm run smoke:platform:all
34
34
  ```
35
35
 
36
- `smoke:platform:all` runs `smoke:platform:doctor` first and only starts the target matrix after doctor passes. Maintainers may still run `npm run smoke:platform:doctor` by itself for setup diagnosis.
37
-
36
+ That command runs doctor first, then all configured targets/suites. The CLI can run one existing suite on one target without running the full matrix. Doctor itself makes no Cursor calls but checks the comprehensive setup; it is not proof of a provider turn. Per-target commands without `--suite` run every configured suite on that target, so prefer explicit suite selection.
38
37
 
39
- Per-target commands exist for diagnosis and iteration. They are not additional release-gate commands because requiring each per-target command plus `all` doubles Cursor token use.
38
+ Only a PR/issue explicitly focused on Cursor Cloud permits necessary paid Cloud testing; generic PRs/releases and incidental Cloud changes do not. Even for Cloud-focused work, `npm run smoke:cloud` is an optional multi-lane matrix, not mandatory. Select coverage only when its live proof is needed, retaining the selected check's full assertions and cleanup contract.
40
39
 
41
- No partial adoption exists. The release evidence must include macOS, Ubuntu, and Windows native passing through `smoke:platform:all`.
40
+ Do not repeat successful unchanged lanes or automatically retry paid failures. Preserve raw failed evidence, diagnose offline, and choose another call only when necessary. Report selected scope honestly; optional unrun hosts/lanes do not block landing, and focused evidence is not full-matrix proof.
42
41
 
43
42
  ## Non-negotiable constraints
44
43
 
45
44
  - No GitHub Actions dependency.
46
- - No cloud provider dependency in the default local platform gate; cloud-runtime changes use the separate paid cloud gate.
45
+ - No paid Cloud testing for generic PRs/releases. Explicitly Cursor Cloud-focused PRs/issues are the only testing exception; keep Cloud checks separate from local checks.
47
46
  - No Crabbox broker/coordinator dependency.
48
- - No release gate that runs on only one operating system.
49
- - No release gate that proves command behavior but not TUI visual behavior.
50
- - No platform release gate based on `pi -e .`.
51
- - No skipped target because setup is missing; missing setup is a doctor failure.
47
+ - Keep offline cross-platform coverage; do not require a paid host matrix merely for matrix coverage.
48
+ - Visual claims require rendered evidence, not command success alone.
49
+ - Packed-install claims require packed-install evidence; direct `pi -e .` proof has a narrower scope.
50
+ - Missing setup fails a selected check; optional unselected targets are not failed release prerequisites.
52
51
  - No one-prompt-per-card visual matrix.
53
52
  - No `tmux` as the canonical visual test contract.
54
53
  - No target passes from stdout alone when JSONL or visual proof is required.
55
54
  - No target loses artifacts on failure.
56
- - No hidden optional evidence. Every required artifact is produced or the suite fails.
55
+ - Every selected suite retains its required artifacts and assertions or fails. Do not weaken a check to reduce cost.
57
56
 
58
57
  ## Required Crabbox baseline
59
58
 
@@ -67,7 +66,7 @@ version: 0.26.0 or newer
67
66
  binary: Homebrew `crabbox` on PATH (`/opt/homebrew/bin/crabbox` on Apple Silicon Homebrew installs)
68
67
  ```
69
68
 
70
- Use the Homebrew Crabbox binary on PATH for normal release gates. `PLATFORM_SMOKE_CRABBOX=/path/to/crabbox` is only an explicit override for testing a non-default binary. `smoke:platform:doctor` verifies the configured binary and fails when it is older than the configured minimum version.
69
+ Use the Homebrew Crabbox binary on PATH for selected platform checks. `PLATFORM_SMOKE_CRABBOX=/path/to/crabbox` is only an explicit override for testing a non-default binary. `smoke:platform:doctor` verifies the configured binary and fails when it is older than the configured minimum version.
71
70
 
72
71
  Required Crabbox providers:
73
72
 
@@ -106,7 +105,7 @@ Rendering is host-side. Targets capture the real ANSI stream; the macOS host ren
106
105
 
107
106
  ## Target session model
108
107
 
109
- Each target opens one Crabbox target session, syncs once, runs all suites for that target under one coherent target run id, collects artifacts, and stops/releases the target. The release-gate entrypoint runs required targets sequentially to prevent shared host, VM/container, and Cursor API contention; each target runs its own suites in order and fails fast within that target. Platform smoke disables Crabbox git-seed sync (`CRABBOX_SYNC_GIT_SEED=false`) so every run tests the current local checkout and uncommitted smoke-runner changes rather than a remote Git seed.
108
+ Each selected target opens one Crabbox session, syncs once, runs selected suites under one coherent run id, collects artifacts, and stops/releases the target. The comprehensive entrypoint runs configured targets sequentially to prevent shared host, VM/container, and Cursor API contention; each target runs its own suites in order and fails fast within that target. Platform smoke disables Crabbox git-seed sync (`CRABBOX_SYNC_GIT_SEED=false`) so every run tests the current local checkout and uncommitted smoke-runner changes rather than a remote Git seed.
110
109
 
111
110
  ```text
112
111
  start target session
@@ -135,7 +134,7 @@ start target session
135
134
  end target session
136
135
  ```
137
136
 
138
- The target session fails fast. The release-gate path handles one target at a time: it warms one Crabbox lease, performs one fresh sync, runs suites in order, and stops that target after the first failure before starting the next target. Total wall time is therefore additive across required targets; this avoids nondeterministic worker loss and live-run stalls from competing VM/container load and concurrent Cursor API calls. Per-suite commands remain available for diagnosis, but they are intentionally not the normal release path because repeated warmup/sync/install cycles make releases too slow.
137
+ The target session fails fast. The comprehensive path handles one target at a time: it warms one lease, syncs once, runs suites in order, and stops the target after its first failure before starting the next. This avoids competing VM/container load and Cursor calls. Routine verification should instead select the smallest meaningful changed-behavior suite on one representative target; do not replay the full campaign or duplicate a focused run with the full matrix.
139
138
 
140
139
  Runtime budget is part of the contract:
141
140
 
@@ -146,7 +145,7 @@ Runtime budget is part of the contract:
146
145
  - Visual coverage is batched into one native prompt, one focused HTTP/1.1 transport prompt, one bridge prompt, and one abort/cleanup prompt per target. Do not split the card matrices into one prompt per card.
147
146
  - The gate is fail-fast by target to avoid burning Cursor calls after a platform has already failed.
148
147
 
149
- ## Required targets
148
+ ## Comprehensive matrix targets
150
149
 
151
150
  | Target | Crabbox provider | Execution contract | TUI visual contract |
152
151
  | --- | --- | --- | --- |
@@ -156,15 +155,15 @@ Runtime budget is part of the contract:
156
155
 
157
156
  Ubuntu is covered as its own local-container target, and Windows native remains a full visual TUI target.
158
157
 
159
- ## Required cloud smoke gate
158
+ ## Explicit Cursor Cloud-focused testing exception
160
159
 
161
- Cloud validation stays separate from `smoke:platform:all`. Releases that touch actual cloud execution must run both the local platform gate and:
160
+ No paid Cloud testing for generic PRs or releases. Only a PR or issue explicitly focused on Cursor Cloud may select necessary paid Cloud proof, after offline/faux checks and retained-evidence reuse. Incidental Cloud code changes do not qualify. The no-flag multi-lane command remains callable but is not mandatory even for Cloud-focused work:
162
161
 
163
162
  ```bash
164
163
  npm run smoke:cloud
165
164
  ```
166
165
 
167
- The no-flag command is the required `cursor/grok-4.6` matrix. It uses current `gh` CLI authentication to create one private throwaway GitHub repository, seeds clean `main`, `starting-ref`, and `direct-push` branches, and runs persisted-session named lanes for:
166
+ This optional `cursor/grok-4.6` matrix retains its complete run/evidence and cleanup contract. It uses current `gh` CLI authentication to create one private throwaway GitHub repository, seeds clean `main`, `starting-ref`, and `direct-push` branches, and runs persisted-session named lanes for:
168
167
 
169
168
  - cancellation, with exact agent/run IDs captured before abort, retained `runIdSource` (`metadata` or installed-SDK `Agent.listRuns()` recovery), and terminal `cancelled` independently read through the SDK;
170
169
  - explicit HTTPS repository plus `startingRef`, requiring a distinct pushed cloud branch with remote-content and starting-ref-ancestry proof, recording whether the SDK returned branch metadata, and validating any returned PR URL through GitHub;
@@ -179,13 +178,13 @@ Every path harvests exact IDs from provider metadata and canonical lifecycle ses
179
178
 
180
179
  Before removing successful raw artifacts, the gate atomically replaces `docs/evidence/cursor-cloud-smoke-matrix-latest.json` with a known-shape summary containing timestamp, model, lane observations, exact agent/run IDs, agent cleanup proof, repository cleanup proof, and retained evidence provenance. Provenance records the extension package version, installed `@cursor/sdk` version, git source revision, and a deterministic `packageSourceSha256` over the full published package surface from `package.json` `files` plus `package.json` itself (relative path + bytes; directories expanded; symlinks/non-regular paths rejected). Generated `docs/evidence/*` is outside that published surface and is not hashed. Because successful pre-commit checkouts may be uncommitted, the package-source hash is authoritative for code identity and the revision is baseline identity only. The summary is a runtime-validated known shape (explicit six-lane allowlist, complete lane-agent cleanup coverage, repository proof, provenance) that round-trips through the persisted-evidence validator with no prompts or raw output, and must pass canonical secret scrubbing plus forbidden-field scanning; a run or cleanup failure observed before the atomic rename commit point never overwrites the last successful summary. A signal first dispatched after that point can fail the process while retaining the newly committed completed-cleanup summary, as defined above. Offline release-gate resource coordination (run → harvest IDs → cleanup agents → cleanup repo → evidence only on complete success) lives in `coordinateCloudSmokeReleaseGate()` inside `scripts/lib/cloud-smoke-cleanup-evidence.mjs`. GitHub throwaway fixture ownership lives in `scripts/lib/cloud-smoke-github.mjs`, signal-safe child shutdown lives in `scripts/lib/cloud-smoke-shutdown.mjs`, and `scripts/cloud-runtime-smoke.mjs` keeps concrete lane logic.
181
180
 
182
- `npm run smoke:cloud:context` (`--context-matrix`) remains optional, separate proof for fresh-versus-bootstrap context handoff. `fresh` must answer `NO_MARKER`; `bootstrap` must recall the marker. Its agents receive the same archive, delete, `Agent.get` not-found/404, and archived-inclusive list-exclusion verification, but it does not create a GitHub repository or replace the required-matrix evidence summary.
181
+ Only within the explicitly Cursor Cloud-focused exception, `npm run smoke:cloud:context` (`--context-matrix`) remains optional, separate proof for fresh-versus-bootstrap context handoff. `fresh` must answer `NO_MARKER`; `bootstrap` must recall the marker. Its agents receive the same archive, delete, `Agent.get` not-found/404, and archived-inclusive list-exclusion verification, but it does not create a GitHub repository or replace the required-matrix evidence summary.
183
182
 
184
- This cloud gate does not replace the local macOS/Ubuntu/Windows `smoke:platform:all` gate.
183
+ Cloud product capabilities and offline contracts remain supported. Neither this Cloud matrix nor the comprehensive local matrix is an unconditional ship requirement.
185
184
 
186
185
  ## Focused local resume smoke
187
186
 
188
- The platform matrix includes the required local-resume lanes: restart, safety, tool-surface, abort, tree, copy/switch, fallback, compaction, default/opt-out proof, and recorded-ID-only cleanup. Platform lanes run those scripts against the target's shared packed package path, then copy each lane's session JSONL, Cursor SDK debug metadata, runtime-launch record, and other bounded smoke artifacts into its canonical platform suite directory. The same scripts still load the source checkout by default when run directly as focused host-local inner-loop checks. Windows uses the intentionally short target-side evidence component `lr` so the Cursor SDK's derived SQLite path remains below legacy `MAX_PATH`; every suite removes and verifies that directory before use, failing closed on stale or locked evidence.
187
+ The optional comprehensive platform matrix includes these local-resume lanes: restart, safety, tool-surface, abort, tree, copy/switch, fallback, compaction, default/opt-out proof, and recorded-ID-only cleanup. Platform lanes run those scripts against the target's shared packed package path, then copy each lane's session JSONL, Cursor SDK debug metadata, runtime-launch record, and other bounded smoke artifacts into its canonical platform suite directory. The same scripts still load the source checkout by default when run directly as focused host-local inner-loop checks. Windows uses the intentionally short target-side evidence component `lr` so the Cursor SDK's derived SQLite path remains below legacy `MAX_PATH`; every suite removes and verifies that directory before use, failing closed on stale or locked evidence.
189
188
 
190
189
  The smoke starts one sessionful local Cursor run with local resume enabled by default, records the SDK agent id from provider debug metadata, restarts pi against the same session, asks for the remembered marker, and verifies:
191
190
 
@@ -319,7 +318,7 @@ export default {
319
318
  };
320
319
  ```
321
320
 
322
- `ubuntuContainerBaseImage` is `cimg/node:24.21`, the Ubuntu 24.04 Node 24 base with the current glibc baseline for native test dependencies. The runner builds the local `ubuntuContainerImage` wrapper with only `USER root` changed before warmup because Crabbox 0.36.0 must install SSH/Git/rsync/curl during bootstrap and `cimg/node` defaults to an unprivileged user. An explicit `PLATFORM_SMOKE_UBUNTU_IMAGE` bypasses that build and must already support Crabbox bootstrap. Package 0.4.0 requires Node 24+, and this gate validates Node 24 on macOS, Ubuntu, and Windows.
321
+ `ubuntuContainerBaseImage` is `cimg/node:24.21`, the Ubuntu 24.04 Node 24 base with the current glibc baseline for native test dependencies. The runner builds the local `ubuntuContainerImage` wrapper with only `USER root` changed before warmup because Crabbox 0.36.0 must install SSH/Git/rsync/curl during bootstrap and `cimg/node` defaults to an unprivileged user. An explicit `PLATFORM_SMOKE_UBUNTU_IMAGE` bypasses that build and must already support Crabbox bootstrap. The package requires Node 24+, and this gate validates Node 24 on macOS, Ubuntu, and Windows.
323
322
 
324
323
  `windowsParallels` records this repo's default shared Windows template contract. Environment overrides may point at a temporary candidate template during infrastructure work, but release runs should use the shared `pi-extension-windows-template` / `crabbox-ready` baseline unless this document is updated.
325
324
 
@@ -524,7 +523,7 @@ Cursor calls: `2`.
524
523
 
525
524
  Purpose:
526
525
 
527
- - prove guarded local resume default-on behavior across a pi process restart on each required OS;
526
+ - prove guarded local resume default-on behavior across a pi process restart on each selected OS;
528
527
  - assert the first turn creates a local `agent-*` and the second turn resumes the same `agent-*`;
529
528
  - force local runtime and clear cloud env knobs so ambient cloud settings cannot satisfy this suite.
530
529
 
@@ -637,9 +636,9 @@ PI_CURSOR_SDK_EVENT_DEBUG=1
637
636
 
638
637
  Purpose:
639
638
 
640
- - prove the packed extension completes a real local provider turn through the SDK's opt-in HTTP/1.1/SSE transport on every required OS;
639
+ - prove the packed extension completes a real local provider turn through the SDK's opt-in HTTP/1.1/SSE transport on each selected OS;
641
640
  - prove the final TUI status visibly includes `cursor:local ... http1`;
642
- - keep the default transport covered by the other required live suites.
641
+ - use offline contracts or valid retained default-transport evidence rather than adding unrelated live runs.
643
642
 
644
643
  Required final marker: `HTTP1_LIVE_OK`.
645
644
 
@@ -796,9 +795,9 @@ cursor-local-resume-cleanup: 4
796
795
 
797
796
  Maximum per target: `37` Cursor invocations.
798
797
 
799
- Maximum full gate: `111` Cursor invocations.
798
+ Declared maximum full matrix: `111` Cursor invocations (`37` per target across three targets). These are scenario budgets, not measured fixed billable model-call or SDK-step counts; actual service usage can differ.
800
799
 
801
- The merge gate is `npm run smoke:platform:all`; that script runs doctor first and then the matrix to preserve this budget. No suite adds a new Cursor invocation without updating this plan and the scenario source of truth (`scripts/platform-smoke/scenarios.mjs`, plus `scripts/platform-smoke/local-resume-suites.mjs` for local-resume lanes).
800
+ `npm run smoke:platform:all` is optional comprehensive coverage; it runs doctor first and then the matrix within these declared budgets. Routine verification selects only necessary changed-behavior proof on one representative environment. No suite adds a new Cursor invocation without updating this plan and the scenario source of truth (`scripts/platform-smoke/scenarios.mjs`, plus `scripts/platform-smoke/local-resume-suites.mjs` for local-resume lanes).
802
801
 
803
802
  ## Artifact contract
804
803
 
@@ -982,16 +981,6 @@ Bridge diagnostics may include safe tool names and correlation IDs only.
982
981
 
983
982
  ## Release bar
984
983
 
985
- A local provider/runtime release is ready only after this exact command passes on the maintainer machine:
986
-
987
- ```bash
988
- npm run smoke:platform:all
989
- ```
990
-
991
- Cloud-runtime releases additionally require:
992
-
993
- ```bash
994
- npm run smoke:cloud
995
- ```
984
+ Complete offline/faux verification and reuse valid exact-input retained proof. If changed behavior still needs real-service evidence, run the smallest meaningful existing check on one representative environment. Keep its assertions, visual proof when relevant, persisted session/debug evidence, and cleanup intact. Docs/metadata-only changes need no paid calls; optional unrun matrix lanes do not block landing.
996
985
 
997
- `smoke:platform:all` runs doctor first and then all required local targets and suites in one full gate execution.
986
+ No paid Cloud testing for generic PRs/releases; only explicitly Cursor Cloud-focused PRs/issues may select necessary focused Cloud proof. The multi-lane Cloud command and `smoke:platform:all` remain optional comprehensive tools, not unconditional gates. No full paid campaign replay, matrix-only host coverage, or automatic paid retries. Automated Cursor PR reviews continue unchanged. Record exactly what passed, what retained evidence was reused, and any specific necessary proof still missing.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-cursor-sdk",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "pi provider extension backed by @cursor/sdk local and cloud agents",
5
5
  "author": "Mitch Fultz (https://github.com/fitchmultz)",
6
6
  "license": "MIT",
@@ -120,7 +120,7 @@
120
120
  "node": ">=24"
121
121
  },
122
122
  "scripts": {
123
- "check:compat": "npm run build && npm run verify && node --test test/native-provider.test.mjs",
123
+ "check:compat": "npm run build && npm run verify && node --test test/ci-compatibility.test.mjs test/native-provider.test.mjs test/native-cursor-flow.test.mjs",
124
124
  "build": "node ./scripts/build.mjs",
125
125
  "prepare": "node ./scripts/prepare.mjs",
126
126
  "typecheck": "npm run typecheck:src && npm run typecheck:tests && npm run typecheck:replay-compile",
@@ -180,16 +180,16 @@
180
180
  "typebox": "*"
181
181
  },
182
182
  "devDependencies": {
183
- "@earendil-works/pi-ai": "0.87.1",
184
- "@earendil-works/pi-coding-agent": "0.87.1",
185
- "@earendil-works/pi-tui": "0.87.1",
183
+ "@earendil-works/pi-ai": "0.99.1",
184
+ "@earendil-works/pi-coding-agent": "0.99.1",
185
+ "@earendil-works/pi-tui": "0.99.1",
186
186
  "@modelcontextprotocol/client": "2.1.0",
187
187
  "@types/node": "24.19.0",
188
188
  "@typescript/typescript6": "6.0.2",
189
189
  "@xterm/xterm": "6.0.0",
190
190
  "node-pty": "1.1.0",
191
191
  "playwright": "1.63.0",
192
- "typebox": "1.3.34",
192
+ "typebox": "1.3.27",
193
193
  "typescript": "7.0.2",
194
194
  "vitest": "5.0.2"
195
195
  },
@@ -84,14 +84,16 @@ class SmokeFailure extends Error {
84
84
  }
85
85
 
86
86
  function printHelp() {
87
- console.log(`Required live Cursor cloud release smoke for pi-cursor-sdk.
87
+ console.log(`Explicit Cursor Cloud-focused live smoke for pi-cursor-sdk.
88
+
89
+ No paid Cloud testing for generic PRs/releases. Only PRs/issues explicitly focused on Cursor Cloud may select necessary focused Cloud proof after offline checks and retained-evidence reuse. Automated Cursor PR reviews continue unchanged. The multi-lane matrix is optional, not an unconditional ship gate; no automatic paid retries.
88
90
 
89
91
  Usage:
90
92
  npm run smoke:cloud
91
93
  npm run smoke:cloud:context
92
94
  node scripts/cloud-runtime-smoke.mjs [--context-matrix]
93
95
 
94
- The no-flag release gate creates and deletes one private throwaway GitHub repository and runs named lanes for cancel, explicit repo/startingRef branch reporting, direct push, missing branch failure, lifecycle delete, and account-conditional artifacts/raw usage. It requires current gh auth with private-repo create/push/delete access and Cursor Cloud access to that repository.
96
+ The optional no-flag matrix creates and deletes one private throwaway GitHub repository and runs named lanes for cancel, explicit repo/startingRef branch reporting, direct push, missing branch failure, lifecycle delete, and account-conditional artifacts/raw usage. It requires current gh auth with private-repo create/push/delete access and Cursor Cloud access to that repository.
95
97
 
96
98
  Environment:
97
99
  CURSOR_API_KEY Required for cloud runs and verified agent cleanup.
@@ -473,7 +473,7 @@ async function runFallbackSmoke() {
473
473
  const fallback = await promptAndRead({
474
474
  rpc,
475
475
  artifactDir: artifactRoot,
476
- message: "What exact LOCAL_FALLBACK marker is visible after the missing local agent fallback? Reply exactly MARKER=<marker> if visible, otherwise NO_MARKER.",
476
+ message: "Repeat the exact LOCAL_FALLBACK marker from my immediately previous message in this conversation. Use only conversation history; do not use tools or inspect files. Reply exactly MARKER=<marker> if that prior message is present, otherwise NO_MARKER.",
477
477
  timeoutMs,
478
478
  seenMetadata,
479
479
  });
@@ -8,7 +8,6 @@
8
8
 
9
9
  import { writeFileSync, mkdirSync } from "node:fs";
10
10
  import { resolve } from "node:path";
11
- import { stripVTControlCharacters } from "node:util";
12
11
  import { matchesWrappedLineAt } from "./wrapped-line-match.mjs";
13
12
 
14
13
  const CARD_PATTERNS = [
@@ -35,21 +34,16 @@ const CARD_PATTERNS = [
35
34
  { id: "footer-status", pattern: /\bgrok-4\.6\b|\bcomposer-2-5\b|\bcomposer-2\.5\b/i },
36
35
  ];
37
36
 
38
- function cleanLine(line) {
39
- return stripVTControlCharacters(line).replace(/\r/g, "");
40
- }
41
-
42
37
  function matchesCardAt(lines, index, card) {
43
38
  return matchesWrappedLineAt(lines, index, card.pattern, card.wrappedPattern);
44
39
  }
45
40
 
46
41
  /**
47
- * Detect stable rendered evidence regions in terminal text.
42
+ * Detect stable evidence regions in canonical rendered xterm buffer lines.
48
43
  *
49
44
  * Returns an array of { id, label, startLine, endLine }.
50
45
  */
51
- export function detectCards(txtContent) {
52
- const lines = txtContent.split("\n").map(cleanLine);
46
+ export function detectCards(lines) {
53
47
  const cards = [];
54
48
  const seen = new Set();
55
49
 
@@ -23,7 +23,7 @@ export { isSafePlatformBundlePath as isSafeBundlePath } from "./artifacts.mjs";
23
23
  import { getScenario } from "./scenarios.mjs";
24
24
  import { warmupLease, runOnLease, stopLease } from "./crabbox-runner.mjs";
25
25
  import { renderAll } from "./render-ansi.mjs";
26
- import { assertRequiredCards, detectCards, writeCardArtifacts } from "./card-detect.mjs";
26
+ import { assertRequiredCards } from "./card-detect.mjs";
27
27
  import { collectVisualEvidence } from "./visual-evidence.mjs";
28
28
  import { extractContentText, extractFinalTextContent } from "./jsonl-text.mjs";
29
29
  import { executeLocalResumeSuite } from "./local-resume-runner.mjs";
@@ -531,7 +531,6 @@ async function executeLiveSuite(config, targetName, suiteName, suiteDir, slug, l
531
531
  const terminalAnsi = resolve(liveArtifactDir, "terminal.ansi");
532
532
  const terminalTxt = resolve(liveArtifactDir, "terminal.txt");
533
533
  let renderResult = { pngOk: false };
534
- let cards = [];
535
534
  if (existsSync(terminalAnsi)) {
536
535
  renderResult = await renderAll(terminalAnsi, liveArtifactDir, {
537
536
  label: `${targetName}-${suiteName}`,
@@ -540,17 +539,12 @@ async function executeLiveSuite(config, targetName, suiteName, suiteDir, slug, l
540
539
  sessionId: `${targetName}-${suiteName}`,
541
540
  });
542
541
  }
543
- if (existsSync(terminalTxt)) {
544
- cards = detectCards(readFileSync(terminalTxt, "utf8"));
545
- writeCardArtifacts(liveArtifactDir, cards);
546
- }
547
542
 
548
543
  const statusPath = resolve(liveArtifactDir, "live-status.json");
549
544
  const status = readJson(statusPath);
550
545
  const terminalText = existsSync(terminalTxt) ? readFileSync(terminalTxt, "utf8") : "";
551
546
  const jsonlPath = resolve(liveArtifactDir, "session.jsonl");
552
547
  const jsonlRaw = existsSync(jsonlPath) ? readFileSync(jsonlPath, "utf8") : "";
553
- const cardChecks = assertRequiredCards(liveArtifactDir, cards, scenario?.requiredCards ?? []);
554
548
  const jsonlToolNames = collectJsonlToolNames(jsonlRaw);
555
549
  const jsonlResults = collectJsonlToolResults(jsonlRaw);
556
550
  const usageChecks = collectUsageChecks(jsonlRaw);
@@ -586,14 +580,13 @@ async function executeLiveSuite(config, targetName, suiteName, suiteDir, slug, l
586
580
  { id: "bridge-diagnostic-request-resolved", fn: () => bridgeDiagnostics.some((event) => event.event === "request_resolved") },
587
581
  ] : [];
588
582
  const visualEvidenceSpecs = scenario?.visualEvidence ?? [];
589
- const visualEvidence = existsSync(resolve(liveArtifactDir, "terminal.html"))
590
- ? await collectVisualEvidence({
591
- htmlPath: resolve(liveArtifactDir, "terminal.html"),
592
- pngPath: resolve(liveArtifactDir, "terminal.full.png"),
593
- outDir: liveArtifactDir,
594
- specs: visualEvidenceSpecs,
595
- })
596
- : { ok: false, checks: [{ id: "visual-html-present", ok: false, error: "terminal.html missing" }] };
583
+ const visualEvidence = await collectVisualEvidence({
584
+ htmlPath: resolve(liveArtifactDir, "terminal.html"),
585
+ pngPath: resolve(liveArtifactDir, "terminal.full.png"),
586
+ outDir: liveArtifactDir,
587
+ specs: visualEvidenceSpecs,
588
+ });
589
+ const cardChecks = assertRequiredCards(liveArtifactDir, visualEvidence.cards, scenario?.requiredCards ?? []);
597
590
  const visualEvidenceResultChecks = visualEvidenceSpecs
598
591
  .filter((spec) => spec.jsonlResultId)
599
592
  .map((spec) => ({
@@ -2,6 +2,7 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
2
  import { resolve } from "node:path";
3
3
  import { pathToFileURL } from "node:url";
4
4
  import { matchesWrappedLineAt } from "./wrapped-line-match.mjs";
5
+ import { detectCards, writeCardArtifacts } from "./card-detect.mjs";
5
6
 
6
7
  function pngSize(path) {
7
8
  try {
@@ -39,6 +40,7 @@ export function findVisualEvidenceItems(lines, specs = []) {
39
40
 
40
41
  export async function collectVisualEvidence({ htmlPath, pngPath, outDir, specs = [] }) {
41
42
  mkdirSync(outDir, { recursive: true });
43
+ let cards = [];
42
44
  const evidence = {
43
45
  ok: false,
44
46
  htmlPath,
@@ -52,8 +54,9 @@ export async function collectVisualEvidence({ htmlPath, pngPath, outDir, specs =
52
54
 
53
55
  if (!existsSync(htmlPath)) {
54
56
  evidence.checks.push({ id: "visual-html-present", ok: false, error: "terminal.html missing" });
57
+ writeCardArtifacts(outDir, cards);
55
58
  writeFileSync(resolve(outDir, "visual-evidence.json"), JSON.stringify(evidence, null, 2));
56
- return evidence;
59
+ return { ...evidence, cards };
57
60
  }
58
61
 
59
62
  let browser;
@@ -99,6 +102,7 @@ export async function collectVisualEvidence({ htmlPath, pngPath, outDir, specs =
99
102
  }
100
103
  return out;
101
104
  });
105
+ cards = detectCards(lines);
102
106
 
103
107
  for (const item of findVisualEvidenceItems(lines, specs)) {
104
108
  if (item.ok) {
@@ -133,6 +137,7 @@ export async function collectVisualEvidence({ htmlPath, pngPath, outDir, specs =
133
137
  evidence.checks.push({ id: `visual-evidence-${item.id}`, ok: item.ok === true, line: item.line, screenshot: item.screenshot, pattern: item.pattern, error: item.error });
134
138
  }
135
139
  evidence.ok = evidence.checks.every((check) => check.ok);
140
+ writeCardArtifacts(outDir, cards);
136
141
  writeFileSync(resolve(outDir, "visual-evidence.json"), JSON.stringify(evidence, null, 2));
137
- return evidence;
142
+ return { ...evidence, cards };
138
143
  }