@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.
Files changed (91) hide show
  1. package/CHANGELOG.md +22 -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-extension-factory-guard.js +31 -0
  7. package/dist/cursor-pi-tool-bridge-server.js +3 -0
  8. package/dist/cursor-pi-tool-bridge.js +5 -0
  9. package/dist/cursor-provider-errors.js +3 -58
  10. package/dist/cursor-provider-live-run-drain.js +0 -3
  11. package/dist/cursor-provider-run-finalizer.js +4 -7
  12. package/dist/cursor-provider-run-outcome.js +1 -1
  13. package/dist/cursor-provider-turn-finalize.js +3 -55
  14. package/dist/cursor-provider-turn-prepare.js +8 -144
  15. package/dist/cursor-provider-turn-runner.js +9 -23
  16. package/dist/cursor-provider-turn-send.js +8 -35
  17. package/dist/cursor-runtime-state.js +6 -268
  18. package/dist/cursor-sdk-billed-usage.js +3 -18
  19. package/dist/cursor-sdk-platform-package.js +88 -0
  20. package/dist/cursor-session-agent.js +18 -14
  21. package/dist/cursor-skill-tool.js +12 -22
  22. package/dist/cursor-state.js +5 -8
  23. package/dist/cursor-usage-accounting.js +1 -1
  24. package/dist/index.js +65 -49
  25. package/docs/cursor-dogfood-checklist.md +4 -4
  26. package/docs/cursor-live-smoke-checklist.md +24 -24
  27. package/docs/cursor-model-ux-spec.md +27 -30
  28. package/docs/cursor-native-tool-replay.md +4 -4
  29. package/docs/cursor-native-tool-visual-audit.md +10 -10
  30. package/docs/cursor-testing-lessons.md +38 -16
  31. package/docs/cursor-tool-surfaces.md +2 -4
  32. package/docs/platform-smoke-implementation.md +5 -5
  33. package/docs/platform-smoke.md +28 -73
  34. package/node_modules/cross-spawn/node_modules/which/CHANGELOG.md +166 -0
  35. package/package.json +3 -17
  36. package/platform-smoke.config.mjs +2 -1
  37. package/scripts/lib/cursor-visual-render.mjs +12 -2
  38. package/scripts/lib/local-resume-smoke-harness.mjs +0 -18
  39. package/scripts/platform-smoke/card-detect.mjs +1 -1
  40. package/scripts/platform-smoke/local-resume-runner.mjs +1 -1
  41. package/scripts/platform-smoke/scenarios.mjs +1 -1
  42. package/scripts/platform-smoke/targets.mjs +14 -5
  43. package/scripts/visual-tui-smoke-self-test.mjs +9 -33
  44. package/scripts/visual-tui-smoke.mjs +54 -11
  45. package/src/cursor-agents-context-registration.ts +0 -5
  46. package/src/cursor-agents-context.ts +1 -3
  47. package/src/cursor-config.ts +8 -379
  48. package/src/cursor-extension-factory-guard.ts +57 -0
  49. package/src/cursor-pi-tool-bridge-server.ts +4 -0
  50. package/src/cursor-pi-tool-bridge.ts +5 -0
  51. package/src/cursor-provider-errors.ts +2 -62
  52. package/src/cursor-provider-live-run-drain.ts +0 -3
  53. package/src/cursor-provider-run-finalizer.ts +4 -12
  54. package/src/cursor-provider-run-outcome.ts +0 -3
  55. package/src/cursor-provider-turn-finalize.ts +3 -61
  56. package/src/cursor-provider-turn-prepare.ts +8 -165
  57. package/src/cursor-provider-turn-runner.ts +15 -31
  58. package/src/cursor-provider-turn-send.ts +7 -38
  59. package/src/cursor-provider-turn-types.ts +9 -30
  60. package/src/cursor-runtime-state.ts +6 -345
  61. package/src/cursor-sdk-billed-usage.ts +3 -24
  62. package/src/cursor-sdk-platform-package.ts +106 -0
  63. package/src/cursor-session-agent.ts +16 -12
  64. package/src/cursor-skill-tool.ts +10 -26
  65. package/src/cursor-state.ts +5 -10
  66. package/src/cursor-usage-accounting.ts +1 -3
  67. package/src/index.ts +70 -49
  68. package/dist/cursor-cloud-lifecycle.js +0 -650
  69. package/dist/cursor-cloud-local-state.js +0 -460
  70. package/dist/cursor-cloud-options.js +0 -145
  71. package/dist/cursor-cloud-reporting.js +0 -222
  72. package/dist/cursor-ripgrep-path.js +0 -27
  73. package/scripts/cloud-runtime-smoke.d.mts +0 -42
  74. package/scripts/cloud-runtime-smoke.mjs +0 -623
  75. package/scripts/lib/cloud-smoke-artifacts.d.mts +0 -16
  76. package/scripts/lib/cloud-smoke-artifacts.mjs +0 -94
  77. package/scripts/lib/cloud-smoke-cleanup-evidence.d.mts +0 -209
  78. package/scripts/lib/cloud-smoke-cleanup-evidence.mjs +0 -585
  79. package/scripts/lib/cloud-smoke-github.d.mts +0 -78
  80. package/scripts/lib/cloud-smoke-github.mjs +0 -331
  81. package/scripts/lib/cloud-smoke-pi-runner.d.mts +0 -42
  82. package/scripts/lib/cloud-smoke-pi-runner.mjs +0 -214
  83. package/scripts/lib/cloud-smoke-shutdown.d.mts +0 -67
  84. package/scripts/lib/cloud-smoke-shutdown.mjs +0 -133
  85. package/shared/cursor-cloud-lifecycle-constants.d.mts +0 -3
  86. package/shared/cursor-cloud-lifecycle-constants.mjs +0 -7
  87. package/src/cursor-cloud-lifecycle.ts +0 -733
  88. package/src/cursor-cloud-local-state.ts +0 -536
  89. package/src/cursor-cloud-options.ts +0 -182
  90. package/src/cursor-cloud-reporting.ts +0 -267
  91. package/src/cursor-ripgrep-path.ts +0 -32
