@emmaneugene/pi-cursor-sdk 0.3.7 → 0.4.0

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 (79) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/README.md +99 -126
  3. package/dist/cursor-agents-context-registration.js +1 -3
  4. package/dist/cursor-agents-context.js +2 -2
  5. package/dist/cursor-config.js +2 -290
  6. package/dist/cursor-provider-errors.js +3 -58
  7. package/dist/cursor-provider-live-run-drain.js +0 -3
  8. package/dist/cursor-provider-run-finalizer.js +4 -7
  9. package/dist/cursor-provider-run-outcome.js +1 -1
  10. package/dist/cursor-provider-turn-finalize.js +3 -55
  11. package/dist/cursor-provider-turn-prepare.js +8 -144
  12. package/dist/cursor-provider-turn-runner.js +9 -23
  13. package/dist/cursor-provider-turn-send.js +8 -35
  14. package/dist/cursor-runtime-state.js +6 -268
  15. package/dist/cursor-sdk-billed-usage.js +3 -18
  16. package/dist/cursor-sdk-platform-package.js +88 -0
  17. package/dist/cursor-session-agent.js +18 -14
  18. package/dist/cursor-skill-tool.js +12 -22
  19. package/dist/cursor-state.js +5 -8
  20. package/dist/cursor-usage-accounting.js +1 -1
  21. package/docs/cursor-dogfood-checklist.md +4 -4
  22. package/docs/cursor-live-smoke-checklist.md +23 -23
  23. package/docs/cursor-model-ux-spec.md +27 -30
  24. package/docs/cursor-native-tool-replay.md +4 -4
  25. package/docs/cursor-native-tool-visual-audit.md +7 -7
  26. package/docs/cursor-testing-lessons.md +23 -16
  27. package/docs/cursor-tool-surfaces.md +2 -4
  28. package/docs/platform-smoke-implementation.md +5 -5
  29. package/docs/platform-smoke.md +27 -72
  30. package/package.json +3 -17
  31. package/platform-smoke.config.mjs +2 -1
  32. package/scripts/lib/local-resume-smoke-harness.mjs +0 -18
  33. package/scripts/platform-smoke/card-detect.mjs +1 -1
  34. package/scripts/platform-smoke/local-resume-runner.mjs +1 -1
  35. package/scripts/platform-smoke/scenarios.mjs +1 -1
  36. package/scripts/platform-smoke/targets.mjs +14 -5
  37. package/src/cursor-agents-context-registration.ts +0 -5
  38. package/src/cursor-agents-context.ts +1 -3
  39. package/src/cursor-config.ts +8 -379
  40. package/src/cursor-provider-errors.ts +2 -62
  41. package/src/cursor-provider-live-run-drain.ts +0 -3
  42. package/src/cursor-provider-run-finalizer.ts +4 -12
  43. package/src/cursor-provider-run-outcome.ts +0 -3
  44. package/src/cursor-provider-turn-finalize.ts +3 -61
  45. package/src/cursor-provider-turn-prepare.ts +8 -165
  46. package/src/cursor-provider-turn-runner.ts +15 -31
  47. package/src/cursor-provider-turn-send.ts +7 -38
  48. package/src/cursor-provider-turn-types.ts +9 -30
  49. package/src/cursor-runtime-state.ts +6 -345
  50. package/src/cursor-sdk-billed-usage.ts +3 -24
  51. package/src/cursor-sdk-platform-package.ts +106 -0
  52. package/src/cursor-session-agent.ts +16 -12
  53. package/src/cursor-skill-tool.ts +10 -26
  54. package/src/cursor-state.ts +5 -10
  55. package/src/cursor-usage-accounting.ts +1 -3
  56. package/dist/cursor-cloud-lifecycle.js +0 -650
  57. package/dist/cursor-cloud-local-state.js +0 -460
  58. package/dist/cursor-cloud-options.js +0 -145
  59. package/dist/cursor-cloud-reporting.js +0 -222
  60. package/dist/cursor-ripgrep-path.js +0 -27
  61. package/scripts/cloud-runtime-smoke.d.mts +0 -42
  62. package/scripts/cloud-runtime-smoke.mjs +0 -623
  63. package/scripts/lib/cloud-smoke-artifacts.d.mts +0 -16
  64. package/scripts/lib/cloud-smoke-artifacts.mjs +0 -94
  65. package/scripts/lib/cloud-smoke-cleanup-evidence.d.mts +0 -209
  66. package/scripts/lib/cloud-smoke-cleanup-evidence.mjs +0 -585
  67. package/scripts/lib/cloud-smoke-github.d.mts +0 -78
  68. package/scripts/lib/cloud-smoke-github.mjs +0 -331
  69. package/scripts/lib/cloud-smoke-pi-runner.d.mts +0 -42
  70. package/scripts/lib/cloud-smoke-pi-runner.mjs +0 -214
  71. package/scripts/lib/cloud-smoke-shutdown.d.mts +0 -67
  72. package/scripts/lib/cloud-smoke-shutdown.mjs +0 -133
  73. package/shared/cursor-cloud-lifecycle-constants.d.mts +0 -3
  74. package/shared/cursor-cloud-lifecycle-constants.mjs +0 -7
  75. package/src/cursor-cloud-lifecycle.ts +0 -733
  76. package/src/cursor-cloud-local-state.ts +0 -536
  77. package/src/cursor-cloud-options.ts +0 -182
  78. package/src/cursor-cloud-reporting.ts +0 -267
  79. package/src/cursor-ripgrep-path.ts +0 -32
