pi-cursor-sdk 0.5.2 → 0.5.4

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 (114) hide show
  1. package/CHANGELOG.md +66 -1
  2. package/README.md +62 -795
  3. package/dist/context.js +13 -7
  4. package/dist/cursor-agents-context.js +6 -6
  5. package/dist/cursor-api-key.js +1 -1
  6. package/dist/cursor-bridge-contract.js +1 -13
  7. package/dist/cursor-cloud-local-state.js +0 -1
  8. package/dist/cursor-config.js +1 -17
  9. package/dist/cursor-custom-subagent-definitions.js +1 -1
  10. package/dist/cursor-fallback-models.generated.js +665 -1
  11. package/dist/cursor-live-run-coordinator.js +1 -0
  12. package/dist/cursor-local-agent-ownership.js +17 -0
  13. package/dist/cursor-mcp-timeout-override.js +7 -18
  14. package/dist/cursor-native-tool-display-replay.js +5 -5
  15. package/dist/cursor-native-tools.js +11 -0
  16. package/dist/cursor-pi-tool-bridge-diagnostics.js +4 -0
  17. package/dist/cursor-pi-tool-bridge-env.js +25 -2
  18. package/dist/cursor-pi-tool-bridge-run.js +10 -3
  19. package/dist/cursor-pi-tool-bridge-server.js +98 -48
  20. package/dist/cursor-pi-tool-bridge-snapshot.js +1 -1
  21. package/dist/cursor-pi-tool-bridge.js +4 -12
  22. package/dist/cursor-provider-errors.js +2 -8
  23. package/dist/cursor-provider-live-run-drain.js +8 -3
  24. package/dist/cursor-provider-overflow.js +7 -21
  25. package/dist/cursor-provider-turn-prepare.js +8 -1
  26. package/dist/cursor-provider-turn-runner.js +17 -6
  27. package/dist/cursor-provider-turn-summary.js +1 -1
  28. package/dist/cursor-provider.js +1 -4
  29. package/dist/cursor-runtime-state.js +3 -3
  30. package/dist/cursor-sdk-event-debug.js +2 -2
  31. package/dist/cursor-sdk-output-filter.js +1 -1
  32. package/dist/cursor-sdk-process-error-guard.js +1 -2
  33. package/dist/cursor-session-agent-cleanup.js +10 -1
  34. package/dist/cursor-session-agent-lifecycle.js +2 -2
  35. package/dist/cursor-session-agent-lineage.js +11 -7
  36. package/dist/cursor-session-agent-resume.js +15 -9
  37. package/dist/cursor-session-agent.js +28 -15
  38. package/dist/cursor-session-scope.js +28 -21
  39. package/dist/cursor-session-settings.js +3 -3
  40. package/dist/cursor-session-store.js +2 -2
  41. package/dist/cursor-state.js +26 -29
  42. package/dist/cursor-tool-lifecycle.js +0 -3
  43. package/dist/cursor-tool-manifest.js +3 -1
  44. package/dist/cursor-tool-presentation-registry.js +2 -4
  45. package/dist/cursor-tool-visibility.js +0 -3
  46. package/dist/cursor-transcript-tool-specs.js +1 -2
  47. package/dist/index.js +21 -0
  48. package/dist/model-discovery.js +8 -10
  49. package/dist/model-list-cache.js +4 -9
  50. package/docs/cursor-live-smoke-checklist.md +4 -4
  51. package/docs/cursor-model-ux-spec.md +36 -19
  52. package/docs/cursor-native-tool-replay.md +8 -5
  53. package/docs/cursor-native-tool-visual-audit.md +1 -1
  54. package/docs/cursor-testing-lessons.md +39 -9
  55. package/docs/cursor-tool-surfaces.md +48 -4
  56. package/docs/development.md +93 -0
  57. package/docs/platform-smoke-implementation.md +3 -2
  58. package/docs/reference.md +806 -0
  59. package/package.json +13 -11
  60. package/scripts/platform-smoke/artifact-secrets.mjs +13 -4
  61. package/scripts/platform-smoke/local-resume-runner.mjs +0 -1
  62. package/scripts/platform-smoke/target-runtime.mjs +4 -2
  63. package/scripts/platform-smoke/targets.mjs +0 -1
  64. package/shared/cursor-sdk-output-filter.d.mts +2 -0
  65. package/shared/cursor-sdk-output-filter.mjs +92 -28
  66. package/shared/cursor-sensitive-text.d.mts +1 -0
  67. package/shared/cursor-sensitive-text.mjs +7 -0
  68. package/src/context.ts +18 -11
  69. package/src/cursor-agents-context.ts +6 -6
  70. package/src/cursor-api-key.ts +1 -1
  71. package/src/cursor-bridge-contract.ts +1 -15
  72. package/src/cursor-cloud-local-state.ts +0 -2
  73. package/src/cursor-config.ts +1 -24
  74. package/src/cursor-custom-subagent-definitions.ts +1 -1
  75. package/src/cursor-fallback-models.generated.ts +665 -1
  76. package/src/cursor-live-run-coordinator.ts +5 -0
  77. package/src/cursor-local-agent-ownership.ts +18 -0
  78. package/src/cursor-mcp-timeout-override.ts +7 -31
  79. package/src/cursor-native-tool-display-replay.ts +4 -6
  80. package/src/cursor-native-tools.ts +14 -0
  81. package/src/cursor-pi-tool-bridge-diagnostics.ts +17 -0
  82. package/src/cursor-pi-tool-bridge-env.ts +31 -2
  83. package/src/cursor-pi-tool-bridge-run.ts +10 -3
  84. package/src/cursor-pi-tool-bridge-server.ts +89 -45
  85. package/src/cursor-pi-tool-bridge-snapshot.ts +0 -1
  86. package/src/cursor-pi-tool-bridge.ts +2 -18
  87. package/src/cursor-provider-errors.ts +2 -10
  88. package/src/cursor-provider-live-run-drain.ts +8 -2
  89. package/src/cursor-provider-overflow.ts +6 -24
  90. package/src/cursor-provider-turn-prepare.ts +10 -1
  91. package/src/cursor-provider-turn-runner.ts +17 -6
  92. package/src/cursor-provider-turn-summary.ts +1 -1
  93. package/src/cursor-provider.ts +0 -5
  94. package/src/cursor-runtime-state.ts +3 -3
  95. package/src/cursor-sdk-event-debug.ts +2 -2
  96. package/src/cursor-sdk-output-filter.ts +2 -1
  97. package/src/cursor-sdk-process-error-guard.ts +1 -3
  98. package/src/cursor-session-agent-cleanup.ts +6 -1
  99. package/src/cursor-session-agent-lifecycle.ts +2 -2
  100. package/src/cursor-session-agent-lineage.ts +10 -5
  101. package/src/cursor-session-agent-resume.ts +14 -7
  102. package/src/cursor-session-agent.ts +31 -15
  103. package/src/cursor-session-scope.ts +34 -20
  104. package/src/cursor-session-settings.ts +3 -3
  105. package/src/cursor-session-store.ts +2 -2
  106. package/src/cursor-state.ts +34 -41
  107. package/src/cursor-tool-lifecycle.ts +0 -4
  108. package/src/cursor-tool-manifest.ts +4 -1
  109. package/src/cursor-tool-presentation-registry.ts +3 -5
  110. package/src/cursor-tool-visibility.ts +0 -4
  111. package/src/cursor-transcript-tool-specs.ts +0 -3
  112. package/src/index.ts +18 -0
  113. package/src/model-discovery.ts +7 -9
  114. package/src/model-list-cache.ts +4 -10
@@ -12,6 +12,7 @@ Current implementation notes:
12
12
 
13
13
  - Provider contexts support official Pi 0.87.1/latest and current `fitchmultz/pi` main, including transcript-only hosts. `cursor-pi-context.ts` directly imports Pi 0.87.1's required public replay helpers. Current instructions and tools are replayed natively, system deltas are fingerprinted, and system messages are excluded from Cursor conversation formatting and trailing-result scans. Fresh cloud context preserves current instructions while excluding history. Native replay uses the request tool snapshot (including empty); the pi bridge retains its separate registry-owned surface. XML prompt sections are sanitized/deduplicated alongside the stock format.
14
14
 
15
+ - Structured conversation history labels typed tool blocks `Historical tool request/result/error`, preserving arguments, IDs and errors without declaring unmatched requests completed. User/assistant prose and code fences remain unchanged. This removes the extension's literal `Tool call` template, not model-authored narration; no prose-to-tool conversion, stripping or automatic resend is performed (#257).
15
16
  - Cursor context variants use `base@context` pi model IDs.
16
17
  - Cursor `reasoning`, `effort`, `reasoning_effort`, and boolean `thinking` parameters are driven by pi native thinking when the Cursor SDK exposes those controls.
17
18
  - Cursor `fast` is extension state by default; models that expose `fast` also get selection-only `:fast` / `:slow` virtual aliases for per-agent overrides.
@@ -21,33 +22,41 @@ Current implementation notes:
21
22
  - Native request receipts retain canonical source-role/order scalars from public `SessionManager.buildSessionProjection()` and `convertToLlm()`, not prompt text. Only exact equivalence with the actual provider request enables native input selection. Hidden `display:false` notices remain model-visible background context, never genuine user submissions. Pending native input travels together in bootstrap/incremental prompts and remains required under transcript budgeting. Custom-only continuation has no new user request or attachment; historical bootstrap requests are explicitly history. Cloud fresh prompts select the same pending input group without previous requests; bootstrap handoff preserves history with the same native-origin selection. Origin means the persisted native source, not a role-only rewrite yielding identical LLM content. If request-local transforms change payload/order (including blocked-image conversion), origin is unavailable: preserve conservative last-converted-user/reset behavior rather than guessing. This is not universal final-preconversion provenance.
22
23
  - Image payload forwarding for request-equivalent native inputs sends images only from the latest pending native user message, even with trailing custom notices. Other/unmatched contexts retain last-converted-user selection. 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.
23
24
  - Shared SDK initialization resolves the native platform package relative to `@cursor/sdk` before import and sets `CURSOR_TREE_SITTER_VENDOR_DIR` only when unset and both vendored parser entrypoints exist. Explicit overrides remain SDK-owned. This repairs sibling-install native lookup. Static SDK and bridge imports additionally repair the compiled-Bun extension graph; the packed extension was qualified in official compiled Pi 1.0.3 on macOS arm64 with isolated empty credentials and a fake key. Embedded Windows missing-export behavior remains unqualified.