@@ -1,5 +1,4 @@
1
1
  import type { RunError, RunResult } from "@cursor/sdk";
2
- import type { CursorRuntime } from "./cursor-config.js";
3
2
  import { asRecord } from "./cursor-record-utils.js";
4
3
  import { scrubSensitiveText } from "./cursor-sensitive-text.js";
5
4
 
@@ -9,8 +8,6 @@ const GENERIC_CURSOR_SDK_ERROR_MESSAGE =
9
8
  "Cursor SDK request failed. The Cursor SDK API key may be missing, invalid, or unauthorized. Cursor Agent CLI/Desktop login is not reused. Run /login -> Use an API key -> Cursor, verify CURSOR_API_KEY, or pass --api-key, then retry.";
10
9
  const AUTH_CURSOR_SDK_ERROR_MESSAGE =
11
10
  "Cursor SDK request failed because the Cursor SDK API key may be invalid or unauthorized. Cursor Agent CLI/Desktop login is not reused. Run /login -> Use an API key -> Cursor, verify CURSOR_API_KEY, or pass --api-key, then retry.";
12
- const CLOUD_AUTH_CURSOR_SDK_ERROR_MESSAGE =
13
- "Cursor Cloud Agents request failed because Cloud API authentication rejected the API key. Use a user API key from Cursor Dashboard -> API Keys or a service account API key from Team settings; Team Admin API keys are not supported as Cursor Cloud Agents credentials. Configure the key with /login -> Use an API key -> Cursor, CURSOR_API_KEY, or --api-key, then retry.";
14
11
  // Keep "Network error" aligned with pi's agent-level retry classifier.
15
12
  const NETWORK_CURSOR_SDK_ERROR_MESSAGE =
16
13
  "Network error: Cursor SDK request failed during network or service I/O. Check your connection; pi will retry automatically when auto-retry is enabled.";
@@ -47,54 +44,6 @@ function getErrorName(error: unknown, record: Record<string, unknown> | undefine
47
44
  return error instanceof Error ? error.name : getErrorStringField(record, "name");
48
45
  }
49
46
 
50
- function scrubHttpsHelpUrl(helpUrl: string | undefined, apiKey: string | undefined): string | undefined {
51
- if (!helpUrl) return undefined;
52
- try {
53
- const url = new URL(helpUrl);
54
- if (url.protocol !== "https:") return undefined;
55
- url.username = "";
56
- url.password = "";
57
- url.pathname = url.pathname
58
- .split("/")
59
- .map((segment) => {
60
- const decoded = decodeURIComponent(segment);
61
- const scrubbed = scrubSensitiveText(decoded, apiKey);
62
- return scrubbed === decoded ? segment : encodeURIComponent(scrubbed);
63
- })
64
- .join("/");
65
- const scrubbedParams = new URLSearchParams();
66
- for (const [name, value] of url.searchParams) {
67
- const scrubbedName = scrubSensitiveText(name, apiKey);
68
- const scrubbedPair = scrubSensitiveText(`${name}=${value}`, apiKey);
69
- scrubbedParams.append(scrubbedName, scrubbedPair === `${name}=${value}` ? value : "[redacted]");
70
- }
71
- url.search = scrubbedParams.toString();
72
- if (url.hash) {
73
- const decoded = decodeURIComponent(url.hash.slice(1));
74
- const scrubbed = scrubSensitiveText(decoded, apiKey);
75
- if (scrubbed !== decoded) url.hash = encodeURIComponent(scrubbed);
76
- }
77
- return scrubSensitiveText(url.href, apiKey).trim() || undefined;
78
- } catch {
79
- return undefined;
80
- }
81
- }
82
-
83
- function formatIntegrationNotConnectedError(
84
- error: unknown,
85
- record: Record<string, unknown> | undefined,
86
- scrubbedMessage: string,
87
- apiKey: string | undefined,
88
- ): string | undefined {
89
- if (getErrorName(error, record) !== "IntegrationNotConnectedError") return undefined;
90
- const provider = scrubSensitiveText(getErrorStringField(record, "provider") ?? "", apiKey).trim();
91
- if (!provider) return undefined;
92
- const message = scrubbedMessage || "Cursor Cloud integration is not connected.";
93
- const punctuation = /[.!?]$/.test(message) ? "" : ".";
94
- const helpUrl = scrubHttpsHelpUrl(getErrorStringField(record, "helpUrl"), apiKey);
95
- return `${message}${punctuation} Connect the ${provider} integration${helpUrl ? `: ${helpUrl}` : "."}`;
96
- }
97
-
98
47
  function getErrorStack(error: unknown, record: Record<string, unknown> | undefined): string {
99
48
  return error instanceof Error ? error.stack ?? "" : getErrorStringField(record, "stack") ?? "";
100
49
  }
