pi-cursor-sdk 0.1.55 → 0.1.57
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +60 -2
- package/README.md +100 -28
- package/docs/cursor-live-smoke-checklist.md +7 -7
- package/docs/cursor-model-ux-spec.md +48 -41
- package/docs/cursor-native-tool-replay.md +4 -4
- package/docs/cursor-native-tool-visual-audit.md +1 -1
- package/docs/cursor-testing-lessons.md +20 -7
- package/docs/cursor-tool-surfaces.md +13 -2
- package/docs/platform-smoke-implementation.md +220 -0
- package/docs/platform-smoke.md +183 -247
- package/package.json +39 -7
- package/platform-smoke.config.mjs +5 -1
- package/scripts/cloud-runtime-smoke.d.mts +3 -0
- package/scripts/cloud-runtime-smoke.mjs +502 -0
- package/scripts/debug-provider-events.mjs +7 -2
- package/scripts/isolated-cursor-smoke.sh +4 -6
- package/scripts/lib/cursor-child-process.d.mts +1 -0
- package/scripts/lib/cursor-child-process.mjs +137 -7
- package/scripts/lib/local-resume-smoke-harness.mjs +543 -0
- package/scripts/local-resume-cleanup-smoke.mjs +108 -0
- package/scripts/local-resume-smoke.d.mts +1 -0
- package/scripts/local-resume-smoke.mjs +642 -0
- package/scripts/platform-smoke/artifact-anchored-extract.d.mts +10 -0
- package/scripts/platform-smoke/artifact-anchored-extract.mjs +111 -0
- package/scripts/platform-smoke/artifact-bundle-chunk.mjs +57 -0
- package/scripts/platform-smoke/artifact-bundle-contract.mjs +34 -0
- package/scripts/platform-smoke/artifact-fs-safety.mjs +311 -0
- package/scripts/platform-smoke/artifact-openat-extract.c +335 -0
- package/scripts/platform-smoke/artifact-secrets.mjs +155 -0
- package/scripts/platform-smoke/artifacts.mjs +293 -65
- package/scripts/platform-smoke/card-detect.mjs +16 -4
- package/scripts/platform-smoke/crabbox-runner.mjs +45 -3
- package/scripts/platform-smoke/doctor.mjs +20 -10
- package/scripts/platform-smoke/live-suite-runner.mjs +18 -57
- package/scripts/platform-smoke/local-resume-runner.mjs +252 -0
- package/scripts/platform-smoke/local-resume-suites.d.mts +15 -0
- package/scripts/platform-smoke/local-resume-suites.mjs +104 -0
- package/scripts/platform-smoke/scenarios.mjs +16 -2
- package/scripts/platform-smoke/target-runtime.mjs +206 -0
- package/scripts/platform-smoke/targets.mjs +33 -141
- package/scripts/platform-smoke/visual-evidence.mjs +6 -7
- package/scripts/platform-smoke/wrapped-line-match.mjs +9 -0
- package/scripts/platform-smoke.mjs +40 -27
- package/scripts/refresh-cursor-model-snapshots.mjs +18 -6
- package/scripts/steering-rpc-smoke.mjs +12 -2
- package/scripts/tmux-live-smoke.sh +3 -5
- package/shared/cursor-cloud-lifecycle-constants.d.mts +3 -0
- package/shared/cursor-cloud-lifecycle-constants.mjs +7 -0
- package/shared/cursor-sensitive-text.mjs +7 -1
- package/src/context.ts +5 -2
- package/src/cursor-agents-context-registration.ts +7 -0
- package/src/cursor-agents-context.ts +3 -1
- package/src/cursor-api-key.ts +15 -1
- package/src/cursor-bridge-contract.ts +3 -0
- package/src/cursor-cloud-lifecycle.ts +733 -0
- package/src/cursor-cloud-options.ts +206 -0
- package/src/cursor-cloud-reporting.ts +246 -0
- package/src/cursor-config.ts +659 -0
- package/src/cursor-display-only-trace.ts +14 -0
- package/src/cursor-display-text.ts +8 -2
- package/src/cursor-durable-fs.ts +49 -0
- package/src/cursor-fallback-models.generated.ts +2045 -485
- package/src/cursor-live-run-accounting.ts +7 -1
- package/src/cursor-live-run-coordinator.ts +1 -0
- package/src/cursor-pi-tool-bridge-run.ts +14 -4
- package/src/cursor-provider-errors.ts +31 -5
- package/src/cursor-provider-live-run-drain.ts +5 -0
- package/src/cursor-provider-run-finalizer.ts +37 -28
- package/src/cursor-provider-run-outcome.ts +7 -2
- package/src/cursor-provider-turn-finalize.ts +77 -7
- package/src/cursor-provider-turn-prepare.ts +228 -12
- package/src/cursor-provider-turn-runner.ts +42 -13
- package/src/cursor-provider-turn-send.ts +59 -16
- package/src/cursor-provider-turn-types.ts +44 -10
- package/src/cursor-runtime-state.ts +478 -0
- package/src/cursor-sdk-event-debug.ts +46 -6
- package/src/cursor-sdk-process-error-guard.ts +101 -30
- package/src/cursor-session-agent-cleanup.ts +328 -0
- package/src/cursor-session-agent-resume.ts +439 -0
- package/src/cursor-session-agent.ts +109 -13
- package/src/cursor-session-scope.ts +35 -2
- package/src/cursor-session-send-policy.ts +1 -1
- package/src/cursor-skill-tool.ts +30 -11
- package/src/cursor-state.ts +112 -69
- package/src/cursor-tool-manifest.ts +1 -1
- package/src/cursor-usage-accounting.ts +14 -4
- package/src/index.ts +11 -2
- package/src/model-discovery.ts +10 -56
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#!/
|
|
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
|
-
|
|
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:
|
|
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.
|
|
29
|
-
if (
|
|
30
|
-
|
|
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
|
-
|
|
34
|
-
}
|
|
35
|
-
|
|
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
|
|