@@ -1,48 +1,15 @@
1
- import { registerCursorCloudLifecycleLedger, runCursorCloudLifecycleCommand, } from "./cursor-cloud-lifecycle.js";
2
- import { CURSOR_AUTO_REVIEW_ENV, CURSOR_CLOUD_ACK_ENV, CURSOR_CLOUD_ALLOW_LOCAL_STATE_ENV, CURSOR_CLOUD_AUTO_CREATE_PR_ENV, CURSOR_CLOUD_BRANCH_ENV, CURSOR_CLOUD_CONTEXT_ENV, CURSOR_CLOUD_DIRECT_PUSH_ENV, CURSOR_CLOUD_ENV_ENV, CURSOR_CLOUD_ENV_FROM_FILES_ENV, CURSOR_CLOUD_ENV_NAME_ENV, CURSOR_CLOUD_ENV_TYPE_ENV, CURSOR_CLOUD_REPO_ENV, CURSOR_CLOUD_SKIP_REVIEWER_REQUEST_ENV, CURSOR_LOCAL_FORCE_ENV, CURSOR_LOCAL_RESUME_ENV, CURSOR_RUNTIME_ENV, CURSOR_SANDBOX_ENV, getCursorSdkProjectConfigPath, getCursorSdkUserConfigPath, loadCursorSdkConfig, mergeCursorSdkConfigForUpdate, parseExplicitCursorCloudEnvNames, resolveCursorSdkConfig, updateCursorSdkConfig, } from "./cursor-config.js";
3
- import { asRecord } from "./cursor-record-utils.js";
1
+ import { CURSOR_AUTO_REVIEW_ENV, CURSOR_LOCAL_FORCE_ENV, CURSOR_LOCAL_RESUME_ENV, CURSOR_SANDBOX_ENV, loadCursorSdkConfig, resolveCursorSdkConfig, } from "./cursor-config.js";
4
2
  import { getResolvedSessionCursorHttp1Enabled } from "./cursor-http1.js";
5
3
  import { getCursorSessionCwd, getCursorSessionProjectTrusted } from "./cursor-session-scope.js";
6
- export const CURSOR_RUNTIME_ENTRY_TYPE = "cursor-runtime-state";
7
- export const CURSOR_CLOUD_ACK_DISCLOSURE = [
8
- "Cursor Cloud executes this work remotely.",
9
- "Fresh context is used by default; prior Pi context is included only with explicit bootstrap opt-in.",
10
- "Pi-local tools and the Pi bridge are unavailable, and Pi environment variables are not forwarded.",
11
- "Cursor may create branches, commit, push, and open pull requests.",
12
- "Cloud agents remain until you archive or delete them.",
13
- "Cloud Agents run in Max Mode, are billed at Cursor API pricing, and may require spend-limit setup.",
14
- ].join("\n\n");
15
4
  let cliCursorSnapshot = { config: {} };
16
5
  let cliLocalForceConsumed = false;
17
6
  let envLocalForceConsumed = false;
18
- let sessionCursorRuntime;
19
- let sessionCursorCloudAcknowledged = false;
20
- function isCursorRuntimeEntryData(value) {
21
- const record = asRecord(value);
22
- if (!record)
23
- return false;
24
- return (record.runtime === "local" || record.runtime === "cloud")
25
- && (record.cloudAcknowledged === undefined || typeof record.cloudAcknowledged === "boolean");
26
- }
27
- function stringFlagValue(value) {
28
- return typeof value === "string" && value.trim() ? value.trim() : undefined;
29
- }
30
7
  export function getCursorCliConfig() {
31
- const config = structuredClone(cliCursorSnapshot.config);
32
- const envNames = parseExplicitCursorCloudEnvNames(cliCursorSnapshot.cloudEnvNames, "--cursor-cloud-env");
33
- if (envNames)
34
- config.cloud = { ...config.cloud, envNames };
35
- return config;
8
+ return structuredClone(cliCursorSnapshot.config);
36
9
  }
37
10
  export function getCursorSessionConfig() {
38
11
  const useHttp1ForAgent = getResolvedSessionCursorHttp1Enabled();
39
12
  return {
40
- ...(sessionCursorRuntime
41
- ? {
42
- runtime: sessionCursorRuntime,
43
- ...(sessionCursorCloudAcknowledged ? { cloud: { acknowledged: true } } : {}),
44
- }
45
- : {}),
46
13
  ...(useHttp1ForAgent === undefined ? {} : { local: { useHttp1ForAgent } }),
47
14
  };
48
15
  }
@@ -66,7 +33,6 @@ export function resolveCursorStatusRuntime(ctx) {
66
33
  const config = resolveEffectiveCursorConfigForContext(ctx);
67
34
  return {
68
35
  kind: "valid",
69
- runtime: config.runtime,
70
36
  useHttp1ForAgent: config.local.useHttp1ForAgent,
71
37
  };
72
38
  }
@@ -74,15 +40,9 @@ export function resolveCursorStatusRuntime(ctx) {
74
40
  return { kind: "invalid", message: error instanceof Error ? error.message : String(error) };
75
41
  }
76
42
  }
