pi-cursor-sdk 0.1.56 → 0.1.57
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 +45 -1
- package/README.md +100 -28
- package/docs/cursor-live-smoke-checklist.md +7 -7
- package/docs/cursor-model-ux-spec.md +45 -38
- package/docs/cursor-native-tool-replay.md +2 -2
- package/docs/cursor-native-tool-visual-audit.md +1 -1
- package/docs/cursor-testing-lessons.md +20 -7
- package/docs/cursor-tool-surfaces.md +13 -2
- package/docs/platform-smoke-implementation.md +220 -0
- package/docs/platform-smoke.md +183 -247
- package/package.json +38 -6
- package/platform-smoke.config.mjs +5 -1
- package/scripts/cloud-runtime-smoke.d.mts +3 -0
- package/scripts/cloud-runtime-smoke.mjs +502 -0
- package/scripts/debug-provider-events.mjs +7 -2
- package/scripts/isolated-cursor-smoke.sh +4 -6
- package/scripts/lib/cursor-child-process.d.mts +1 -0
- package/scripts/lib/cursor-child-process.mjs +137 -7
- package/scripts/lib/local-resume-smoke-harness.mjs +543 -0
- package/scripts/local-resume-cleanup-smoke.mjs +108 -0
- package/scripts/local-resume-smoke.d.mts +1 -0
- package/scripts/local-resume-smoke.mjs +642 -0
- package/scripts/platform-smoke/artifact-anchored-extract.d.mts +10 -0
- package/scripts/platform-smoke/artifact-anchored-extract.mjs +111 -0
- package/scripts/platform-smoke/artifact-bundle-chunk.mjs +57 -0
- package/scripts/platform-smoke/artifact-bundle-contract.mjs +34 -0
- package/scripts/platform-smoke/artifact-fs-safety.mjs +311 -0
- package/scripts/platform-smoke/artifact-openat-extract.c +335 -0
- package/scripts/platform-smoke/artifact-secrets.mjs +155 -0
- package/scripts/platform-smoke/artifacts.mjs +293 -65
- package/scripts/platform-smoke/card-detect.mjs +16 -4
- package/scripts/platform-smoke/crabbox-runner.mjs +45 -3
- package/scripts/platform-smoke/doctor.mjs +20 -10
- package/scripts/platform-smoke/live-suite-runner.mjs +18 -57
- package/scripts/platform-smoke/local-resume-runner.mjs +252 -0
- package/scripts/platform-smoke/local-resume-suites.d.mts +15 -0
- package/scripts/platform-smoke/local-resume-suites.mjs +104 -0
- package/scripts/platform-smoke/scenarios.mjs +16 -2
- package/scripts/platform-smoke/target-runtime.mjs +206 -0
- package/scripts/platform-smoke/targets.mjs +33 -141
- package/scripts/platform-smoke/visual-evidence.mjs +6 -7
- package/scripts/platform-smoke/wrapped-line-match.mjs +9 -0
- package/scripts/platform-smoke.mjs +40 -27
- package/scripts/refresh-cursor-model-snapshots.mjs +18 -6
- package/scripts/steering-rpc-smoke.mjs +12 -2
- package/scripts/tmux-live-smoke.sh +3 -5
- package/shared/cursor-cloud-lifecycle-constants.d.mts +3 -0
- package/shared/cursor-cloud-lifecycle-constants.mjs +7 -0
- package/shared/cursor-sensitive-text.mjs +7 -1
- package/src/context.ts +5 -2
- package/src/cursor-agents-context-registration.ts +7 -0
- package/src/cursor-agents-context.ts +3 -1
- package/src/cursor-api-key.ts +15 -1
- package/src/cursor-bridge-contract.ts +3 -0
- package/src/cursor-cloud-lifecycle.ts +733 -0
- package/src/cursor-cloud-options.ts +206 -0
- package/src/cursor-cloud-reporting.ts +246 -0
- package/src/cursor-config.ts +659 -0
- package/src/cursor-display-only-trace.ts +14 -0
- package/src/cursor-display-text.ts +8 -2
- package/src/cursor-durable-fs.ts +49 -0
- package/src/cursor-fallback-models.generated.ts +2045 -485
- package/src/cursor-live-run-accounting.ts +7 -1
- package/src/cursor-live-run-coordinator.ts +1 -0
- package/src/cursor-pi-tool-bridge-run.ts +14 -4
- package/src/cursor-provider-live-run-drain.ts +5 -0
- package/src/cursor-provider-run-finalizer.ts +36 -28
- package/src/cursor-provider-turn-finalize.ts +72 -6
- package/src/cursor-provider-turn-prepare.ts +228 -12
- package/src/cursor-provider-turn-runner.ts +41 -13
- package/src/cursor-provider-turn-send.ts +59 -16
- package/src/cursor-provider-turn-types.ts +44 -10
- package/src/cursor-runtime-state.ts +478 -0
- package/src/cursor-sdk-event-debug.ts +46 -6
- package/src/cursor-sdk-process-error-guard.ts +101 -30
- package/src/cursor-session-agent-cleanup.ts +328 -0
- package/src/cursor-session-agent-resume.ts +439 -0
- package/src/cursor-session-agent.ts +109 -13
- package/src/cursor-session-scope.ts +35 -2
- package/src/cursor-session-send-policy.ts +1 -1
- package/src/cursor-skill-tool.ts +30 -11
- package/src/cursor-state.ts +112 -69
- package/src/cursor-usage-accounting.ts +14 -4
- package/src/index.ts +11 -2
- package/src/model-discovery.ts +10 -56
|
@@ -15,29 +15,33 @@ Current implementation notes:
|
|
|
15
15
|
- Cursor status uses one coordinated `ctx.ui.setStatus("cursor", ...)` value for fast and non-default plan mode; the default pi footer remains intact.
|
|
16
16
|
- Installed `@cursor/sdk` user messages accept images, and Cursor models are treated as image-capable; registered input metadata is `text` plus `image`.
|
|
17
17
|
- 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.
|
|
18
|
-
- Exact `@cursor/sdk@1.0.23` is a package dependency of this extension; users should not need a global SDK install. pi 0.80.
|
|
19
|
-
-
|
|
20
|
-
-
|
|
18
|
+
- Exact `@cursor/sdk@1.0.23` is a package dependency of this extension; users should not need a global SDK install. pi 0.80.5 is the current recommended validation baseline, while published pi core peer dependencies use `"*"` ranges per current pi package guidance. Newer pi versions are allowed to attempt loading this extension before a matching extension release exists; compatibility is best-effort until validated.
|
|
19
|
+
- Startup discovery does not duplicate Pi CLI parsing: it uses stored `~/.pi/agent/auth.json` API-key auth from `/login`, then `CURSOR_API_KEY`, otherwise it registers the bundled fallback catalog. Provider turns keep Pi's resolved `options.apiKey`. `/cursor-refresh-models` and `/cursor-cloud` mutations resolve provider `cursor` through the command context's Pi ModelRegistry, then normalize placeholders through `CURSOR_API_KEY`. The extension config file stores only non-secret Cursor-only state such as fast defaults.
|
|
20
|
+
- Cursor Cloud repository overrides accept only HTTPS repository URLs without userinfo, query parameters, or fragments. Invalid values fail during preflight before `Agent.create()`, messages never echo the supplied URL, and shared provider/maintainer scrubbing removes URL/SCP-style userinfo defensively.
|
|
21
|
+
- Cursor Cloud requires a persisted pi session. Immediately after remote `Agent.create()` returns, before debug work or abort checks, the provider appends a branch-local pi lifecycle entry, fsyncs the existing Pi session JSONL anchor through a read-write descriptor, and then fsyncs a newline-framed sidecar keyed by the stable pi session ID (POSIX mode `0600`; Windows inherits the user session directory ACL) in the session directory. Journal creation is exclusive, and existing append/read opens reject symlinks and require matching regular-file descriptor/path identity before using the descriptor. Existing session files use the exact lifecycle entry ID as anchor; fileless first turns use an orphan marker that a same-session-ID restart durably claims onto exactly one matching or replacement branch, surviving the timestamped path change and later JSONL creation without granting sibling access; returned run IDs are fsynced before abort/wait handling, readers skip malformed/truncated lines independently, and branch-only history events are reduced after deduplicated sidecar history, and tombstones are tracked before records exist. A durable sidecar result remains authoritative if its optional Pi mirror append fails; if the sidecar result itself fails, the prior durable intent remains pending and blocks retry rather than claiming success. `--no-session` and durable-ledger failures fail closed before send, while post-send ledger failure requests bounded cancellation. `/cursor-cloud` always validates the embedded session ID, accepts only null anchors while truly fileless, and reconciles each orphan to one branch before listing or mutation. Successive record events merge run/branch metadata monotonically even when mirrored sources arrive out of order. Archive/delete require resolved Cursor auth, fsync a durable intent before the SDK call, and fsync a durable success result afterward; unresolved intent blocks repeat mutation and requires dashboard inspection.
|
|
22
|
+
- 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`. `/cursor-refresh-config` calls the current pooled SDK agent's `agent.reload()` to refresh filesystem Cursor config without recreating the agent. 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.
|
|
21
23
|
- On `cursor/*` models, pi-cursor-sdk removes only pi-generated `<project_instructions>` blocks that overlap the effective Cursor `settingSources`: `user` for `~/.pi/agent/AGENTS.md`; `project` for discovered repo/parent `AGENTS.md` and `CLAUDE.md` (verified Cursor behavior: local agents load project `AGENTS.md` and `CLAUDE.md`). `~/.pi/agent/CLAUDE.md` is not removed (Cursor user layer uses `~/.claude/CLAUDE.md`). Blocks are removed by exact pi serialization match from structured `contextFiles` via the `before_agent_start` hook, not in `buildCursorPrompt` sanitization. Suppression is skipped with `-nc`, `PI_CURSOR_SETTING_SOURCES=none`, narrowed sources such as `plugins` that omit the matching layer, or `PI_CURSOR_PRESERVE_PI_AGENTS_MD=1`. Switching away from a Cursor model restores pi's full context block on the next user message.
|
|
22
24
|
- 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.
|
|
23
25
|
- Cursor-side thinking remains visible through pi's native thinking rendering when the Cursor SDK emits thinking or summary deltas.
|
|
24
26
|
- 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.
|
|
25
27
|
- `buildCursorPiToolBridgeSnapshot()` is the runtime capability source for pi bridge tools. It snapshots `pi.getActiveTools()` and `pi.getAllTools()`, carries pi 0.77+ per-tool `promptGuidelines` into bridge MCP descriptions, 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`. Bootstrap prompts include a compact callable-surface manifest from `buildCursorToolManifestText()` by default (`PI_CURSOR_TOOL_MANIFEST=1`); disable with `PI_CURSOR_TOOL_MANIFEST=0`. There is no per-turn visible tool list, status manifest, or footer manifest. User-facing summary: [Cursor tool surfaces in pi](./cursor-tool-surfaces.md).
|
|
26
|
-
- Prompt text is the primary provider/bridge contract. Bootstrap prompts carry a short boundary block plus the callable-surface manifest by default (`PI_CURSOR_TOOL_MANIFEST=1`). MCP `listTools` descriptions use a one-line pointer to the bootstrap prompt instead of repeating the full contract (`buildCursorPiBridgeMcpToolDescription()`). 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. Maintainer debug: `/cursor-tools` prints bridge/manifest enablement, effective `PI_CURSOR_SETTING_SOURCES`, and the current callable-surface snapshot.
|
|
28
|
+
- Prompt text is the primary provider/bridge contract. Bootstrap prompts carry a short boundary block plus the callable-surface manifest by default (`PI_CURSOR_TOOL_MANIFEST=1`). MCP `listTools` descriptions use a one-line pointer to the bootstrap prompt instead of repeating the full contract (`buildCursorPiBridgeMcpToolDescription()`). Cursor must call the exposed `pi__*` MCP name, not the real pi tool name shown in pi history or transcripts. When exposed, `pi__mcp` takes preference over Cursor-configured MCP for MCP work and `pi__subagent` takes preference over Cursor-native subagents for delegation; the Cursor-native surfaces remain fallbacks when the matching pi bridge tool is absent or unavailable. Pi emits and executes the real pi tool name. Maintainer debug: `/cursor-tools` prints bridge/manifest enablement, effective `PI_CURSOR_SETTING_SOURCES`, and the current callable-surface snapshot.
|
|
27
29
|
- 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. When pi has visible Agent Skills loaded, the provider rewrites the skill catalog for Cursor and registers `cursor_activate_skill` as `pi__cursor_activate_skill`; pi executes it through the normal tool path so Cursor can load the full `SKILL.md` and skill resource list for the current pi-loaded skill source of truth. `PI_CURSOR_PI_TOOL_BRIDGE=0` disables the local bridge, including question and skill activation bridging. Cloud Cursor agents remain out of scope for the bridge.
|
|
28
30
|
- 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.
|
|
29
31
|
- Cursor SDK MCP tool calls use a guarded timeout override because installed `@cursor/sdk` 1.0.23 still has a 60-second MCP request default with no public per-server timeout option. The extension extends the verified Cursor SDK MCP `callTool` timeout path to 3600 seconds by default and shortens the verified first-send MCP initialize/listTools timeout paths to 10 seconds by default so unavailable configured MCP servers do not block the first reply for a full minute; unknown MCP protocol timeout stacks keep the SDK default. Users can override tool-call timeouts with `PI_CURSOR_MCP_TOOL_TIMEOUT_MS` or `PI_CURSOR_MCP_TOOL_TIMEOUT_SECONDS`, and initialize/listTools timeouts with `PI_CURSOR_MCP_CONNECT_TIMEOUT_MS` or `PI_CURSOR_MCP_CONNECT_TIMEOUT_SECONDS`.
|
|
32
|
+
- Cursor SDK local safety controls are off by default. `--cursor-auto-review` / `PI_CURSOR_AUTO_REVIEW` and `--cursor-sandbox` / `PI_CURSOR_SANDBOX` pass only explicit enabled values into `Agent.create({ local })`; user or trusted project config can set `local.autoReview` and `local.sandboxOptions.enabled`.
|
|
30
33
|
- 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.
|
|
31
34
|
- 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.
|
|
32
35
|
- Cursor internal tool activity is recorded from SDK events and scrubbed. Maintainer reference for `@cursor/sdk@1.0.23` `ToolType` values, runtime alias normalization, and intentional mapping/fallback rules: [Cursor native tool replay — SDK ToolType replay matrix](./cursor-native-tool-replay.md#sdk-tooltype-replay-matrix) (official SDK docs: https://cursor.com/docs/sdk/typescript). In TUI sessions and structured JSON/RPC modes, supported completed `read`, `bash`, `grep`, `find`, `ls`, `edit`, `write`, diagnostics, delete, todo/plan, task, image generation, MCP, semantic search, and screen recording activity is replayed through pi's native tool-call rendering path with recorded Cursor results, so users and JSON/RPC consumers can see native-looking cards/events 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/subagent, image, and MCP activity use neutral Cursor activity cards with pi's default success/error shell. Cursor SDK `task` activity is labeled **Cursor subagent** by default because it represents Cursor-spawned child-agent work; the card summary includes description plus subagent kind/model/short ID when Cursor reports them, and `PI_CURSOR_TASK_PRESENTATION=task` restores the older **Cursor task** wording for comparison. This is visibility over Cursor SDK task events, not a native pi subagent session: pi shows start/final output plus any `conversationSteps` tool-call summaries Cursor returns, but cannot show a live nested read/shell/MCP trail when the SDK only returns final subagent text. Neutral Cursor activity calls include `activityTitle` and, when available, `activitySummary` so partial/collapsed cards preserve identity such as `Cursor plan`, `Cursor todos`, `Cursor subagent`, `Cursor MCP`, or `Cursor edit`. For long-running or externally meaningful Cursor tools (`task`, `shell`, `mcp`, `generateImage`, `recordScreen`, `semSearch`, web search/fetch, plan/todo), the provider may surface one low-noise deferred in-progress thinking line such as `Cursor MCP: external_search` from bounded, scrubbed SDK args; fast local tools (`read`, `grep`, `glob`, and similar) skip lifecycle lines when completion follows immediately, and pi bridge MCP calls are excluded because pi already shows real pi tool execution ([lifecycle visibility](./cursor-native-tool-replay.md#low-noise-tool-lifecycle-visibility)). 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 mode/task/todo/plan activity 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 surfaced with bounded user-visible labels/traces (neutral activity cards when native replay routing allows, otherwise the same inactive or transcript trace fallbacks used for completed replay) instead of being silently discarded when the run failed/aborted, produced no assistant text, or involved external/side-effectful tools; incomplete fast local discovery starts (`read`, `grep`, `glob`, `ls`) remain maintainer-debug-only after successful text-producing runs so stale SDK start events do not create red post-answer cards. Explicit failures remain visible when Cursor reports them through completed tool calls or step results. Pi bridge MCP starts remain excluded from duplicate incomplete Cursor cards because pi already shows real pi tool execution. `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 also shown as bounded live progress while one shell call is active and used as display-only fallback for empty successful shell completions, while overlapping shell calls drop ambiguous deltas instead of guessing. Print mode keeps bounded scrubbed transcript output instead, preserving `pi -p` assistant text output. Cursor text deltas stream live when no live-run turn split is active.
|
|
33
36
|
- Cursor native replay uses one neutral replay tool name, `cursor`, plus native-compatible card names when renderer-compatible (`read`, `bash`, `grep`, `find`, `ls`, `edit`, `write`). Neutral replay identity lives in `activityTitle`, `activitySummary`, and typed replay details, not in extra registered tool names. Bridge MCP names such as `pi__sem_reindex` are MCP-only; pi session output uses real pi tool names.
|
|
34
|
-
- Cursor SDK usage events are used when the SDK reports them before the corresponding pi turn is emitted. For each SDK-attributed assistant turn, `usage.input`, `usage.output`, `usage.cacheRead`, and `usage.cacheWrite` come from the latest per-turn SDK usage; `usage.totalTokens = latestTurn.inputTokens + latestTurn.outputTokens
|
|
37
|
+
- Cursor SDK usage events are used when the SDK reports them before the corresponding pi turn is emitted and the reported counts fit the selected pi model window. For each safe SDK-attributed assistant turn, `usage.input`, `usage.output`, `usage.cacheRead`, and `usage.cacheWrite` come from the latest per-turn SDK usage; `usage.totalTokens = latestTurn.inputTokens + latestTurn.outputTokens + latestTurn.cacheReadTokens + latestTurn.cacheWriteTokens`, matching the Cursor SDK and pi compaction contracts. Cumulative `RunResult.usage` is never used for per-message occupancy. If the SDK reports no usage in time, or reports full-agent-context-sized usage outside the selected model window, the provider falls back to local `input/output` activity estimates while setting `usage.totalTokens` to the current replayable context estimate so footer/compaction context does not collapse after split tool turns; after a split live-run turn times out waiting for SDK usage, later SDK usage for that live run is ignored rather than risk applying stale usage to the wrong pi turn. Cursor SDK cost is unavailable, so cost remains absent/zero. `src/cursor-usage-accounting.ts` owns this policy.
|
|
35
38
|
- Audit observation, 2026-05-19, superseded by the 2026-05-21 replay pass and #68 incomplete visibility, then narrowed by the 2026-05-26 fast-local suppression: 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 external/side-effectful started calls are surfaced at run completion through the same native replay routing as completed tools (activity cards when allowed, otherwise inactive/transcript traces), while fast local discovery starts are debug-only after a successful text-producing run. Cursor-reported completed/step errors remain visible.
|
|
36
39
|
- 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.
|
|
37
|
-
- Cursor provider/runtime releases must pass the [Platform Smoke Gate](./platform-smoke.md): `npm run smoke:platform:
|
|
40
|
+
- Cursor provider/runtime releases must pass the local [Platform Smoke Gate](./platform-smoke.md): `npm run smoke:platform:all`. Cloud-runtime changes must also pass `npm run smoke:cloud`. Use [Cursor Live Smoke Checklist](./cursor-live-smoke-checklist.md) only for focused inner-loop/debug runs with real `pi --approve -e . --cursor-no-fast --model cursor/composer-2-5` invocations, manual observation, temporary session dirs, diagnostics scans, and persisted JSONL inspection. See [Cursor testing lessons](./cursor-testing-lessons.md) for auth.json seeding, isolated smoke harnesses, and replay JSONL scans.
|
|
38
41
|
- 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.
|
|
39
42
|
- Max Mode context windows are distinct from default/non-Max context windows. `@cursor/sdk` 1.0.23 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.
|
|
40
43
|
- The installed `@cursor/sdk` exposes 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`. Cursor-only fast preferences are keyed by the selected SDK model ID/alias, with read fallback for older preferences keyed by 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.
|
|
44
|
+
- Local restart resume treats user entries already present at `session_start` or selected by tree navigation as crash-ambiguous: an older SDK handle cannot span them because the prior process may already have submitted that prompt. A user entry appended after startup in the current process may span the last completed handle for the normal next send.
|
|
41
45
|
- Session-scoped Cursor SDK agent pooling reuses one live `@cursor/sdk` agent across compatible follow-up turns within the same pi session scope. `planCursorSessionSend()` in `src/cursor-session-send-policy.ts` decides whether the next turn sends a full bootstrap prompt or an incremental follow-up, whether the SDK agent must be recreated, and why. `computeCursorContextFingerprint()` and `shouldBootstrapCursorContext()` remain the context-only bootstrap signal. The pool recreates the agent when context diverges, when branch or compaction summaries appear after `/tree` navigation or compaction, after 20 completed incremental sends, 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, but every send ends with a short tool tail guard placed after the latest user request (including an explicit shell `cd` hint).
|
|
42
46
|
- 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. Pooled session agents mark busy as soon as live/direct `run.wait()` tracking starts (`trackRunCompletion` on the session lease), and `acquireSessionCursorAgent()` awaits that busy state before returning a lease so send planning, transcript offsets, and later `Agent.send()` do not race the prior turn's SDK run completion (for example pi auto-compaction summarization). `session_before_compact` calls `prepareCursorSessionForCompaction()` to release scoped live-run drain state and reset the pooled agent before summarization streams. Tracked completions and send commits are scoped to the pooled agent `instanceId` so disposal/replacement drops stale tracking and ignores late commits from disposed agents.
|
|
43
47
|
|
|
@@ -77,13 +81,12 @@ At startup, the extension calls:
|
|
|
77
81
|
Cursor.models.list({ apiKey });
|
|
78
82
|
```
|
|
79
83
|
|
|
80
|
-
|
|
84
|
+
Startup discovery resolves `apiKey` in this order:
|
|
81
85
|
|
|
82
|
-
1.
|
|
83
|
-
2.
|
|
84
|
-
3. `CURSOR_API_KEY`.
|
|
86
|
+
1. Stored pi auth for provider `cursor` from `AuthStorage.create().getApiKey("cursor", { includeFallback: false })`.
|
|
87
|
+
2. `CURSOR_API_KEY`.
|
|
85
88
|
|
|
86
|
-
Users can persist the stored key through `/login` -> `Use an API key` -> `Cursor`. If auth is added after startup, fallback models can run once
|
|
89
|
+
Startup never parses `process.argv`; Pi remains the sole owner of CLI model/provider/key parsing. Provider turns keep Pi's resolved `options.apiKey`. Users can persist the stored key through `/login` -> `Use an API key` -> `Cursor`. If auth is added after startup, fallback models can run once Pi resolves the saved key for provider requests, and `/cursor-refresh-models` asks the command context's ModelRegistry for provider `cursor` and passes that normalized key explicitly to a forced live refresh.
|
|
87
90
|
|
|
88
91
|
For each model, use:
|
|
89
92
|
|
|
@@ -110,7 +113,13 @@ If a Cursor parameter changes any of those pi-native fields, model registration
|
|
|
110
113
|
|
|
111
114
|
### Refresh Current Cursor Matrix
|
|
112
115
|
|
|
113
|
-
|
|
116
|
+
Before releases, compare the generated fallback with the authenticated live catalog:
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
CURSOR_API_KEY="your-key" npm run check:cursor-snapshots
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Run this whenever Cursor releases or changes models:
|
|
114
123
|
|
|
115
124
|
```bash
|
|
116
125
|
CURSOR_API_KEY="your-key" npm run refresh:cursor-snapshots -- --write
|
|
@@ -123,7 +132,7 @@ CURSOR_API_KEY="your-key" npm run refresh:cursor-snapshots -- --write \
|
|
|
123
132
|
--context-windows ~/.pi/agent/cursor-sdk-context-windows.json
|
|
124
133
|
```
|
|
125
134
|
|
|
126
|
-
|
|
135
|
+
Both modes call `Cursor.models.list({ apiKey })` and use the same sanitizer and stable sort. `--check` byte-compares `src/cursor-fallback-models.generated.ts` without writing; `--write` refreshes it and updates `src/bundled-context-windows.ts` only when `--context-windows` is provided. Generated provenance and command output record the installed `@cursor/sdk` version and model count. The script prints model IDs/counts only and scrubs known auth material from SDK errors; it must not print or store API keys. Review generated diffs before committing because Cursor can change aliases, defaults, and parameter meanings.
|
|
127
136
|
|
|
128
137
|
## Design Direction
|
|
129
138
|
|
|
@@ -247,6 +256,7 @@ Cursor extension controls:
|
|
|
247
256
|
|---|---:|---|
|
|
248
257
|
| Toggle fast | `/cursor-fast` | model has `fast` |
|
|
249
258
|
| Set SDK mode | `/cursor-mode agent\|plan` | Cursor model selected |
|
|
259
|
+
| Refresh filesystem Cursor config | `/cursor-refresh-config` | Cursor model selected and an SDK agent may exist |
|
|
250
260
|
| Show tool surfaces (maintainer) | `/cursor-tools` | Cursor model selected |
|
|
251
261
|
|
|
252
262
|
Do not register a shortcut for `shift+tab`. Pi reserves the native thinking keybinding, and the extension should only influence it through model metadata.
|
|
@@ -338,9 +348,6 @@ cursor/gpt-5.5@1m
|
|
|
338
348
|
|
|
339
349
|
cursor/claude-opus-4-8@300k
|
|
340
350
|
cursor/claude-opus-4-8@1m
|
|
341
|
-
|
|
342
|
-
cursor/grok-4.3@200k
|
|
343
|
-
cursor/grok-4.3@1m
|
|
344
351
|
```
|
|
345
352
|
|
|
346
353
|
Each variant must:
|
|
@@ -370,7 +377,7 @@ Rules:
|
|
|
370
377
|
- Toggle unsuffixed models with `/cursor-fast`; do not persist a new default while a virtual fast alias is selected.
|
|
371
378
|
- Store per-session and global per-base-model preferences for unsuffixed models.
|
|
372
379
|
- When calling `Agent.create()` or `agent.send()`, include the selected `fast` value in Cursor model params.
|
|
373
|
-
- Show fast-capable models as `cursor
|
|
380
|
+
- Show fast-capable local models as `cursor:local · fast:on` or `cursor:local · fast:off` through `ctx.ui.setStatus()` while a Cursor model is active; cloud runtime shows `cursor:cloud · fast:n/a`.
|
|
374
381
|
- Keep `--cursor-fast` and `--cursor-no-fast` as explicit process-level force flags.
|
|
375
382
|
|
|
376
383
|
Reason:
|
|
@@ -381,8 +388,8 @@ Reason:
|
|
|
381
388
|
Status examples:
|
|
382
389
|
|
|
383
390
|
```text
|
|
384
|
-
cursor
|
|
385
|
-
cursor
|
|
391
|
+
cursor:local · fast:off
|
|
392
|
+
cursor:local · fast:on
|
|
386
393
|
```
|
|
387
394
|
|
|
388
395
|
## Cursor SDK Mode Behavior
|
|
@@ -410,9 +417,10 @@ Rules:
|
|
|
410
417
|
Status examples:
|
|
411
418
|
|
|
412
419
|
```text
|
|
413
|
-
cursor
|
|
414
|
-
cursor
|
|
415
|
-
cursor
|
|
420
|
+
cursor:local · fast:n/a · plan
|
|
421
|
+
cursor:local · fast:off · plan
|
|
422
|
+
cursor:local · fast:on · plan
|
|
423
|
+
cursor:cloud · fast:n/a · plan
|
|
416
424
|
```
|
|
417
425
|
|
|
418
426
|
## Footer Behavior
|
|
@@ -421,7 +429,7 @@ Hard requirement:
|
|
|
421
429
|
|
|
422
430
|
- Leave pi's default footer intact.
|
|
423
431
|
- Do not use `ctx.ui.setFooter()` for the first pass.
|
|
424
|
-
- Use `ctx.ui.setStatus()` only while a Cursor model is active, showing Cursor-only state that pi cannot show natively, such as `cursor
|
|
432
|
+
- Use `ctx.ui.setStatus()` only while a Cursor model is active, showing Cursor-only state that pi cannot show natively, such as `cursor:local`, `cursor:cloud`, local `fast:on|off|n/a`, and non-default Cursor SDK `plan` mode.
|
|
425
433
|
- Non-cursor models must have no Cursor status.
|
|
426
434
|
|
|
427
435
|
Reason:
|
|
@@ -435,13 +443,13 @@ Expected native footer behavior:
|
|
|
435
443
|
- provider/model is shown by pi from the selected `cursor` model,
|
|
436
444
|
- thinking level is shown by pi when `reasoning` is true,
|
|
437
445
|
- context usage is computed from `contextWindow`,
|
|
438
|
-
- extension status adds only Cursor-only text such as `cursor
|
|
446
|
+
- extension status adds only Cursor-only text such as `cursor:local · fast:n/a`, `cursor:local · fast:off`, `cursor:local · fast:on · plan`, or `cursor:cloud · fast:n/a`.
|
|
439
447
|
|
|
440
448
|
`ctx.ui.setStatus()` adds an extension status line in the default footer. It does not patch the built-in model segment. The native shape is closer to:
|
|
441
449
|
|
|
442
450
|
```text
|
|
443
451
|
... (cursor) gpt-5.5@1m • medium
|
|
444
|
-
cursor
|
|
452
|
+
cursor:local · fast:off · plan
|
|
445
453
|
```
|
|
446
454
|
|
|
447
455
|
not:
|
|
@@ -571,14 +579,14 @@ Initial Cursor default for Composer 2.5:
|
|
|
571
579
|
pi model: cursor/composer-2-5
|
|
572
580
|
Cursor params: fast=true
|
|
573
581
|
pi thinking: off
|
|
574
|
-
Cursor status: cursor
|
|
582
|
+
Cursor status: cursor:local · fast:on
|
|
575
583
|
```
|
|
576
584
|
|
|
577
585
|
Toggle fast:
|
|
578
586
|
|
|
579
587
|
```text
|
|
580
588
|
Cursor params: fast=false
|
|
581
|
-
Cursor status: cursor
|
|
589
|
+
Cursor status: cursor:local · fast:off
|
|
582
590
|
```
|
|
583
591
|
|
|
584
592
|
`shift+tab`: no-op because the model is not reasoning-capable.
|
|
@@ -591,7 +599,7 @@ Initial Cursor default:
|
|
|
591
599
|
pi model: cursor/gpt-5.5@1m
|
|
592
600
|
Cursor params: context=1m; reasoning=medium; fast=false
|
|
593
601
|
pi thinking: medium
|
|
594
|
-
Cursor status: cursor
|
|
602
|
+
Cursor status: cursor:local · fast:off
|
|
595
603
|
```
|
|
596
604
|
|
|
597
605
|
After selecting the 272k variant:
|
|
@@ -606,7 +614,7 @@ After fast toggle:
|
|
|
606
614
|
|
|
607
615
|
```text
|
|
608
616
|
Cursor params: context=272k; reasoning=medium; fast=true
|
|
609
|
-
Cursor status: cursor
|
|
617
|
+
Cursor status: cursor:local · fast:on
|
|
610
618
|
```
|
|
611
619
|
|
|
612
620
|
After `shift+tab` to xhigh:
|
|
@@ -624,7 +632,7 @@ Initial Cursor default:
|
|
|
624
632
|
pi model: cursor/gpt-5.3-codex
|
|
625
633
|
Cursor params: reasoning=high; fast=true
|
|
626
634
|
pi thinking: high
|
|
627
|
-
Cursor status: cursor
|
|
635
|
+
Cursor status: cursor:local · fast:on
|
|
628
636
|
```
|
|
629
637
|
|
|
630
638
|
After `shift+tab` to low:
|
|
@@ -668,18 +676,17 @@ pi thinking: off
|
|
|
668
676
|
Cursor params: thinking=false; context=300k
|
|
669
677
|
```
|
|
670
678
|
|
|
671
|
-
### `grok-4.
|
|
679
|
+
### `grok-4.5`
|
|
672
680
|
|
|
673
|
-
Supports context
|
|
681
|
+
Supports `effort=low|medium|high` and `fast=false|true`; it does not advertise context variants.
|
|
674
682
|
|
|
675
683
|
```text
|
|
676
|
-
cursor/grok-4.
|
|
677
|
-
cursor/grok-4.3@200k
|
|
684
|
+
cursor/grok-4.5
|
|
678
685
|
```
|
|
679
686
|
|
|
680
|
-
Fast toggle
|
|
687
|
+
Fast toggle maps to the Cursor `fast` parameter.
|
|
681
688
|
|
|
682
|
-
`shift+tab
|
|
689
|
+
`shift+tab` maps the available low, medium, and high levels to Cursor `effort`; levels without a catalog value do not invent one.
|
|
683
690
|
|
|
684
691
|
## Validation Plan
|
|
685
692
|
|
|
@@ -705,8 +712,8 @@ Before calling done:
|
|
|
705
712
|
- launch interactive with Cursor
|
|
706
713
|
- verify default pi footer remains unchanged
|
|
707
714
|
- verify Cursor status appears only for Cursor models
|
|
708
|
-
- verify Cursor fast-capable models show `cursor
|
|
709
|
-
- verify Cursor `plan` status appears only in non-default mode and combines with status as `cursor
|
|
715
|
+
- verify Cursor fast-capable local models show `cursor:local · fast:on` or `cursor:local · fast:off`
|
|
716
|
+
- verify Cursor `plan` status appears only in non-default mode and combines with status as `cursor:local · fast:n/a · plan`, `cursor:local · fast:on · plan`, `cursor:local · fast:off · plan`, or `cursor:cloud · fast:n/a · plan`
|
|
710
717
|
- verify non-cursor footer/status unchanged
|
|
711
718
|
- verify `shift+tab` uses pi native thinking
|
|
712
719
|
- verify context changes through native model selection
|
|
@@ -158,7 +158,7 @@ These are integration boundaries, not pi replay bugs:
|
|
|
158
158
|
- **Live web-search ordering:** local Cursor WebSearch can be absent from live `onDelta`, `onStep`, and `run.stream()` tool events. When the only evidence is a post-run local transcript `webSearchToolCall`, pi can display the **Cursor web search** card only after `run.wait()` finishes. The extension intentionally keeps assistant text streaming instead of buffering the whole answer just to reorder that card.
|
|
159
159
|
- **WebFetch availability:** `pi-cursor-sdk` can display a Cursor web fetch only after the SDK reports a `webFetchToolCall`, web-fetch-shaped MCP completion, or web-fetch host alias. It cannot make the Cursor SDK expose or execute WebFetch in a run where Cursor's tool set does not include it.
|
|
160
160
|
- **Future SDK tools:** Cursor's official SDK docs say tool names, args, and result payloads can change. Unknown completed tools therefore fall back to neutral Cursor activity cards with bounded, scrubbed text. The extension cannot render tools that the SDK never emits.
|
|
161
|
-
- **
|
|
161
|
+
- **Process-level transport exceptions:** Connect/network/abort suppression remains scoped to active provider turns. The exact SDK-provenance `WriteIterableClosedError: WritableIterable is closed` is guarded for the Pi session lifecycle because SDK 1.0.23 controlled-exec can reject after its originating provider turn when it writes an error frame after output closes. Unrelated failures remain fatal; a materially different future SDK process error must be added only after observation.
|
|
162
162
|
|
|
163
163
|
Maintainer debug (`PI_CURSOR_SDK_EVENT_DEBUG=1`) still records the same discarded started-call events in `coordinator-events.jsonl` under phase `discarded-incomplete-started-tool-call` for investigation (**#52**), including fast local starts suppressed from successful text-producing runs. User-visible incomplete cards and debug artifacts are complementary: cards explain actionable gaps in the TUI; debug files retain normalized tool names and scrubbed call-id hashes without changing default stderr behavior.
|
|
164
164
|
|
|
@@ -182,7 +182,7 @@ Lifecycle rules:
|
|
|
182
182
|
|
|
183
183
|
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.
|
|
184
184
|
|
|
185
|
-
Bridged pi tool calls follow the same visible pi `toolUse` turn shape, but they are real pi tool executions rather than replayed Cursor results. Usage accounting uses per-turn Cursor SDK usage when available before the corresponding pi turn is emitted, including cache read/write fields; SDK-backed `usage.totalTokens`
|
|
185
|
+
Bridged pi tool calls follow the same visible pi `toolUse` turn shape, but they are real pi tool executions rather than replayed Cursor results. Usage accounting uses per-turn Cursor SDK usage when available before the corresponding pi turn is emitted, including cache read/write fields; SDK-backed `usage.totalTokens` includes latest-turn input, output, cache-read, and cache-write counters per the SDK and pi compaction contracts. If the SDK reports no usage in time, the provider falls back to local `input/output` activity estimates with `usage.totalTokens` set to the current replayable context estimate, then ignores later usage for that live run rather than risk applying stale usage to the wrong pi turn.
|
|
186
186
|
|
|
187
187
|
For shell replay, completed `stdout` / `stderr` remain the primary source. While exactly one shell call is active, the provider also emits a bounded scrubbed preview of the first few `shell-output-delta` stdout/stderr chunks so long-running commands show visible progress before completion. If a successful completed shell result is empty, the replay card uses unambiguous buffered delta data as display-only fallback data. Overlapping shell calls make delta attribution ambiguous, so those fallback/progress deltas are dropped rather than guessed. `(no output)` is kept only when no completed output or safe delta fallback is available.
|
|
188
188
|
|
|
@@ -6,7 +6,7 @@ This workflow is the canonical repo path for verifying Cursor SDK tool replay th
|
|
|
6
6
|
|
|
7
7
|
Use it before accepting replay-card commits or PRs, and for every Cursor provider/runtime release where TUI card/color behavior could regress. Text logs and JSONL are necessary, but they are not enough when the claim is visual parity: always keep PNGs for the exact prompt, and keep before/after PNGs when reviewing a rendering change.
|
|
8
8
|
|
|
9
|
-
Current validation baseline: pi 0.80.
|
|
9
|
+
Current validation baseline: pi 0.80.5, exact `@cursor/sdk@1.0.23`, local validation packages `@earendil-works/pi-ai`, `@earendil-works/pi-coding-agent`, and `@earendil-works/pi-tui` at 0.80.5. Published pi core peer dependencies use `"*"` ranges per current pi package guidance, so newer pi installs can try the extension before a matching validation release exists.
|
|
10
10
|
|
|
11
11
|
## Cursor SDK 1.0.17 / pi 0.79.0 cutover visual record
|
|
12
12
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Cursor Testing Lessons
|
|
2
2
|
|
|
3
|
-
> **Platform Smoke
|
|
3
|
+
> **Platform Smoke:** The required local cross-platform release gate is `npm run smoke:platform:all`; cloud-runtime changes additionally require `npm run smoke:cloud`. See [the platform smoke runbook](./platform-smoke.md). For portable guidance, see the [implementation reference](./platform-smoke-implementation.md#portability-to-other-pi-extensions) and the repo-local `docs/pi-extension-platform-testing.md` from a Crabbox checkout. The live smoke checklist remains useful for inner-loop development but is not the release gate.
|
|
4
4
|
|
|
5
5
|
## Purpose
|
|
6
6
|
|
|
@@ -159,6 +159,17 @@ The scan fails only on **persisted error messages**, not arbitrary substring mat
|
|
|
159
159
|
|
|
160
160
|
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).
|
|
161
161
|
|
|
162
|
+
## Usage and compaction JSONL lessons
|
|
163
|
+
|
|
164
|
+
Session summaries can hide per-message usage bugs. When investigating token or compaction regressions, inspect assistant message `usage` rows directly:
|
|
165
|
+
|
|
166
|
+
- `usage.input`, `usage.output`, `usage.cacheRead`, and `usage.cacheWrite` are additive spend-style counters for the assistant turn.
|
|
167
|
+
- `usage.totalTokens` is pi context occupancy for that turn, not a value to sum across all assistant messages.
|
|
168
|
+
- No single assistant message should persist SDK/full-agent-context-sized usage outside the selected model window.
|
|
169
|
+
- Real bad-session evidence should be reduced to a sanitized fixture, like `test/fixtures/cursor-run-usage-compaction-poison.jsonl`, instead of committing raw session JSONL.
|
|
170
|
+
|
|
171
|
+
The compaction poison fixture mirrors the observed failure shape: one assistant message with `RunResult`-sized input/cache-read counts near 1M immediately before compaction. Regression coverage should prove that such usage falls back to bounded pi estimates before it reaches `AssistantMessage.usage`.
|
|
172
|
+
|
|
162
173
|
### False-positive edge case (2026-05-23)
|
|
163
174
|
|
|
164
175
|
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`).
|
|
@@ -191,7 +202,7 @@ Pass criteria:
|
|
|
191
202
|
|
|
192
203
|
## Local validation ladder
|
|
193
204
|
|
|
194
|
-
Run local checks first, then the platform smoke gate before claiming release-ready for provider/runtime changes:
|
|
205
|
+
Run local checks first, then the local platform smoke gate before claiming release-ready for provider/runtime changes. Add `npm run smoke:cloud` for cloud-runtime changes:
|
|
195
206
|
|
|
196
207
|
```bash
|
|
197
208
|
npm test
|
|
@@ -202,6 +213,7 @@ npm run smoke:isolated # inner-loop helper; requires auth.json or CUR
|
|
|
202
213
|
npm run smoke:live # inner-loop partial tmux checklist subset
|
|
203
214
|
npm run smoke:platform:doctor
|
|
204
215
|
npm run smoke:platform:all
|
|
216
|
+
npm run smoke:cloud # required for cloud-runtime changes
|
|
205
217
|
```
|
|
206
218
|
|
|
207
219
|
After changing `scripts/validate-smoke-jsonl.mjs` or replay scan expectations, also run:
|
|
@@ -210,12 +222,13 @@ After changing `scripts/validate-smoke-jsonl.mjs` or replay scan expectations, a
|
|
|
210
222
|
npm test -- test/validate-smoke-jsonl.test.ts
|
|
211
223
|
```
|
|
212
224
|
|
|
213
|
-
Then use the [Cursor live smoke checklist](./cursor-live-smoke-checklist.md) only for focused inner-loop surfaces the scripts do not cover (bridge MCP, abort/cancel, full TUI observation, packaging review, cleanup) before rerunning the platform smoke gate
|
|
225
|
+
Then use the [Cursor live smoke checklist](./cursor-live-smoke-checklist.md) only for focused inner-loop surfaces the scripts do not cover (bridge MCP, abort/cancel, full TUI observation, packaging review, cleanup) before rerunning the local platform smoke gate and, for cloud-runtime changes, `npm run smoke:cloud`.
|
|
214
226
|
|
|
215
227
|
## What belongs in CI vs platform/manual smoke
|
|
216
228
|
|
|
217
229
|
- **CI / default `npm test`:** mocked provider tests, extension lifecycle tests, JSONL validator tests, script syntax/help checks. No live Cursor calls.
|
|
218
|
-
- **
|
|
230
|
+
- **Local platform release gate:** `npm run smoke:platform:all` (runs doctor first). Requires real Cursor auth and cross-platform Crabbox setup.
|
|
231
|
+
- **Cloud runtime release gate:** `npm run smoke:cloud` for PRs that touch actual cloud runtime execution.
|
|
219
232
|
- **Focused manual smoke:** `npm run smoke:isolated`, `npm run smoke:live`, and selected live-checklist sections for inner-loop debugging of behavior mocks cannot reproduce.
|
|
220
233
|
|
|
221
234
|
If platform smoke auth or target setup is unavailable, report the release as **blocked**, not skipped-ready.
|
|
@@ -243,7 +256,7 @@ The script writes timestamped artifacts under `--out` (default `/tmp/pi-cursor-s
|
|
|
243
256
|
|
|
244
257
|
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.
|
|
245
258
|
|
|
246
|
-
Hard repo rule: Cursor SDK behavior claims must come from the installed `@cursor/sdk` package and/or https://cursor.com/docs/sdk/typescript, not from memory or ad-hoc probes alone. Current cutover validation targets exact `@cursor/sdk@1.0.23` and pi 0.80.
|
|
259
|
+
Hard repo rule: Cursor SDK behavior claims must come from the installed `@cursor/sdk` package and/or https://cursor.com/docs/sdk/typescript, not from memory or ad-hoc probes alone. Current cutover validation targets exact `@cursor/sdk@1.0.23` and pi 0.80.5 local packages.
|
|
247
260
|
|
|
248
261
|
## Pi provider SDK event capture
|
|
249
262
|
|
|
@@ -399,7 +412,7 @@ npm run debug:sdk-events -- \
|
|
|
399
412
|
|
|
400
413
|
Start with whether pi stayed alive:
|
|
401
414
|
|
|
402
|
-
0. **pi process exited / shell returned with uncaught `ConnectError`
|
|
415
|
+
0. **pi process exited / shell returned with an uncaught SDK transport error** — examples include `ConnectError` with `ETIMEDOUT`/`ECONNRESET` and `WriteIterableClosedError: WritableIterable is closed`. Current code keeps Connect/network/abort suppression scoped to active provider turns. The exact SDK-provenance closed-writable shape is guarded for the Pi session lifecycle because `@cursor/sdk` 1.0.23 controlled-exec can reject after the originating provider turn when it attempts to write a `throw` frame after its internal output iterable has already closed; Bun requires an explicit rejection listener because it bypasses the patched `process.emit` path. Unrelated failures remain fatal. This proves the secondary process-killing write, not the earlier condition that first closed the SDK iterable. Treat a fresh process exit as a process-guard regression, capture the stack/session tail, and route it separately from #40 model text echo. If tools were mid-flight, note whether session JSONL ends abruptly and whether the final tool call lacks a result.
|
|
403
416
|
|
|
404
417
|
Then inspect the failing assistant turn in `$SMOKE_DIR/session/*.jsonl`:
|
|
405
418
|
|
|
@@ -419,7 +432,7 @@ rg '"type": "toolCall"|Tool call \(Cursor|cursor-replay-' "$SMOKE_DIR/session"/*
|
|
|
419
432
|
|
|
420
433
|
### When to file follow-ups
|
|
421
434
|
|
|
422
|
-
- **#43/#107** — pi exited from uncaught Cursor SDK
|
|
435
|
+
- **#43/#107** — pi exited from an uncaught Cursor SDK transport failure (hard crash, not a scrubbed #55 toast). Observed Connect/network/abort shapes remain guarded only during active provider turns; the exact SDK-provenance `WriteIterableClosedError` is guarded for the Pi session lifecycle because controlled-exec can reject after a turn. Unrelated failures remain fatal, and new exits need stack/session evidence.
|
|
423
436
|
- **#55** — caught SDK run failure or abort with missing/opaque detail (already addressed on main for surfacing).
|
|
424
437
|
- **#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.
|
|
425
438
|
- **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.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Cursor tool surfaces in pi
|
|
2
2
|
|
|
3
|
-
pi-cursor-sdk runs Cursor models through the local `@cursor/sdk` agent runtime. A
|
|
3
|
+
pi-cursor-sdk runs Cursor models through the local `@cursor/sdk` agent runtime by default. A local pi session can expose **three related but different** tool namespaces. This page is the user-facing guide; maintainer replay details live in [Cursor native tool replay](./cursor-native-tool-replay.md).
|
|
4
4
|
|
|
5
5
|
## The three surfaces
|
|
6
6
|
|
|
@@ -25,7 +25,8 @@ Pi CLI tool toggles apply at the pi tool-registry boundary. `--no-tools`, `--too
|
|
|
25
25
|
|
|
26
26
|
Default behavior:
|
|
27
27
|
|
|
28
|
-
- Cursor host tools handle files, shell, grep,
|
|
28
|
+
- Cursor host tools handle files, shell, grep, and edits.
|
|
29
|
+
- When exposed, `pi__mcp` is preferred for MCP work and `pi__subagent` is preferred for delegation. Cursor-configured MCP and Cursor-native subagents are fallbacks when the matching pi tool is not exposed or is unavailable.
|
|
29
30
|
- The pi bridge exposes **active pi tools** as `pi__*` MCP names when `PI_CURSOR_PI_TOOL_BRIDGE` is enabled (default on).
|
|
30
31
|
- Overlapping pi builtins (`read`, `bash`, `write`, `edit`, `grep`, `find`, `ls`) are **hidden** from the bridge unless `PI_CURSOR_EXPOSE_BUILTIN_TOOLS=1`.
|
|
31
32
|
|
|
@@ -42,6 +43,16 @@ PI_CURSOR_EXPOSE_BUILTIN_TOOLS=1 pi --model cursor/composer-2-5
|
|
|
42
43
|
PI_CURSOR_TOOL_MANIFEST=0 pi --model cursor/composer-2-5
|
|
43
44
|
```
|
|
44
45
|
|
|
46
|
+
## Runtime and transport policy
|
|
47
|
+
|
|
48
|
+
Current defaults:
|
|
49
|
+
|
|
50
|
+
- Local runtime is the default.
|
|
51
|
+
- The pi bridge uses loopback MCP and is the sole implemented Pi-tool transport for local Cursor agents.
|
|
52
|
+
- SDK `local.customTools` remains deferred and needs SDK cancellation/deadline support before it can replace the loopback MCP bridge; no transport config is exposed.
|
|
53
|
+
- Explicit cloud runtime selection requires first-use acknowledgement (`/cursor-runtime cloud`, `/cursor-runtime cloud --save-user`, `--cursor-cloud-ack`, or `PI_CURSOR_CLOUD_ACK=1`) plus preflight. Project config may save a cloud runtime default but not the acknowledgement. Cloud runs use fresh context by default and do **not** get local pi tools through loopback MCP or `local.customTools`; cloud Pi-tool access would require a separate secure remote bridge and a new product decision.
|
|
54
|
+
- Inline cloud MCP is not exposed in the initial cloud runtime because live probes showed first-run/replacement/resume behavior was not deterministic enough.
|
|
55
|
+
|
|
45
56
|
## Cursor settings vs pi toggles
|
|
46
57
|
|
|
47
58
|
Disabling or removing an MCP server **only in pi** does not remove Cursor ambient MCP loaded from Cursor config.
|