@@ -352,23 +301,14 @@ export function resolveCursorSdkAbortCause(options: {
352
301
  export function sanitizeCursorProviderError(
353
302
  error: unknown,
354
303
  apiKey?: string,
355
- runtimeTarget?: CursorRuntime,
356
304
  ): string {
357
305
  const record = asRecord(error);
358
306
  const message = error instanceof Error ? error.message : typeof error === "string" ? error : "";
359
307
  if (message === MISSING_CURSOR_API_KEY_MESSAGE) return MISSING_CURSOR_API_KEY_MESSAGE;
360
308
  const scrubbed = scrubSensitiveText(message, apiKey).trim();
361
- const integrationMessage = runtimeTarget === "cloud"
362
- ? formatIntegrationNotConnectedError(error, record, scrubbed, apiKey)
363
- : undefined;
364
- if (integrationMessage) return integrationMessage;
365
309
  const connectClassification = classifyCursorConnectError(error);
366
- if (
367
- (runtimeTarget === "cloud" && getErrorName(error, record) === "AuthenticationError") ||
368
- connectClassification?.kind === "unauthenticated" ||
369
- isLikelyAuthError(scrubbed)
370
- ) {
371
- return runtimeTarget === "cloud" ? CLOUD_AUTH_CURSOR_SDK_ERROR_MESSAGE : AUTH_CURSOR_SDK_ERROR_MESSAGE;
310
+ if (connectClassification?.kind === "unauthenticated" || isLikelyAuthError(scrubbed)) {
311
+ return AUTH_CURSOR_SDK_ERROR_MESSAGE;
372
312
  }
373
313
  if (connectClassification?.kind === "network" || isCursorSdkConnectionStalledError(error) || isLikelyNetworkTimeout(scrubbed)) return NETWORK_CURSOR_SDK_ERROR_MESSAGE;
374
314
  if (isGenericCursorRunFailureMessage(scrubbed)) return RETRYABLE_CURSOR_RUN_FAILURE_PREFIX;
@@ -196,7 +196,6 @@ function emitCursorNativeToolUseTurn(
196
196
  }
197
197
  }
198
198
  applyCursorUsage(partial, model, context, cursorLiveRuns.takeTurnInputTokens(run, toolResultInputTokens), {
199
- runtime: "local",
200
199
  turn: cursorLiveRuns.takeSdkTurnUsage(run),
201
200
  });
202
201
  partial.stopReason = "toolUse";
@@ -244,7 +243,6 @@ function emitCursorBridgeToolUseTurn(
244
243
  if (block.type === "toolCall") stream.push({ type: "toolcall_end", contentIndex, toolCall: block, partial });
245
244
  }
246
245
  applyCursorUsage(partial, model, context, cursorLiveRuns.takeTurnInputTokens(run, toolResultInputTokens), {
247
- runtime: "local",
248
246
  turn: cursorLiveRuns.takeSdkTurnUsage(run),
249
247
  });
250
248
  partial.stopReason = "toolUse";
@@ -380,7 +378,6 @@ export async function drainCursorLiveRunTurn(
380
378
  await emitTextDeltas(stream, partial, splitTextIntoReplayDeltas(finalText));
381
379
  }
382
380
  applyCursorUsage(partial, model, context, cursorLiveRuns.takeTurnInputTokens(run, toolResultInputTokens), {
383
- runtime: "local",
384
381
  turn: cursorLiveRuns.takeSdkTurnUsage(run),
385
382
  billed: run.billedTurnUsage,
386
383
  });
@@ -10,7 +10,6 @@ import {
10
10
  resolveCursorSdkAbortCause,
11
11
  sanitizeCursorProviderError,
12
12
  } from "./cursor-provider-errors.js";
13
- import type { CursorRuntime } from "./cursor-config.js";
14
13
  import { CursorLiveRunAbortError } from "./cursor-live-run-coordinator.js";
15
14
  import {
16
15
  buildIncompleteCursorToolRunOutcome,
@@ -25,7 +24,6 @@ import type {
25
24
  CursorProviderTurnSend,
26
25
  CursorProviderTurnSendResult,
27
26
  LiveCursorProviderTurnRuntime,
28
- LocalCursorProviderTurnPrepareResult,
29
27
  } from "./cursor-provider-turn-types.js";
30
28
  import { applyCursorUsage } from "./cursor-usage-accounting.js";
31
29
  import { hasUsableText } from "./cursor-record-utils.js";
@@ -35,13 +33,12 @@ export type CursorTurnTerminalEvent =
35
33
  kind: "direct";
36
34
  prepared: CursorProviderTurnPrepareResult;
37
35
  outcome: CursorRunOutcome;
38
- displayOnlyTraceBlock?: string;
39
36
  }
40
37
  | { kind: "error"; prepared: CursorProviderTurnPrepareResult | undefined; error: unknown };
41
38
 
42
39
  function applyLiveRunOutcome(
43
40
  outcome: CursorRunOutcome,
44
- prepared: LocalCursorProviderTurnPrepareResult & { runtime: LiveCursorProviderTurnRuntime },
41
+ prepared: CursorProviderTurnPrepareResult & { runtime: LiveCursorProviderTurnRuntime },
45
42
  context: CursorProviderTurnRunnerParams["context"],
46
43
  ): void {
47
44
  if (prepared.runtime.liveRun.disposed) return;
@@ -71,12 +68,11 @@ export interface CursorRunFinalizerParams {
71
68
  sdkEventDebug: () => CursorSdkEventDebugSink | undefined;
72
69
  sdkProcessErrorGuard: ReturnType<typeof installCursorSdkProcessErrorGuard>;
73
70
  resolvedApiKey: () => string | undefined;
74
- runtimeTarget: () => CursorRuntime | undefined;
75
71
  }
76
72
 
77
73
  export interface StartCursorLiveRunCompletionParams {
78
74
  send: CursorProviderTurnSend;
79
- prepared: LocalCursorProviderTurnPrepareResult & { runtime: LiveCursorProviderTurnRuntime };
75
+ prepared: CursorProviderTurnPrepareResult & { runtime: LiveCursorProviderTurnRuntime };
80
76
  modelId: string;
81
77
  discardIncompleteTools: (outcome: IncompleteCursorToolRunOutcomeInput) => void;
82
78
  }
@@ -114,7 +110,7 @@ export class CursorRunFinalizer {
114
110
  if (!liveRun.disposed) {
115
111
  cursorLiveRuns.markError(
116
112
  liveRun,
117
- sanitizeCursorProviderError(error, this.params.resolvedApiKey() ?? runnerParams.options?.apiKey, "local"),
113
+ sanitizeCursorProviderError(error, this.params.resolvedApiKey() ?? runnerParams.options?.apiKey),
118
114
  );
119
115
  }
120
116
  this.safeCleanup(() => sdkEventDebug?.recordWaitResult({ status: "error", error: String(error) }));
@@ -129,7 +125,7 @@ export class CursorRunFinalizer {
129
125
  async applyTerminalEvent(event: CursorTurnTerminalEvent): Promise<void> {
130
126
  if (this.terminalApplied) return;
131
127
  if (event.kind === "direct") {
132
- await this.applyDirectOutcome(event.prepared, event.outcome, event.displayOnlyTraceBlock);
128
+ await this.applyDirectOutcome(event.prepared, event.outcome);
133
129
  this.terminalApplied = true;
134
130
  return;
135
131
  }
@@ -165,7 +161,6 @@ export class CursorRunFinalizer {
165
161
  private async applyDirectOutcome(
166
162
  prepared: CursorProviderTurnPrepareResult,
167
163
  outcome: CursorRunOutcome,
168
- displayOnlyTraceBlock: string | undefined,
169
164
  ): Promise<void> {
170
165
  const { stream, partial, model, context } = this.params.runnerParams;
171
166
  prepared.runtime.turnCoordinator.closeTraceBlock();
@@ -184,12 +179,10 @@ export class CursorRunFinalizer {
184
179
  outcome.kind === "finished" && hasUsableText(outcome.finalText) ? [outcome.finalText] : [],
185
180
  );
186
181
  applyCursorUsage(partial, model, context, prepared.meta.promptInputTokens, {
187
- runtime: prepared.runtimeTarget,
188
182
  turn: prepared.runtime.turnCoordinator.lastSdkTurnUsage,
189
183
  billed: prepared.runtime.billedTurnUsage,
190
184
  });
191
185
  if (prepared.meta.resumeNotice) emitDisplayOnlyTraceBlock(stream, partial, prepared.meta.resumeNotice);
192
- if (displayOnlyTraceBlock) emitDisplayOnlyTraceBlock(stream, partial, displayOnlyTraceBlock);
193
186
  stream.push({ type: "done", reason: "stop", message: partial });
194
187
  break;
195
188
  }
@@ -219,7 +212,6 @@ export class CursorRunFinalizer {
219
212
  sanitizeCursorProviderError(
220
213
  error,
221
214
  this.params.resolvedApiKey() ?? this.params.runnerParams.options?.apiKey,
222
- prepared?.runtimeTarget ?? this.params.runtimeTarget(),
223
215
  ),
224
216
  );
225
217
  }
@@ -6,7 +6,6 @@ import {
6
6
  resolveCursorSdkAbortCause,
7
7
  sanitizeCursorProviderError,
8
8
  } from "./cursor-provider-errors.js";
9
- import type { CursorRuntime } from "./cursor-config.js";
10
9
  import { hasUsableText } from "./cursor-record-utils.js";
11
10
  import {
12
11
  buildIncompleteCursorToolRunOutcome,
@@ -46,7 +45,6 @@ export interface ResolveCursorRunOutcomeParams {
46
45
  runErrorFallback?: RunError;
47
46
  resolvedApiKey?: string;
48
47
  optionsApiKey?: string;
49
- runtimeTarget: CursorRuntime;
50
48
  }
51
49
 
52
50
  function hasCursorAssistantText(
@@ -109,7 +107,6 @@ export function resolveCursorRunOutcome(params: ResolveCursorRunOutcomeParams):
109
107
  errorMessage: sanitizeCursorProviderError(
110
108
  failureDetail,
111
109
  params.resolvedApiKey ?? params.optionsApiKey,
112
- params.runtimeTarget,
113
110
  ),
114
111
  };
115
112
  }
@@ -1,13 +1,6 @@
1
1
  import type { LocalAgentStore, RunError, SDKAgent } from "@cursor/sdk";
2
2
  import { loadCursorTranscriptWebToolCallsAfterOffset } from "./cursor-agent-message-web-tools.js";
3
- import {
4
- collectCursorCloudRunReport,
5
- formatCursorCloudRunReport,
6
- type CursorCloudRunReport,
7
- } from "./cursor-cloud-reporting.js";
8
- import { recordCursorCloudLifecycleRun } from "./cursor-cloud-lifecycle.js";
9
3
  import { getCheckpointContextWindow, saveCachedContextWindow } from "./context-window-cache.js";
10
- import { scrubSensitiveText } from "./cursor-sensitive-text.js";
11
4
  import type { CursorSdkEventDebugSink } from "./cursor-sdk-event-debug.js";
12
5
  import type { CursorSdkTurnCoordinator } from "./cursor-provider-turn-coordinator.js";
13
6
  import {
@@ -68,7 +61,6 @@ export function buildCursorRunOutcomeFromWait(params: BuildCursorRunOutcomeParam
68
61
  runErrorFallback: params.runErrorFallback,
69
62
  resolvedApiKey: params.resolvedApiKey,
70
63
  optionsApiKey: params.optionsApiKey,
71
- runtimeTarget: prepared.runtimeTarget,
72
64
  });
73
65
  }
74
66
 
@@ -99,22 +91,6 @@ async function replayCursorTranscriptWebToolCalls(
99
91
  }
100
92
  }
101
93
 
102
- function scrubCursorCloudReportingError(error: unknown, apiKey: string | undefined): Error {
103
- return new Error(scrubSensitiveText(error instanceof Error ? error.message : String(error), apiKey));
104
- }
105
-
106
- function recordCursorCloudReportingError(
107
- sdkEventDebug: CursorSdkEventDebugSink | undefined,
108
- error: unknown,
109
- apiKey: string | undefined,
110
- ): void {
111
- try {
112
- sdkEventDebug?.recordError("cloud_run_report", scrubCursorCloudReportingError(error, apiKey));
113
- } catch {
114
- // Debug reporting must never affect provider execution.
115
- }
116
- }
117
-
118
94
  export interface AwaitFinalizeCursorRunOutcomeParams {
119
95
  run: Awaited<ReturnType<SDKAgent["send"]>>;
120
96
  prepared: CursorProviderTurnPrepareResult;
@@ -134,12 +110,10 @@ export interface AwaitFinalizeCursorRunOutcomeParams {
134
110
 
135
111
  export interface FinalizedCursorRunOutcome {
136
112
  outcome: CursorRunOutcome;
137
- displayOnlyTraceBlock?: string;
138
113
  }
139
114
 
140
115
  /** Single wait/finalize path for SDK runs: wait, debug capture, transcript replay, incomplete tools, artifacts, context cache. */
141
116
  export async function awaitFinalizeCursorRunOutcome(params: AwaitFinalizeCursorRunOutcomeParams): Promise<FinalizedCursorRunOutcome> {
142
- const apiKey = params.resolvedApiKey ?? params.optionsApiKey;
143
117
  const waitResult = params.waitResult ?? (await params.run.wait());
144
118
  const outcome = buildCursorRunOutcomeFromWait({
145
119
  waitResult,
@@ -153,49 +127,17 @@ export async function awaitFinalizeCursorRunOutcome(params: AwaitFinalizeCursorR
153
127
  const billed = await attachCursorSdkBilledTurnUsage({
154
128
  agent: params.prepared.agent,
155
129
  agentId: params.run.agentId,
156
- runtime: params.prepared.runtimeTarget,
157
- runId: params.run.id,
158
130
  });
159
131
  params.prepared.runtime.billedTurnUsage = billed.turn;
160
132
  if (params.prepared.runtime.liveRun) {
161
133
  params.prepared.runtime.liveRun.billedTurnUsage = billed.turn;
162
134
  }
163
- let displayOnlyTraceBlock: string | undefined;
164
- if (params.prepared.runtimeTarget === "cloud" && isCursorRunFinishedSuccessfully(outcome)) {
165
- let report: CursorCloudRunReport = { agentId: params.run.agentId, runId: params.run.id, branches: [] };
166
- try {
167
- report = await collectCursorCloudRunReport({
168
- agent: params.prepared.agent,
169
- run: params.run,
170
- waitResult,
171
- apiKey,
172
- agentUsage: billed.agentUsage,
173
- });
174
- } catch (error) {
175
- recordCursorCloudReportingError(params.sdkEventDebug, error, apiKey);
176
- }
177
- try {
178
- recordCursorCloudLifecycleRun(report, { apiKey });
179
- } catch (error) {
180
- recordCursorCloudReportingError(params.sdkEventDebug, error, apiKey);
181
- }
182
- try {
183
- params.sdkEventDebug?.recordProviderEvent("cloud_run_report", report);
184
- } catch (error) {
185
- recordCursorCloudReportingError(params.sdkEventDebug, error, apiKey);
186
- }
187
- try {
188
- displayOnlyTraceBlock = formatCursorCloudRunReport(report, { apiKey });
189
- } catch (error) {
190
- recordCursorCloudReportingError(params.sdkEventDebug, error, apiKey);
191
- }
192
- }
193
135
  try {
194
136
  params.sdkEventDebug?.recordWaitResult(waitResult);
195
137
  } catch {
196
138
  // Debug reporting must never affect provider execution.
197
139
  }
198
- if (params.prepared.runtimeTarget === "local" && isCursorRunFinishedSuccessfully(outcome)) {
140
+ if (isCursorRunFinishedSuccessfully(outcome)) {
199
141
  await replayCursorTranscriptWebToolCalls(
200
142
  params.run.agentId,
201
143
  params.prepared.cwd,
@@ -211,7 +153,7 @@ export async function awaitFinalizeCursorRunOutcome(params: AwaitFinalizeCursorR
211
153
  } catch {
212
154
  // Debug artifact failures must never affect provider execution.
213
155
  }
214
- if (params.prepared.runtimeTarget === "local" && params.cacheContextWindow !== false) {
156
+ if (params.cacheContextWindow !== false) {
215
157
  await cacheSdkContextWindow(
216
158
  params.contextWindowAgentId ?? params.run.agentId,
217
159
  params.modelId,
@@ -219,5 +161,5 @@ export async function awaitFinalizeCursorRunOutcome(params: AwaitFinalizeCursorR
219
161
  params.prepared.sessionAgentLease.store,
220
162
  );
221
163
  }
222
- return { outcome, displayOnlyTraceBlock };
164
+ return { outcome };
223
165
  }
@@ -1,8 +1,8 @@
1
1
  import type { Context, SimpleStreamOptions } from "@earendil-works/pi-ai";
2
- import type { AgentModeOption, ModelSelection, SDKAgent } from "@cursor/sdk";
2
+ import type { AgentModeOption, ModelSelection } from "@cursor/sdk";
3
3
  import { configureCursorSdkHttp1 } from "./cursor-http1.js";
4
4
  import { installCursorMcpToolTimeoutOverride } from "./cursor-mcp-timeout-override.js";
5
- import { ensureCursorRipgrepPath } from "./cursor-ripgrep-path.js";
5
+ import { ensureCursorRipgrepPath, ensureCursorTreeSitterVendorDir } from "./cursor-sdk-platform-package.js";
6
6
  import { installCursorSdkOutputFilter, suppressCursorSdkOutput } from "./cursor-sdk-output-filter.js";
7
7
  import {
8
8
  acquireSessionCursorAgent,
@@ -31,33 +31,21 @@ import { resolveEffectiveCursorConfig } from "./cursor-runtime-state.js";
31
31
  import { buildCursorBridgeExcludeToolNames, type CursorResolvedSdkConfig } from "./cursor-config.js";
32
32
  import { buildCursorModelSelection } from "./model-discovery.js";
33
33
  import { getEffectiveCursorSettingSources } from "./cursor-setting-sources.js";
34
- import {
35
- formatCursorCloudPreflightError,
36
- buildCursorCloudAgentOptions,
37
- preflightCursorCloudRuntime,
38
- } from "./cursor-cloud-options.js";
39
- import { inspectCursorCloudLocalState } from "./cursor-cloud-local-state.js";
40
- import { getCursorSessionName, getCursorSessionProjectTrusted } from "./cursor-session-scope.js";
34
+ import { getCursorSessionProjectTrusted } from "./cursor-session-scope.js";
41
35
  import { resolveCursorPiToolBridgeEnabled } from "./cursor-pi-tool-bridge-env.js";
42
36
  import {
43
37
  buildCursorToolManifestText,
44
38
  resolveCursorToolManifestEnabled,
45
39
  } from "./cursor-tool-manifest.js";
46
40
  import { isCursorNativeToolDisplayRuntimeEnabled } from "./cursor-native-tool-display-state.js";
47
- import {
48
- createCursorCloudLifecyclePersistenceError,
49
- recordCursorCloudLifecycleSafely,
50
- } from "./cursor-cloud-lifecycle.js";
51
41
  import { MISSING_CURSOR_API_KEY_MESSAGE } from "./cursor-provider-errors.js";
52
42
  import { CursorSdkTurnCoordinator } from "./cursor-provider-turn-coordinator.js";
53
43
  import { resolveCursorApiKey } from "./cursor-api-key.js";
54
44
  import { loadCursorSdk } from "./cursor-sdk-runtime.js";
55
45
  import type {
56
- CloudCursorProviderTurnPrepareResult,
57
46
  CursorProviderTurnLifecycle,
58
47
  CursorProviderTurnPrepareResult,
59
48
  CursorProviderTurnRunnerParams,
60
- LocalCursorProviderTurnPrepareResult,
61
49
  } from "./cursor-provider-turn-types.js";
62
50
  import type { CursorSdkEventDebugSink } from "./cursor-sdk-event-debug.js";
63
51
  import type { SessionCursorAgentLease } from "./cursor-session-agent.js";
@@ -78,32 +66,10 @@ interface PrepareCursorProviderTurnContext extends PrepareCursorProviderTurnPara
78
66
  fastEnabled: boolean | undefined;
79
67
  }
80
68
 
81
- function buildCursorCloudPromptContext(context: Context, handoff: "fresh" | "bootstrap" | "never"): Context {
82
- if (handoff === "bootstrap") return context;
83
- for (let index = context.messages.length - 1; index >= 0; index -= 1) {
84
- const message = context.messages[index];
85
- if (message.role === "user") return { ...context, messages: [message] };
86
- }
87
- return { ...context, messages: context.messages.slice(-1) };
88
- }
89
-
90
- const CLOUD_SEND_PLAN: CursorSessionSendPlan = { mode: "bootstrap", resetAgent: false, reason: "initial" };
91
-
92
69
  export function resolveCursorProviderTurnConfig(cwd: string) {
93
70
  return resolveEffectiveCursorConfig({ cwd, projectTrusted: getCursorSessionProjectTrusted() });
94
71
  }
95
72
 
96
- function buildCloudCursorProviderTurnLifecycle(agent: SDKAgent): CursorProviderTurnLifecycle {
97
- return {
98
- trackRunCompletion: () => {},
99
- commitSend: () => {},
100
- abandon: async () => {},
101
- dispose: async () => {
102
- await agent[Symbol.asyncDispose]?.();
103
- },
104
- };
105
- }
106
-
107
73
  function buildLocalCursorProviderTurnLifecycle(
108
74
  lease: SessionCursorAgentLease,
109
75
  scopeKey: string,
@@ -116,129 +82,9 @@ function buildLocalCursorProviderTurnLifecycle(
116
82
  };
117
83
  }
118
84
 
119
- async function prepareCursorCloudProviderTurn(
120
- prepareParams: PrepareCursorProviderTurnContext,
121
- ): Promise<CloudCursorProviderTurnPrepareResult> {
122
- const { params, cwd, resolvedApiKey, sdkEventDebug, throwIfAborted, resolvedConfig, agentMode, selection, fastEnabled } = prepareParams;
123
- const { model, context, options } = params;
124
-
125
- let restoreCursorSdkOutputFilter: (() => void) | undefined;
126
- let cloudAgentForCleanup: SDKAgent | undefined;
127
- let completed = false;
128
-
129
- try {
130
- const preflight = preflightCursorCloudRuntime({
131
- resolvedConfig,
132
- localState: resolvedConfig.cloud.allowLocalState.value
133
- ? { insideGitRepo: false }
134
- : inspectCursorCloudLocalState(cwd, {
135
- repo: resolvedConfig.cloud.repo.value,
136
- branch: resolvedConfig.cloud.branch.value,
137
- }),
138
- hasPriorContext: context.messages.length > 1,
139
- });
140
- if (!preflight.ok) throw new Error(formatCursorCloudPreflightError(preflight));
141
- if (getPendingCursorLiveRun(context) || getActiveCursorLiveRunForCurrentScope()) {
142
- throw new Error("Cursor cloud runtime cannot start while a local Cursor live run is pending; finish or abort the local run, then retry.");
143
- }
144
-
145
- const { Agent } = await loadCursorSdk();
146
- restoreCursorSdkOutputFilter = installCursorSdkOutputFilter();
147
- const promptOptions = {
148
- ...getCursorPromptOptions(model),
149
- agentMode,
150
- includePiBridgeGuidance: false,
151
- includePiAskQuestionGuidance: false,
152
- };
153
- const prompt = buildCursorPrompt(
154
- buildCursorCloudPromptContext(context, resolvedConfig.cloud.contextHandoff.value),
155
- promptOptions,
156
- );
157
- const promptInputTokens = estimateCursorPromptTokens(prompt, promptOptions);
158
- const agent = await suppressCursorSdkOutput(() =>
159
- Agent.create(buildCursorCloudAgentOptions({
160
- apiKey: resolvedApiKey,
161
- modelSelection: selection,
162
- agentMode,
163
- resolvedConfig,
164
- name: getCursorSessionName(),
165
- })),
166
- );
167
- cloudAgentForCleanup = agent;
168
- if (!recordCursorCloudLifecycleSafely({ agentId: agent.agentId }, resolvedApiKey)) {
169
- throw createCursorCloudLifecyclePersistenceError(agent.agentId, "intent", undefined, resolvedApiKey);
170
- }
171
- sdkEventDebug?.recordProviderMeta({ runtime: "cloud", cloudAgentId: agent.agentId, phase: "agent_created" });
172
- throwIfAborted();
173
-
174
- const textDeltas: string[] = [];
175
- const nativeReplayId = createCursorNativeReplayId();
176
- const turnCoordinator = new CursorSdkTurnCoordinator({
177
- stream: params.stream,
178
- partial: params.partial,
179
- cwd,
180
- resolvedApiKey,
181
- useNativeToolReplay: false,
182
- nativeReplayId,
183
- textDeltas,
184
- debugRecorder: sdkEventDebug,
185
- });
186
- sdkEventDebug?.recordProviderMeta({
187
- runtime: "cloud",
188
- cloudAgentId: agent.agentId,
189
- model: {
190
- id: model.id,
191
- provider: model.provider,
192
- api: model.api,
193
- reasoning: options?.reasoning ?? "off",
194
- fastEnabled,
195
- selection,
196
- },
197
- contextHandoff: resolvedConfig.cloud.contextHandoff.value,
198
- sendPlan: CLOUD_SEND_PLAN,
199
- promptOptions,
200
- agentMode,
201
- localForce: false,
202
- });
203
-
204
- completed = true;
205
- cloudAgentForCleanup = undefined;
206
- return {
207
- runtimeTarget: "cloud",
208
- agent,
209
- cwd,
210
- payload: {
211
- text: prompt.text,
212
- images: prompt.images.length > 0 ? prompt.images : undefined,
213
- },
214
- meta: {
215
- sendPlan: CLOUD_SEND_PLAN,
216
- prompt,
217
- bootstrap: true,
218
- promptInputTokens,
219
- useNativeToolReplay: false,
220
- bridgeEnabled: false,
221
- nativeReplayId,
222
- agentMode,
223
- modelSelection: selection,
224
- },
225
- contextWindowAgentId: agent.agentId,
226
- textDeltas,
227
- restoreCursorSdkOutputFilter,
228
- lifecycle: buildCloudCursorProviderTurnLifecycle(agent),
229
- runtime: { kind: "direct", turnCoordinator },
230
- };
231
- } finally {
232
- if (!completed) {
233
- await cloudAgentForCleanup?.[Symbol.asyncDispose]?.().catch(() => {});
234
- restoreCursorSdkOutputFilter?.();
235
- }
236
- }
237
- }
238
-
239
85
  async function prepareCursorLocalProviderTurn(
240
86
  prepareParams: PrepareCursorProviderTurnContext,
241
- ): Promise<LocalCursorProviderTurnPrepareResult> {
87
+ ): Promise<CursorProviderTurnPrepareResult> {
242
88
  const { params, cwd, resolvedApiKey, sdkEventDebug, throwIfAborted, resolvedConfig, agentMode, selection, fastEnabled } = prepareParams;
243
89
  const { model, context, options } = params;
244
90
 
@@ -249,6 +95,7 @@ async function prepareCursorLocalProviderTurn(
249
95
 
250
96
  try {
251
97
  ensureCursorRipgrepPath();
98
+ ensureCursorTreeSitterVendorDir();
252
99
  const localSafety = {
253
100
  autoReview: resolvedConfig.local.autoReview.value,
254
101
  sandboxEnabled: resolvedConfig.local.sandboxEnabled.value,
@@ -398,7 +245,6 @@ async function prepareCursorLocalProviderTurn(
398
245
 
399
246
  completed = true;
400
247
  return {
401
- runtimeTarget: "local",
402
248
  agent,
403
249
  cwd,
404
250
  payload: sendPayload,
@@ -439,21 +285,18 @@ async function prepareCursorLocalProviderTurn(
439
285
  }
440
286
  }
441
287
 
442
- /** Resolves the runtime target from the caller-supplied snapshot and dispatches to the owning branch. */
443
288
  export async function prepareCursorProviderTurn(
444
289
  prepareParams: PrepareCursorProviderTurnParams,
445
290
  ): Promise<CursorProviderTurnPrepareResult> {
446
- const { params, resolvedConfig } = prepareParams;
291
+ const { params } = prepareParams;
447
292
  const { model, options } = params;
448
293
 
449
294
  const agentMode = getCursorProviderAgentModeOrThrow();
450
- const fastEnabled = resolvedConfig.runtime.value === "cloud" ? undefined : getEffectiveFastForModelId(model.id);
295
+ const fastEnabled = getEffectiveFastForModelId(model.id);
451
296
  const selection = buildCursorModelSelection(model.id, options?.reasoning ?? "off", fastEnabled);
452
297
  const context: PrepareCursorProviderTurnContext = { ...prepareParams, agentMode, selection, fastEnabled };
453
298
 
454
- return resolvedConfig.runtime.value === "cloud"
455
- ? prepareCursorCloudProviderTurn(context)
456
- : prepareCursorLocalProviderTurn(context);
299
+ return prepareCursorLocalProviderTurn(context);
457
300
  }
458
301
 
459
302
  export function requireCursorApiKey(options: SimpleStreamOptions | undefined): string {