pi-cursor-sdk 0.1.56 → 0.1.57

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/CHANGELOG.md +45 -1
  2. package/README.md +100 -28
  3. package/docs/cursor-live-smoke-checklist.md +7 -7
  4. package/docs/cursor-model-ux-spec.md +45 -38
  5. package/docs/cursor-native-tool-replay.md +2 -2
  6. package/docs/cursor-native-tool-visual-audit.md +1 -1
  7. package/docs/cursor-testing-lessons.md +20 -7
  8. package/docs/cursor-tool-surfaces.md +13 -2
  9. package/docs/platform-smoke-implementation.md +220 -0
  10. package/docs/platform-smoke.md +183 -247
  11. package/package.json +38 -6
  12. package/platform-smoke.config.mjs +5 -1
  13. package/scripts/cloud-runtime-smoke.d.mts +3 -0
  14. package/scripts/cloud-runtime-smoke.mjs +502 -0
  15. package/scripts/debug-provider-events.mjs +7 -2
  16. package/scripts/isolated-cursor-smoke.sh +4 -6
  17. package/scripts/lib/cursor-child-process.d.mts +1 -0
  18. package/scripts/lib/cursor-child-process.mjs +137 -7
  19. package/scripts/lib/local-resume-smoke-harness.mjs +543 -0
  20. package/scripts/local-resume-cleanup-smoke.mjs +108 -0
  21. package/scripts/local-resume-smoke.d.mts +1 -0
  22. package/scripts/local-resume-smoke.mjs +642 -0
  23. package/scripts/platform-smoke/artifact-anchored-extract.d.mts +10 -0
  24. package/scripts/platform-smoke/artifact-anchored-extract.mjs +111 -0
  25. package/scripts/platform-smoke/artifact-bundle-chunk.mjs +57 -0
  26. package/scripts/platform-smoke/artifact-bundle-contract.mjs +34 -0
  27. package/scripts/platform-smoke/artifact-fs-safety.mjs +311 -0
  28. package/scripts/platform-smoke/artifact-openat-extract.c +335 -0
  29. package/scripts/platform-smoke/artifact-secrets.mjs +155 -0
  30. package/scripts/platform-smoke/artifacts.mjs +293 -65
  31. package/scripts/platform-smoke/card-detect.mjs +16 -4
  32. package/scripts/platform-smoke/crabbox-runner.mjs +45 -3
  33. package/scripts/platform-smoke/doctor.mjs +20 -10
  34. package/scripts/platform-smoke/live-suite-runner.mjs +18 -57
  35. package/scripts/platform-smoke/local-resume-runner.mjs +252 -0
  36. package/scripts/platform-smoke/local-resume-suites.d.mts +15 -0
  37. package/scripts/platform-smoke/local-resume-suites.mjs +104 -0
  38. package/scripts/platform-smoke/scenarios.mjs +16 -2
  39. package/scripts/platform-smoke/target-runtime.mjs +206 -0
  40. package/scripts/platform-smoke/targets.mjs +33 -141
  41. package/scripts/platform-smoke/visual-evidence.mjs +6 -7
  42. package/scripts/platform-smoke/wrapped-line-match.mjs +9 -0
  43. package/scripts/platform-smoke.mjs +40 -27
  44. package/scripts/refresh-cursor-model-snapshots.mjs +18 -6
  45. package/scripts/steering-rpc-smoke.mjs +12 -2
  46. package/scripts/tmux-live-smoke.sh +3 -5
  47. package/shared/cursor-cloud-lifecycle-constants.d.mts +3 -0
  48. package/shared/cursor-cloud-lifecycle-constants.mjs +7 -0
  49. package/shared/cursor-sensitive-text.mjs +7 -1
  50. package/src/context.ts +5 -2
  51. package/src/cursor-agents-context-registration.ts +7 -0
  52. package/src/cursor-agents-context.ts +3 -1
  53. package/src/cursor-api-key.ts +15 -1
  54. package/src/cursor-bridge-contract.ts +3 -0
  55. package/src/cursor-cloud-lifecycle.ts +733 -0
  56. package/src/cursor-cloud-options.ts +206 -0
  57. package/src/cursor-cloud-reporting.ts +246 -0
  58. package/src/cursor-config.ts +659 -0
  59. package/src/cursor-display-only-trace.ts +14 -0
  60. package/src/cursor-display-text.ts +8 -2
  61. package/src/cursor-durable-fs.ts +49 -0
  62. package/src/cursor-fallback-models.generated.ts +2045 -485
  63. package/src/cursor-live-run-accounting.ts +7 -1
  64. package/src/cursor-live-run-coordinator.ts +1 -0
  65. package/src/cursor-pi-tool-bridge-run.ts +14 -4
  66. package/src/cursor-provider-live-run-drain.ts +5 -0
  67. package/src/cursor-provider-run-finalizer.ts +36 -28
  68. package/src/cursor-provider-turn-finalize.ts +72 -6
  69. package/src/cursor-provider-turn-prepare.ts +228 -12
  70. package/src/cursor-provider-turn-runner.ts +41 -13
  71. package/src/cursor-provider-turn-send.ts +59 -16
  72. package/src/cursor-provider-turn-types.ts +44 -10
  73. package/src/cursor-runtime-state.ts +478 -0
  74. package/src/cursor-sdk-event-debug.ts +46 -6
  75. package/src/cursor-sdk-process-error-guard.ts +101 -30
  76. package/src/cursor-session-agent-cleanup.ts +328 -0
  77. package/src/cursor-session-agent-resume.ts +439 -0
  78. package/src/cursor-session-agent.ts +109 -13
  79. package/src/cursor-session-scope.ts +35 -2
  80. package/src/cursor-session-send-policy.ts +1 -1
  81. package/src/cursor-skill-tool.ts +30 -11
  82. package/src/cursor-state.ts +112 -69
  83. package/src/cursor-usage-accounting.ts +14 -4
  84. package/src/index.ts +11 -2
  85. package/src/model-discovery.ts +10 -56
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env bash
1
+ #!/bin/bash
2
2
  # Isolated /tmp install + fail-fast live smoke for pi-cursor-sdk native replay.