24
- - The selected vendor path is process-wide and inherited by shell commands and child Pi processes. A child using another install/worktree preserves the inherited value as an explicit override, even when stale. Unset it in the child's environment to rediscover that install's parser, or provide its absolute vendor path; see [native parser troubleshooting](../README.md#native-shell-parsing-or-module-loading-fails).
25
+ - The selected vendor path is process-wide and inherited by shell commands and child Pi processes. A child using another install/worktree preserves the inherited value as an explicit override, even when stale. Unset it in the child's environment to rediscover that install's parser, or provide its absolute vendor path; see [native parser troubleshooting](reference.md#native-shell-parsing-or-module-loading-fails).
25
26
  - Provider and discovery errors retain bounded, scrubbed name/code/message/cause fields without serializing arbitrary error objects. Module-loading and ambiguous session-authentication failures do not imply a rejected API key. Explicit key rejection retains actionable setup guidance; no automatic reload or authentication recovery is performed.
26
- - Exact `@cursor/sdk@1.0.36` is an unbundled package dependency; users do not need a global SDK install. Package 0.5.2 requires Node 24+ and official Pi 0.87.1+, while optional published Pi and TypeBox peer dependencies use `"*"` ranges per Pi guidance. Direct development Pi dependencies and their lockfile-resolved transitives are 1.0.4; nested host TypeBox remains 1.3.27; the extension's root TypeBox validation baseline is 1.3.36.
27
+ - Exact `@cursor/sdk@1.0.37` is an unbundled package dependency; users do not need a global SDK install. This package requires Node 24+ and official Pi 0.87.1+, while optional published Pi and TypeBox peer dependencies use `"*"` ranges per Pi guidance. Direct development Pi dependencies and their lockfile-resolved Pi cohort are 1.1.0 reproducible snapshots, not qualification targets or user runtime upgrades; nested host TypeBox remains 1.3.27; the extension's root TypeBox validation baseline is 1.3.36.
27
28
  - Native usage contains coherent current-context components only. The origin-owned ledger records every raw turn, original wait/handle snapshots, terminal success/error/abort/abandon, and bounded public whole-agent `getUsage()` observations separately. Cumulative bills never enter native usage. Request floors use actual native source chronology and projection equivalence, not text or clocks; see [Context and accounting boundaries](#context-and-accounting-boundaries).
28
29
  - Cursor owns one public native Provider with native API-key login and a small resolver for stored/provider-env, runtime, composed config, and ambient credentials. Startup and auth mutations are cache-only, retaining known cached/fallback identities. `PI_CURSOR_HIDE_MODELS_WHEN_LOGGED_OUT=1` hides only unauthenticated picker/list availability, not CLI selection or login. Discovery receives explicit effective native auth and never guesses a default auth store. `/cursor-refresh-models` uses native registry refresh/publication. Provider turns keep Pi's resolved `options.apiKey`; non-secret Cursor-only settings remain separate.
29
30
  - 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.
30
31
  - 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.
31
- - 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 shared process sink filters complete, verified timestamp/INFO inventory and completion frames plus exact supported hook-compatibility notices, including pre-bound SDK console output during first send and unresolved live SDK completion across Pi bridge/replay turns. The same lease restores on completion/cancellation cleanup, not when a Pi tool-use stream ends. A new write invocation starts a frame after unrelated partial output has forwarded, so an unterminated native Pi footer/prompt does not admit the following SDK diagnostic. stdout/stderr framing is independent; outside the retained creation-only mute around `Agent.create()`, unrecognized output retains its original bytes, callbacks and delegated backpressure. Only possible diagnostic prefixes buffer (up to 16 KiB); undecided tails flush on final restoration and oversized candidates pass through. Outside-root local-plugin symlink rejection and the exact native-parser-unavailable warning escape the creation mute and route through the request's async owner to a bounded native notification (stderr in non-UI modes) and a `pi-cursor-sdk:capability-notice` non-model session entry, at most once per kind per request. Notices contain fixed recovery guidance, not raw paths or arbitrary SDK metadata. Unowned maintainer scripts retain these meaningful diagnostics. Outside the creation mute, real plugin/hook/config/parser/ripgrep failures stay visible; the plugin confinement guard, settings sources and native parser setup are unchanged. Text alone cannot distinguish an unrelated emitter printing an identical complete SDK frame; strict attribution requires a future public SDK logger/sink API.
32
+ - 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.
33
+ - The shared process sink filters complete, verified timestamp/INFO inventory and completion frames plus exact hook-compatibility notices. Unsupported and unknown Claude Code events match only the SDK's two complete warning templates, with a variable quoted event name; new event names do not require an allowlist update, and unrelated `[hooks]` warnings remain visible. Extension initialization installs one process-lifetime lease before session/turn work. It remains active across idle time, detached rules/skills loads, filesystem watchers, config refresh, teardown, the asynchronous interval during `/reload`, and failed reloads. This is deliberate: SDK service disposal does not await every pending load, and Pi's old shutdown/new startup events cannot safely hand off a lease when reload fails or removes Cursor. The narrowly classified sink therefore remains installed until Pi exits, even if Cursor is removed from the active extension set. Reloaded module copies share the sink, refcounts and both async attribution scopes through one versioned process singleton; repeated initialization and concurrent sessions do not stack permanent leases. Existing provider/live-run/script leases remain temporary and nested, so direct provider/script use without extension initialization still restores on its own cleanup. The sink also catches pre-bound SDK console methods.
34
+ - A new write invocation starts a frame after unrelated partial output has forwarded, so an unterminated native Pi footer/prompt does not admit the following SDK diagnostic. stdout/stderr framing is independent; outside the retained creation-only mute around `Agent.create()`, unrecognized output retains its original bytes, callbacks and delegated backpressure. The creation mute expires when its operation returns/settles, so detached async work cannot inherit an indefinite mute for unrelated warnings. Only possible diagnostic prefixes buffer (up to 16 KiB); undecided tails flush on final restoration (process exit for an initialized extension) and oversized candidates pass through. Final restoration only removes stream methods still owned by this filter; later third-party wrappers remain installed and retained filter methods become transparent delegates.
35
+ - Outside-root local-plugin symlink rejection and the exact native-parser-unavailable warning escape the creation mute and route through the request's async owner to a bounded native notification (stderr in non-UI modes) and a `pi-cursor-sdk:capability-notice` non-model session entry, at most once per kind per request. Notices contain fixed recovery guidance, not raw paths or arbitrary SDK metadata. Unowned maintainer scripts retain these meaningful diagnostics. Outside the creation mute, real plugin/hook/config/parser/ripgrep failures stay visible; the plugin confinement guard, settings sources and native parser setup are unchanged. Text alone cannot distinguish an unrelated emitter printing an identical complete SDK frame; strict attribution requires a future public SDK logger/sink API.
32
36
  - 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.
33
37
  - 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.
34
38
  - Cursor-side thinking remains visible through pi's native thinking rendering when the Cursor SDK emits thinking or summary deltas.
35
- - 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 stable MCP v2 bridge when bridgeable tools exist. The bundled bridge runtime closure is `@modelcontextprotocol/server@2.3.1`, `@modelcontextprotocol/hono@2.0.2`, `hono@4.13.13`, and `@hono/node-server@2.1.3`; it binds loopback and validates Hono `Host` and `Origin` headers.
39
+ - 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 stable MCP v2 bridge when bridgeable tools exist. The bundled bridge runtime closure is `@modelcontextprotocol/server@2.3.1`, `@modelcontextprotocol/hono@2.0.2`, `hono@4.13.13`, and `@hono/node-server@2.1.3`; it binds loopback and validates Hono `Host` and `Origin` headers. The default owns IPv4 only; opt-in `localhost` owns both explicit loopbacks on one port before advertising a URL, fails closed without IPv6, and freezes the advertised host/listener policy for that shared server lifetime. Failed run startup releases its run state and partial listeners without changing sibling routes.
40
+ - Ordinary LOCAL turns preserve the SDK default tools unless `PI_CURSOR_NATIVE_TOOLS=0` (canonical boolean parsing) opts into public `AgentOptions.tools: ["mcp"]`. This excludes all other main tools, especially native task/subagents whose toolsets are independently curated. Capture before drain/acquisition; forward identically to create/resume and append a compatibility suffix only for opt-out, preserving default durable bytes. Cloud fails before SDK/lifecycle side effects with explicit native disable; isolated LOCAL summaries retain `tools: []`. Bootstrap/plan/tail guidance and manifest must not advertise unavailable host tools under opt-out. Pi builtins still require explicit `PI_CURSOR_EXPOSE_BUILTIN_TOOLS=1`. MCP-only retains ambient configured Cursor MCP unless setting sources are disabled; it is not Pi-only, a sandbox, or an OS security boundary. Permitted Pi/MCP tools can execute arbitrary work.
41
+ - Pi `tool_call` hooks govern actual bridged Pi execution, not native Cursor execution already completed before replay. Hiding replay/registration, disabling the bridge, or narrowing Pi tools is not native enforcement; do not fabricate pre-execution callbacks. Before any drain consumption/result delivery, compare the pending run's captured native policy under its drain lease; reject opposite-policy continuation without implicit cancellation and with restore-or-`/cursor-abort` guidance. `/cursor-abort` is a registered explicit local command using the current bound runtime scope and existing live-run release owner; it sends no model turn and never releases a sibling scope or Cloud agent. Only when that owned run exists and the current Cursor Pi turn is active does the command also invoke public `ctx.abort()` before release; no-owned-run and non-Cursor Pi turns are not aborted. Idle native Pi abort has no active signal/event after a settled mismatch, so it cannot be advertised as recovery for the old Cursor waiter. On explicit active provider abort before preparation, turn-owned cleanup releases only the pending live run from that request's captured runtime scope; ordinary errors and requests aborted before acquiring the turn lease do not release it. Already-executed work is not undone; no retroactive promise is made.
36
42
  - `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).
37
- - 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. The shared prompt tail retains GitHub issue/PR comment guidance on both bootstrap and incremental sends; this is model guidance, not an execution permission guard.
43
+ - 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. Exposed `pi__*` tools take preference for their described capabilities, including delegation or MCP work, using the current schemas and prompt guidelines rather than historical tool aliases; Cursor-configured MCP and Cursor-native subagents remain fallbacks when no matching exposed Pi capability is available. Pi emits and executes the real pi tool name. Maintainer debug: `/cursor-tools` prints bridge/manifest enablement, effective `PI_CURSOR_SETTING_SOURCES`, and current-registry exposure eligibility with builtin filtering, not a live-run surface or health check. The shared prompt tail retains GitHub issue/PR comment guidance on both bootstrap and incremental sends; this is model guidance, not an execution permission guard.
38
44
  - The provider also registers `cursor_ask_question` for Cursor models when the bridge is on and `PI_CURSOR_ASK_QUESTION=1`. While the tool awaits pi UI input it emits package event `pi-cursor-sdk:ask-question:blocked` with `{ active: true }` and clears `{ active: false }` in `finally`; the tool runs with `executionMode: "sequential"` so parallel sibling calls cannot overlap dialogs. Cursor sees it as `pi__cursor_ask_question`, and pi executes it through the normal tool path. The control is off by default so Cursor states an assumption instead of prompting. 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.
39
45
  - 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.
40
46
  - Pending pi bridge calls keep their owning local Cursor live run active, including queued/emitted calls and pi execution awaiting human input. The last matching settlement restarts the full idle interval (five minutes by default) after the provider drain lease ends, including when cleanup was armed before the lease. Lease/queue/pending state is rechecked at expiry, and the per-run observer is detached on release/replacement. A pending call in another session does not delay cleanup. Explicit abort/shutdown/reset/compaction and the fail-closed MCP/bridge deadline (one hour by default) remain effective; display-only native replay abandonment is unchanged.
41
47
 
42
48
  - Eligible local replay idle cleanup uses `PI_CURSOR_LIVE_RUN_IDLE_DISPOSE_MS`, default `300000` (five minutes). Accept only decimal digits representing `1`–`2147483647`; unset, blank, malformed (including surrounding whitespace), or out-of-range input uses the default. The existing explicit testing override takes precedence; reset returns to environment/default selection. Each timer arm selects the duration, and the full configured window starts after the last owned pending call settles and the provider drain lease ends. Bridge/MCP deadlines and explicit abort/shutdown release remain independent; this is not backend authentication, transport, or lost-executor recovery.
43
49
 
44
- - Cursor SDK MCP tool calls use a guarded timeout override because installed `@cursor/sdk` 1.0.36 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`. Bridged `CallTool` waits also have a local fail-closed deadline that defaults to and cannot exceed the effective MCP tool timeout; `PI_CURSOR_PI_BRIDGE_CALL_TIMEOUT_MS` can lower it, expiry or MCP cancellation aborts active pi execution when available, and expired bridge events are dropped before pi tool emission.
50
+ - Cursor SDK MCP tool calls use a guarded timeout override because `@cursor/sdk` 1.0.37 retains 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`. Bridged `CallTool` waits also have a local fail-closed deadline that defaults to and cannot exceed the effective MCP tool timeout; `PI_CURSOR_PI_BRIDGE_CALL_TIMEOUT_MS` can lower it, expiry or MCP cancellation aborts active pi execution when available, and expired bridge events are dropped before pi tool emission.
45
51
  - 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`; project config is active only when Pi's project-trust flow reached the extension and approved the project or the run used explicit `--approve`, and project saves require the same immutable trust provenance rather than creating Pi trust resources automatically. Pi loads `pi install -l` project-local extensions after the trust event, so those installs require `--approve` on every run that reads or writes `.pi/cursor-sdk.json`. Explicit runtime, fast-default, and HTTP transport saves preserve unrecognized config fields, reject malformed or non-object JSON without rewriting it, and use one lock-protected read-modify-write path; fast saves mutate only the selected model key. Because Pi can mutate its in-memory session branch before a journal append throws, a completed global save is authoritative and the command reports the partial journal failure instead of attempting an ambiguous rollback; the new global value stays authoritative over stale branch entries until a later successful save or session restart.
46
52
  - Local HTTP/1.1/SSE compatibility is strictly opt-in through `PI_CURSOR_HTTP_1_1`, `/cursor-http on|off|toggle`, or user `cursor-sdk.json` `local.useHttp1ForAgent`. Precedence is session, environment, user, then the built-in unset default; project config is excluded. Unset makes no `Cursor.configure()` call. Explicit values configure the installed SDK before local `Agent.create()`, extension-owned explicit state is cleared with the SDK's documented `null` reset when returning to unset and during session shutdown before module reload, and default/HTTP2/HTTP1 choices split pooled local agents. Pi's supported CLI/TUI/print/RPC lifecycle has one active session runtime per process; concurrent independent `AgentSession` embedding in one process is outside this transport toggle's contract because the installed SDK setting and executor cache are module-global. The footer adds `http1` only for enabled local runtime; cloud creation and status remain untouched.
47
53
  - 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. Provider-facing bridge tool-call IDs use the run UUID without separators in the `cursor-pi-bridge-<32 hex>-t<counter>` shape and are capped at 64 characters; bridge run IDs and tool-call IDs remain distinct.
54
+ - Bridge `mcp_initialized` diagnostics record the installed MCP server's validated `notifications/initialized` callback; `mcp_tools_list` records catalog-handler output and `toolCount` after request validation. Both use only the safe run ID and existing opt-in sinks. These are server observations, not SDK/client identity, response delivery, model consumption or ongoing health. No connection cache or readiness gate is inferred. Exposure and receipt absence cannot diagnose the original Windows/two-provider #206 failure; see [bridge troubleshooting](./cursor-tool-surfaces.md#diagnosing-advertised-but-unused-bridge-tools).
55
+ - SDK 1.0.37 source retains the Shell hook pipeline that can apply `updated_input.command`. The retained SDK 1.0.36 private-lexical/permissive-approval canary executed the rewritten command with and without native parsing; it is not executed SDK 1.0.37 proof. This is not full Pi/rtk or policy-enforcement qualification (#173). The parser-unavailable warning returns `parsingFailed` and does not itself explain the non-Cursor/opencode SIGTERM report (#339). Attribution options for both commit and PR remain absent from SDK 1.0.37's public contract (#302). See [testing evidence boundaries](./cursor-testing-lessons.md#installed-sdk-vs-native-host-evidence).
48
56
  - 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.
49
- - Cursor internal tool activity is recorded from SDK events and scrubbed. Maintainer reference for `@cursor/sdk@1.0.36` `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.
50
- - Shell correlation prefers exact IDs and otherwise requires a unique pending shell with identical arguments, accepting legacy aliases as compatibility inputs. SDK 1.0.36's public shell ToolCall uses `type: "shell"`; its installed accumulator preserves the completed ToolCall in an ID-less `onStep` before the completed delta. Offline contracts verify this producer shape and provider deduplication, not an observed alias-changing service pair or resolution of the reported stale-shell symptom. See [shell correlation evidence](./cursor-native-tool-replay.md#shell-correlation-and-evidence).
57
+ - SDK 1.0.37 adds public `computerUse` output, handled by the existing bounded generic **Cursor computerUse** activity/summary fallback. There is no dedicated action/screenshot presentation or new public local execution option; see the [canonical replay matrix](./cursor-native-tool-replay.md#sdk-tooltype-replay-matrix).
58
+ - Cursor internal tool activity is recorded from SDK events and scrubbed. Maintainer reference for `@cursor/sdk@1.0.37` `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.
59
+ - Shell correlation prefers exact IDs and otherwise requires a unique pending shell with identical arguments, accepting legacy aliases as compatibility inputs. SDK 1.0.37's public shell ToolCall uses `type: "shell"`; retained SDK 1.0.36 installed-accumulator evidence shows the completed ToolCall in an ID-less `onStep` before the completed delta. Those offline contracts verify that producer shape and provider deduplication, not an observed alias-changing service pair or resolution of the reported stale-shell symptom. See [shell correlation evidence](./cursor-native-tool-replay.md#shell-correlation-and-evidence).
51
60
  - 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.
52
61
  - In-time safe LOCAL raw `turn-ended.usage` maps full-prompt input into disjoint native components (`input = inputTokens - cacheReadTokens - cacheWriteTokens`), retaining caches and output; their sum equals `totalTokens = inputTokens + outputTokens`. Fresh usage supersedes historical floors. Fallback components describe the same estimated current context as total. Late SDK data never overwrites an emitted pi message, but raw events remain ledger facts independent of replay/display gating. `RunResult.usage`, Cloud raw data, and billed snapshots never become per-message occupancy. `src/cursor-usage-accounting.ts` owns native mapping; `src/cursor-usage-ledger.ts` owns durable telemetry.
53
62
  - Discovery registers zero cost rates. `models.json` configured per-million rates and tiers use native `calculateCost(model, partial.usage)` on each emitted message's coherent context components. Fallback has no inferred cache split, and split-message estimates are not internal request pricing. Public billed usage already has disjoint uncached input/cache/output categories; it is not normalized like raw LOCAL full-prompt input. Optional `cost.rawCostCents` and `cost.chargedCents` remain separate, revisable floating-point-cent observations, never substituted for configured native costs.
@@ -94,13 +103,13 @@ Cursor SDK is the source of truth for Cursor model IDs and Cursor-supported para
94
103
 
95
104
  Startup registers a known cached/fallback baseline without catalog/network calls. The SDK remains in the static extension graph for compiled-Bun loading; cache-only does not promise zero SDK module evaluation. Native cache-only refresh follows real login/logout/rotation/runtime-key mutations; the genuine extension context supplies composed config auth once bound. There is no polling, replayed auth lifecycle event, or independent CLI parser.
96
105
 
97
- Known host limitation: official Pi 1.0.4 has a demonstrated startup refresh-admission race that can temporarily leave fallback models and an incorrect missing-key warning despite a matching authenticated cache. The shared-owner repair is merged in `fitchmultz/pi` via [#190](https://github.com/fitchmultz/pi/pull/190); this is not an official-host fix. See [#337](https://github.com/fitchmultz/pi-cursor-sdk/pull/337). It is separate from Cursor service authentication failures and the compiled-loader repair.
106
+ Known host limitation: official Pi 1.0.4 and 1.1.0 have a demonstrated startup refresh-admission/readiness race that can temporarily leave fallback models and an incorrect missing-key warning despite a matching authenticated cache. The shared-owner repair is merged in `fitchmultz/pi` via [#190](https://github.com/fitchmultz/pi/pull/190); this is not an official-host fix. See [#337](https://github.com/fitchmultz/pi-cursor-sdk/pull/337) and the [native auth/catalog fixture boundary](./cursor-testing-lessons.md#native-authcatalog-fixture-boundary). It is separate from Cursor service authentication failures and the compiled-loader repair.
98
107
 
99
108
  Effective auth follows native precedence: runtime override, stored API-key credential (key then provider-scoped `env.CURSOR_API_KEY` then ambient), composed `models.json` key when no stored credential exists, then ambient `CURSOR_API_KEY`. Placeholder normalization does not shadow composed config. An unsupported stored credential type remains unconfigured. Native logout removes both stored credentials and the runtime override; surviving env/config auth prevents false orphan-cache deletion. Same-process environment/config changes are picked up by native registry refresh, not a watcher.
100
109
 
101
110
  `PI_CURSOR_HIDE_MODELS_WHEN_LOGGED_OUT=1` is opt-in and affects `getAvailable` (picker/list) only. `getModels` retains known fallback/cached identities so native CLI selection can happen before `--api-key` is applied. The default no-auth fallback availability policy remains unchanged; it does not imply a usable Cursor key.
102
111
 
103
- `/cursor-refresh-models` calls `ctx.modelRegistry.refresh({ providers: ["cursor"], allowNetwork: true, force: true, signal: ctx.signal })`. Cache-only phases never call the catalog or network, even when forced. Network-allowed discovery calls `Cursor.models.list({ apiKey })` with the explicit effective key. SDK 1.0.36's public `CursorRequestOptions` has no caller signal; cancellation protects publication, not underlying transport rollback. Pi 1.0.3's started OAuth refresh may outlive caller abort for up to 15 seconds to save rotated credentials; this API-key/catalog mechanism makes no OAuth rollback promise.
112
+ `/cursor-refresh-models` calls `ctx.modelRegistry.refresh({ providers: ["cursor"], allowNetwork: true, force: true, signal: ctx.signal })`. Cache-only phases never call the catalog or network, even when forced. Network-allowed discovery calls `Cursor.models.list({ apiKey })` with the explicit effective key. SDK 1.0.37's public `CursorRequestOptions` has no caller signal; cancellation protects publication, not underlying transport rollback. Pi 1.0.3's started OAuth refresh may outlive caller abort for up to 15 seconds to save rotated credentials; this API-key/catalog mechanism makes no OAuth rollback promise.
104
113
 
105
114
  Discovery stages models and selection metadata. Native `context.publish({ update })` accepts the owning generation before synchronously applying models, metadata, best-effort cache persistence/owned orphan cleanup, and one replaceable/clearable fallback-warning state. Cancelled/superseded responses and closed bindings cannot mutate those surfaces. Effective no-auth cleanup removes only a valid extension catalog regular file, never a user-managed link. Authenticated errors still prefer a fingerprint-matching cache or the bundled fallback.
106
115
 
@@ -150,7 +159,9 @@ CURSOR_API_KEY="your-key" npm run refresh:cursor-snapshots -- --write \
150
159
 
151
160
  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. Context-window inputs are limited to current selectable model IDs; redundant default `:fast`/`:slow` aliases collapse to one key, conflicting equivalent selections fail generation, and stale or ambiguous aliases are omitted. 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.
152
161
 
153
- The authenticated SDK 1.0.36 catalog refresh on 2026-10-06 returned 45 models matching the reviewed metadata. The generated header records that observation; checkpoint limits were retained.
162
+ The authenticated SDK 1.0.37 catalog-only refresh on 2026-10-07 returned 46 models, adding Claude Haiku 5.5 (`claude-haiku-5-5`) while preserving all 45 model records from the SDK 1.0.36 capture on 2026-10-06. That refresh's generated header recorded SDK 1.0.37 and the 46-model count. Haiku 5.5 exposes 20 variants across `thinking=false|true`, `context=300k|1m`, and `reasoning_effort=low|medium|high|xhigh|max`; its default is `thinking=true`, `context=1m`, `reasoning_effort=high`. The 33 bundled checkpoint-derived entries were unchanged; this refresh started no Agent/model turn or Cloud run and does not qualify effective checkpoint limits.
163
+
164
+ The current snapshot is the authenticated SDK 1.0.37 catalog-only refresh on 2026-10-22: 48 models, adding Gemini 4 Argon (`gemini-4-argon`, low/medium/high reasoning with medium default) and Muse Wonder (`muse-wonder-1.0`, 300k/1m contexts and minimal through max reasoning, default 1m/xhigh) while preserving the prior 46 records. Existing generic context/thinking selection handles both; neither adds fast controls or aliases. Preserve SDK variant labels verbatim. The 33 checkpoint-derived entries remain unchanged; this refresh started no Agent/model turn or Cloud run and establishes no new checkpoint limit.
154
165
 
155
166
  Dated evidence for Cursor's assistant-visible, model-specific system text and reconstructed tool guidance lives in [Cursor System Prompts and Tool Guidance — 2026-08-02](https://github.com/fitchmultz/pi-cursor-sdk/blob/main/docs/evidence/cursor-system-prompts-2026-08-02/README.md). Keep that evidence separate from pi-cursor-sdk's own bootstrap prompt: Cursor persists its base system message in the local SDK checkpoint, while this extension sends Pi context and bridge instructions as user content.
156
167
 
@@ -283,6 +294,7 @@ Cursor extension controls:
283
294
  | Set SDK mode | `/cursor-mode agent\|plan` | Cursor model selected |
284
295
  | Set local HTTP transport | `/cursor-http on\|off\|toggle` | local Cursor runtime |
285
296
  | Refresh filesystem Cursor config | `/cursor-refresh-config` | Cursor model selected and an SDK agent may exist |
297
+ | Explicitly abort pending local Cursor work | `/cursor-abort` | current session owns a pending local Cursor run |
286
298
  | Show tool surfaces (maintainer) | `/cursor-tools` | Cursor model selected |
287
299
  | View, refresh, or export accounting | `/cursor-usage [refresh\|export <new-file.json>\|help]` | current branch has recorded Cursor facts |
288
300
 
@@ -346,20 +358,23 @@ For boolean-only `thinking`, unsupported pi levels must be explicit `null`; othe
346
358
 
347
359
  ## Claude Behavior
348
360
 
349
- Some Claude models support both:
361
+ Some Claude models support boolean `thinking` plus an effort parameter:
350
362
 
351
363
  ```text
352
364
  thinking=true|false
353
365
  effort=low|medium|high|xhigh|max
366
+ # or reasoning_effort=low|medium|high|xhigh|max
354
367
  ```
355
368
 
356
369
  Rules:
357
370
 
358
- - Pi `off` sends `thinking=false`.
359
- - Pi enabled levels send `thinking=true` and the mapped `effort`.
360
- - `shift+tab` changes pi thinking, which changes Cursor `effort`.
371
+ - Pi `off` sends `thinking=false` and omits the effort parameter.
372
+ - Pi enabled levels send `thinking=true` and the mapped `effort` or `reasoning_effort`, using only catalog-supported values.
373
+ - `shift+tab` changes pi thinking, which changes the model's effort parameter.
361
374
  - There is no separate `thinking` toggle.
362
375
 
376
+ Haiku 5.5 uses `reasoning_effort`: both `@300k` and `@1m` expose native `off`, `low`, `medium`, `high`, `xhigh`, and `max`. `minimal` is unsupported; direct selections retain the catalog's `thinking=true`, `reasoning_effort=high` default without changing the selected context.
377
+
363
378
  Reason:
364
379
 
365
380
  - This matches pi's single thinking mental model.
@@ -411,13 +426,13 @@ The revised context-only builder is verified offline; this is not a successful l
411
426
 
412
427
  The extension validates names with `/^[A-Za-z][A-Za-z0-9._-]{0,63}$/` (extension policy, not an SDK restriction), trims required nonblank description/prompt and optional model, accepts Pi thinking levels and boolean fast, skips unusable entries, and drops unsupported fields. Own-entry construction preserves names such as `constructor` as data. Existing unrelated config saves retain definitions.
413
428
 
414
- `buildCursorCustomSubagentDefinitions()` keeps omitted model absent and explicit `inherit` literal; installed SDK 1.0.36 conversion makes both inherit the parent. Their thinking/fast fields cannot override that inheritance. Explicit models reuse canonical catalog selection, with registered fast/slow identity ahead of entry fast and catalog defaults otherwise. Parent/session/process fast settings and parent thinking are independent. Unknown IDs stay ID-only; no additional discovery request or fabricated SDK ID encoding occurs.
429
+ `buildCursorCustomSubagentDefinitions()` keeps omitted model absent and explicit `inherit` literal; SDK 1.0.37 conversion makes both inherit the parent. Their thinking/fast fields cannot override that inheritance. Explicit models reuse canonical catalog selection, with registered fast/slow identity ahead of entry fast and catalog defaults otherwise. Parent/session/process fast settings and parent thinking are independent. Unknown IDs stay ID-only; no additional discovery request or fabricated SDK ID encoding occurs.
415
430
 
416
- The full resolved selection is submitted for both runtimes. **SDK 1.0.36 LOCAL conversion retains only ID/inherit and drops thinking/fast/context params.** Cloud conversion retains params, not a qualified claim about delegated service acceptance. `local.subagentInherit` controls child resources, not model params.
431
+ The full resolved selection is submitted for both runtimes. **SDK 1.0.37 LOCAL conversion retains only ID/inherit and drops thinking/fast/context params.** Cloud conversion retains params, not a qualified claim about delegated service acceptance. `local.subagentInherit` controls child resources, not model params.
417
432
 
418
433
  Nonempty definitions add a SHA256 pool-key suffix over name-sorted fixed-shape content with sorted model params. Definition/property/param order does not affect identity; text/model/param/add/remove changes do. No-feature keys stay byte-identical. Each turn captures one definition set, including forced rebootstrap; next-turn config edits repool and exact persisted-key matching rejects stale resumes. Existing persistence stores the key digest only, not definition prompts.
419
434
 
420
- LOCAL compact/tree summary constructors remain fresh, tool-free, definition-free and ephemeral, without conversation resume/lineage. Bug-report streams have no public summary-purpose hook and retain ordinary capabilities. Bridge snapshots/manifests/routing are unchanged and `pi__subagent` stays preferred when available. User configuration and limits: [SDK-native custom agents](../README.md#sdk-native-custom-agents).
435
+ LOCAL compact/tree summary constructors remain fresh, tool-free, definition-free and ephemeral, without conversation resume/lineage. Bug-report streams have no public summary-purpose hook and retain ordinary capabilities. Bridge snapshots/manifests/routing are unchanged; an exposed Pi delegation capability stays preferred under its actual current name, schema and prompt guidelines. User configuration and limits: [SDK-native custom agents](reference.md#sdk-native-custom-agents).
421
436
 
422
437
  ## Fast Behavior
423
438
 
@@ -577,7 +592,7 @@ Persistent LOCAL storage selects `PI_CURSOR_SDK_STATE_ROOT > user local.storeRoo
577
592
 
578
593
  Custom leases are keyed by raw cwd and captured normalized selected root and retained through admission/open/fallback/disposal. Creating/ready/busy reuse and same-key supersession compare storage compatibility too. Paths must be native fully qualified absolute non-root paths without explicit dot components or interpolation. POSIX ancestry rejects unowned/group/other-writable components except trusted sticky root-owned temporary ancestors; subsequent components must be current-user owned. Missing components are created individually (0700); only owned destinations are restricted through identity-checked no-follow directory descriptors. Verified root-owned system aliases may canonicalize; user-controlled ancestor/owned links are rejected. Bounded admission, fixed SQLite leaf checks and before/after-public-open identities protect the chain. This trusts current-user private contents, not hostile same-user/admin pathname races or arbitrary lazy checkpoint contents. Windows checks Node-visible links/junctions, identity/type and writability, not ACL hardening or every reparse tag; choose an ACL-protected location. Native Windows proof requires Windows execution, not POSIX platform mocks. Fileless and LOCAL compact/tree temporary paths bypass all persistent selection/validation/leases.
579
594
 
580
- When default storage is selected, SDK 1.0.36's public default-root getter migrates MD5(cwd) to SHA256(cwd) with a whole-workspace rename. Persisted Pi identities are admitted only as exact cwd/scope-derived roots below the SDK-owned prefix. A missing legacy workspace can map to the current root only when that precise destination contains the recorded agent with the same cwd. Existing legacy roots retain ownership, including when both stores exist; no merge or sweep occurs. Resume and recorded-ID cleanup share this admission. Persistent same-cwd callers acquire ownership synchronously before any await or public root derivation, share the pending derivation, and retain it through resume/cleanup admission and store open/disposal. Failed acquisition/disposal releases ownership; no permanent root cache or whole-turn serialization is introduced. Other processes must be stopped before upgrading because this is not a cross-process lock. User-managed links/junctions above the SDK-owned prefix remain supported. A small pure layout preflight, contract-verified against the installed SDK factory/public getter because no public read-only resolver exists, rejects links/non-directories in the owned prefix and old/current workspace components before the migration-capable getter. Post-getter/open/removal guards remain in place. Recorded identities must first match exact derived current/legacy roots; only admitted roots enter a normalized finite component walk. Malformed/outside/sibling identities fall back on resume or become durably non-retryable cleanup candidates. A layout failure after exact admission names the rejected owned component and remains retryable for cleanup after that component is repaired; dangling destination links are rejected before a missing destination can be mistaken for absent migration proof. Persistent opens require an already-active matching derived workspace lease; no caller-supplied root adoption or unleased mode exists. Cleanup requires a durable session file and recorded intent before opening persistent storage. Fileless turns and LOCAL compaction/tree summaries open unique temporary stores directly, without workspace derivation, preflight, migration or a workspace lease; they never resume and retain guarded removal on failure/disposal. Pool keys, branch resume admission and native usage journals/footer are unchanged.
595
+ When default storage is selected, SDK 1.0.37 retains SDK 1.0.36's public default-root getter and MD5(cwd)-to-SHA256(cwd) whole-workspace rename; the inspected storage/root factories are byte-identical. Persisted Pi identities are admitted only as exact cwd/scope-derived roots below the SDK-owned prefix. A missing legacy workspace can map to the current root only when that precise destination contains the recorded agent with the same cwd. Existing legacy roots retain ownership, including when both stores exist; no merge or sweep occurs. Resume and recorded-ID cleanup share this admission. Persistent same-cwd callers acquire ownership synchronously before any await or public root derivation, share the pending derivation, and retain it through resume/cleanup admission and store open/disposal. Failed acquisition/disposal releases ownership; no permanent root cache or whole-turn serialization is introduced. Other processes must be stopped before upgrading because this is not a cross-process lock. User-managed links/junctions above the SDK-owned prefix remain supported. A small pure layout preflight, contract-verified against the installed SDK factory/public getter because no public read-only resolver exists, rejects links/non-directories in the owned prefix and old/current workspace components before the migration-capable getter. Post-getter/open/removal guards remain in place. Recorded identities must first match exact derived current/legacy roots; only admitted roots enter a normalized finite component walk. Malformed/outside/sibling identities fall back on resume or become durably non-retryable cleanup candidates. A layout failure after exact admission names the rejected owned component and remains retryable for cleanup after that component is repaired; dangling destination links are rejected before a missing destination can be mistaken for absent migration proof. Persistent opens require an already-active matching derived workspace lease; no caller-supplied root adoption or unleased mode exists. Cleanup requires a durable session file and recorded intent before opening persistent storage. Fileless turns and LOCAL compaction/tree summaries open unique temporary stores directly, without workspace derivation, preflight, migration or a workspace lease; they never resume and retain guarded removal on failure/disposal. Pool keys, branch resume admission and native usage journals/footer are unchanged.
581
596
 
582
597
  ### Resume Session
583
598
 
@@ -600,6 +615,8 @@ Use:
600
615
 
601
616
  Each ExtensionAPI registers its own provider closure. Its native header receipt captures immutable scope/projection provenance and the usage origin before deferred work; stream entry consumes that receipt and captures the owning bridge, replay state, and cloud recorder. Default SDK sessions create independent ModelRuntime instances on supported official Pi 0.87.1+ and the maintained fork. Concurrent sessions, children launched inside a parent's tool before `turn_end`, and direct manual/automatic compaction, tree summaries, and bug-report summaries retain their own pool/store, persisted lineage/resume entries, journals, and debug metadata. Ownership does not depend on turn/agent event windows. Inherited tool-result IDs cannot select a sibling's live run. Replay wrappers require both their owning display state and the normalized recorded tool name before consuming a result; a foreign-owner or wrong-tool ID is rejected without consuming the original card or executing native work.
602
617
 
618
+ Runtime scope keys combine the persistent identity with a process-local owner assigned to the real public SessionManager object. That manager is stable across native contexts/reload on the supported hosts; independent managers opening one file do not share generations, pools, live runs, branch caches or terminal tombstones. Durable scope keys, pool-compatibility bytes, store roots and persisted resume/lineage entries keep their prior identity. Atomic exact store-root/agent-ID claims prevent an already-owned handle from being resumed or deleted by another runtime; unavailable resume claims create a fresh agent, while cleanup refuses the occupied ID. Claims release only after successful SDK disposal, not a timeout/rejection. The native ownership regression proves same-file ordinary continuation and pending bridge survival across sibling reload/tree/shutdown, restart resume and simultaneous acquisition. It does not establish #282's original trigger, cross-process locking or concurrent real SQLite/journal-write safety.
619
+
603
620
  Pi's SDK supplies a session-owned `before_provider_headers` callback for ordinary and auxiliary streams. The extension records ownership against the real headers object without modifying it; Pi forwards that same object to the registered provider. Stream entry consumes and checks this one-shot request receipt before touching SDK/storage state, then verifies actual request projection equivalence when resolving a fallback floor. Other extensions' header mutations are preserved. No turn windows, lifecycle scans, sibling graph, or cross-session queue are needed; the existing per-scope send queue remains unchanged.
604
621
 
605
622
  An explicitly shared ModelRuntime still has only one Cursor registration. A request can run only when its receipt belongs to that registration's active session. Mismatched or missing receipts fail before SDK/storage work:
@@ -7,7 +7,7 @@ pi-cursor-sdk has two separate pi-facing paths plus Cursor's own local-agent too
7
7
  1. **Local pi MCP bridge:** default-on for local Cursor agents. It exposes the current pi session's bridgeable active tools to Cursor through a tokenized `127.0.0.1` MCP endpoint, excluding internal Cursor replay activity names and, by default, overlapping built-in pi tools (`read`, `bash`, `write`, `edit`, `grep`, `find`, `ls`). When Cursor calls one of those MCP tools, pi executes the real pi tool through the normal pi tool path.
8
8
  2. **Cursor native tool replay:** display-only. It renders completed Cursor SDK tool activity as pi-native-looking cards using recorded Cursor results.
9
9
 
10
- This document is about replay. Replay is not execution and is not the local pi bridge.
10
+ This document is about replay. Replay is not execution and is not the local pi bridge. Pi `tool_call` hooks cannot govern native work that already ran before replay; blocking or rewriting its card cannot prevent or transform the original command. Hiding replay or disabling the bridge is not enforcement. For the opt-in public SDK MCP-only local surface, its ambient-MCP limits, and why it is not a sandbox, see [MCP-only local turns](./cursor-tool-surfaces.md#opt-in-mcp-only-local-turns).
11
11
 
12
12
  ## Live bridge vs replay
13
13
 
@@ -49,7 +49,7 @@ PI_CURSOR_PI_TOOL_BRIDGE_DEBUG=1 pi --model cursor/grok-4.6
49
49
 
50
50
  Shell start/completion correlation accepts legacy shell aliases with identical arguments even if IDs differ, but only for a unique pending match. Multiple identical pending shells remain unmatched without an exact ID, which takes precedence. Unmatched shell starts remain visible; assistant text or stdout progress alone does not prove completion.
51
51
 
52
- This alias handling is an extension compatibility contract, not an observed alias-changing SDK callback pair. SDK 1.0.36's public started/completed updates carry `callId`, `modelCallId`, and a ToolCall with `type: "shell"`. The installed `run-interaction-accumulator` copies the completed ToolCall into `onStep({ step: { type: "toolCall", message: toolCall } })`, without adding `step.id`, before forwarding the completed delta. An offline provider contract executes that installed producer and checks reconciliation/deduplication; legacy alias tests separately check compatibility inputs. No retained alias-changing service capture establishes that this fixes the reported stale-shell symptom.
52
+ This alias handling is an extension compatibility contract, not an observed alias-changing SDK callback pair. SDK 1.0.37's public started/completed updates carry `callId`, `modelCallId`, and a ToolCall with `type: "shell"`. In retained SDK 1.0.36 installed-package evidence, `run-interaction-accumulator` copies the completed ToolCall into `onStep({ step: { type: "toolCall", message: toolCall } })`, without adding `step.id`, before forwarding the completed delta. The SDK 1.0.36 offline provider contract executes that producer and checks reconciliation/deduplication; legacy alias tests separately check compatibility inputs. No retained alias-changing service capture establishes that this fixes the reported stale-shell symptom.
53
53
 
54
54
  ## What gets replayed
55
55
 
@@ -74,13 +74,13 @@ When Cursor reports completed tool activity, the extension can display recorded
74
74
 
75
75
  Cursor `glob` activity is displayed through native `find` cards.
76
76
 
77
- For the full `@cursor/sdk@1.0.36` `ToolType` set, disposition matrix, and runtime alias normalization, see [SDK ToolType replay matrix](#sdk-tooltype-replay-matrix) below. Official SDK reference: https://cursor.com/docs/sdk/typescript
77
+ For the full `@cursor/sdk@1.0.37` `ToolType` set, disposition matrix, and runtime alias normalization, see [SDK ToolType replay matrix](#sdk-tooltype-replay-matrix) below. Official SDK reference: https://cursor.com/docs/sdk/typescript
78
78
 
79
79
  Edit and write activity replays through pi-facing `edit` and `write` cards only when replay arguments truthfully satisfy the matching pi schema, but still uses recorded Cursor results only. The adapter passes through truthful Cursor paths, content when Cursor reported it, and recorded diff/details; it does not pretend Cursor's editing schema is pi's schema and it fails closed if a recorded replay result is missing. Cursor `StrReplace` with recorded replacement text displays as native-looking `edit`; path-only Cursor `edit` and notebook edit activity fall back to neutral Cursor activity so pi does not reject the replay before recorded-result handling. Cursor `write` displays as native-looking `write`. Diagnostics, delete, todos/plans, task/subagent, image, MCP, semantic search, screen recording, and web search/fetch activity use neutral Cursor activity cards with pi's default success/error tool shell. Cursor SDK `task` activity is labeled **Cursor subagent** by default because it represents Cursor-spawned child-agent work; `PI_CURSOR_TASK_PRESENTATION=task` keeps the older **Cursor task** wording for comparison. MCP completions whose `toolName` is `WebSearch` / `web_search` / `WebFetch` / similar are labeled **Cursor web search** or **Cursor web fetch** instead of generic **Cursor MCP**. Neutral Cursor activity cards carry display metadata such as `activityTitle` and `activitySummary`, so partial/collapsed cards can say `Cursor plan`, `Cursor todos`, `Cursor subagent`, `Cursor MCP`, `Cursor semantic search`, `Cursor screen recording`, `Cursor web search`, `Cursor web fetch`, or `Cursor edit` instead of only `Cursor activity`. These replay tools only display recorded Cursor results; they never mutate files or execute tool work directly. Replay paths are normalized to workspace-relative paths when possible. Most collapsed replay cards include bounded previews for diffs and text details so small edits, todos, task output, and MCP results are visible without expanding; web search/fetch activity stays summary-only while collapsed because those cards often arrive after final text and can otherwise bury the answer. Ctrl+O expansion shows the recorded details. Edit previews omit raw unified diff headers and show compact numbered changed/context lines using pi's native diff added/removed/context colors, and write previews use syntax highlighting when pi can infer a language from the path. Image generation replay cards show the saved image path in the collapsed summary and render the image inline when pi terminal image display is enabled and the generated file is still readable.
80
80
 
81
81
  ## SDK ToolType replay matrix
82
82
 
83
- Source of truth for SDK tool names: `@cursor/sdk@1.0.36` conversation `ToolType` values and https://cursor.com/docs/sdk/typescript
83
+ Source of truth for SDK tool names: `@cursor/sdk@1.0.37` public `ToolType = ToolCall["type"]` union and https://cursor.com/docs/sdk/typescript. All SDK 1.0.36 variants remain; SDK 1.0.37 adds `computerUse`.
84
84
 
85
85
  Implementation owners: `src/cursor-tool-presentation-registry.ts` (canonical names, labels, visibility, replay policy, bridge exclusions for internal replay wrappers, alias normalization, and display-spec key completeness), `src/cursor-transcript-tool-specs.ts` (registry-keyed display implementations for transcript formatting and pi display builders), `src/cursor-native-tool-display-replay.ts` (replay card rendering derived from registry replay metadata), and `src/cursor-web-tool-activity.ts` (MCP/web alias remapping before display lookup).
86
86
 
@@ -106,10 +106,13 @@ This matrix covers **Cursor native tool replay only**. It does not describe the
106
106
  | `mcp` | neutral activity | `cursor` | Collapsed label **Cursor MCP** for non-web MCP completions; web search/fetch MCP `toolName` values reclassify to the rows below |
107
107
  | `semSearch` | neutral activity | `cursor` | Collapsed label **Cursor semantic search**; semantic codebase search, not web search |
108
108
  | `recordScreen` | neutral activity | `cursor` | Collapsed label **Cursor screen recording** |
109
+ | `computerUse` | bounded generic activity/summary fallback | `cursor` | New SDK 1.0.37 public output variant; label **Cursor computerUse**. No dedicated action cards or screenshot presentation, and no new public local execution option |
109
110
  | *(host/MCP alias)* `WebSearch` / `web_search` / similar | neutral activity | `cursor` | Collapsed label **Cursor web search**; display-only Cursor web access reported by the SDK, not an executable pi web tool |
110
111
  | *(host/MCP alias)* `WebFetch` / `web_fetch` / similar | neutral activity | `cursor` | Collapsed label **Cursor web fetch**; display-only Cursor web access reported by the SDK, not an executable pi web tool |
111
112
  | _(no spec; future/unknown SDK name)_ | neutral activity | `cursor` | Collapsed label **Cursor** plus SDK tool name via `buildGenericPiToolDisplay()`; bounded fallback transcript only |
112
113
 
114
+ **SDK 1.0.37 `computerUse` ceiling:** The public completed payload uses `type: "computerUse"` with `args.actions`, `args.description` and a success/error result; `computerUseToolCall` is its protobuf discriminant, not a pi executable tool name. The existing generic fallback summarizes scrubbed arguments/results and preserves error status within bounds (up to eight argument entries, three array preview items, and default result/error limits of 240 characters/six lines). It does not provide dedicated action cards, screenshot-path previews or lossless action/result display. The output union addition does not add a public local computer-use executor/configuration option.
115
+
113
116
  **Unknown/future fallback path:** SDK tool names with no registry-backed display implementation entry (future or unknown types) use `buildGenericPiToolDisplay()` in `src/cursor-transcript-tool-specs.ts` with bounded `formatFallback()` content from `src/cursor-transcript-tool-formatters.ts`. Lookup uses `Object.hasOwn()` on the display implementation table so inherited object keys such as `constructor` or `toString` cannot accidentally match a registry spec. When native replay is enabled, those completions queue through neutral pi tool name `cursor` (not native pi `read`/`bash`/… cards). Collapsed labels read like **Cursor futureSemSearchWidget** (title `Cursor` plus the SDK tool name) with optional bounded `activitySummary` from scrubbed args/result lines. Errors keep `details.summary` undefined so unbounded raw errors do not leak into replay cards (#52). Known explicit specs still win over this path; real pi bridge tool names such as `edit` and `write` are not suppressed by internal replay-wrapper exclusions.
114
117
 
115
118
  **Replay detail disposition model:** `src/cursor-replay-tool-details.ts` stores replay card disposition separately from SDK source tool identity. Variants are `nativeEdit`, `nativeWrite`, `activity` (`sourceToolName` + display `title`), `generateImage`, and `genericFallback`. Path-only or notebook edit/write fallbacks produce `activity` details (neutral `cursor` cards) instead of structured edit/write variants with optional `title` escape hatches. Native edit/write cards use `nativeEdit` / `nativeWrite` only when pi-facing replay args satisfy the matching schema. The renderer dispatches on `variant` only.
@@ -194,7 +197,7 @@ Lifecycle rules:
194
197
 
195
198
  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.
196
199
 
197
- Bridged pi tool calls follow the same visible pi `toolUse` turn shape, but they are real pi tool executions rather than replayed Cursor results. Local usage accounting uses per-turn raw Cursor SDK `turn-ended.usage` when available before the corresponding pi turn is emitted, including cache read/write fields; observed local full-prompt/partition semantics map into disjoint pi input/cache components and set `usage.totalTokens = inputTokens + outputTokens` for occupancy (distinct from published SDK `toTokenUsage` additive totals). If the local SDK reports no safely attributable usage in time, the provider estimates the current context; its disjoint input/cache/output components sum to `usage.totalTokens`. Later raw usage is retained by the separate ledger, not applied retroactively to emitted pi messages. Public billed facts never enter native occupancy. Cloud raw telemetry is retained without assuming LOCAL normalization, while native Cloud messages use approximate context accounting. See [context, compaction, and Cursor usage](../README.md#context-compaction-and-cursor-usage).
200
+ Bridged pi tool calls follow the same visible pi `toolUse` turn shape, but they are real pi tool executions rather than replayed Cursor results. Local usage accounting uses per-turn raw Cursor SDK `turn-ended.usage` when available before the corresponding pi turn is emitted, including cache read/write fields; observed local full-prompt/partition semantics map into disjoint pi input/cache components and set `usage.totalTokens = inputTokens + outputTokens` for occupancy (distinct from published SDK `toTokenUsage` additive totals). If the local SDK reports no safely attributable usage in time, the provider estimates the current context; its disjoint input/cache/output components sum to `usage.totalTokens`. Later raw usage is retained by the separate ledger, not applied retroactively to emitted pi messages. Public billed facts never enter native occupancy. Cloud raw telemetry is retained without assuming LOCAL normalization, while native Cloud messages use approximate context accounting. See [context, compaction, and Cursor usage](reference.md#context-compaction-and-cursor-usage).
198
201
 
199
202
  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.
200
203
 
@@ -6,7 +6,7 @@ This workflow is the canonical repo path for verifying Cursor SDK tool replay th
6
6
 
7
7
  Use it when changed replay-card behavior needs new visual proof not covered by offline rendering contracts or valid retained evidence. Text logs and JSONL are not enough for visual-parity claims: keep PNGs for the exact prompt, and before/after PNGs for a rendering comparison. Reuse a valid retained baseline instead of paying to recreate it.
8
8
 
9
- Current validation baseline: Node 24+, exact `@cursor/sdk@1.0.36`, official Pi 0.87.1/latest and current `fitchmultz/pi` main. Development Pi packages `@earendil-works/pi-ai`, `@earendil-works/pi-coding-agent`, and `@earendil-works/pi-tui` are exact locked 1.0.4 snapshots, with host TypeBox 1.3.27. These locked development snapshots are not qualification targets; resolve latest stable official Pi and current `fitchmultz/pi` main once per workflow run. Optional published Pi and TypeBox peer dependencies use `"*"` ranges per Pi guidance.
9
+ Current validation baseline: Node 24+, exact `@cursor/sdk@1.0.37`, official Pi 0.87.1/latest and current `fitchmultz/pi` main. Development Pi packages `@earendil-works/pi-ai`, `@earendil-works/pi-coding-agent`, and `@earendil-works/pi-tui` are exact locked 1.1.0 snapshots, with host TypeBox 1.3.27. These locked development snapshots are not qualification targets; resolve latest stable official Pi and current `fitchmultz/pi` main once per workflow run. Optional published Pi and TypeBox peer dependencies use `"*"` ranges per Pi guidance.
10
10
 
11
11
  ## Cursor SDK 1.0.17 / pi 0.79.0 cutover visual record
12
12
 
@@ -8,6 +8,12 @@ This document records maintainer testing lessons for `pi-cursor-sdk`. It complem
8
8
 
9
9
  For a **minimal one-session dogfood pass** (baseline env, one native + one bridge call, JSONL ID patterns, bootstrap manifest, edit diff card), use the [Cursor dogfood checklist](./cursor-dogfood-checklist.md) only when its selected changed behavior needs new live proof; do not follow it with a full paid matrix by default.
10
10
 
11
+ ## Durable store identity versus SDK pathname
12
+
13
+ Persisted `storeIdentity.stateRoot` is a durable identity, while the SQLite-open boundary passes `toNamespacedPath(stateRoot)` to the SDK. On Windows, the SDK pathname can therefore have the `\\?\` prefix without changing the stored identity. Native resume tests must compare the SDK value exactly with `toNamespacedPath(handle.storeIdentity.stateRoot)` and verify both existing directories with `realpath`; do not strip arbitrary prefixes or weaken the stable-handle/live-sibling assertions.
14
+
15
+ For native harness diagnostics, set `PI_CURSOR_TEST_EVIDENCE_DIR` before starting the test and use a fresh owned temp directory. Evidence-configured `concurrentFixture` runs retain their original fixture roots and write `fixture-root-*.json` metadata before execution; copied JSONL does not authorize deleting the original JSONL or its ancestors. The targeted macOS/Windows native ownership CI step runs the whole ownership test file, avoiding a name filter that could silently match no cases, and places both original roots and copied evidence under the existing `native-host` artifact directory. Runs without evidence configuration keep their existing teardown behavior.
16
+
11
17
  ## Core lesson: integration-shaped bugs beat unit mocks
12
18
 
13
19
  The native replay `Tool grep not found` failure was integration-shaped, not unit-shaped:
@@ -19,11 +25,11 @@ The native replay `Tool grep not found` failure was integration-shaped, not unit
19
25
 
20
26
  Passing hundreds of unit tests did not prove that chain was safe. Regression coverage now includes:
21
27
 
22
- - `test/index.test.ts` — `before_agent_start` and `turn_start` resync after plan-style strip
28
+ - `test/index-registration.test.ts` — `before_agent_start` and `turn_start` resync after plan-style strip
23
29
  - `test/cursor-native-replay-stress.test.ts` — plan strip → resync → grep replay; inactive-tool trace fallbacks
24
30
  - `test/cursor-provider-replay-live-run.test.ts` — inactive replay tools emit trace instead of broken `toolUse`
25
31
  - `test/cursor-native-replay-trace.test.ts` — shared inactive replay trace formatting
26
- - `test/cursor-native-replay-routing.test.ts` — `resolveNativeReplayDisposition` and `partitionNativeToolsByActiveContext`
32
+ - `test/cursor-native-replay-routing.test.ts` — disabled-replay disposition, absent-snapshot compatibility, and mixed active/inactive tool partitioning (other disposition behavior is exercised at the provider boundary above)
27
33
  - `test/validate-smoke-jsonl.test.ts` — replay scan semantics (real errors vs doc mentions in successful reads)
28
34
 
29
35
  When changing provider/runtime behavior, ask whether the bug spans **pi extension lifecycle**, **active tool state**, **provider streaming**, and **persisted JSONL**. If yes, add an integration-style unit test or live smoke coverage for that chain.
@@ -50,9 +56,33 @@ If resync runs but `context.tools` is still stale (e.g. only `read` listed), the
50
56
 
51
57
  ## Stock/transcript provider contract
52
58
 
53
- `test/cursor-provider-pi-context.test.ts` drives real `ModelRuntime` / `ModelRegistry` requests into `streamCursor`, with only Cursor SDK execution mocked. Run it against each supported host with all Pi peer imports pinned to that host (including nested native imports); a top-level package version alone is not resolution evidence. Cover official Pi 0.87.1, official latest, and current `fitchmultz/pi` main. The direct development Pi packages are locked 1.0.4 snapshots, not qualification targets; resolve latest stable official Pi and current `fitchmultz/pi` main once per workflow run. Their actual nested resolution is recorded with each check, including host TypeBox 1.3.27.
59
+ `test/cursor-provider-pi-context.test.ts` drives real `ModelRuntime` / `ModelRegistry` requests into `streamCursor`, with only Cursor SDK execution mocked. Run it against each supported host with all Pi peer imports pinned to that host (including nested native imports); a top-level package version alone is not resolution evidence. Cover official Pi 0.87.1, official latest, and current `fitchmultz/pi` main. The direct development Pi packages are locked 1.1.0 snapshots, not qualification targets; resolve latest stable official Pi and current `fitchmultz/pi` main once per workflow run. Their actual nested resolution is recorded with each check, including host TypeBox 1.3.27.
60
+
61
+ The test covers bootstrap/incremental prompts, empty-vs-absent request tools, native replay/drain, cloud fresh/bootstrap selection, and actual host prompt serialization for context files and skills. `test/native-cursor-flow.test.mjs` additionally exercises the compiled extension through the real loader and registered Cursor provider, substituting only the external SDK transport/storage. It verifies actual bridge tool execution, pending-work idle/abort/shutdown/deadline ownership, replay without file access, persisted usage/lineage, tree, compaction, queued steering, abort and reload/disposal. `test/native-cursor-prompt.test.mjs` owns genuine native input, hidden notices, required-budget input, current attachments, custom-only continuation, equivalent conversion, payload/order-changing rejection, and GitHub comment soft guidance across bootstrap/incremental/custom-only sends. `test/native-cursor-ownership.test.mjs` owns concurrent-session contracts, including independently reopened managers for the same file, pending bridge survival across sibling reload/tree/shutdown, durable-key stability and atomic live-handle exclusion. These owners use `test/helpers/native-cursor-harness.mjs` and run in the explicit `check:compat` list and manual official-host compatibility route; the native-platform Vitest job is not execution evidence for these Node test owners. Explicit `tools` is now an allowlist, not merely an initial active set; use `defaultTools` for a native fixture that allows replay wrappers to activate. `test/native-provider.test.mjs` additionally verifies canonical `SessionManager.appendContextEdit()` plus `refreshContext()`, unchanged raw history, and conversation filtering that preserves system instructions, the current tool declaration, and explicit tool removal (the focused native slice from #271). It is offline contract evidence. Add live proof only for changed behavior that still needs real-service evidence under the cost policy above; do not rerun unchanged live lanes or infer full live coverage from offline tests.
62
+
63
+ ### Native auth/catalog fixture boundary
64
+
65
+ `test/cursor-native-auth-catalog.test.ts` uses public `ModelRuntime.create({ modelsPath: null })` for cases that do not exercise configuration files. Credentials and the Cursor catalog cache remain real; config-precedence and config-edit cases still use a temporary `models.json`. Seed startup credentials and the matching cache before creating the runtime.
66
+
67
+ Official Pi 1.1.0 retains the [file-backed startup refresh-admission/readiness limitation](./cursor-model-ux-spec.md#source-of-truth): registration starts an unawaited refresh, and an asynchronous config load can supersede the awaited session refresh before its catalog publishes. This can return the bundled fallback despite an authenticated matching cache, including when auth/cache were seeded before runtime creation. The in-memory fixture isolates Cursor auth/publication assertions from that shared host defect; it does **not** qualify or repair official file-backed startup. The shared-owner repair in `fitchmultz/pi` [#190](https://github.com/fitchmultz/pi/pull/190) admits refresh ownership before config I/O and awaits replacement publication. File-backed startup proof must use that repaired host and retain its exact revision.
68
+
69
+ ## Installed SDK vs native host evidence
70
+
71
+ The native harness substitutes external Cursor SDK transport/storage. It qualifies Pi ownership, scheduling and persisted-session behavior, not real SDK request-time linking. `test/cursor-sdk-lazy-import.test.ts` separately drives public installed `Agent.create()` → `send()` → `run.wait()` with `JsonlLocalAgentStore`, an empty credential environment/private HOME, and pre-import Node network/process guards. The positive case reaches exactly one blocked key-exchange fetch; the negative case denies an SDK module only after creation and fails before network. This protects first-send linking beyond a successful static import. The guards are public Node API interception, not an OS sandbox, and neither case qualifies authenticated gRPC, compiled Pi or the original Windows embedded hosts.
72
+
73
+ For missing exports, inspect resolution **from the failing importer**, not a root-level import. The installed SDK 1.0.36 nested Connect 1.7.0 / Protobuf 1.10.0 graph links; forcing that import to root Protobuf 2.16.0 reproduces `protoBase64`'s absence. This establishes a mechanism, not #233's unnamed host mapping. SDK-declared ranges, installed dependency versions and vendored transport versions are distinct facts.
74
+
75
+ An installed-package research probe can isolate a mechanism but cannot certify the public runtime. Exact SDK 1.0.23 source already has settings-selected hook loading, config leases, and Shell/ShellStream `updated_input.command` rewriting. Its direct hook-payload transport depends on `hooks_stdin_transport` (false by default or if initialization fails); false still delivers JSON through a POSIX heredoc or Windows PowerShell here-string, not missing hooks or missing stdin payload. SDK 1.0.36 always selects the direct transport mode. These are source endpoints, not a verified change date or rtk repair. The retained SDK 1.0.36 Shell-hook canary used private lexical bindings and a permissive approval collaborator: it read user hooks, passed the invocation ID, applied `updated_input.command`, and observed rewritten file output, with both native parsing and `parsingFailed`. It is not execution evidence for SDK 1.0.23, Pi/rtk integration or security-policy enforcement and must not become production integration. Replay arguments may precede rewriting; require original input, hook decision and actual executed command for the same invocation before diagnosing #173. The parser-warning branch does not establish the emitter or SIGTERM sender in #339.
76
+
77
+ For #247, exact SDK 1.0.27 source caches the exchanged bearer until invalidation, without expiry-based refresh; SDK 1.0.36 adds JWT-aware refresh, a 30-minute fallback when expiry cannot be parsed, and raw-Unauthenticated stream invalidation without interceptor resend. These source endpoints establish neither a change date, backend TTL, the original failure cause nor service recovery. SDK 1.0.27 already distinguishes classified authentication errors from raw Connect retry, shares reference-counted executor leases, and limits `agent.reload()` to workspace reload—not an auth-reset API. Failed provider runs reset their owned pool, but disposing one agent need not dispose an executor leased elsewhere. Require the redacted error chain and agent/executor/run transitions around retry and reload; do not infer a safe resend or add a guessed TTL from sanitized text.
78
+
79
+ The integrity-verified [SDK 1.0.37 package](https://registry.npmjs.org/@cursor/sdk/-/sdk-1.0.37.tgz) source comparison retains those SDK 1.0.36 bearer, executor-lease, workspace-reload, Shell/ShellStream hook-rewrite and parser-warning mechanisms. Public `index.d.ts`, `options.d.ts` and `sdk-config.d.ts` are byte-identical: both commit/PR attribution controls remain absent, with no new auth-reset, hook-transport, MCP timeout/readiness or local GUI option. LOCAL custom-subagent conversion remains ID/inherit-only; inspected SQLite/checkpoint/default-root factories retain the same layouts and MD5-to-SHA256 migration. This comparison establishes bounded source equivalence, not executed SDK 1.0.37 hook-canary, SQLite-migration or platform qualification. Keep the retained SDK 1.0.36 canary and 2026-10-06 45-model catalog attributed to their actual version.
80
+
81
+ A subsequent authenticated SDK 1.0.37 catalog-only refresh on 2026-10-07 returned 46 models and regenerated `src/cursor-fallback-models.generated.ts`, adding Claude Haiku 5.5 (`claude-haiku-5-5`) while preserving all 45 prior records. Its 20 variants expose `thinking=false|true`, `context=300k|1m` and `reasoning_effort=low|medium|high|xhigh|max`, with defaults `true`, `1m` and `high`. No checkpoint input was supplied: all 33 bundled checkpoint-derived entries were retained unchanged. This is catalog metadata proof only; it started no Agent/model turn or Cloud run and does not qualify hook execution, effective checkpoint limits, SQLite migration or native/platform behavior.
82
+
83
+ SDK 1.0.37 does materially change its vendored transport/retry source: HTTP/2 ping verification gains bounded completion and listener cleanup, HTTP/1 and HTTP/2 response iteration awaits the iterator directly while preserving already-rejected sentinel errors, and local retry classification adds `UND_ERR_SOCKET` plus failure-kind budget selection. SDK-declared external ranges are unchanged; neither those ranges nor a new vendored patch identity proves the resolved host graph or service recovery. Requalify installed-source transport and first-send contracts against the actual package; none of this establishes #247's original auth cause, #339's signal sender, #173's rtk behavior, #233's Windows importer graph or #206's readiness.
54
84
 
55
- The test covers bootstrap/incremental prompts, empty-vs-absent request tools, native replay/drain, cloud fresh/bootstrap selection, and actual host prompt serialization for context files and skills. `test/native-cursor-flow.test.mjs` additionally exercises the compiled extension through the real loader and registered Cursor provider, substituting only the external SDK transport/storage. It verifies actual bridge tool execution, pending-work idle/abort/shutdown/deadline ownership, replay without file access, persisted usage/lineage, tree, compaction, queued steering, abort and reload/disposal. `test/native-cursor-prompt.test.mjs` owns genuine native input, hidden notices, required-budget input, current attachments, custom-only continuation, equivalent conversion, payload/order-changing rejection, and GitHub comment soft guidance across bootstrap/incremental/custom-only sends. Both owners use `test/helpers/native-cursor-harness.mjs` and run in the explicit `check:compat` list and manual official-host compatibility route; the native-platform Vitest job is not execution evidence for these Node test owners. Explicit `tools` is now an allowlist, not merely an initial active set; use `defaultTools` for a native fixture that allows replay wrappers to activate. `test/native-provider.test.mjs` additionally verifies canonical `SessionManager.appendContextEdit()` plus `refreshContext()`, unchanged raw history, and conversation filtering that preserves system instructions, the current tool declaration, and explicit tool removal (the focused native slice from #271). It is offline contract evidence. Add live proof only for changed behavior that still needs real-service evidence under the cost policy above; do not rerun unchanged live lanes or infer full live coverage from offline tests.
85
+ The 60-second MCP protocol default and lack of a public timeout option remain. SDK 1.0.37's internal `setToolCallRequestOptions`, `setToolCallOutputSchemaPassthrough` and `setListingDeadline` each occur only as definitions in the inspected ESM JavaScript, with no invocation found. They are not supported public setters or proof of readiness; preserve the extension's verified timeout compatibility path rather than adding private patches. The new public `computerUse` output variant uses the existing bounded generic replay fallback, not dedicated action/screenshot rendering or a new local execution option; see the [canonical replay matrix](./cursor-native-tool-replay.md#sdk-tooltype-replay-matrix).
56
86
 
57
87
  ## Maintainer-only offline proof retention
58
88
 
@@ -85,7 +115,7 @@ offline node_modules/vitest/vitest.mjs run test/cursor-sdk-output-filter.test.ts
85
115
  test/cursor-provider-stream-usage.test.ts test/cursor-provider-replay-live-run.test.ts \
86
116
  test/cursor-session-agent-local-resume.test.ts
87
117
  offline --test test/native-provider.test.mjs test/native-cursor-flow.test.mjs \
88
- test/native-cursor-prompt.test.mjs
118
+ test/native-cursor-prompt.test.mjs test/native-cursor-ownership.test.mjs
89
119
  ```
90
120
 
91
121
  These artifacts may include prompts, tool arguments/results, local paths and private session facts. Never print, commit or publish them; never supply real keys to this offline workflow. Inspect persisted joins and bytes privately, then retain or remove only your owned proof directory. Offline artifacts establish the tested local SDK/native contracts, **not** all-host/backend qualification or paid-service acceptance.
@@ -256,7 +286,7 @@ Pass criteria:
256
286
 
257
287
  Run offline checks first, then reuse retained proof whose tested inputs are unchanged. If changed behavior still needs real-service proof, choose one existing meaningful check on a representative environment. Do not run every command below as a paid ladder; docs/metadata-only changes need zero paid runs.
258
288
 
259
- TypeScript 7 owns builds and type checks. `@typescript/typescript6` is dev-only for AST architecture and installed-SDK contract tests because TypeScript 7 has no stable compiler API. Vitest 5 defaults `clearMocks` to `true`.
289
+ TypeScript 7 owns builds and type checks. Build and typecheck commands invoke `node_modules/typescript/bin/tsc` explicitly to avoid npm's shared `tsc` shim selecting a different compiler. `@typescript/typescript6` is dev-only for AST architecture and installed-SDK contract tests because TypeScript 7 has no stable compiler API. Vitest 5 defaults `clearMocks` to `true`.
260
290
 
261
291
  ```bash
262
292
  npm test
@@ -325,7 +355,7 @@ The script writes timestamped artifacts under `--out` (default `/tmp/pi-cursor-s
325
355
 
326
356
  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.
327
357
 
328
- 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 validation targets Node 24+, exact `@cursor/sdk@1.0.36`, official Pi 0.87.1/latest, and current `fitchmultz/pi` main.
358
+ 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 validation targets Node 24+, exact `@cursor/sdk@1.0.37`, official Pi 0.87.1/latest, and current `fitchmultz/pi` main.
329
359
 
330
360
  ## Pi provider SDK event capture
331
361
 
@@ -416,7 +446,7 @@ Stderr output for these records requires `PI_CURSOR_SDK_EVENT_DEBUG_STDERR=1`. T
416
446
 
417
447
  **Symptom:** Assistant output lists tool invocations (for example `Tool call`, `Cursor activity`, `call cursor-replay-…`, `toolName`, `browser_navigate`) instead of pi tool execution cards/results.
418
448
 
419
- **What the screenshot in [#40](https://github.com/fitchmultz/pi-cursor-sdk/issues/40) shows:** Plain assistant text that mirrors pi's **prompt transcript format** for replay tool calls (`Tool call (Cursor activity, call cursor-replay-…): …` from `src/context.ts`) rather than a rendered pi `toolCall` card. That pattern usually means the Cursor model **narrated** a tool call as text; it is not proof that pi failed to emit `toolcall_start` / `toolUse`.
449
+ **What the screenshot in [#40](https://github.com/fitchmultz/pi-cursor-sdk/issues/40) shows:** Plain assistant text that mirrors the old prompt transcript format (`Tool call (Cursor activity, call cursor-replay-…): …`) rather than a rendered pi `toolCall` card. That pattern is narration, not proof that pi failed to emit `toolcall_start` / `toolUse`. Structured tool history now uses `Historical tool request/result/error` in `src/context.ts`; an unmatched request remains a request. Regression coverage retains arguments, IDs, errors, raw messages/fingerprints, prose/code fences, prompt paths and coherent estimates. The change removes an imitable template, not prior assistant text or the underlying model behavior in #257.
420
450
 
421
451
  **Do not close #40 as duplicate of #55 without session JSONL.** #55 surfaces scrubbed SDK run failures and abort causes in the TUI. #40 can occur with no error toast when the model prints tool metadata as assistant text, when replay is display-only but the user expected real execution, when stale native replay routing or plan-strip resync gaps produce `Tool * not found` errors (see **#52**), or when started SDK tools were discarded at run end (see **#52** maintainer debug and [Discarded incomplete SDK tool calls](#discarded-incomplete-sdk-tool-calls) above). A hard **process exit** from uncaught `ConnectError` / `ETIMEDOUT` is **#43**, not #40 text echo.
422
452
 
@@ -490,7 +520,7 @@ Then inspect the failing assistant turn in `$SMOKE_DIR/session/*.jsonl`:
490
520
  3. **`content` is only `type: "text"` and text contains `Tool call (` / `cursor-replay-` / serialized arg keys** — model text echo of prompt transcript format; not #55, not #52 stale routing. Compare with `buildCursorPrompt()` output in the prior turn.
491
521
  4. **No `toolCall` blocks, no error toast, user expected real execution** — check whether names are replay-only (`cursor-replay-*`) or Cursor-native MCP; replay never re-runs work ([replay doc](./cursor-native-tool-replay.md)).
492
522
  5. **`stopReason: "error"` or scrubbed `errorMessage`** — classify under **#55**; check whether incomplete started tools were discarded (`discardIncompleteStartedToolCalls()`). Discarded starts with no completion and no model text echo: see `coordinator-events.jsonl` phase `discarded-incomplete-started-tool-call` ([Discarded incomplete SDK tool calls](#discarded-incomplete-sdk-tool-calls) above); route broader stale/inactive replay gaps to **#52**.
493
- 6. **Bridge expected (`pi__*` in Cursor MCP)** — inspect stderr `[pi-cursor-sdk:bridge]` JSONL with `PI_CURSOR_PI_TOOL_BRIDGE_DEBUG=1` for pending/unresolved bridge requests.
523
+ 6. **Bridge expected (`pi__*` in Cursor MCP)** — inspect stderr `[pi-cursor-sdk:bridge]` JSONL with `PI_CURSOR_PI_TOOL_BRIDGE_DEBUG=1`. Exposure records are not connection proof; `mcp_initialized` is a validated server notification receipt, `mcp_tools_list` is catalog-handler output, and request records establish actual call handling. None proves model consumption or ongoing health. See [bridge diagnostic boundaries](./cursor-tool-surfaces.md#diagnosing-advertised-but-unused-bridge-tools).
494
524
 
495
525
  Quick structural scan (no secrets):
496
526