@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
|
@@ -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
|
-
|
|
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:
|
|
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:
|
|
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
|
|
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
|
|
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 (
|
|
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.
|
|
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
|
|
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
|
|
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-
|
|
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<
|
|
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
|
|
291
|
+
const { params } = prepareParams;
|
|
447
292
|
const { model, options } = params;
|
|
448
293
|
|
|
449
294
|
const agentMode = getCursorProviderAgentModeOrThrow();
|
|
450
|
-
const fastEnabled =
|
|
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
|
|
455
|
-
? prepareCursorCloudProviderTurn(context)
|
|
456
|
-
: prepareCursorLocalProviderTurn(context);
|
|
299
|
+
return prepareCursorLocalProviderTurn(context);
|
|
457
300
|
}
|
|
458
301
|
|
|
459
302
|
export function requireCursorApiKey(options: SimpleStreamOptions | undefined): string {
|