3
3
  #
4
4
  # Validates packed extension load, plan-strip resync, and absence of "Tool * not found".
@@ -39,8 +39,7 @@ PI_NONE_ENV=()
39
39
  SELF_TEST_TEMP_DIR=""
40
40
 
41
41
  print_help() {
42
- cat <<EOF
43
- Isolated /tmp install smoke for pi-cursor-sdk (native replay + plan-strip resync).
42
+ printf '%s\n' 'Isolated /tmp install smoke for pi-cursor-sdk (native replay + plan-strip resync).
44
43
 
45
44
  Usage:
46
45
  ./scripts/isolated-cursor-smoke.sh
@@ -50,7 +49,7 @@ Usage:
50
49
  Environment:
51
50
  REPO Repo under test (default: script parent directory).
52
51
  ISOLATED Artifact root (default: /tmp/pi-cursor-sdk-isolated-<timestamp>).
53
- REAL_HOME Source for auth.json (default: \$HOME).
52
+ REAL_HOME Source for auth.json (default: $HOME).
54
53
  AUTH_JSON Path to pi auth.json to seed isolated HOME (default: ~/.pi/agent/auth.json).
55
54
  PI_LIVE_TIMEOUT Per live pi check timeout in seconds (default: 45).
56
55
  PI_BIN Optional pi command/path to resolve from the parent PATH (default: pi).
@@ -71,8 +70,7 @@ Options:
71
70
 
72
71
  Exit codes:
73
72
  0 all requested checks passed
74
- 1 prerequisite, unit, pack, live smoke, or JSONL replay validation failure
75
- EOF
73
+ 1 prerequisite, unit, pack, live smoke, or JSONL replay validation failure'
76
74
  }
77
75
 
78
76
  log() { smoke_log "$@"; }
@@ -1,6 +1,7 @@
1
1
  import type { ChildProcess } from "node:child_process";
