pi-cursor-sdk 0.1.60 → 0.1.61

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 (56) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/README.md +123 -9
  3. package/docs/cursor-model-ux-spec.md +19 -11
  4. package/docs/cursor-native-tool-replay.md +4 -2
  5. package/docs/cursor-tool-surfaces.md +7 -1
  6. package/docs/platform-smoke.md +51 -20
  7. package/package.json +13 -2
  8. package/platform-smoke.config.mjs +1 -0
  9. package/scripts/cloud-runtime-smoke.d.mts +40 -1
  10. package/scripts/cloud-runtime-smoke.mjs +444 -325
  11. package/scripts/lib/cloud-smoke-artifacts.d.mts +16 -0
  12. package/scripts/lib/cloud-smoke-artifacts.mjs +94 -0
  13. package/scripts/lib/cloud-smoke-cleanup-evidence.d.mts +209 -0
  14. package/scripts/lib/cloud-smoke-cleanup-evidence.mjs +585 -0
  15. package/scripts/lib/cloud-smoke-github.d.mts +78 -0
  16. package/scripts/lib/cloud-smoke-github.mjs +331 -0
  17. package/scripts/lib/cloud-smoke-pi-runner.d.mts +42 -0
  18. package/scripts/lib/cloud-smoke-pi-runner.mjs +214 -0
  19. package/scripts/lib/cloud-smoke-shutdown.d.mts +67 -0
  20. package/scripts/lib/cloud-smoke-shutdown.mjs +133 -0
  21. package/scripts/lib/local-resume-smoke-harness.mjs +2 -0
  22. package/scripts/local-resume-cleanup-smoke.mjs +11 -2
  23. package/scripts/platform-smoke/card-detect.mjs +1 -0
  24. package/scripts/platform-smoke/scenarios.d.mts +22 -0
  25. package/scripts/platform-smoke/scenarios.mjs +17 -0
  26. package/scripts/platform-smoke/targets.mjs +1 -0
  27. package/src/cursor-agent-message-web-tools.ts +14 -6
  28. package/src/cursor-cloud-local-state.ts +536 -0
  29. package/src/cursor-cloud-options.ts +46 -70
  30. package/src/cursor-config.ts +160 -14
  31. package/src/cursor-http1.ts +65 -0
  32. package/src/cursor-live-run-coordinator.ts +8 -1
  33. package/src/cursor-pi-tool-bridge-abort.ts +10 -3
  34. package/src/cursor-pi-tool-bridge-constants.ts +6 -0
  35. package/src/cursor-pi-tool-bridge-env.ts +9 -0
  36. package/src/cursor-pi-tool-bridge-run.ts +27 -5
  37. package/src/cursor-pi-tool-bridge.ts +11 -2
  38. package/src/cursor-provider-errors.ts +72 -2
  39. package/src/cursor-provider-live-run-drain.ts +2 -1
  40. package/src/cursor-provider-run-finalizer.ts +8 -2
  41. package/src/cursor-provider-run-outcome.ts +7 -1
  42. package/src/cursor-provider-turn-finalize.ts +23 -4
  43. package/src/cursor-provider-turn-prepare.ts +17 -3
  44. package/src/cursor-provider-turn-runner.ts +5 -0
  45. package/src/cursor-provider-turn-send.ts +1 -1
  46. package/src/cursor-question-tool.ts +8 -0
  47. package/src/cursor-ripgrep-path.ts +32 -0
  48. package/src/cursor-runtime-state.ts +119 -100
  49. package/src/cursor-sdk-process-error-guard.ts +19 -0
  50. package/src/cursor-session-agent-cleanup.ts +66 -15
  51. package/src/cursor-session-agent-lifecycle.ts +9 -4
  52. package/src/cursor-session-agent-resume.ts +50 -7
  53. package/src/cursor-session-agent.ts +106 -67
  54. package/src/cursor-session-scope.ts +22 -2
  55. package/src/cursor-session-store.ts +155 -0
  56. package/src/cursor-state.ts +162 -21
@@ -6,6 +6,14 @@ import {
6
6
  resolveCursorToolManifestEnabled,
7
7
  } from "./cursor-tool-manifest.js";
8
8
  import { runCursorSessionAgentCleanupCommand } from "./cursor-session-agent-cleanup.js";
