@emmaneugene/pi-cursor-sdk 0.3.7 → 0.4.1
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 +22 -0
- package/README.md +99 -126
- package/dist/cursor-agents-context-registration.js +1 -3
- package/dist/cursor-agents-context.js +2 -2
- package/dist/cursor-config.js +2 -290
- package/dist/cursor-extension-factory-guard.js +31 -0
- package/dist/cursor-pi-tool-bridge-server.js +3 -0
- package/dist/cursor-pi-tool-bridge.js +5 -0
- package/dist/cursor-provider-errors.js +3 -58
- package/dist/cursor-provider-live-run-drain.js +0 -3
- package/dist/cursor-provider-run-finalizer.js +4 -7
- package/dist/cursor-provider-run-outcome.js +1 -1
- package/dist/cursor-provider-turn-finalize.js +3 -55
- package/dist/cursor-provider-turn-prepare.js +8 -144
- package/dist/cursor-provider-turn-runner.js +9 -23
- package/dist/cursor-provider-turn-send.js +8 -35
- package/dist/cursor-runtime-state.js +6 -268
- package/dist/cursor-sdk-billed-usage.js +3 -18
- package/dist/cursor-sdk-platform-package.js +88 -0
- package/dist/cursor-session-agent.js +18 -14
- package/dist/cursor-skill-tool.js +12 -22
- package/dist/cursor-state.js +5 -8
- package/dist/cursor-usage-accounting.js +1 -1
- package/dist/index.js +65 -49
- package/docs/cursor-dogfood-checklist.md +4 -4
- package/docs/cursor-live-smoke-checklist.md +24 -24
- package/docs/cursor-model-ux-spec.md +27 -30
- package/docs/cursor-native-tool-replay.md +4 -4
- package/docs/cursor-native-tool-visual-audit.md +10 -10
- package/docs/cursor-testing-lessons.md +38 -16
- package/docs/cursor-tool-surfaces.md +2 -4
- package/docs/platform-smoke-implementation.md +5 -5
- package/docs/platform-smoke.md +28 -73
- package/node_modules/cross-spawn/node_modules/which/CHANGELOG.md +166 -0
- package/package.json +3 -17
- package/platform-smoke.config.mjs +2 -1
- package/scripts/lib/cursor-visual-render.mjs +12 -2
- package/scripts/lib/local-resume-smoke-harness.mjs +0 -18
- package/scripts/platform-smoke/card-detect.mjs +1 -1
- package/scripts/platform-smoke/local-resume-runner.mjs +1 -1
- package/scripts/platform-smoke/scenarios.mjs +1 -1
- package/scripts/platform-smoke/targets.mjs +14 -5
- package/scripts/visual-tui-smoke-self-test.mjs +9 -33
- package/scripts/visual-tui-smoke.mjs +54 -11
- package/src/cursor-agents-context-registration.ts +0 -5
- package/src/cursor-agents-context.ts +1 -3
- package/src/cursor-config.ts +8 -379
- package/src/cursor-extension-factory-guard.ts +57 -0
- package/src/cursor-pi-tool-bridge-server.ts +4 -0
- package/src/cursor-pi-tool-bridge.ts +5 -0
- package/src/cursor-provider-errors.ts +2 -62
- package/src/cursor-provider-live-run-drain.ts +0 -3
- package/src/cursor-provider-run-finalizer.ts +4 -12
- package/src/cursor-provider-run-outcome.ts +0 -3
- package/src/cursor-provider-turn-finalize.ts +3 -61
- package/src/cursor-provider-turn-prepare.ts +8 -165
- package/src/cursor-provider-turn-runner.ts +15 -31
- package/src/cursor-provider-turn-send.ts +7 -38
- package/src/cursor-provider-turn-types.ts +9 -30
- package/src/cursor-runtime-state.ts +6 -345
- package/src/cursor-sdk-billed-usage.ts +3 -24
- package/src/cursor-sdk-platform-package.ts +106 -0
- package/src/cursor-session-agent.ts +16 -12
- package/src/cursor-skill-tool.ts +10 -26
- package/src/cursor-state.ts +5 -10
- package/src/cursor-usage-accounting.ts +1 -3
- package/src/index.ts +70 -49
- package/dist/cursor-cloud-lifecycle.js +0 -650
- package/dist/cursor-cloud-local-state.js +0 -460
- package/dist/cursor-cloud-options.js +0 -145
- package/dist/cursor-cloud-reporting.js +0 -222
- package/dist/cursor-ripgrep-path.js +0 -27
- package/scripts/cloud-runtime-smoke.d.mts +0 -42
- package/scripts/cloud-runtime-smoke.mjs +0 -623
- package/scripts/lib/cloud-smoke-artifacts.d.mts +0 -16
- package/scripts/lib/cloud-smoke-artifacts.mjs +0 -94
- package/scripts/lib/cloud-smoke-cleanup-evidence.d.mts +0 -209
- package/scripts/lib/cloud-smoke-cleanup-evidence.mjs +0 -585
- package/scripts/lib/cloud-smoke-github.d.mts +0 -78
- package/scripts/lib/cloud-smoke-github.mjs +0 -331
- package/scripts/lib/cloud-smoke-pi-runner.d.mts +0 -42
- package/scripts/lib/cloud-smoke-pi-runner.mjs +0 -214
- package/scripts/lib/cloud-smoke-shutdown.d.mts +0 -67
- package/scripts/lib/cloud-smoke-shutdown.mjs +0 -133
- package/shared/cursor-cloud-lifecycle-constants.d.mts +0 -3
- package/shared/cursor-cloud-lifecycle-constants.mjs +0 -7
- package/src/cursor-cloud-lifecycle.ts +0 -733
- package/src/cursor-cloud-local-state.ts +0 -536
- package/src/cursor-cloud-options.ts +0 -182
- package/src/cursor-cloud-reporting.ts +0 -267
- package/src/cursor-ripgrep-path.ts +0 -32
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
# Changes
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
## 2.0.2
|
|
5
|
+
|
|
6
|
+
* Rename bin to `node-which`
|
|
7
|
+
|
|
8
|
+
## 2.0.1
|
|
9
|
+
|
|
10
|
+
* generate changelog and publish on version bump
|
|
11
|
+
* enforce 100% test coverage
|
|
12
|
+
* Promise interface
|
|
13
|
+
|
|
14
|
+
## 2.0.0
|
|
15
|
+
|
|
16
|
+
* Parallel tests, modern JavaScript, and drop support for node < 8
|
|
17
|
+
|
|
18
|
+
## 1.3.1
|
|
19
|
+
|
|
20
|
+
* update deps
|
|
21
|
+
* update travis
|
|
22
|
+
|
|
23
|
+
## v1.3.0
|
|
24
|
+
|
|
25
|
+
* Add nothrow option to which.sync
|
|
26
|
+
* update tap
|
|
27
|
+
|
|
28
|
+
## v1.2.14
|
|
29
|
+
|
|
30
|
+
* appveyor: drop node 5 and 0.x
|
|
31
|
+
* travis-ci: add node 6, drop 0.x
|
|
32
|
+
|
|
33
|
+
## v1.2.13
|
|
34
|
+
|
|
35
|
+
* test: Pass missing option to pass on windows
|
|
36
|
+
* update tap
|
|
37
|
+
* update isexe to 2.0.0
|
|
38
|
+
* neveragain.tech pledge request
|
|
39
|
+
|
|
40
|
+
## v1.2.12
|
|
41
|
+
|
|
42
|
+
* Removed unused require
|
|
43
|
+
|
|
44
|
+
## v1.2.11
|
|
45
|
+
|
|
46
|
+
* Prevent changelog script from being included in package
|
|
47
|
+
|
|
48
|
+
## v1.2.10
|
|
49
|
+
|
|
50
|
+
* Use env.PATH only, not env.Path
|
|
51
|
+
|
|
52
|
+
## v1.2.9
|
|
53
|
+
|
|
54
|
+
* fix for paths starting with ../
|
|
55
|
+
* Remove unused `is-absolute` module
|
|
56
|
+
|
|
57
|
+
## v1.2.8
|
|
58
|
+
|
|
59
|
+
* bullet items in changelog that contain (but don't start with) #
|
|
60
|
+
|
|
61
|
+
## v1.2.7
|
|
62
|
+
|
|
63
|
+
* strip 'update changelog' changelog entries out of changelog
|
|
64
|
+
|
|
65
|
+
## v1.2.6
|
|
66
|
+
|
|
67
|
+
* make the changelog bulleted
|
|
68
|
+
|
|
69
|
+
## v1.2.5
|
|
70
|
+
|
|
71
|
+
* make a changelog, and keep it up to date
|
|
72
|
+
* don't include tests in package
|
|
73
|
+
* Properly handle relative-path executables
|
|
74
|
+
* appveyor
|
|
75
|
+
* Attach error code to Not Found error
|
|
76
|
+
* Make tests pass on Windows
|
|
77
|
+
|
|
78
|
+
## v1.2.4
|
|
79
|
+
|
|
80
|
+
* Fix typo
|
|
81
|
+
|
|
82
|
+
## v1.2.3
|
|
83
|
+
|
|
84
|
+
* update isexe, fix regression in pathExt handling
|
|
85
|
+
|
|
86
|
+
## v1.2.2
|
|
87
|
+
|
|
88
|
+
* update deps, use isexe module, test windows
|
|
89
|
+
|
|
90
|
+
## v1.2.1
|
|
91
|
+
|
|
92
|
+
* Sometimes windows PATH entries are quoted
|
|
93
|
+
* Fixed a bug in the check for group and user mode bits. This bug was introduced during refactoring for supporting strict mode.
|
|
94
|
+
* doc cli
|
|
95
|
+
|
|
96
|
+
## v1.2.0
|
|
97
|
+
|
|
98
|
+
* Add support for opt.all and -as cli flags
|
|
99
|
+
* test the bin
|
|
100
|
+
* update travis
|
|
101
|
+
* Allow checking for multiple programs in bin/which
|
|
102
|
+
* tap 2
|
|
103
|
+
|
|
104
|
+
## v1.1.2
|
|
105
|
+
|
|
106
|
+
* travis
|
|
107
|
+
* Refactored and fixed undefined error on Windows
|
|
108
|
+
* Support strict mode
|
|
109
|
+
|
|
110
|
+
## v1.1.1
|
|
111
|
+
|
|
112
|
+
* test +g exes against secondary groups, if available
|
|
113
|
+
* Use windows exe semantics on cygwin & msys
|
|
114
|
+
* cwd should be first in path on win32, not last
|
|
115
|
+
* Handle lower-case 'env.Path' on Windows
|
|
116
|
+
* Update docs
|
|
117
|
+
* use single-quotes
|
|
118
|
+
|
|
119
|
+
## v1.1.0
|
|
120
|
+
|
|
121
|
+
* Add tests, depend on is-absolute
|
|
122
|
+
|
|
123
|
+
## v1.0.9
|
|
124
|
+
|
|
125
|
+
* which.js: root is allowed to execute files owned by anyone
|
|
126
|
+
|
|
127
|
+
## v1.0.8
|
|
128
|
+
|
|
129
|
+
* don't use graceful-fs
|
|
130
|
+
|
|
131
|
+
## v1.0.7
|
|
132
|
+
|
|
133
|
+
* add license to package.json
|
|
134
|
+
|
|
135
|
+
## v1.0.6
|
|
136
|
+
|
|
137
|
+
* isc license
|
|
138
|
+
|
|
139
|
+
## 1.0.5
|
|
140
|
+
|
|
141
|
+
* Awful typo
|
|
142
|
+
|
|
143
|
+
## 1.0.4
|
|
144
|
+
|
|
145
|
+
* Test for path absoluteness properly
|
|
146
|
+
* win: Allow '' as a pathext if cmd has a . in it
|
|
147
|
+
|
|
148
|
+
## 1.0.3
|
|
149
|
+
|
|
150
|
+
* Remove references to execPath
|
|
151
|
+
* Make `which.sync()` work on Windows by honoring the PATHEXT variable.
|
|
152
|
+
* Make `isExe()` always return true on Windows.
|
|
153
|
+
* MIT
|
|
154
|
+
|
|
155
|
+
## 1.0.2
|
|
156
|
+
|
|
157
|
+
* Only files can be exes
|
|
158
|
+
|
|
159
|
+
## 1.0.1
|
|
160
|
+
|
|
161
|
+
* Respect the PATHEXT env for win32 support
|
|
162
|
+
* should 0755 the bin
|
|
163
|
+
* binary
|
|
164
|
+
* guts
|
|
165
|
+
* package
|
|
166
|
+
* 1st
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@emmaneugene/pi-cursor-sdk",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "pi provider extension backed by @cursor/sdk local
|
|
3
|
+
"version": "0.4.1",
|
|
4
|
+
"description": "pi provider extension backed by @cursor/sdk local agents",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -41,8 +41,6 @@
|
|
|
41
41
|
"scripts/visual-tui-smoke.mjs",
|
|
42
42
|
"scripts/visual-tui-smoke-self-test.mjs",
|
|
43
43
|
"scripts/isolated-cursor-smoke.sh",
|
|
44
|
-
"scripts/cloud-runtime-smoke.mjs",
|
|
45
|
-
"scripts/cloud-runtime-smoke.d.mts",
|
|
46
44
|
"scripts/local-resume-smoke.mjs",
|
|
47
45
|
"scripts/local-resume-cleanup-smoke.mjs",
|
|
48
46
|
"scripts/local-resume-smoke.d.mts",
|
|
@@ -81,16 +79,6 @@
|
|
|
81
79
|
"scripts/platform-smoke/targets.mjs",
|
|
82
80
|
"scripts/platform-smoke/visual-evidence.mjs",
|
|
83
81
|
"scripts/platform-smoke/wrapped-line-match.mjs",
|
|
84
|
-
"scripts/lib/cloud-smoke-cleanup-evidence.mjs",
|
|
85
|
-
"scripts/lib/cloud-smoke-cleanup-evidence.d.mts",
|
|
86
|
-
"scripts/lib/cloud-smoke-artifacts.mjs",
|
|
87
|
-
"scripts/lib/cloud-smoke-artifacts.d.mts",
|
|
88
|
-
"scripts/lib/cloud-smoke-pi-runner.mjs",
|
|
89
|
-
"scripts/lib/cloud-smoke-pi-runner.d.mts",
|
|
90
|
-
"scripts/lib/cloud-smoke-github.mjs",
|
|
91
|
-
"scripts/lib/cloud-smoke-github.d.mts",
|
|
92
|
-
"scripts/lib/cloud-smoke-shutdown.mjs",
|
|
93
|
-
"scripts/lib/cloud-smoke-shutdown.d.mts",
|
|
94
82
|
"scripts/lib/cursor-cli-args.mjs",
|
|
95
83
|
"scripts/lib/cursor-cli-args.d.mts",
|
|
96
84
|
"scripts/lib/cursor-child-process.mjs",
|
|
@@ -140,8 +128,6 @@
|
|
|
140
128
|
"smoke:live": "npm run build && scripts/tmux-live-smoke.sh",
|
|
141
129
|
"smoke:visual": "npm run build && node scripts/visual-tui-smoke.mjs",
|
|
142
130
|
"smoke:isolated": "npm run build && scripts/isolated-cursor-smoke.sh",
|
|
143
|
-
"smoke:cloud": "node scripts/cloud-runtime-smoke.mjs",
|
|
144
|
-
"smoke:cloud:context": "node scripts/cloud-runtime-smoke.mjs --context-matrix",
|
|
145
131
|
"smoke:local-resume": "node scripts/local-resume-smoke.mjs",
|
|
146
132
|
"smoke:local-resume:safety": "node scripts/local-resume-smoke.mjs --safety",
|
|
147
133
|
"smoke:local-resume:tool-surface": "node scripts/local-resume-smoke.mjs --tool-surface",
|
|
@@ -157,7 +143,7 @@
|
|
|
157
143
|
"debug:sdk-events": "node scripts/debug-sdk-events.mjs",
|
|
158
144
|
"debug:provider-events": "node scripts/debug-provider-events.mjs",
|
|
159
145
|
"debug:mcp-coldstart": "node scripts/probe-mcp-coldstart.mjs",
|
|
160
|
-
"check:platform-smoke": "node --check platform-smoke.config.mjs && node --check scripts/
|
|
146
|
+
"check:platform-smoke": "node --check platform-smoke.config.mjs && node --check scripts/local-resume-cleanup-smoke.mjs && node --check scripts/local-resume-smoke.mjs && node --check scripts/lib/local-resume-smoke-harness.mjs && node --check scripts/platform-smoke.mjs && node --check scripts/platform-smoke/artifact-bundle-chunk.mjs && node --check scripts/platform-smoke/artifact-bundle-contract.mjs && node --check scripts/platform-smoke/artifact-fs-safety.mjs && node --check scripts/platform-smoke/artifact-anchored-extract.mjs && node --check scripts/platform-smoke/artifact-secrets.mjs && node --check scripts/platform-smoke/assertions.mjs && node --check scripts/platform-smoke/artifacts.mjs && node --check scripts/platform-smoke/card-detect.mjs && node --check scripts/platform-smoke/crabbox-runner.mjs && node --check scripts/platform-smoke/doctor.mjs && node --check scripts/platform-smoke/jsonl-text.mjs && node --check scripts/platform-smoke/live-suite-runner.mjs && node --check scripts/platform-smoke/local-resume-runner.mjs && node --check scripts/platform-smoke/pty-capture.mjs && node --check scripts/platform-smoke/render-ansi.mjs && node --check scripts/platform-smoke/scenarios.mjs && node --check scripts/platform-smoke/target-runtime.mjs && node --check scripts/platform-smoke/targets.mjs && node --check scripts/platform-smoke/visual-evidence.mjs && node --check scripts/platform-smoke/wrapped-line-match.mjs && vitest run test/platform-artifact-boundaries.test.ts test/platform-smoke-artifact-transport.test.ts test/smoke-cli-package-contracts.test.ts test/smoke-tooling.test.ts",
|
|
161
147
|
"smoke:platform": "node scripts/platform-smoke.mjs",
|
|
162
148
|
"smoke:platform:doctor": "node scripts/platform-smoke.mjs doctor",
|
|
163
149
|
"smoke:platform:macos": "node scripts/platform-smoke.mjs run --target macos",
|
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
import { LOCAL_RESUME_SUITE_NAMES } from "./scripts/platform-smoke/local-resume-suites.mjs";
|
|
5
5
|
|
|
6
6
|
export default {
|
|
7
|
-
packageName: "pi-cursor-sdk",
|
|
7
|
+
packageName: "@emmaneugene/pi-cursor-sdk",
|
|
8
|
+
packageSlug: "pi-cursor-sdk",
|
|
8
9
|
cursorModel: "cursor/grok-4.6",
|
|
9
10
|
artifactRoot: ".artifacts/platform-smoke",
|
|
10
11
|
artifactRetention: {
|
|
@@ -112,7 +112,17 @@ export async function writeTerminalScreenshot(htmlPath, pngPath, width, height)
|
|
|
112
112
|
let browser;
|
|
113
113
|
try {
|
|
114
114
|
const { chromium } = await import("playwright");
|
|
115
|
-
|
|
115
|
+
try {
|
|
116
|
+
browser = await chromium.launch();
|
|
117
|
+
} catch (bundledBrowserError) {
|
|
118
|
+
try {
|
|
119
|
+
browser = await chromium.launch({ channel: "chrome" });
|
|
120
|
+
} catch (systemBrowserError) {
|
|
121
|
+
const bundledMessage = bundledBrowserError instanceof Error ? bundledBrowserError.message : String(bundledBrowserError);
|
|
122
|
+
const systemMessage = systemBrowserError instanceof Error ? systemBrowserError.message : String(systemBrowserError);
|
|
123
|
+
throw new Error(`bundled Chromium: ${bundledMessage}\nsystem Chrome: ${systemMessage}`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
116
126
|
const page = await browser.newPage({
|
|
117
127
|
viewport: {
|
|
118
128
|
width: Math.max(1_200, width * 10),
|
|
@@ -125,7 +135,7 @@ export async function writeTerminalScreenshot(htmlPath, pngPath, width, height)
|
|
|
125
135
|
await page.locator("#terminal").screenshot({ path: pngPath });
|
|
126
136
|
} catch (error) {
|
|
127
137
|
const message = error instanceof Error ? error.message : String(error);
|
|
128
|
-
throw new Error(`failed to capture PNG with Playwright: ${message}\nInstall Chromium with: npx playwright install chromium\nOr rerun with --no-screenshot and capture ${htmlPath} with agent_browser.`);
|
|
138
|
+
throw new Error(`failed to capture PNG with Playwright: ${message}\nInstall system Chrome or Chromium with: npx playwright install chromium\nOr rerun with --no-screenshot and capture ${htmlPath} with agent_browser.`);
|
|
129
139
|
} finally {
|
|
130
140
|
if (browser) await browser.close();
|
|
131
141
|
}
|
|
@@ -21,20 +21,6 @@ import { buildCursorSmokeEnv } from "./cursor-smoke-env.mjs";
|
|
|
21
21
|
import { scrubSensitiveText } from "../../shared/cursor-sensitive-text.mjs";
|
|
22
22
|
|
|
23
23
|
const root = resolve(dirname(fileURLToPath(import.meta.url)), "../..");
|
|
24
|
-
const CLOUD_RUNTIME_ENV_NAMES = [
|
|
25
|
-
"PI_CURSOR_CLOUD_ACK",
|
|
26
|
-
"PI_CURSOR_CLOUD_ALLOW_LOCAL_STATE",
|
|
27
|
-
"PI_CURSOR_CLOUD_CONTEXT",
|
|
28
|
-
"PI_CURSOR_CLOUD_REPO",
|
|
29
|
-
"PI_CURSOR_CLOUD_BRANCH",
|
|
30
|
-
"PI_CURSOR_CLOUD_DIRECT_PUSH",
|
|
31
|
-
"PI_CURSOR_CLOUD_AUTO_CREATE_PR",
|
|
32
|
-
"PI_CURSOR_CLOUD_SKIP_REVIEWER_REQUEST",
|
|
33
|
-
"PI_CURSOR_CLOUD_ENV",
|
|
34
|
-
"PI_CURSOR_CLOUD_ENV_FROM_FILES",
|
|
35
|
-
"PI_CURSOR_CLOUD_ENV_TYPE",
|
|
36
|
-
"PI_CURSOR_CLOUD_ENV_NAME",
|
|
37
|
-
];
|
|
38
24
|
|
|
39
25
|
export function scrubSmokeText(value) {
|
|
40
26
|
return scrubSensitiveText(String(value), process.env.CURSOR_API_KEY);
|
|
@@ -128,7 +114,6 @@ export function buildLocalResumeSmokeEnv(
|
|
|
128
114
|
: localResumeEnv === false
|
|
129
115
|
? "unset"
|
|
130
116
|
: localResumeEnv;
|
|
131
|
-
for (const name of CLOUD_RUNTIME_ENV_NAMES) delete env[name];
|
|
132
117
|
delete env.PI_CURSOR_LOCAL_RESUME;
|
|
133
118
|
if (resumeMode === "on") env.PI_CURSOR_LOCAL_RESUME = "1";
|
|
134
119
|
else if (resumeMode === "off") env.PI_CURSOR_LOCAL_RESUME = "0";
|
|
@@ -137,7 +122,6 @@ export function buildLocalResumeSmokeEnv(
|
|
|
137
122
|
return {
|
|
138
123
|
...env,
|
|
139
124
|
PI_CODING_AGENT_DIR: agentDir,
|
|
140
|
-
PI_CURSOR_RUNTIME: "local",
|
|
141
125
|
};
|
|
142
126
|
}
|
|
143
127
|
|
|
@@ -472,8 +456,6 @@ export async function promptAbortAndRead({
|
|
|
472
456
|
|
|
473
457
|
export function assertTurnMetadata(label, turn, expected) {
|
|
474
458
|
const meta = turn.metadata.providerMeta ?? {};
|
|
475
|
-
if (meta.runtime === "cloud")
|
|
476
|
-
fail(`${label} unexpectedly recorded cloud runtime`, turn.metadataPath);
|
|
477
459
|
if (meta.localResume !== true)
|
|
478
460
|
fail(`${label} did not record localResume=true`, turn.metadataPath);
|
|
479
461
|
if (meta.resumedAgent !== expected.resumedAgent) {
|
|
@@ -30,7 +30,7 @@ const CARD_PATTERNS = [
|
|
|
30
30
|
},
|
|
31
31
|
{ id: "bridge-read-failure", pattern: /^\s*(?:read \.\/definitely-missing-platform-smoke-file\.txt|ENOENT: no such file)\s*/i },
|
|
32
32
|
{ id: "bridge-shell-success", pattern: /^\s*bridge visual smoke\s*$/i },
|
|
33
|
-
{ id: "http1-status", pattern: /\bcursor
|
|
33
|
+
{ id: "http1-status", pattern: /\bcursor\b.*\bhttp1\b/i },
|
|
34
34
|
{ id: "footer-status", pattern: /\bgrok-4\.6\b|\bcomposer-2-5\b|\bcomposer-2\.5\b/i },
|
|
35
35
|
];
|
|
36
36
|
|
|
@@ -244,7 +244,7 @@ export function buildLocalResumeSuiteCommand(
|
|
|
244
244
|
const evidenceDir = `${prepDir}/${powershellTarget ? "lr" : `local-resume-${suiteName}`}`;
|
|
245
245
|
const packagePath = `${prepDir}/packed-workspace/node_modules/${packageName}`;
|
|
246
246
|
if (powershellTarget) {
|
|
247
|
-
const powershell = `$ErrorActionPreference='Stop';$p=${quotePowerShell(prepDir)};$e=$p+'/lr';$w=$e.Replace('/','\\');$x=$p+${quotePowerShell(`/packed-workspace/node_modules/${packageName}`)};node scripts/platform-smoke/live-suite-runner.mjs --prepare-only --target ${targetName} --package-name ${packageName} --prep-dir $p;if($LASTEXITCODE){exit $LASTEXITCODE};for($i=0;$i -lt 10 -and (Test-Path -LiteralPath $e);$i++){cmd.exe /d /c rd /s /q $w;if(Test-Path -LiteralPath $e){Start-Sleep -Milliseconds 200}};if(Test-Path -LiteralPath $e){throw 'local-resume evidence cleanup failed'};$env:CURSOR_LOCAL_RESUME_SMOKE_EXTENSION_PATH=$x;$env:CURSOR_LOCAL_RESUME_SMOKE_ARTIFACT_DIR=$e;$env:CURSOR_LOCAL_RESUME_SMOKE_KEEP_ARTIFACTS='1';$env:CURSOR_LOCAL_RESUME_SMOKE_EMIT_BUNDLE='1';npm run ${script}`;
|
|
247
|
+
const powershell = `$ErrorActionPreference='Stop';$p=${quotePowerShell(prepDir)};$e=$p+'/lr';$w=$e.Replace('/','\\');$x=$p+${quotePowerShell(`/packed-workspace/node_modules/${packageName}`)};node scripts/platform-smoke/live-suite-runner.mjs --prepare-only --target ${targetName} --package-name ${quotePowerShell(packageName)} --prep-dir $p;if($LASTEXITCODE){exit $LASTEXITCODE};for($i=0;$i -lt 10 -and (Test-Path -LiteralPath $e);$i++){cmd.exe /d /c rd /s /q $w;if(Test-Path -LiteralPath $e){Start-Sleep -Milliseconds 200}};if(Test-Path -LiteralPath $e){throw 'local-resume evidence cleanup failed'};$env:CURSOR_LOCAL_RESUME_SMOKE_EXTENSION_PATH=$x;$env:CURSOR_LOCAL_RESUME_SMOKE_ARTIFACT_DIR=$e;$env:CURSOR_LOCAL_RESUME_SMOKE_KEEP_ARTIFACTS='1';$env:CURSOR_LOCAL_RESUME_SMOKE_EMIT_BUNDLE='1';npm run ${script}`;
|
|
248
248
|
return `powershell.exe -NoLogo -NoProfile -ExecutionPolicy Bypass -EncodedCommand ${Buffer.from(powershell, "utf16le").toString("base64")}`;
|
|
249
249
|
}
|
|
250
250
|
const prep = `node scripts/platform-smoke/live-suite-runner.mjs --prepare-only --target ${quotePosix(targetName)} --package-name ${quotePosix(packageName)} --prep-dir ${quotePosix(prepDir)}`;
|
|
@@ -60,7 +60,7 @@ Steps:
|
|
|
60
60
|
8. run shell and preserve the failure: {{shellFailure}}
|
|
61
61
|
9. stop using tools and answer exactly:
|
|
62
62
|
NATIVE_MATRIX_OK package=<name> grep=<yes/no> find=<yes/no> list=<yes/no> shell=<yes/no> shell_fail=<yes/no> write=<yes/no> edit=<yes/no>`,
|
|
63
|
-
finalMarker: "NATIVE_MATRIX_OK package
|
|
63
|
+
finalMarker: "NATIVE_MATRIX_OK package=@emmaneugene/pi-cursor-sdk",
|
|
64
64
|
requiredCards: [
|
|
65
65
|
"read", "grep", "find", "shell-success", "write", "edit-diff", "shell-failure", "footer-status",
|
|
66
66
|
],
|
|
@@ -44,13 +44,18 @@ function makeRunId() {
|
|
|
44
44
|
return `run-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
+
function targetSlug(config, targetName) {
|
|
48
|
+
const packageSlug = config.packageSlug ?? String(config.packageName ?? "pi-cursor-sdk").replace(/^@/, "").replaceAll("/", "-");
|
|
49
|
+
return `${packageSlug}-${targetName}`;
|
|
50
|
+
}
|
|
51
|
+
|
|
47
52
|
export function createLeaseCleanupResult(config, targetName, leaseId, stopResult, runId = makeRunId()) {
|
|
48
53
|
const suiteName = "lease-cleanup";
|
|
49
54
|
const suiteDir = createSuiteDir(config.artifactRoot, runId, targetName, suiteName);
|
|
50
55
|
writeFileSync(resolve(suiteDir, "target.json"), JSON.stringify({
|
|
51
56
|
targetName,
|
|
52
57
|
platform: platformFor(targetName),
|
|
53
|
-
slug:
|
|
58
|
+
slug: targetSlug(config, targetName),
|
|
54
59
|
runId,
|
|
55
60
|
writtenAt: new Date().toISOString(),
|
|
56
61
|
}, null, 2));
|
|
@@ -88,7 +93,7 @@ export async function runTargetSuite(config, targetName, suiteName, leaseSession
|
|
|
88
93
|
const runId = leaseSession?.runId ?? makeRunId();
|
|
89
94
|
const suiteDir = createSuiteDir(config.artifactRoot, runId, targetName, suiteName);
|
|
90
95
|
const platform = platformFor(targetName);
|
|
91
|
-
const slug =
|
|
96
|
+
const slug = targetSlug(config, targetName);
|
|
92
97
|
|
|
93
98
|
console.log(`\n── [${targetName}] ${suiteName} ──`);
|
|
94
99
|
console.log(` runId: ${runId}`);
|
|
@@ -133,7 +138,7 @@ export async function runTargetSuite(config, targetName, suiteName, leaseSession
|
|
|
133
138
|
* This is the release-gate path; per-suite runs remain available for diagnosis.
|
|
134
139
|
*/
|
|
135
140
|
export async function runTargetSuites(config, targetName, suiteNames) {
|
|
136
|
-
const slug =
|
|
141
|
+
const slug = targetSlug(config, targetName);
|
|
137
142
|
const runId = makeRunId();
|
|
138
143
|
console.log(` targetRunId: ${runId}`);
|
|
139
144
|
console.log(` warmup ${targetName}...`);
|
|
@@ -369,6 +374,10 @@ function posixSection(name, command) {
|
|
|
369
374
|
/**
|
|
370
375
|
* Build a shell command that runs the full platform-build pipeline and packed-install contract.
|
|
371
376
|
*/
|
|
377
|
+
function quotePowerShellArgument(value) {
|
|
378
|
+
return `'${String(value).replaceAll("'", "''")}'`;
|
|
379
|
+
}
|
|
380
|
+
|
|
372
381
|
export function buildPlatformBuildCommand(targetName, packageName = "pi-cursor-sdk", nodeValidationMajor = 24) {
|
|
373
382
|
const platform = platformFor(targetName);
|
|
374
383
|
const lines = [];
|
|
@@ -471,7 +480,7 @@ export function buildPlatformBuildCommand(targetName, packageName = "pi-cursor-s
|
|
|
471
480
|
lines.push("fi");
|
|
472
481
|
lines.push('echo "PLATFORM_BUILD_OK"');
|
|
473
482
|
} else {
|
|
474
|
-
lines.push(`powershell.exe -NoLogo -NoProfile -ExecutionPolicy Bypass -File .\\scripts\\platform-smoke\\platform-build-windows.ps1 -PackageName ${packageName} -NodeValidationMajor ${nodeValidationMajor}`);
|
|
483
|
+
lines.push(`powershell.exe -NoLogo -NoProfile -ExecutionPolicy Bypass -File .\\scripts\\platform-smoke\\platform-build-windows.ps1 -PackageName ${quotePowerShellArgument(packageName)} -NodeValidationMajor ${nodeValidationMajor}`);
|
|
475
484
|
}
|
|
476
485
|
return lines.join("\n");
|
|
477
486
|
}
|
|
@@ -666,7 +675,7 @@ function buildLiveSuiteCommand(config, targetName, suiteName, prepDir) {
|
|
|
666
675
|
const packageName = config.packageName ?? "pi-cursor-sdk";
|
|
667
676
|
const prepArgs = prepDir ? ` --prep-dir ${platformFor(targetName) === "powershell" ? prepDir : shellQuote(prepDir)}` : "";
|
|
668
677
|
if (platformFor(targetName) === "powershell") {
|
|
669
|
-
return `powershell.exe -NoLogo -NoProfile -ExecutionPolicy Bypass -Command "node scripts/platform-smoke/live-suite-runner.mjs --suite ${suiteName} --target ${targetName} --model ${model} --package-name ${packageName}${prepArgs}"`;
|
|
678
|
+
return `powershell.exe -NoLogo -NoProfile -ExecutionPolicy Bypass -Command "node scripts/platform-smoke/live-suite-runner.mjs --suite ${suiteName} --target ${targetName} --model ${model} --package-name ${quotePowerShellArgument(packageName)}${prepArgs}"`;
|
|
670
679
|
}
|
|
671
680
|
return `node scripts/platform-smoke/live-suite-runner.mjs --suite ${shellQuote(suiteName)} --target ${shellQuote(targetName)} --model ${shellQuote(model)} --package-name ${shellQuote(packageName)}${prepArgs}`;
|
|
672
681
|
}
|
|
@@ -24,7 +24,7 @@ function parseEnvCapture(path) {
|
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
export function runVisualSmokeSelfTest(deps) {
|
|
27
|
-
const { ROOT, DEFAULT_MODE, DEFAULT_MODEL, DEFAULT_SETTING_SOURCES, DEBUG_ENV_NAMES, shellQuote, parseArgs, snapshotJsonlMtimes, findLatestJsonl, sealedNodePath, resolveCommand, requireNode, requireCommand, buildLaunchPlan, run, runVisualSmoke } = deps;
|
|
27
|
+
const { ROOT, DEFAULT_MODE, DEFAULT_MODEL, DEFAULT_SETTING_SOURCES, DEBUG_ENV_NAMES, shellQuote, parseArgs, snapshotJsonlMtimes, findLatestJsonl, jsonlContainsUserPrompt, waitForCursorTui, sealedNodePath, resolveCommand, requireNode, requireCommand, buildLaunchPlan, run, runVisualSmoke } = deps;
|
|
28
28
|
const tempDir = mkdtempSync(join(tmpdir(), "pi-cursor-sdk-visual-self-test-"));
|
|
29
29
|
try {
|
|
30
30
|
const binDir = join(tempDir, "bin");
|
|
@@ -59,6 +59,9 @@ export function runVisualSmokeSelfTest(deps) {
|
|
|
59
59
|
utimesSync(freshJsonl, new Date(3_000), new Date(3_000));
|
|
60
60
|
assertSelfTest(findLatestJsonl(jsonlDir, { sinceMs: 2_000, previousMtimes: previousJsonlMtimes }) === freshJsonl, "JSONL discovery should ignore unchanged stale files before run start");
|
|
61
61
|
assertSelfTest(findLatestJsonl(jsonlDir, { sinceMs: 4_000, previousMtimes: snapshotJsonlMtimes(jsonlDir) }) === undefined, "JSONL discovery should not return stale evidence when current run has no changed JSONL");
|
|
62
|
+
writeFileSync(freshJsonl, `${JSON.stringify({ type: "message", message: { role: "user", content: "submitted prompt" } })}\n`, "utf8");
|
|
63
|
+
assertSelfTest(jsonlContainsUserPrompt(freshJsonl, "submitted prompt"), "JSONL prompt check should find the submitted user message");
|
|
64
|
+
assertSelfTest(!jsonlContainsUserPrompt(freshJsonl, "different prompt"), "JSONL prompt check should reject a different user message");
|
|
62
65
|
|
|
63
66
|
assertSelfTest(!sealedNodePath(process.execPath, "").includes(delimiter), "empty inherited PATH must not leave an empty PATH segment");
|
|
64
67
|
const hostilePath = `${binDir}${delimiter}${process.env.PATH ?? ""}`;
|
|
@@ -73,6 +76,7 @@ export function runVisualSmokeSelfTest(deps) {
|
|
|
73
76
|
cwd: ROOT,
|
|
74
77
|
mode: DEFAULT_MODE,
|
|
75
78
|
model: DEFAULT_MODEL,
|
|
79
|
+
prompt: "self-test prompt",
|
|
76
80
|
outDir: tempDir,
|
|
77
81
|
safeLabel: "self-test",
|
|
78
82
|
sessionDir: join(tempDir, "session"),
|
|
@@ -96,6 +100,7 @@ export function runVisualSmokeSelfTest(deps) {
|
|
|
96
100
|
assertSelfTest(plan.clearEnvNames.includes(name), `${name} must be cleared by default`);
|
|
97
101
|
}
|
|
98
102
|
assertSelfTest(plan.script.includes(shellQuote(fakePi)), "launch script must use resolved pi path");
|
|
103
|
+
assertSelfTest(!plan.script.includes(baseOptions.prompt), "launch script must keep the prompt out of the process command line");
|
|
99
104
|
assertSelfTest(!plan.script.includes(" exec pi "), "launch script must not use bare pi");
|
|
100
105
|
const hostileEnv = {
|
|
101
106
|
...process.env,
|
|
@@ -144,48 +149,18 @@ export function runVisualSmokeSelfTest(deps) {
|
|
|
144
149
|
assertSelfTest(!capturedEventDebugEnv.has("PI_CURSOR_SDK_EVENT_DEBUG_STDERR"), "stale event debug stderr flag should be cleared");
|
|
145
150
|
|
|
146
151
|
const fakeTmux = join(binDir, "tmux");
|
|
147
|
-
const deleteBufferMarker = join(tempDir, "delete-buffer-called");
|
|
148
|
-
writeFileSync(
|
|
149
|
-
fakeTmux,
|
|
150
|
-
`#!/bin/sh\ncase "$1" in\n -V) echo 'tmux fake'; exit 0 ;;\n new-session) exit 0 ;;\n load-buffer) cat >/dev/null; exit 0 ;;\n paste-buffer) exit 77 ;;\n delete-buffer) echo deleted > ${shellQuote(deleteBufferMarker)}; exit 0 ;;\n kill-session) exit 0 ;;\n *) echo "unexpected tmux command: $*" >&2; exit 64 ;;\nesac\n`,
|
|
151
|
-
"utf8",
|
|
152
|
-
);
|
|
153
|
-
chmodSync(fakeTmux, 0o755);
|
|
154
|
-
const originalPath = process.env.PATH;
|
|
155
|
-
try {
|
|
156
|
-
process.env.PATH = hostilePath;
|
|
157
|
-
let pasteFailed = false;
|
|
158
|
-
try {
|
|
159
|
-
runVisualSmoke({
|
|
160
|
-
...baseOptions,
|
|
161
|
-
prompt: "buffer cleanup prompt",
|
|
162
|
-
startupMs: 1,
|
|
163
|
-
waitMs: 1,
|
|
164
|
-
width: 80,
|
|
165
|
-
height: 24,
|
|
166
|
-
historyLines: 100,
|
|
167
|
-
});
|
|
168
|
-
} catch (error) {
|
|
169
|
-
pasteFailed = /paste-buffer failed/.test(error instanceof Error ? error.message : String(error));
|
|
170
|
-
}
|
|
171
|
-
assertSelfTest(pasteFailed, "fake tmux paste failure should exercise prompt-buffer cleanup path");
|
|
172
|
-
assertSelfTest(existsSync(deleteBufferMarker), "prompt tmux buffer should be deleted when paste/send fails");
|
|
173
|
-
} finally {
|
|
174
|
-
if (originalPath === undefined) delete process.env.PATH;
|
|
175
|
-
else process.env.PATH = originalPath;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
152
|
writeFileSync(
|
|
179
153
|
fakeTmux,
|
|
180
154
|
`#!/bin/sh
|
|
181
155
|
case "$1" in
|
|
182
156
|
-V) echo 'tmux fake'; exit 0 ;;
|
|
183
157
|
new-session) exit 0 ;;
|
|
158
|
+
set-option) exit 0 ;;
|
|
184
159
|
load-buffer) cat >/dev/null; exit 0 ;;
|
|
185
160
|
paste-buffer) exit 0 ;;
|
|
186
161
|
send-keys) exit 0 ;;
|
|
187
162
|
delete-buffer) exit 0 ;;
|
|
188
|
-
capture-pane)
|
|
163
|
+
capture-pane) printf 'captured visual smoke output\ncursor · fast:off\n'; exit 0 ;;
|
|
189
164
|
kill-session) exit 0 ;;
|
|
190
165
|
*) echo "unexpected tmux command: $*" >&2; exit 64 ;;
|
|
191
166
|
esac
|
|
@@ -194,6 +169,7 @@ esac
|
|
|
194
169
|
);
|
|
195
170
|
chmodSync(fakeTmux, 0o755);
|
|
196
171
|
const noJsonlManifest = join(tempDir, "self-test-jsonl-missing.manifest.json");
|
|
172
|
+
const originalPath = process.env.PATH;
|
|
197
173
|
try {
|
|
198
174
|
process.env.PATH = hostilePath;
|
|
199
175
|
let missingJsonlFailed = false;
|
|
@@ -14,7 +14,7 @@ const ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
|
14
14
|
const DEFAULT_WIDTH = 150;
|
|
15
15
|
const DEFAULT_HEIGHT = 45;
|
|
16
16
|
const DEFAULT_WAIT_MS = 60_000;
|
|
17
|
-
const DEFAULT_STARTUP_MS =
|
|
17
|
+
const DEFAULT_STARTUP_MS = 15_000;
|
|
18
18
|
const DEFAULT_HISTORY_LINES = 3_000;
|
|
19
19
|
const DEFAULT_MODEL = "cursor/grok-4.6";
|
|
20
20
|
const DEFAULT_MODE = "plan";
|
|
@@ -33,15 +33,15 @@ Usage:
|
|
|
33
33
|
|
|
34
34
|
Required:
|
|
35
35
|
--label LABEL Artifact filename prefix. Sanitized for paths.
|
|
36
|
-
--prompt PROMPT Prompt to
|
|
36
|
+
--prompt PROMPT Prompt to submit in the interactive pi TUI.
|
|
37
37
|
Use --prompt-file PATH for multi-line prompts.
|
|
38
38
|
|
|
39
39
|
Common options:
|
|
40
40
|
--ext PATH Extension repo to load with pi -e. Default: repo root.
|
|
41
41
|
--cwd PATH Working directory for the pi session. Default: current directory.
|
|
42
42
|
--out-dir PATH Artifact directory. Default: /tmp/pi-cursor-sdk-visual-smoke-<timestamp>.
|
|
43
|
-
--wait-ms N Milliseconds to wait
|
|
44
|
-
--startup-ms N
|
|
43
|
+
--wait-ms N Milliseconds to wait for the response before capture. Default: ${DEFAULT_WAIT_MS}.
|
|
44
|
+
--startup-ms N Maximum milliseconds to wait for TUI readiness. Default: ${DEFAULT_STARTUP_MS}.
|
|
45
45
|
--model MODEL Cursor model. Default: ${DEFAULT_MODEL}.
|
|
46
46
|
--mode agent|plan Cursor SDK mode. Default: ${DEFAULT_MODE}.
|
|
47
47
|
--session-dir PATH pi session directory. Default: <out-dir>/<label>.session.
|
|
@@ -82,8 +82,8 @@ Artifacts written:
|
|
|
82
82
|
Prerequisites:
|
|
83
83
|
- pi, node, tmux, and npm-installed dev dependencies on PATH / in node_modules.
|
|
84
84
|
- The runner resolves pi/tmux from the parent PATH, uses process.execPath for node, and seals pi-shim PATH for prereq checks and tmux.
|
|
85
|
-
-
|
|
86
|
-
|
|
85
|
+
- Automatic PNG capture uses Playwright's Chromium or a system Chrome installation.
|
|
86
|
+
If neither is available, install Chromium once: npx playwright install chromium
|
|
87
87
|
- In the pi agent harness, --no-screenshot plus agent_browser on the generated HTML is also acceptable.
|
|
88
88
|
|
|
89
89
|
Examples:
|
|
@@ -340,6 +340,41 @@ function findLatestJsonl(root, { sinceMs = 0, previousMtimes = new Map() } = {})
|
|
|
340
340
|
return matches[0]?.path;
|
|
341
341
|
}
|
|
342
342
|
|
|
343
|
+
function jsonlContainsUserPrompt(path, prompt) {
|
|
344
|
+
for (const line of readFileSync(path, "utf8").split("\n")) {
|
|
345
|
+
if (!line.trim()) continue;
|
|
346
|
+
let entry;
|
|
347
|
+
try {
|
|
348
|
+
entry = JSON.parse(line);
|
|
349
|
+
} catch {
|
|
350
|
+
continue;
|
|
351
|
+
}
|
|
352
|
+
if (entry?.type !== "message" || entry.message?.role !== "user") continue;
|
|
353
|
+
const content = entry.message.content;
|
|
354
|
+
const text = typeof content === "string"
|
|
355
|
+
? content
|
|
356
|
+
: Array.isArray(content)
|
|
357
|
+
? content.filter((part) => part?.type === "text" && typeof part.text === "string").map((part) => part.text).join("")
|
|
358
|
+
: "";
|
|
359
|
+
if (text === prompt) return true;
|
|
360
|
+
}
|
|
361
|
+
return false;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
function waitForCursorTui(commands, sessionName, timeoutMs) {
|
|
365
|
+
const deadline = Date.now() + timeoutMs;
|
|
366
|
+
do {
|
|
367
|
+
try {
|
|
368
|
+
const pane = capturePane(commands.tmux, sessionName, ["-p", "-S", "-100"]);
|
|
369
|
+
if (pane.includes("cursor ·")) return;
|
|
370
|
+
} catch {
|
|
371
|
+
// The pane can be unavailable briefly while tmux starts the shell.
|
|
372
|
+
}
|
|
373
|
+
sleep(100);
|
|
374
|
+
} while (Date.now() < deadline);
|
|
375
|
+
throw new Error(`Cursor TUI did not become ready within ${timeoutMs}ms`);
|
|
376
|
+
}
|
|
377
|
+
|
|
343
378
|
function checkLeftovers(patterns) {
|
|
344
379
|
if (patterns.length === 0) return;
|
|
345
380
|
const result = run("ps", ["-axo", "pid,etime,command"]);
|
|
@@ -465,18 +500,19 @@ function runVisualSmoke(options) {
|
|
|
465
500
|
]);
|
|
466
501
|
if (start.status !== 0) throw new Error(`tmux new-session failed: ${start.stderr?.toString().trim() || start.status}`);
|
|
467
502
|
sessionStarted = true;
|
|
503
|
+
const remainOnExit = run(commands.tmux, ["set-option", "-t", sessionName, "remain-on-exit", "on"]);
|
|
504
|
+
if (remainOnExit.status !== 0) throw new Error(`tmux remain-on-exit setup failed: ${remainOnExit.stderr?.toString().trim() || remainOnExit.status}`);
|
|
468
505
|
|
|
469
|
-
|
|
506
|
+
waitForCursorTui(commands, sessionName, options.startupMs);
|
|
470
507
|
const load = run(commands.tmux, ["load-buffer", "-b", bufferName, "-"], { input: Buffer.from(options.prompt, "utf8") });
|
|
471
508
|
if (load.status !== 0) throw new Error(`tmux load-buffer failed: ${load.stderr?.toString().trim() || load.status}`);
|
|
472
509
|
bufferLoaded = true;
|
|
473
510
|
try {
|
|
474
|
-
const paste = run(commands.tmux, ["paste-buffer", "-b", bufferName, "-t", sessionName]);
|
|
511
|
+
const paste = run(commands.tmux, ["paste-buffer", "-p", "-S", "-b", bufferName, "-t", sessionName]);
|
|
475
512
|
if (paste.status !== 0) throw new Error(`tmux paste-buffer failed: ${paste.stderr?.toString().trim() || paste.status}`);
|
|
476
|
-
// Give bracketed paste handling a moment to finish before submitting.
|
|
477
513
|
sleep(250);
|
|
478
|
-
const
|
|
479
|
-
if (
|
|
514
|
+
const submit = run(commands.tmux, ["send-keys", "-t", sessionName, "-H", "0d"]);
|
|
515
|
+
if (submit.status !== 0) throw new Error(`tmux prompt submit failed: ${submit.stderr?.toString().trim() || submit.status}`);
|
|
480
516
|
} finally {
|
|
481
517
|
run(commands.tmux, ["delete-buffer", "-b", bufferName]);
|
|
482
518
|
bufferLoaded = false;
|
|
@@ -507,6 +543,11 @@ function runVisualSmoke(options) {
|
|
|
507
543
|
writeVisualManifest(manifestPath, options, partialArtifacts, { message, writtenAt: new Date().toISOString() });
|
|
508
544
|
throw new Error(message);
|
|
509
545
|
}
|
|
546
|
+
if (!jsonlContainsUserPrompt(jsonlPath, options.prompt)) {
|
|
547
|
+
const message = "current-run JSONL does not contain the submitted prompt";
|
|
548
|
+
writeVisualManifest(manifestPath, options, { ...partialArtifacts, jsonlPath }, { message, writtenAt: new Date().toISOString() });
|
|
549
|
+
throw new Error(message);
|
|
550
|
+
}
|
|
510
551
|
writeUtf8(jsonlPathFile, `${jsonlPath}\n`);
|
|
511
552
|
|
|
512
553
|
return { ...partialArtifacts, jsonlPath };
|
|
@@ -531,6 +572,8 @@ try {
|
|
|
531
572
|
parseArgs,
|
|
532
573
|
snapshotJsonlMtimes,
|
|
533
574
|
findLatestJsonl,
|
|
575
|
+
jsonlContainsUserPrompt,
|
|
576
|
+
waitForCursorTui,
|
|
534
577
|
sealedNodePath,
|
|
535
578
|
resolveCommand,
|
|
536
579
|
requireNode,
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { isCursorModel } from "./cursor-model.js";
|
|
2
2
|
import { registerCursorModelLifecycle, type CursorModelLifecycleExtensionApi } from "./cursor-model-lifecycle.js";
|
|
3
|
-
import { resolveEffectiveCursorConfigForContext } from "./cursor-runtime-state.js";
|
|
4
3
|
import { resolveCursorFacingSystemPrompt } from "./cursor-agents-context.js";
|
|
5
4
|
|
|
6
5
|
export type CursorAgentsContextExtensionApi = CursorModelLifecycleExtensionApi;
|
|
@@ -9,14 +8,10 @@ export function registerCursorAgentsContextDedup(pi: CursorAgentsContextExtensio
|
|
|
9
8
|
registerCursorModelLifecycle(pi, {
|
|
10
9
|
beforeAgentStart: (event, ctx) => {
|
|
11
10
|
if (!isCursorModel(ctx.model)) return undefined;
|
|
12
|
-
const runtime = resolveEffectiveCursorConfigForContext(ctx).runtime.value;
|
|
13
11
|
const resolved = resolveCursorFacingSystemPrompt(
|
|
14
12
|
event.systemPrompt,
|
|
15
13
|
ctx.model,
|
|
16
14
|
event.systemPromptOptions,
|
|
17
|
-
undefined,
|
|
18
|
-
undefined,
|
|
19
|
-
runtime,
|
|
20
15
|
);
|
|
21
16
|
if (resolved === event.systemPrompt) return undefined;
|
|
22
17
|
return { systemPrompt: resolved };
|