2
2
 
3
3
  export declare const DEFAULT_CHILD_SHUTDOWN_GRACE_MS: number;
4
+ export declare const CHILD_PROCESS_TREE_SPAWN_OPTIONS: Readonly<{ detached: boolean }>;
4
5
  export declare function waitForChildClose(child: ChildProcess): Promise<number>;
5
6
  export declare function signalChild(child: ChildProcess, signal: NodeJS.Signals): void;
6
7
  export declare function terminateChild(
@@ -1,4 +1,9 @@
1
+ import { execFile } from "node:child_process";
2
+
1
3
  export const DEFAULT_CHILD_SHUTDOWN_GRACE_MS = 2_000;
4
+ export const CHILD_PROCESS_TREE_SPAWN_OPTIONS = Object.freeze({
5
+ detached: process.platform !== "win32",
6
+ });
2
7
 
3
8
  export function waitForChildClose(child) {
4
9
  if (child.exitCode !== null || child.signalCode !== null) return Promise.resolve(child.exitCode ?? 1);
@@ -24,15 +29,140 @@ export function signalChild(child, signal) {
24
29
  }
25
30
  }
26
31
 
32
+ function runHiddenWindowsCommand(command, args, timeoutMs) {
33
+ return new Promise((resolve) => {
34
+ let settled = false;
35
+ let timer;
36
+ const finish = (succeeded) => {
37
+ if (settled) return;
38
+ settled = true;
39
+ clearTimeout(timer);
40
+ resolve(succeeded);
41
+ };
42
+ const child = execFile(
43
+ command,
44
+ args,
45
+ { maxBuffer: 64 * 1024, timeout: timeoutMs, windowsHide: true },
46
+ (error) => finish(error === null),
47
+ );
48
+ timer = setTimeout(() => {
49
+ child.kill("SIGKILL");
50
+ finish(false);
51
+ }, timeoutMs);
52
+ });
53
+ }
54
+
55
+ async function terminateWindowsProcessTree(pid, timeoutMs) {
56
+ if (await runHiddenWindowsCommand("taskkill", ["/PID", String(pid), "/T", "/F"], timeoutMs)) return true;
57
+
58
+ const script = `
59
+ $ErrorActionPreference = "Stop"
60
+ $rootPid = [uint32]${pid}
61
+ $deadline = [DateTime]::UtcNow.AddMilliseconds(${timeoutMs})
62
+ try {
63
+ $processes = @(Get-CimInstance -ClassName Win32_Process -Property ProcessId,ParentProcessId)
64
+ $queue = [Collections.Generic.Queue[uint32]]::new()
65
+ $descendants = [Collections.Generic.List[uint32]]::new()
66
+ $seen = [Collections.Generic.HashSet[uint32]]::new()
67
+ $queue.Enqueue($rootPid)
68
+ while ($queue.Count -gt 0) {
69
+ $parentPid = $queue.Dequeue()
70
+ foreach ($process in $processes) {
71
+ $childPid = [uint32]$process.ProcessId
72
+ if ([uint32]$process.ParentProcessId -eq $parentPid -and $seen.Add($childPid)) {
73
+ $descendants.Add($childPid)
74
+ $queue.Enqueue($childPid)
75
+ }
76
+ }
77
+ }
78
+ for ($index = $descendants.Count - 1; $index -ge 0; $index--) {
79
+ Stop-Process -Id $descendants[$index] -Force -ErrorAction SilentlyContinue
80
+ }
81
+ Stop-Process -Id $rootPid -Force -ErrorAction SilentlyContinue
82
+ $targets = @($rootPid) + @($descendants)
83
+ do {
84
+ $alive = @(Get-Process -Id $targets -ErrorAction SilentlyContinue)
85
+ if ($alive.Count -eq 0) { exit 0 }
86
+ Start-Sleep -Milliseconds 25
87
+ } while ([DateTime]::UtcNow -lt $deadline)
88
+ } catch {}
89
+ exit 1
90
+ `;
91
+ return runHiddenWindowsCommand(
92
+ "powershell.exe",
93
+ ["-NoLogo", "-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", "-Command", script],
94
+ timeoutMs,
95
+ );
96
+ }
97
+
98
+ function waitForChildCloseWithin(child, timeoutMs) {
99
+ if (child.exitCode !== null || child.signalCode !== null) return Promise.resolve(true);
100
+ return new Promise((resolve) => {
101
+ const finish = (closed) => {
102
+ clearTimeout(timer);
103
+ child.off("close", onClose);
104
+ resolve(closed);
105
+ };
106
+ const onClose = () => finish(true);
107
+ const timer = setTimeout(() => finish(false), timeoutMs);
108
+ child.once("close", onClose);
109
+ });
110
+ }
111
+
112
+ function posixProcessGroupExists(pid) {
113
+ try {
114
+ process.kill(-pid, 0);
115
+ return true;
116
+ } catch (error) {
117
+ return error?.code !== "ESRCH";
118
+ }
119
+ }
120
+
121
+ async function waitForPosixProcessGroupExit(pid, timeoutMs) {
122
+ const deadline = Date.now() + timeoutMs;
123
+ while (posixProcessGroupExists(pid) && Date.now() < deadline) {
124
+ await new Promise((resolve) => setTimeout(resolve, Math.min(25, deadline - Date.now())));
125
+ }
126
+ return !posixProcessGroupExists(pid);
127
+ }
128
+
27
129
  export async function terminateChild(child, { graceMs = DEFAULT_CHILD_SHUTDOWN_GRACE_MS } = {}) {
28
- child.stdin?.destroy?.();
29
- if (child.exitCode !== null || child.signalCode !== null) return;
30
- signalChild(child, "SIGTERM");
31
- const killTimer = setTimeout(() => signalChild(child, "SIGKILL"), graceMs);
130
+ const pid = child.pid;
131
+ if (!pid) return;
132
+ const timeoutMs = Number.isFinite(graceMs) ? Math.max(1, Math.trunc(graceMs)) : DEFAULT_CHILD_SHUTDOWN_GRACE_MS;
32
133
  try {
33
- await waitForChildClose(child);
34
- } finally {
35
- clearTimeout(killTimer);
134
+ child.stdin?.end?.();
135
+ } catch {
136
+ child.stdin?.destroy?.();
137
+ }
138
+ await waitForChildCloseWithin(child, timeoutMs);
139
+ if (process.platform === "win32") {
140
+ // CIM enumeration can exceed a short graceful-shutdown window on a busy Windows VM;
141
+ // keep the process-tree discovery bounded but give orphan lookup a stable floor.
142
+ const treeTimeoutMs = Math.max(timeoutMs, 15_000);
143
+ if (!(await terminateWindowsProcessTree(pid, treeTimeoutMs))) {
144
+ signalChild(child, "SIGTERM");
145
+ if (!(await waitForChildCloseWithin(child, timeoutMs))) {
146
+ signalChild(child, "SIGKILL");
147
+ await waitForChildCloseWithin(child, timeoutMs);
148
+ }
149
+ throw new Error(`failed to verify termination of Windows process tree rooted at ${pid}`);
150
+ }
151
+ if (!(await waitForChildCloseWithin(child, timeoutMs))) {
152
+ throw new Error(`Windows process tree exited but child ${pid} did not close`);
153
+ }
154
+ return;
155
+ }
156
+
157
+ if (posixProcessGroupExists(pid)) signalChild(child, "SIGTERM");
158
+ let groupExited = await waitForPosixProcessGroupExit(pid, timeoutMs);
159
+ if (!groupExited) {
160
+ signalChild(child, "SIGKILL");
161
+ groupExited = await waitForPosixProcessGroupExit(pid, timeoutMs);
162
+ }
163
+ const childClosed = await waitForChildCloseWithin(child, timeoutMs);
164
+ if (!groupExited || !childClosed) {
165
+ throw new Error(`failed to verify termination of POSIX process tree rooted at ${pid}`);
36
166
  }
37
167
  }
38
168