9
+ import {
10
+ CURSOR_HTTP1_ENTRY_TYPE,
11
+ getStoredCursorHttp1Enabled,
12
+ isCursorHttp1EntryData,
13
+ setCursorHttp1GlobalPreferenceAuthoritative,
14
+ setStoredCursorHttp1Enabled,
15
+ type CursorHttp1EntryData,
16
+ } from "./cursor-http1.js";
9
17
  import {
10
18
  buildCursorPiToolBridgeSnapshot,
11
19
  CURSOR_PI_TOOL_BRIDGE_ENV,
@@ -26,10 +34,9 @@ import {
26
34
  cursorFastDefaultsFromConfig,
27
35
  getCursorSdkUserConfigPath,
28
36
  loadCursorSdkUserConfig,
37
+ mergeCursorSdkConfigForUpdate,
29
38
  resolveCursorFastDefault,
30
- saveCursorSdkUserConfig,
31
- withCursorFastDefaults,
32
- type CursorSdkConfig,
39
+ updateCursorSdkConfig,
33
40
  } from "./cursor-config.js";
34
41
  import {
35
42
  consumeCursorLocalForceOverride,
@@ -79,6 +86,7 @@ type CursorCliModeState =
79
86
  | { kind: "invalid"; raw: string; message: string };
80
87
 
81
88
  const sessionFastPreferences = new Map<string, boolean>();
89
+ const authoritativeGlobalFastPreferenceIds = new Set<string>();
82
90
  let globalFastPreferences = new Map<string, boolean>();
83
91
  let cliForceFast = false;
84
92
  let cliForceNoFast = false;
@@ -118,9 +126,30 @@ function loadGlobalFastPreferences(): Map<string, boolean> {
118
126
  return cursorFastDefaultsFromConfig(loadCursorSdkUserConfig());
119
127
  }
120
128
 
121
- function saveGlobalFastPreferences(): void {
122
- const currentConfig: CursorSdkConfig = loadCursorSdkUserConfig();
123
- saveCursorSdkUserConfig(withCursorFastDefaults(currentConfig, globalFastPreferences));
129
+ function saveGlobalFastPreference(modelId: string, fast: boolean): void {
130
+ updateCursorSdkConfig(
131
+ getConfigPath(),
132
+ (current) => {
133
+ const fastDefaults = { ...asRecord(current.fastDefaults), [modelId]: fast };
134
+ return {
135
+ ...current,
136
+ fastDefaults: Object.fromEntries(
137
+ Object.entries(fastDefaults).sort(([a], [b]) => a.localeCompare(b)),
138
+ ),
139
+ };
140
+ },
141
+ { newFileMode: 0o600 },
142
+ );
143
+ }
144
+
145
+ function saveGlobalCursorHttp1Enabled(enabled: boolean): void {
146
+ updateCursorSdkConfig(
147
+ getConfigPath(),
148
+ (current) => mergeCursorSdkConfigForUpdate(current, {
149
+ local: { useHttp1ForAgent: enabled },
150
+ }),
151
+ { newFileMode: 0o600 },
152
+ );
124
153
  }
125
154
 
126
155
  function restoreSessionFastPreferences(branch: readonly SessionEntry[]): void {
@@ -149,6 +178,17 @@ function restoreSessionCursorPreferences(ctx: { sessionManager: Pick<ExtensionCo
149
178
  restoreSessionCursorRuntimeState(branch);
150
179
  restoreSessionFastPreferences(branch);
151
180
  restoreSessionCursorMode(branch);
181
+ restoreSessionCursorHttp1(branch);
182
+ }
183
+
184
+ function restoreSessionCursorHttp1(branch: readonly SessionEntry[]): void {
185
+ setStoredCursorHttp1Enabled(undefined);
186
+ for (const entry of branch) {
187
+ if (entry.type !== "custom" || entry.customType !== CURSOR_HTTP1_ENTRY_TYPE) continue;
188
+ if (isCursorHttp1EntryData(entry.data)) {
189
+ setStoredCursorHttp1Enabled(entry.data.enabled);
190
+ }
191
+ }
152
192
  }
153
193
 
154
194
  function getFastPreferenceModelId(metadata: NonNullable<ReturnType<typeof getCursorModelMetadata>>): string {
@@ -174,7 +214,9 @@ function getEffectiveFast(modelId: string): boolean | undefined {
174
214
  cliForceNoFast,
175
215
  cliForceFast,
176
216
  aliasOverride: metadata.fastOverride,
177
- sessionValue: getMapFastPreference(sessionFastPreferences, metadata),
217
+ sessionValue: authoritativeGlobalFastPreferenceIds.has(getFastPreferenceModelId(metadata))
218
+ ? undefined
219
+ : getMapFastPreference(sessionFastPreferences, metadata),
178
220
  userValue: getMapFastPreference(globalFastPreferences, metadata),
179
221
  modelDefault: metadata.defaultFast,
180
222
  }).value;
@@ -226,7 +268,10 @@ function updateCursorStatus(ctx: CursorStatusContext & Pick<ExtensionContext, "m
226
268
  }
227
269
  const runtime = resolution.runtime.value;
228
270
  const fast = runtime === "cloud" ? undefined : metadata?.supportsFast ? getEffectiveFast(model.id) : undefined;
229
- ctx.ui.setStatus("cursor", formatCursorStatus(runtime, fast, mode));
271
+ ctx.ui.setStatus(
272
+ "cursor",
273
+ formatCursorStatus(runtime, fast, mode, resolution.useHttp1ForAgent.value),
274
+ );
230
275
  }
231
276
 
232
277
  function getCurrentCursorMetadata(ctx: Pick<ExtensionContext, "model">) {
@@ -243,28 +288,30 @@ function restoreMapValue(map: Map<string, boolean>, key: string, previous: boole
243
288
  }
244
289
  }
245
290
 
246
- function persistFastPreference(pi: Pick<ExtensionAPI, "appendEntry">, modelId: string, fast: boolean): void {
291
+ function persistFastPreference(
292
+ pi: Pick<ExtensionAPI, "appendEntry">,
293
+ modelId: string,
294
+ fast: boolean,
295
+ ): unknown | undefined {
247
296
  const previousSession = sessionFastPreferences.get(modelId);
248
297
  const previousGlobal = globalFastPreferences.get(modelId);
249
- let savedGlobal = false;
250
298
  sessionFastPreferences.set(modelId, fast);
251
299
  globalFastPreferences.set(modelId, fast);
252
300
  try {
253
- saveGlobalFastPreferences();
254
- savedGlobal = true;
255
- pi.appendEntry<CursorFastEntryData>(FAST_ENTRY_TYPE, { modelId, fast });
301
+ saveGlobalFastPreference(modelId, fast);
256
302
  } catch (error) {
257
303
  restoreMapValue(sessionFastPreferences, modelId, previousSession);
258
304
  restoreMapValue(globalFastPreferences, modelId, previousGlobal);
259
- if (savedGlobal) {
260
- try {
261
- saveGlobalFastPreferences();
262
- } catch {
263
- // Preserve the original append failure reported to the user.
264
- }
265
- }
266
305
  throw error;
267
306
  }
307
+ try {
308
+ pi.appendEntry<CursorFastEntryData>(FAST_ENTRY_TYPE, { modelId, fast });
309
+ authoritativeGlobalFastPreferenceIds.delete(modelId);
310
+ return undefined;
311
+ } catch (error) {
312
+ authoritativeGlobalFastPreferenceIds.add(modelId);
313
+ return error;
314
+ }
268
315
  }
269
316
 
270
317
  function persistCursorModePreference(pi: Pick<ExtensionAPI, "appendEntry">, mode: AgentModeOption): void {
@@ -278,6 +325,28 @@ function persistCursorModePreference(pi: Pick<ExtensionAPI, "appendEntry">, mode
278
325
  }
279
326
  }
280
327
 
328
+ function persistCursorHttp1Preference(
329
+ pi: Pick<ExtensionAPI, "appendEntry">,
330
+ enabled: boolean,
331
+ ): unknown | undefined {
332
+ const previousSession = getStoredCursorHttp1Enabled();
333
+ setStoredCursorHttp1Enabled(enabled);
334
+ try {
335
+ saveGlobalCursorHttp1Enabled(enabled);
336
+ } catch (error) {
337
+ setStoredCursorHttp1Enabled(previousSession);
338
+ throw error;
339
+ }
340
+ try {
341
+ pi.appendEntry<CursorHttp1EntryData>(CURSOR_HTTP1_ENTRY_TYPE, { enabled });
342
+ setCursorHttp1GlobalPreferenceAuthoritative(false);
343
+ return undefined;
344
+ } catch (error) {
345
+ setCursorHttp1GlobalPreferenceAuthoritative(true);
346
+ return error;
347
+ }
348
+ }
349
+
281
350
  function restoreCliCursorMode(raw: boolean | string | undefined): void {
282
351
  cliCursorModeState = { kind: "unset" };
283
352
  if (raw === undefined || raw === "" || raw === false) return;
@@ -399,14 +468,22 @@ export function registerCursorRuntimeControls(pi: CursorRuntimeControlsExtension
399
468
  const preferenceModelId = getFastPreferenceModelId(metadata);
400
469
  const current = getEffectiveFast(metadata.piModelId) ?? false;
401
470
  const next = !current;
471
+ let appendError: unknown;
402
472
  try {
403
- persistFastPreference(pi, preferenceModelId, next);
473
+ appendError = persistFastPreference(pi, preferenceModelId, next);
404
474
  } catch (error) {
405
475
  updateCursorStatus(ctx);
406
476
  ctx.ui.notify(`Failed to save Cursor fast preference: ${error instanceof Error ? error.message : String(error)}`, "error");
407
477
  return;
408
478
  }
409
479
  updateCursorStatus(ctx);
480
+ if (appendError !== undefined) {
481
+ ctx.ui.notify(
482
+ `Cursor fast ${next ? "enabled" : "disabled"} was saved globally, but persisting the session entry failed: ${appendError instanceof Error ? appendError.message : String(appendError)}`,
483
+ "error",
484
+ );
485
+ return;
486
+ }
410
487
  ctx.ui.notify(`Cursor fast ${next ? "enabled" : "disabled"}`, "info");
411
488
  },
412
489
  });
@@ -418,6 +495,64 @@ export function registerCursorRuntimeControls(pi: CursorRuntimeControlsExtension
418
495
  },
419
496
  });
420
497
 
498
+ pi.registerCommand("cursor-http", {
499
+ description: "Toggle Cursor SDK HTTP/1.1/SSE transport compatibility mode",
500
+ handler: async (args, ctx) => {
501
+ const usage = "Usage: /cursor-http [on|off|toggle]";
502
+ const normalized = args.trim().toLowerCase();
503
+ const resolution = resolveCursorStatusRuntime(ctx);
504
+ if (resolution.kind === "invalid") {
505
+ ctx.ui.notify(resolution.message, "error");
506
+ return;
507
+ }
508
+ const setting = resolution.useHttp1ForAgent;
509
+ if (!normalized) {
510
+ ctx.ui.notify(
511
+ `Cursor HTTP/1.1/SSE transport is ${setting.value ? "enabled" : "disabled"} (source: ${setting.source}). ${usage}`,
512
+ "info",
513
+ );
514
+ return;
515
+ }
516
+ const next = normalized === "on"
517
+ ? true
518
+ : normalized === "off"
519
+ ? false
520
+ : normalized === "toggle"
521
+ ? !setting.value
522
+ : undefined;
523
+ if (next === undefined) {
524
+ ctx.ui.notify(
525
+ `Invalid Cursor HTTP transport mode "${args.trim()}". ${usage}`,
526
+ "error",
527
+ );
528
+ return;
529
+ }
530
+ let appendError: unknown;
531
+ try {
532
+ appendError = persistCursorHttp1Preference(pi, next);
533
+ } catch (error) {
534
+ updateCursorStatus(ctx);
535
+ ctx.ui.notify(
536
+ `Failed to save Cursor HTTP/1.1 preference: ${error instanceof Error ? error.message : String(error)}`,
537
+ "error",
538
+ );
539
+ return;
540
+ }
541
+ updateCursorStatus(ctx);
542
+ if (appendError !== undefined) {
543
+ ctx.ui.notify(
544
+ `Cursor HTTP/1.1 preference was saved globally, but persisting the session entry failed: ${appendError instanceof Error ? appendError.message : String(appendError)}`,
545
+ "error",
546
+ );
547
+ return;
548
+ }
549
+ ctx.ui.notify(
550
+ `Cursor HTTP/1.1/SSE transport ${next ? "enabled" : "disabled"}`,
551
+ "info",
552
+ );
553
+ },
554
+ });
555
+
421
556
  pi.registerCommand("cursor-local-resume-cleanup", {
422
557
  description: "Dry-run or delete recorded superseded local Cursor SDK agents",
423
558
  handler: async (args, ctx) => {
@@ -495,6 +630,8 @@ export function registerCursorRuntimeControls(pi: CursorRuntimeControlsExtension
495
630
 
496
631
  registerCursorModelLifecycle(pi, {
497
632
  sessionStart: (_event, ctx) => {
633
+ authoritativeGlobalFastPreferenceIds.clear();
634
+ setCursorHttp1GlobalPreferenceAuthoritative(false);
498
635
  globalFastPreferences = loadGlobalFastPreferences();
499
636
  cliForceFast = pi.getFlag("cursor-fast") === true;
500
637
  cliForceNoFast = pi.getFlag("cursor-no-fast") === true;
@@ -511,14 +648,18 @@ export function registerCursorRuntimeControls(pi: CursorRuntimeControlsExtension
511
648
 
512
649
  function resetCursorModeStateForTests(): void {
513
650
  sessionCursorAgentMode = undefined;
651
+ setCursorHttp1GlobalPreferenceAuthoritative(false);
652
+ setStoredCursorHttp1Enabled(undefined);
514
653
  cliCursorModeState = { kind: "unset" };
515
654
  resetCursorRuntimeStateForTests();
516
655
  invalidCursorModeNotifiedSessionScopeKeys.clear();
656
+ authoritativeGlobalFastPreferenceIds.clear();
517
657
  }
518
658
 
519
659
  export const __testUtils = {
520
660
  FAST_ENTRY_TYPE,
521
661
  MODE_ENTRY_TYPE,
662
+ CURSOR_HTTP1_ENTRY_TYPE,
522
663
  RUNTIME_ENTRY_TYPE: CURSOR_RUNTIME_ENTRY_TYPE,
523
664
  DEFAULT_CURSOR_AGENT_MODE,
524
665
  getConfigPath,