77
- export function formatResolvedCursorRuntime(runtime) {
78
- const cap = runtime.cappedBy;
79
- return cap
80
- ? `${runtime.value} (source: ${runtime.source} safety cap over ${cap.cappedSource} ${cap.cappedValue})`
81
- : `${runtime.value} (source: ${runtime.source})`;
82
- }
83
- export function formatCursorStatus(runtime, fast, mode, useHttp1ForAgent = false) {
84
- const parts = [`cursor:${runtime}`, fast === true ? "fast:on" : fast === false ? "fast:off" : "fast:n/a"];
85
- if (runtime === "local" && useHttp1ForAgent)
43
+ export function formatCursorStatus(fast, mode, useHttp1ForAgent = false) {
44
+ const parts = ["cursor", fast === true ? "fast:on" : fast === false ? "fast:off" : "fast:n/a"];
45
+ if (useHttp1ForAgent)
86
46
  parts.push("http1");
87
47
  if (mode === "invalid")
88
48
  parts.push("mode invalid");
@@ -107,40 +67,7 @@ export function consumeCursorLocalForceOverride(resolved) {
107
67
  }
108
68
  return false;
109
69
  }
110
- export function restoreSessionCursorRuntimeState(branch) {
111
- sessionCursorRuntime = undefined;
112
- sessionCursorCloudAcknowledged = false;
113
- for (const entry of branch) {
114
- if (entry.type !== "custom" || entry.customType !== CURSOR_RUNTIME_ENTRY_TYPE)
115
- continue;
116
- if (isCursorRuntimeEntryData(entry.data)) {
117
- sessionCursorRuntime = entry.data.runtime;
118
- sessionCursorCloudAcknowledged ||= entry.data.cloudAcknowledged === true;
119
- }
120
- }
121
- }
122
70
  export function restoreCursorCliState(pi) {
123
- const runtime = stringFlagValue(pi.getFlag("cursor-runtime"));
124
- const repo = stringFlagValue(pi.getFlag("cursor-cloud-repo"));
125
- const branch = stringFlagValue(pi.getFlag("cursor-cloud-branch"));
126
- const contextHandoff = stringFlagValue(pi.getFlag("cursor-cloud-context"));
127
- const environmentType = stringFlagValue(pi.getFlag("cursor-cloud-env-type"));
128
- const environmentName = stringFlagValue(pi.getFlag("cursor-cloud-env-name"));
129
- const cloudEnvNames = stringFlagValue(pi.getFlag("cursor-cloud-env"));
130
- const cloud = {
131
- ...(repo ? { repo } : {}),
132
- ...(branch ? { branch } : {}),
133
- ...(contextHandoff ? { contextHandoff } : {}),
134
- ...(pi.getFlag("cursor-cloud-direct-push") === true ? { directPush: true } : {}),
135
- ...(pi.getFlag("cursor-cloud-auto-create-pr") === true ? { autoCreatePR: true } : {}),
136
- ...(pi.getFlag("cursor-cloud-skip-reviewer-request") === true ? { skipReviewerRequest: true } : {}),
137
- ...(pi.getFlag("cursor-cloud-allow-local-state") === true ? { allowLocalState: true } : {}),
138
- ...(pi.getFlag("cursor-cloud-env-from-files") === true ? { envFromFiles: true } : {}),
139
- ...(environmentType || environmentName
140
- ? { environment: { ...(environmentType ? { type: environmentType } : {}), ...(environmentName ? { name: environmentName } : {}) } }
141
- : {}),
142
- ...(pi.getFlag("cursor-cloud-ack") === true ? { acknowledged: true } : {}),
143
- };
144
71
  const local = {
145
72
  ...(pi.getFlag("cursor-auto-review") === true ? { autoReview: true } : {}),
146
73
  ...(pi.getFlag("cursor-sandbox") === true ? { sandboxOptions: { enabled: true } } : {}),
@@ -153,88 +80,11 @@ export function restoreCursorCliState(pi) {
153
80
  };
154
81
  cliCursorSnapshot = {
155
82
  config: {
156
- ...(runtime ? { runtime } : {}),
157
- ...(Object.keys(cloud).length ? { cloud } : {}),
158
83
  ...(Object.keys(local).length ? { local } : {}),
159
84
  },
160
- ...(cloudEnvNames ? { cloudEnvNames } : {}),
161
85
  };
162
86
  }
163
- function persistCursorRuntimePreference(pi, runtime, cloudAcknowledged = false) {
164
- const acknowledged = sessionCursorCloudAcknowledged || cloudAcknowledged;
165
- pi.appendEntry(CURSOR_RUNTIME_ENTRY_TYPE, {
166
- runtime,
167
- ...(acknowledged ? { cloudAcknowledged: true } : {}),
168
- });
169
- sessionCursorRuntime = runtime;
170
- sessionCursorCloudAcknowledged = acknowledged;
171
- }
172
- function registerCursorRuntimeFlags(pi) {
173
- pi.registerFlag("cursor-runtime", {
174
- description: `Select Cursor runtime for this run: local or cloud (or set ${CURSOR_RUNTIME_ENV})`,
175
- type: "string",
176
- default: "",
177
- });
178
- pi.registerFlag("cursor-cloud-repo", {
179
- description: `Set Cursor cloud repository URL for this run (or set ${CURSOR_CLOUD_REPO_ENV})`,
180
- type: "string",
181
- default: "",
182
- });
183
- pi.registerFlag("cursor-cloud-branch", {
184
- description: `Set Cursor cloud branch/ref for this run (or set ${CURSOR_CLOUD_BRANCH_ENV})`,
185
- type: "string",
186
- default: "",
187
- });
188
- pi.registerFlag("cursor-cloud-context", {
189
- description: `Set Cursor cloud context handoff: never, fresh, or bootstrap (or set ${CURSOR_CLOUD_CONTEXT_ENV})`,
190
- type: "string",
191
- default: "",
192
- });
193
- pi.registerFlag("cursor-cloud-direct-push", {
194
- description: `Allow Cursor cloud direct push for this run (or set ${CURSOR_CLOUD_DIRECT_PUSH_ENV}=1)`,
195
- type: "boolean",
196
- default: false,
197
- });
198
- pi.registerFlag("cursor-cloud-auto-create-pr", {
199
- description: `Ask Cursor cloud to create a pull request for this run (or set ${CURSOR_CLOUD_AUTO_CREATE_PR_ENV}=1)`,
200
- type: "boolean",
201
- default: false,
202
- });
203
- pi.registerFlag("cursor-cloud-skip-reviewer-request", {
204
- description: `Ask Cursor cloud not to request you as a reviewer for this run (or set ${CURSOR_CLOUD_SKIP_REVIEWER_REQUEST_ENV}=1)`,
205
- type: "boolean",
206
- default: false,
207
- });
208
- pi.registerFlag("cursor-cloud-allow-local-state", {
209
- description: `Allow Cursor cloud to proceed with local-only state for this run (or set ${CURSOR_CLOUD_ALLOW_LOCAL_STATE_ENV}=1)`,
210
- type: "boolean",
211
- default: false,
212
- });
213
- pi.registerFlag("cursor-cloud-env", {
214
- description: `Reserved Cursor cloud env var names; env forwarding is not implemented yet (or set ${CURSOR_CLOUD_ENV_ENV})`,
215
- type: "string",
216
- default: "",
217
- });
218
- pi.registerFlag("cursor-cloud-env-from-files", {
219
- description: `Reserved Cursor cloud env-file forwarding flag; not implemented yet (or set ${CURSOR_CLOUD_ENV_FROM_FILES_ENV}=1)`,
220
- type: "boolean",
221
- default: false,
222
- });
223
- pi.registerFlag("cursor-cloud-env-type", {
224
- description: `Select Cursor-managed cloud environment type: cloud, pool, or machine (or set ${CURSOR_CLOUD_ENV_TYPE_ENV})`,
225
- type: "string",
226
- default: "",
227
- });
228
- pi.registerFlag("cursor-cloud-env-name", {
229
- description: `Select Cursor-managed cloud environment name (or set ${CURSOR_CLOUD_ENV_NAME_ENV})`,
230
- type: "string",
231
- default: "",
232
- });
233
- pi.registerFlag("cursor-cloud-ack", {
234
- description: `Acknowledge first-use Cursor cloud runtime risks for this run (or set ${CURSOR_CLOUD_ACK_ENV}=1)`,
235
- type: "boolean",
236
- default: false,
237
- });
87
+ export function registerCursorLocalRuntimeFlags(pi) {
238
88
  pi.registerFlag("cursor-auto-review", {
239
89
  description: `Enable Cursor SDK local Auto-review for this run (or set ${CURSOR_AUTO_REVIEW_ENV}=1)`,
240
90
  type: "boolean",
@@ -261,120 +111,8 @@ function registerCursorRuntimeFlags(pi) {
261
111
  default: false,
262
112
  });
263
113
  }
264
- async function confirmCloudAcknowledgement(ctx, alreadyAcknowledged) {
265
- if (alreadyAcknowledged)
266
- return true;
267
- if (!ctx.hasUI) {
268
- ctx.ui.notify(`Cursor cloud runtime requires first-use acknowledgement. Pass --cursor-cloud-ack or set ${CURSOR_CLOUD_ACK_ENV}=1, then retry.`, "error");
269
- return false;
270
- }
271
- const confirmed = await ctx.ui.confirm("Enable Cursor Cloud runtime?", CURSOR_CLOUD_ACK_DISCLOSURE);
272
- if (!confirmed)
273
- ctx.ui.notify("Cursor cloud runtime change cancelled; no settings were written.", "info");
274
- return confirmed;
275
- }
276
- function registerCursorRuntimeCommand(pi, refreshStatus) {
277
- pi.registerCommand("cursor-runtime", {
278
- description: "Set Cursor runtime for this session: local or cloud",
279
- handler: async (args, ctx) => {
280
- const usage = "Usage: /cursor-runtime local|cloud [--save-user|--save-project]";
281
- const tokens = args.trim().split(/\s+/).filter(Boolean);
282
- const raw = tokens[0];
283
- const saveUser = tokens.includes("--save-user");
284
- const saveProject = tokens.includes("--save-project");
285
- const extra = tokens.slice(1).filter((token) => token !== "--save-user" && token !== "--save-project");
286
- const currentResolution = resolveCursorStatusRuntime(ctx);
287
- if (!raw) {
288
- ctx.ui.notify(currentResolution.kind === "invalid"
289
- ? `${currentResolution.message} ${usage}`
290
- : `Cursor runtime is ${formatResolvedCursorRuntime(currentResolution.runtime)}. ${usage}`, currentResolution.kind === "invalid" ? "error" : "info");
291
- return;
292
- }
293
- if ((raw !== "local" && raw !== "cloud") || extra.length > 0 || (saveUser && saveProject)) {
294
- ctx.ui.notify(`Invalid Cursor runtime arguments. ${usage}`, "error");
295
- return;
296
- }
297
- if (currentResolution.kind === "invalid") {
298
- ctx.ui.notify(`${currentResolution.message} Fix the explicit override before changing the session runtime.`, "error");
299
- return;
300
- }
301
- if (saveProject && (getCursorSessionCwd() !== ctx.cwd || !getCursorSessionProjectTrusted())) {
302
- ctx.ui.notify("Cannot save Cursor project config without explicit project-trust provenance. Ensure .pi/settings.json or another Pi project resource exists, trust the project, then restart pi; or restart with --approve. Project-local package installs must use --approve on every run that reads or writes .pi/cursor-sdk.json.", "error");
303
- return;
304
- }
305
- const cloudAcknowledged = raw === "cloud" && await confirmCloudAcknowledgement(ctx, resolveEffectiveCursorConfigForContext(ctx).cloud.acknowledged.value);
306
- if (raw === "cloud" && !cloudAcknowledged)
307
- return;
308
- if (saveUser || saveProject) {
309
- try {
310
- if (saveUser) {
311
- updateCursorSdkConfig(getCursorSdkUserConfigPath(), (current) => mergeCursorSdkConfigForUpdate(current, {
312
- runtime: raw,
313
- ...(cloudAcknowledged ? { cloud: { acknowledged: true } } : {}),
314
- }), { newFileMode: 0o600 });
315
- }
316
- else {
317
- updateCursorSdkConfig(getCursorSdkProjectConfigPath(ctx.cwd), (current) => mergeCursorSdkConfigForUpdate(current, { runtime: raw }));
318
- }
319
- }
320
- catch (error) {
321
- const effectiveResolution = resolveCursorStatusRuntime(ctx);
322
- refreshStatus(ctx);
323
- const effective = effectiveResolution.kind === "valid"
324
- ? ` Effective runtime remains ${formatResolvedCursorRuntime(effectiveResolution.runtime)}.`
325
- : ` ${effectiveResolution.message}`;
326
- ctx.ui.notify(`Failed to save Cursor runtime preference to ${saveUser ? "user" : "project"} config: ${error instanceof Error ? error.message : String(error)}.${effective}`, "error");
327
- return;
328
- }
329
- }
330
- try {
331
- persistCursorRuntimePreference(pi, raw, cloudAcknowledged);
332
- }
333
- catch (error) {
334
- const effectiveResolution = resolveCursorStatusRuntime(ctx);
335
- refreshStatus(ctx);
336
- const effective = effectiveResolution.kind === "valid"
337
- ? ` Effective runtime is ${formatResolvedCursorRuntime(effectiveResolution.runtime)}.`
338
- : ` ${effectiveResolution.message}`;
339
- const persisted = saveUser
340
- ? "User config was saved, but persisting the session runtime entry failed."
341
- : saveProject
342
- ? "Project config was saved, but persisting the session runtime entry failed."
343
- : "Persisting the session runtime entry failed.";
344
- ctx.ui.notify(`${persisted} ${error instanceof Error ? error.message : String(error)}.${effective}`, "error");
345
- return;
346
- }
347
- const effectiveResolution = resolveCursorStatusRuntime(ctx);
348
- refreshStatus(ctx);
349
- const saved = saveUser
350
- ? " Saved to user config."
351
- : saveProject
352
- ? " Saved to project config; cloud acknowledgement remains session/user-scoped."
353
- : "";
354
- const effective = effectiveResolution.kind === "valid"
355
- ? ` Effective runtime is ${formatResolvedCursorRuntime(effectiveResolution.runtime)}.`
356
- : ` ${effectiveResolution.message}`;
357
- ctx.ui.notify(raw === "cloud"
358
- ? `Cursor runtime request saved for this session and cloud risk acknowledged.${effective}${saved}`
359
- : `Cursor runtime request saved for this session.${effective}${saved}`, "info");
360
- },
361
- });
362
- }
363
- export function registerCursorCloudRuntimeControls(pi, options) {
364
- registerCursorCloudLifecycleLedger(pi);
365
- registerCursorRuntimeFlags(pi);
366
- registerCursorRuntimeCommand(pi, options.refreshStatus);
367
- pi.registerCommand("cursor-cloud", {
368
- description: "List, archive, or delete recorded Cursor cloud agents for this session branch",
369
- handler: async (args, ctx) => {
370
- await runCursorCloudLifecycleCommand(pi, args, ctx);
371
- },
372
- });
373
- }
374
114
  export function resetCursorRuntimeStateForTests() {
375
115
  cliCursorSnapshot = { config: {} };
376
116
  cliLocalForceConsumed = false;
377
117
  envLocalForceConsumed = false;
378
- sessionCursorRuntime = undefined;
379
- sessionCursorCloudAcknowledged = false;
380
118
  }
@@ -4,9 +4,6 @@ const BILLED_USAGE_TIMEOUT_MS = 5000;
4
4
  // ponytail: process-lifetime watermark of billed usage UUIDs per agentId; reset on process exit.
5
5
  // Upgrade to session-scoped storage if multi-day processes retain enough agent IDs to matter.
6
6
  const seenBilledRunIdsByAgent = new Map();
7
- export function isCursorSdkClientMintedRunId(runId) {
8
- return runId.startsWith("run-");
9
- }
10
7
  export function sumCursorSdkTurnUsage(usages) {
11
8
  if (usages.length === 0)
12
9
  return undefined;
@@ -49,31 +46,19 @@ export function selectCursorBilledTurnUsage(agentUsage, options) {
49
46
  const usage = readCursorSdkTurnUsage(record?.usage);
50
47
  return runId && usage ? [{ runId, usage }] : [];
51
48
  });
52
- if (options.runtime === "cloud" && options.runId) {
53
- const match = runs.find((run) => run.runId === options.runId);
54
- return match ? { turn: match.usage, runIds: [match.runId] } : { runIds: [] };
55
- }
56
49
  const unseen = runs.filter((run) => !options.seenRunIds?.has(run.runId));
57
50
  return { turn: sumCursorSdkTurnUsage(unseen.map((run) => run.usage)), runIds: unseen.map((run) => run.runId) };
58
51
  }
59
- export async function fetchCursorSdkAgentUsage(agent, options) {
52
+ export async function fetchCursorSdkAgentUsage(agent) {
60
53
  if (typeof agent.getUsage !== "function")
61
54
  return undefined;
62
- const query = options.runtime === "cloud" && options.runId && isCursorSdkClientMintedRunId(options.runId)
63
- ? { runId: options.runId }
64
- : undefined;
65
- return withTimeout(Promise.resolve(agent.getUsage(query)));
55
+ return withTimeout(Promise.resolve(agent.getUsage()));
66
56
  }
67
57
  export async function attachCursorSdkBilledTurnUsage(options) {
68
- const agentUsage = await fetchCursorSdkAgentUsage(options.agent, {
69
- runtime: options.runtime,
70
- runId: options.runId,
71
- });
58
+ const agentUsage = await fetchCursorSdkAgentUsage(options.agent);
72
59
  if (!agentUsage)
73
60
  return {};
74
61
  const selected = selectCursorBilledTurnUsage(agentUsage, {
75
- runtime: options.runtime,
76
- runId: options.runtime === "cloud" ? options.runId : undefined,
77
62
  seenRunIds: peekCursorBilledUsageRunIds(options.agentId),
78
63
  });
79
64
  rememberCursorBilledUsageRunIds(options.agentId, selected.runIds);
@@ -0,0 +1,88 @@
1
+ import { accessSync, constants, existsSync } from "node:fs";
2
+ import { createRequire } from "node:module";
3
+ import { dirname, isAbsolute, join } from "node:path";
4
+ const RIPGREP_ENV = "CURSOR_RIPGREP_PATH";
5
+ const TREE_SITTER_VENDOR_ENV = "CURSOR_TREE_SITTER_VENDOR_DIR";
6
+ function cursorSdkPlatformPackageName() {
7
+ return `@cursor/sdk-${process.platform}-${process.arch}`;
8
+ }
9
+ function resolveCursorSdkPlatformPackageDir(fromModuleUrl = import.meta.url) {
10
+ try {
11
+ const require = createRequire(fromModuleUrl);
12
+ const sdkEntry = require.resolve("@cursor/sdk");
13
+ return dirname(require.resolve(`${cursorSdkPlatformPackageName()}/package.json`, {
14
+ paths: [dirname(sdkEntry)],
15
+ }));
16
+ }
17
+ catch {
18
+ return undefined;
19
+ }
20
+ }
21
+ function resolveCursorSdkPlatformBinary(binaryName, fromModuleUrl = import.meta.url) {
22
+ try {
23
+ const packageDirectory = resolveCursorSdkPlatformPackageDir(fromModuleUrl);
24
+ if (!packageDirectory)
25
+ return undefined;
26
+ const binaryPath = join(packageDirectory, "bin", binaryName);
27
+ accessSync(binaryPath, constants.X_OK);
28
+ return binaryPath;
29
+ }
30
+ catch {
31
+ return undefined;
32
+ }
33
+ }
34
+ export function resolveBundledCursorRipgrepPath(fromModuleUrl = import.meta.url) {
35
+ return resolveCursorSdkPlatformBinary(process.platform === "win32" ? "rg.exe" : "rg", fromModuleUrl);
36
+ }
37
+ export function ensureCursorRipgrepPath() {
38
+ const configuredPath = process.env[RIPGREP_ENV];
39
+ if (configuredPath && isAbsolute(configuredPath))
40
+ return configuredPath;
41
+ const bundledPath = resolveBundledCursorRipgrepPath();
42
+ if (bundledPath)
43
+ process.env[RIPGREP_ENV] = bundledPath;
44
+ return bundledPath;
45
+ }
46
+ export function resolveBundledCursorTreeSitterVendorDir(fromModuleUrl = import.meta.url) {
47
+ const packageDirectory = resolveCursorSdkPlatformPackageDir(fromModuleUrl);
48
+ if (!packageDirectory)
49
+ return undefined;
50
+ const vendorDir = join(packageDirectory, "vendor");
51
+ if (!existsSync(join(vendorDir, "tree-sitter", "index.js")))
52
+ return undefined;
53
+ return vendorDir;
54
+ }
55
+ export function ensureCursorTreeSitterVendorDir() {
56
+ const configuredPath = process.env[TREE_SITTER_VENDOR_ENV];
57
+ if (configuredPath && isAbsolute(configuredPath))
58
+ return configuredPath;
59
+ const bundledPath = resolveBundledCursorTreeSitterVendorDir();
60
+ if (bundledPath)
61
+ process.env[TREE_SITTER_VENDOR_ENV] = bundledPath;
62
+ return bundledPath;
63
+ }
64
+ export function resolveBundledCursorSandboxPath(fromModuleUrl = import.meta.url) {
65
+ return resolveCursorSdkPlatformBinary(process.platform === "win32" ? "cursorsandbox.exe" : "cursorsandbox", fromModuleUrl);
66
+ }
67
+ /**
68
+ * The SDK finds `cursorsandbox` by walking from `process.argv[1]`, not from
69
+ * `require.resolve`. Point that walk at the installed platform package for the
70
+ * duration of local `Agent.create` / `Agent.resume`.
71
+ */
72
+ export async function runWithCursorSdkPlatformPackageVisible(run, fromModuleUrl = import.meta.url) {
73
+ const packageDirectory = resolveCursorSdkPlatformPackageDir(fromModuleUrl);
74
+ if (!packageDirectory)
75
+ return run();
76
+ const hostEntry = join(packageDirectory, "package.json");
77
+ if (!existsSync(hostEntry))
78
+ return run();
79
+ const previousArgv1 = process.argv[1];
80
+ process.argv[1] = hostEntry;
81
+ try {
82
+ return await run();
83
+ }
84
+ finally {
85
+ if (process.argv[1] === hostEntry)
86
+ process.argv[1] = previousArgv1;
87
+ }
88
+ }
@@ -4,6 +4,7 @@ import { computeCursorContextFingerprint } from "./context.js";
4
4
  import { getCursorSessionFile, getCursorSessionScopeGeneration, getCursorSessionScopeKey } from "./cursor-session-scope.js";
5
5
  import { getMatchingCursorSessionAgentResumeHandle, persistCursorSessionAgentResumeHandle, } from "./cursor-session-agent-resume.js";
6
6
  import { loadCursorSdk } from "./cursor-sdk-runtime.js";
7
+ import { runWithCursorSdkPlatformPackageVisible } from "./cursor-sdk-platform-package.js";
7
8
  import { cursorSessionStoreIdentitiesEqual, openCursorSessionStore, openCursorSessionStoreForScope, } from "./cursor-session-store.js";
8
9
  export const CURSOR_LOCAL_AGENT_IDLE_MS = 5 * 60 * 1000;
9
10
  class SessionCursorAgentCreationSupersededError extends Error {
@@ -330,22 +331,25 @@ async function createSessionAgentEntry(scopeKey, persistentStore, instanceId, se
330
331
  let agent;
331
332
  let effectiveSendState = sendState;
332
333
  let resumed = false;
333
- if (resumeHandle && resumeAttemptAllowed && resumeAgent) {
334
- try {
335
- agent = await resumeAgent(resumeHandle.agentId, buildAgentOptions());
336
- effectiveSendState = { ...resumeHandle.sendState };
337
- resumed = true;
338
- }
339
- catch {
340
- if (persistentStore)
341
- resumeNotice = LOCAL_RESUME_FALLBACK_NOTICE;
342
- if (!cursorSessionStoreIdentitiesEqual(sessionStore.identity, identities.sessionStore)) {
343
- await sessionStore.dispose().catch(() => undefined);
344
- sessionStore = await openCursorSessionStore(params.cwd, identities.sessionStore);
334
+ await runWithCursorSdkPlatformPackageVisible(async () => {
335
+ if (resumeHandle && resumeAttemptAllowed && resumeAgent) {
336
+ try {
337
+ agent = await resumeAgent(resumeHandle.agentId, buildAgentOptions());
338
+ effectiveSendState = { ...resumeHandle.sendState };
339
+ resumed = true;
340
+ }
341
+ catch {
342
+ if (persistentStore)
343
+ resumeNotice = LOCAL_RESUME_FALLBACK_NOTICE;
344
+ const currentStore = sessionStore;
345
+ if (currentStore && !cursorSessionStoreIdentitiesEqual(currentStore.identity, identities.sessionStore)) {
346
+ await currentStore.dispose().catch(() => undefined);
347
+ sessionStore = await openCursorSessionStore(params.cwd, identities.sessionStore);
348
+ }
345
349
  }
346
350
  }
347
- }
348
- agent ??= await createAgent(buildAgentOptions());
351
+ agent ??= await createAgent(buildAgentOptions());
352
+ });
349
353
  if (!agent)
350
354
  throw new Error("Cursor SDK agent creation returned no agent");
351
355
  if (!sessionStore)
@@ -5,7 +5,6 @@ import { arePiToolsDisabled } from "./cursor-active-tools.js";
5
5
  import { isCursorModel } from "./cursor-model.js";
6
6
  import { registerCursorModelLifecycle } from "./cursor-model-lifecycle.js";
7
7
  import { resolveCursorPiToolBridgeEnabled } from "./cursor-pi-tool-bridge-env.js";
8
- import { resolveEffectiveCursorConfigForContext } from "./cursor-runtime-state.js";
9
8
  export const CURSOR_ACTIVATE_SKILL_TOOL_NAME = "cursor_activate_skill";
10
9
  export const CURSOR_ACTIVATE_SKILL_MCP_NAME = "pi__cursor_activate_skill";
11
10
  const AVAILABLE_SKILLS_SECTION_PATTERN = /\n\nThe following skills provide specialized instructions for specific tasks\.[\s\S]*?<\/available_skills>/;
@@ -29,15 +28,12 @@ function setCurrentSkills(skills) {
29
28
  function getAvailableSkillNames() {
30
29
  return [...currentSkillsByName.keys()].sort();
31
30
  }
32
- function resolveEffectiveRuntimeForSkillLifecycle(cursorModel, ctx) {
33
- return cursorModel ? resolveEffectiveCursorConfigForContext(ctx).runtime.value : "local";
31
+ function shouldExposeSkillTool(model) {
32
+ return isCursorModel(model) && resolveCursorPiToolBridgeEnabled() && currentSkillsByName.size > 0;
34
33
  }
35
- function shouldExposeSkillTool(model, runtime) {
36
- return runtime === "local" && isCursorModel(model) && resolveCursorPiToolBridgeEnabled() && currentSkillsByName.size > 0;
37
- }
38
- function syncCursorSkillToolForModel(pi, model, runtime) {
34
+ function syncCursorSkillToolForModel(pi, model) {
39
35
  const activeToolNames = new Set(pi.getActiveTools());
40
- const shouldBeActive = !arePiToolsDisabled(pi) && shouldExposeSkillTool(model, runtime);
36
+ const shouldBeActive = !arePiToolsDisabled(pi) && shouldExposeSkillTool(model);
41
37
  const alreadyActive = activeToolNames.has(CURSOR_ACTIVATE_SKILL_TOOL_NAME);
42
38
  if (shouldBeActive === alreadyActive)
43
39
  return;
@@ -71,11 +67,9 @@ export function formatCursorSkillsForPrompt(skills) {
71
67
  lines.push("</available_skills>");
72
68
  return lines.join("\n");
73
69
  }
74
- export function resolveCursorSkillSystemPrompt(systemPrompt, model, systemPromptOptions, runtime = "local") {
70
+ export function resolveCursorSkillSystemPrompt(systemPrompt, model, systemPromptOptions) {
75
71
  if (!isCursorModel(model))
76
72
  return systemPrompt;
77
- if (runtime === "cloud")
78
- return systemPrompt.replace(AVAILABLE_SKILLS_SECTION_PATTERN, "");
79
73
  const skills = getVisibleSkills(systemPromptOptions?.skills);
80
74
  if (skills.length === 0)
81
75
  return systemPrompt;
@@ -186,9 +180,9 @@ export function registerCursorSkillTool(pi) {
186
180
  }
187
181
  },
188
182
  });
189
- const clearSkillsAndSync = (model, runtime = "local") => {
183
+ const clearSkillsAndSync = (model) => {
190
184
  setCurrentSkills([]);
191
- syncCursorSkillToolForModel(pi, model, runtime);
185
+ syncCursorSkillToolForModel(pi, model);
192
186
  };
193
187
  registerCursorModelLifecycle(pi, {
194
188
  sessionStart: (_event, ctx) => {
@@ -198,23 +192,19 @@ export function registerCursorSkillTool(pi) {
198
192
  clearSkillsAndSync(event.model);
199
193
  },
200
194
  turnStart: (_event, ctx) => {
201
- const cursorModel = isCursorModel(ctx.model);
202
- const runtime = resolveEffectiveRuntimeForSkillLifecycle(cursorModel, ctx);
203
- if (!cursorModel || runtime === "cloud")
195
+ if (!isCursorModel(ctx.model))
204
196
  setCurrentSkills([]);
205
- syncCursorSkillToolForModel(pi, ctx.model, runtime);
197
+ syncCursorSkillToolForModel(pi, ctx.model);
206
198
  },
207
199
  beforeAgentStart: (event, ctx) => {
208
- const cursorModel = isCursorModel(ctx.model);
209
- const runtime = resolveEffectiveRuntimeForSkillLifecycle(cursorModel, ctx);
210
- if (cursorModel && runtime === "local") {
200
+ if (isCursorModel(ctx.model)) {
211
201
  setCurrentSkills(event.systemPromptOptions?.skills);
212
202
  }
213
203
  else {
214
204
  setCurrentSkills([]);
215
205
  }
216
- syncCursorSkillToolForModel(pi, ctx.model, runtime);
217
- const resolved = resolveCursorSkillSystemPrompt(event.systemPrompt, ctx.model, event.systemPromptOptions, runtime);
206
+ syncCursorSkillToolForModel(pi, ctx.model);
207
+ const resolved = resolveCursorSkillSystemPrompt(event.systemPrompt, ctx.model, event.systemPromptOptions);
218
208
  if (resolved === event.systemPrompt)
219
209
  return undefined;
220
210
  return { systemPrompt: resolved };
@@ -10,7 +10,7 @@ import { getCursorSessionScopeKey } from "./cursor-session-scope.js";
10
10
  import { refreshSessionCursorAgentConfig } from "./cursor-session-agent.js";
11
11
  import { getCursorModelMetadata } from "./model-discovery.js";
12
12
  import { buildCursorBridgeExcludeToolNames, cursorFastDefaultsFromConfig, getCursorSdkUserConfigPath, loadCursorSdkUserConfig, mergeCursorSdkConfigForUpdate, resolveCursorFastDefault, updateCursorSdkConfig, } from "./cursor-config.js";
13
- import { CURSOR_RUNTIME_ENTRY_TYPE, formatCursorStatus, registerCursorCloudRuntimeControls, resetCursorRuntimeStateForTests, resolveCursorStatusRuntime, resolveEffectiveCursorConfigForContext, restoreCursorCliState, restoreSessionCursorRuntimeState, } from "./cursor-runtime-state.js";
13
+ import { formatCursorStatus, registerCursorLocalRuntimeFlags, resetCursorRuntimeStateForTests, resolveCursorStatusRuntime, resolveEffectiveCursorConfigForContext, restoreCursorCliState, } from "./cursor-runtime-state.js";
14
14
  export { consumeCursorLocalForceOverride, getCursorCliConfig, getCursorSessionConfig, } from "./cursor-runtime-state.js";
15
15
  const FAST_ENTRY_TYPE = "cursor-fast-state";
16
16
  const MODE_ENTRY_TYPE = "cursor-mode-state";
@@ -88,7 +88,6 @@ function restoreSessionCursorMode(branch) {
88
88
  }
89
89
  function restoreSessionCursorPreferences(ctx) {
90
90
  const branch = ctx.sessionManager.getBranch();
91
- restoreSessionCursorRuntimeState(branch);
92
91
  restoreSessionFastPreferences(branch);
93
92
  restoreSessionCursorMode(branch);
94
93
  restoreSessionCursorHttp1(branch);
@@ -160,12 +159,11 @@ function updateCursorStatus(ctx, model = ctx.model) {
160
159
  const modeResolution = resolveCursorAgentMode();
161
160
  const mode = modeResolution.kind === "invalid" ? "invalid" : modeResolution.mode;
162
161
  if (resolution.kind === "invalid") {
163
- ctx.ui.setStatus("cursor", formatCursorStatus("invalid", undefined, mode));
162
+ ctx.ui.setStatus("cursor", formatCursorStatus(undefined, "invalid"));
164
163
  return;
165
164
  }
166
- const runtime = resolution.runtime.value;
167
- const fast = runtime === "cloud" ? undefined : metadata?.supportsFast ? getEffectiveFast(model.id) : undefined;
168
- ctx.ui.setStatus("cursor", formatCursorStatus(runtime, fast, mode, resolution.useHttp1ForAgent.value));
165
+ const fast = metadata?.supportsFast ? getEffectiveFast(model.id) : undefined;
166
+ ctx.ui.setStatus("cursor", formatCursorStatus(fast, mode, resolution.useHttp1ForAgent.value));
169
167
  }
170
168
  function getCurrentCursorMetadata(ctx) {
171
169
  const model = ctx.model;
@@ -307,7 +305,7 @@ export function getEffectiveFastForModelId(modelId) {
307
305
  return getEffectiveFast(modelId);
308
306
  }
309
307
  export function registerCursorRuntimeControls(pi) {
310
- registerCursorCloudRuntimeControls(pi, { refreshStatus: updateCursorStatus });
308
+ registerCursorLocalRuntimeFlags(pi);
311
309
  pi.registerFlag("cursor-fast", {
312
310
  description: "Force Cursor fast mode for this run when the selected Cursor model supports it",
313
311
  type: "boolean",
@@ -517,7 +515,6 @@ export const __testUtils = {
517
515
  FAST_ENTRY_TYPE,
518
516
  MODE_ENTRY_TYPE,
519
517
  CURSOR_HTTP1_ENTRY_TYPE,
520
- RUNTIME_ENTRY_TYPE: CURSOR_RUNTIME_ENTRY_TYPE,
521
518
  DEFAULT_CURSOR_AGENT_MODE,
522
519
  getConfigPath,
523
520
  loadGlobalFastPreferences,
@@ -150,7 +150,7 @@ function applyResolvedCursorOccupancy(partial, model, context, localTurn) {
150
150
  }
151
151
  export function applyCursorUsage(partial, model, context, sessionInputTokens, sdkUsage) {
152
152
  const billed = sdkUsage?.billed;
153
- const localTurn = sdkUsage?.runtime === "local" ? sdkUsage.turn : undefined;
153
+ const localTurn = sdkUsage?.turn;
154
154
  if (billed && isCursorSdkUsagePartitionSafe(billed, model)) {
155
155
  applyCursorSdkUsage(partial, billed);
156
156
  applyResolvedCursorOccupancy(partial, model, context, localTurn);