pi-cursor-sdk 0.1.55 → 0.1.57
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 +60 -2
- package/README.md +100 -28
- package/docs/cursor-live-smoke-checklist.md +7 -7
- package/docs/cursor-model-ux-spec.md +48 -41
- package/docs/cursor-native-tool-replay.md +4 -4
- package/docs/cursor-native-tool-visual-audit.md +1 -1
- package/docs/cursor-testing-lessons.md +20 -7
- package/docs/cursor-tool-surfaces.md +13 -2
- package/docs/platform-smoke-implementation.md +220 -0
- package/docs/platform-smoke.md +183 -247
- package/package.json +39 -7
- package/platform-smoke.config.mjs +5 -1
- package/scripts/cloud-runtime-smoke.d.mts +3 -0
- package/scripts/cloud-runtime-smoke.mjs +502 -0
- package/scripts/debug-provider-events.mjs +7 -2
- package/scripts/isolated-cursor-smoke.sh +4 -6
- package/scripts/lib/cursor-child-process.d.mts +1 -0
- package/scripts/lib/cursor-child-process.mjs +137 -7
- package/scripts/lib/local-resume-smoke-harness.mjs +543 -0
- package/scripts/local-resume-cleanup-smoke.mjs +108 -0
- package/scripts/local-resume-smoke.d.mts +1 -0
- package/scripts/local-resume-smoke.mjs +642 -0
- package/scripts/platform-smoke/artifact-anchored-extract.d.mts +10 -0
- package/scripts/platform-smoke/artifact-anchored-extract.mjs +111 -0
- package/scripts/platform-smoke/artifact-bundle-chunk.mjs +57 -0
- package/scripts/platform-smoke/artifact-bundle-contract.mjs +34 -0
- package/scripts/platform-smoke/artifact-fs-safety.mjs +311 -0
- package/scripts/platform-smoke/artifact-openat-extract.c +335 -0
- package/scripts/platform-smoke/artifact-secrets.mjs +155 -0
- package/scripts/platform-smoke/artifacts.mjs +293 -65
- package/scripts/platform-smoke/card-detect.mjs +16 -4
- package/scripts/platform-smoke/crabbox-runner.mjs +45 -3
- package/scripts/platform-smoke/doctor.mjs +20 -10
- package/scripts/platform-smoke/live-suite-runner.mjs +18 -57
- package/scripts/platform-smoke/local-resume-runner.mjs +252 -0
- package/scripts/platform-smoke/local-resume-suites.d.mts +15 -0
- package/scripts/platform-smoke/local-resume-suites.mjs +104 -0
- package/scripts/platform-smoke/scenarios.mjs +16 -2
- package/scripts/platform-smoke/target-runtime.mjs +206 -0
- package/scripts/platform-smoke/targets.mjs +33 -141
- package/scripts/platform-smoke/visual-evidence.mjs +6 -7
- package/scripts/platform-smoke/wrapped-line-match.mjs +9 -0
- package/scripts/platform-smoke.mjs +40 -27
- package/scripts/refresh-cursor-model-snapshots.mjs +18 -6
- package/scripts/steering-rpc-smoke.mjs +12 -2
- package/scripts/tmux-live-smoke.sh +3 -5
- package/shared/cursor-cloud-lifecycle-constants.d.mts +3 -0
- package/shared/cursor-cloud-lifecycle-constants.mjs +7 -0
- package/shared/cursor-sensitive-text.mjs +7 -1
- package/src/context.ts +5 -2
- package/src/cursor-agents-context-registration.ts +7 -0
- package/src/cursor-agents-context.ts +3 -1
- package/src/cursor-api-key.ts +15 -1
- package/src/cursor-bridge-contract.ts +3 -0
- package/src/cursor-cloud-lifecycle.ts +733 -0
- package/src/cursor-cloud-options.ts +206 -0
- package/src/cursor-cloud-reporting.ts +246 -0
- package/src/cursor-config.ts +659 -0
- package/src/cursor-display-only-trace.ts +14 -0
- package/src/cursor-display-text.ts +8 -2
- package/src/cursor-durable-fs.ts +49 -0
- package/src/cursor-fallback-models.generated.ts +2045 -485
- package/src/cursor-live-run-accounting.ts +7 -1
- package/src/cursor-live-run-coordinator.ts +1 -0
- package/src/cursor-pi-tool-bridge-run.ts +14 -4
- package/src/cursor-provider-errors.ts +31 -5
- package/src/cursor-provider-live-run-drain.ts +5 -0
- package/src/cursor-provider-run-finalizer.ts +37 -28
- package/src/cursor-provider-run-outcome.ts +7 -2
- package/src/cursor-provider-turn-finalize.ts +77 -7
- package/src/cursor-provider-turn-prepare.ts +228 -12
- package/src/cursor-provider-turn-runner.ts +42 -13
- package/src/cursor-provider-turn-send.ts +59 -16
- package/src/cursor-provider-turn-types.ts +44 -10
- package/src/cursor-runtime-state.ts +478 -0
- package/src/cursor-sdk-event-debug.ts +46 -6
- package/src/cursor-sdk-process-error-guard.ts +101 -30
- package/src/cursor-session-agent-cleanup.ts +328 -0
- package/src/cursor-session-agent-resume.ts +439 -0
- package/src/cursor-session-agent.ts +109 -13
- package/src/cursor-session-scope.ts +35 -2
- package/src/cursor-session-send-policy.ts +1 -1
- package/src/cursor-skill-tool.ts +30 -11
- package/src/cursor-state.ts +112 -69
- package/src/cursor-tool-manifest.ts +1 -1
- package/src/cursor-usage-accounting.ts +14 -4
- package/src/index.ts +11 -2
- package/src/model-discovery.ts +10 -56
|
@@ -38,7 +38,13 @@ export function takeCursorLiveSdkTurnUsage(state: CursorLiveRunAccountingState):
|
|
|
38
38
|
sdkTurnUsage?: CursorSdkTurnUsage;
|
|
39
39
|
} {
|
|
40
40
|
const { sdkTurnUsage, ...nextState } = state;
|
|
41
|
-
return {
|
|
41
|
+
return {
|
|
42
|
+
state: {
|
|
43
|
+
...nextState,
|
|
44
|
+
sdkTurnEnded: false,
|
|
45
|
+
},
|
|
46
|
+
sdkTurnUsage,
|
|
47
|
+
};
|
|
42
48
|
}
|
|
43
49
|
|
|
44
50
|
function asToolResultMessage(message: Message): ToolResultMessage | undefined {
|
|
@@ -150,6 +150,16 @@ export class CursorPiToolBridgeRunImpl implements CursorPiToolBridgeRun {
|
|
|
150
150
|
this.debugRecorder = recorder;
|
|
151
151
|
}
|
|
152
152
|
|
|
153
|
+
private recordBridgeRaw(
|
|
154
|
+
payload: Parameters<NonNullable<CursorPiToolBridgeRunOptions["debugRecorder"]>["recordBridgeRaw"]>[0],
|
|
155
|
+
): void {
|
|
156
|
+
try {
|
|
157
|
+
this.debugRecorder?.recordBridgeRaw(payload);
|
|
158
|
+
} catch {
|
|
159
|
+
// Debug capture must never block or strand a bridge call.
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
153
163
|
async resolveToolResults(toolResults: readonly ToolResultMessage[]): Promise<void> {
|
|
154
164
|
let resolvedCount = 0;
|
|
155
165
|
for (const toolResult of toolResults) {
|
|
@@ -298,11 +308,11 @@ export class CursorPiToolBridgeRunImpl implements CursorPiToolBridgeRun {
|
|
|
298
308
|
}
|
|
299
309
|
this.queuedRequests.push(request);
|
|
300
310
|
this.emitRequestQueuedDiagnostic(request);
|
|
301
|
-
this.
|
|
311
|
+
this.recordBridgeRaw({ kind: "queued", request });
|
|
302
312
|
return;
|
|
303
313
|
}
|
|
304
314
|
this.emitRequestQueuedDiagnostic(request);
|
|
305
|
-
this.
|
|
315
|
+
this.recordBridgeRaw({ kind: "queued", request });
|
|
306
316
|
this.dispatchPendingToolRequest(pending, this.onToolRequest);
|
|
307
317
|
});
|
|
308
318
|
}
|
|
@@ -331,7 +341,7 @@ export class CursorPiToolBridgeRunImpl implements CursorPiToolBridgeRun {
|
|
|
331
341
|
pending.settled = true;
|
|
332
342
|
this.removePending(pending);
|
|
333
343
|
this.emitRequestResolvedDiagnostic(pending.request, result.isError === true);
|
|
334
|
-
this.
|
|
344
|
+
this.recordBridgeRaw({ kind: "resolved", request: pending.request, result });
|
|
335
345
|
pending.resolve(result);
|
|
336
346
|
}
|
|
337
347
|
|
|
@@ -340,7 +350,7 @@ export class CursorPiToolBridgeRunImpl implements CursorPiToolBridgeRun {
|
|
|
340
350
|
pending.settled = true;
|
|
341
351
|
this.removePending(pending);
|
|
342
352
|
this.emitRequestRejectedDiagnostic(pending.request, kind);
|
|
343
|
-
this.
|
|
353
|
+
this.recordBridgeRaw({
|
|
344
354
|
kind: "rejected",
|
|
345
355
|
request: pending.request,
|
|
346
356
|
error: error.message,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { RunResult } from "@cursor/sdk";
|
|
1
|
+
import type { RunError, RunResult } from "@cursor/sdk";
|
|
2
2
|
import { asRecord } from "./cursor-record-utils.js";
|
|
3
3
|
import { scrubSensitiveText } from "./cursor-sensitive-text.js";
|
|
4
4
|
|
|
@@ -15,7 +15,7 @@ const NETWORK_CURSOR_SDK_ERROR_MESSAGE =
|
|
|
15
15
|
// Keep this phrase aligned with pi's agent-level retry classifier (`provider.?returned.?error`).
|
|
16
16
|
const RETRYABLE_CURSOR_RUN_FAILURE_PREFIX = "Provider returned error: Cursor SDK run failed";
|
|
17
17
|
|
|
18
|
-
export type CursorSdkRunFailureSource = Pick<RunResult, "id" | "requestId" | "status" | "durationMs" | "model" | "result">;
|
|
18
|
+
export type CursorSdkRunFailureSource = Pick<RunResult, "id" | "requestId" | "status" | "durationMs" | "model" | "result" | "error">;
|
|
19
19
|
|
|
20
20
|
function isGenericErrorMessage(message: string): boolean {
|
|
21
21
|
const normalized = message.trim().toLowerCase();
|
|
@@ -208,17 +208,43 @@ function shortRunId(runId: string): string {
|
|
|
208
208
|
return `${trimmed.slice(0, 8)}…`;
|
|
209
209
|
}
|
|
210
210
|
|
|
211
|
-
|
|
211
|
+
function runErrorCode(error: RunError | undefined): string | undefined {
|
|
212
|
+
return error?.code?.trim() || undefined;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function nonGenericRunErrorMessage(error: RunError | undefined): string | undefined {
|
|
216
|
+
const message = error?.message?.trim();
|
|
217
|
+
return message && !isKnownGenericRunFailureText(message) ? message : undefined;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function withRunErrorCode(message: string, code: string | undefined): string {
|
|
221
|
+
return code ? `${message} (code: ${code})` : message;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
export function formatCursorSdkRunFailureDetail(
|
|
225
|
+
result: CursorSdkRunFailureSource,
|
|
226
|
+
runResult?: string,
|
|
227
|
+
runError?: RunError,
|
|
228
|
+
): string {
|
|
229
|
+
const errorCode = runErrorCode(result.error) ?? runErrorCode(runError);
|
|
230
|
+
const fromWaitError = nonGenericRunErrorMessage(result.error);
|
|
231
|
+
if (fromWaitError) return withRunErrorCode(fromWaitError, errorCode);
|
|
232
|
+
|
|
212
233
|
const fromWait = result.result?.trim();
|
|
213
234
|
if (fromWait && !isKnownGenericRunFailureText(fromWait)) {
|
|
214
|
-
return fromWait;
|
|
235
|
+
return withRunErrorCode(fromWait, errorCode);
|
|
215
236
|
}
|
|
237
|
+
|
|
238
|
+
const fromRunError = nonGenericRunErrorMessage(runError);
|
|
239
|
+
if (fromRunError) return withRunErrorCode(fromRunError, errorCode);
|
|
240
|
+
|
|
216
241
|
const fromRun = runResult?.trim();
|
|
217
242
|
if (fromRun && !isKnownGenericRunFailureText(fromRun)) {
|
|
218
|
-
return fromRun;
|
|
243
|
+
return withRunErrorCode(fromRun, errorCode);
|
|
219
244
|
}
|
|
220
245
|
|
|
221
246
|
const parts = [RETRYABLE_CURSOR_RUN_FAILURE_PREFIX];
|
|
247
|
+
if (errorCode) parts.push(`code ${errorCode}`);
|
|
222
248
|
if (result.model?.id) parts.push(`model ${result.model.id}`);
|
|
223
249
|
parts.push(`run ${shortRunId(result.id)}`);
|
|
224
250
|
if (result.requestId) parts.push(`request ${shortRunId(result.requestId)}`);
|
|
@@ -22,6 +22,7 @@ import { type CursorPiBridgeToolRequest } from "./cursor-pi-tool-bridge.js";
|
|
|
22
22
|
import { resetSessionCursorAgent } from "./cursor-session-agent.js";
|
|
23
23
|
import { applyCursorUsage } from "./cursor-usage-accounting.js";
|
|
24
24
|
import { CursorPartialContentEmitter } from "./cursor-partial-content-emitter.js";
|
|
25
|
+
import { emitDisplayOnlyTraceBlock } from "./cursor-display-only-trace.js";
|
|
25
26
|
import { trimCurrentTurnAlreadyEmittedCursorText } from "./cursor-run-final-text.js";
|
|
26
27
|
import { formatCursorSdkAbortMessage, resolveCursorSdkAbortCause } from "./cursor-provider-errors.js";
|
|
27
28
|
import { formatInactiveCursorReplayTrace } from "./cursor-native-replay-trace.js";
|
|
@@ -379,6 +380,10 @@ export async function drainCursorLiveRunTurn(
|
|
|
379
380
|
applyCursorUsage(partial, model, context, cursorLiveRuns.takeTurnInputTokens(run, toolResultInputTokens), {
|
|
380
381
|
turn: cursorLiveRuns.takeSdkTurnUsage(run),
|
|
381
382
|
});
|
|
383
|
+
if (run.resumeNotice) {
|
|
384
|
+
emitDisplayOnlyTraceBlock(stream, partial, run.resumeNotice);
|
|
385
|
+
run.resumeNotice = undefined;
|
|
386
|
+
}
|
|
382
387
|
partial.stopReason = "stop";
|
|
383
388
|
stream.push({ type: "done", reason: "stop", message: partial });
|
|
384
389
|
await cursorLiveRuns.release(run);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AssistantMessage } from "@earendil-works/pi-ai/compat";
|
|
2
|
-
import {
|
|
2
|
+
import { cursorLiveRuns } from "./cursor-provider-live-run-drain.js";
|
|
3
3
|
import {
|
|
4
4
|
classifyCursorRunEmission,
|
|
5
5
|
getCursorRunAbortMessage,
|
|
@@ -23,27 +23,32 @@ import type {
|
|
|
23
23
|
CursorProviderTurnRunnerParams,
|
|
24
24
|
CursorProviderTurnSend,
|
|
25
25
|
CursorProviderTurnSendResult,
|
|
26
|
+
LiveCursorProviderTurnRuntime,
|
|
27
|
+
LocalCursorProviderTurnPrepareResult,
|
|
26
28
|
} from "./cursor-provider-turn-types.js";
|
|
27
29
|
import { applyCursorUsage } from "./cursor-usage-accounting.js";
|
|
28
30
|
import { hasUsableText } from "./cursor-record-utils.js";
|
|
31
|
+
import { emitDisplayOnlyTraceBlock } from "./cursor-display-only-trace.js";
|
|
29
32
|
export type CursorTurnTerminalEvent =
|
|
30
33
|
| {
|
|
31
34
|
kind: "direct";
|
|
32
35
|
prepared: CursorProviderTurnPrepareResult;
|
|
33
36
|
outcome: CursorRunOutcome;
|
|
37
|
+
displayOnlyTraceBlock?: string;
|
|
34
38
|
}
|
|
35
39
|
| { kind: "error"; prepared: CursorProviderTurnPrepareResult | undefined; error: unknown };
|
|
36
40
|
|
|
37
41
|
function applyLiveRunOutcome(
|
|
38
42
|
outcome: CursorRunOutcome,
|
|
39
|
-
prepared:
|
|
43
|
+
prepared: LocalCursorProviderTurnPrepareResult & { runtime: LiveCursorProviderTurnRuntime },
|
|
40
44
|
context: CursorProviderTurnRunnerParams["context"],
|
|
41
45
|
): void {
|
|
42
|
-
if (prepared.runtime.
|
|
46
|
+
if (prepared.runtime.liveRun.disposed) return;
|
|
43
47
|
const { liveRun } = prepared.runtime;
|
|
44
48
|
switch (classifyCursorRunEmission(outcome)) {
|
|
45
49
|
case "finished":
|
|
46
|
-
prepared.
|
|
50
|
+
prepared.lifecycle.commitSend(context, prepared.meta.bootstrap);
|
|
51
|
+
if (prepared.meta.resumeNotice) liveRun.resumeNotice = prepared.meta.resumeNotice;
|
|
47
52
|
cursorLiveRuns.markFinished(liveRun, outcome.kind === "finished" ? outcome.finalText : "");
|
|
48
53
|
break;
|
|
49
54
|
case "cancelled":
|
|
@@ -69,7 +74,7 @@ export interface CursorRunFinalizerParams {
|
|
|
69
74
|
|
|
70
75
|
export interface StartCursorLiveRunCompletionParams {
|
|
71
76
|
send: CursorProviderTurnSend;
|
|
72
|
-
prepared:
|
|
77
|
+
prepared: LocalCursorProviderTurnPrepareResult & { runtime: LiveCursorProviderTurnRuntime };
|
|
73
78
|
modelId: string;
|
|
74
79
|
discardIncompleteTools: (outcome: IncompleteCursorToolRunOutcomeInput) => void;
|
|
75
80
|
}
|
|
@@ -84,7 +89,6 @@ export class CursorRunFinalizer {
|
|
|
84
89
|
const sdkEventDebug = this.params.sdkEventDebug();
|
|
85
90
|
const { send, prepared, modelId, discardIncompleteTools } = startParams;
|
|
86
91
|
const { run, cursorAgentMessageOffset } = send;
|
|
87
|
-
if (prepared.runtime.kind !== "live") throw new Error("startLiveRunCompletion requires a live run");
|
|
88
92
|
const { liveRun } = prepared.runtime;
|
|
89
93
|
const waitCompletion = awaitFinalizeCursorRunOutcome({
|
|
90
94
|
run,
|
|
@@ -93,36 +97,37 @@ export class CursorRunFinalizer {
|
|
|
93
97
|
modelId,
|
|
94
98
|
signal: runnerParams.options?.signal,
|
|
95
99
|
runResultFallback: run.result,
|
|
100
|
+
runErrorFallback: run.error,
|
|
96
101
|
resolvedApiKey: this.params.resolvedApiKey(),
|
|
97
102
|
optionsApiKey: runnerParams.options?.apiKey,
|
|
98
103
|
sdkEventDebug,
|
|
99
104
|
cacheContextWindow: true,
|
|
100
105
|
contextWindowAgentId: liveRun.agent.agentId,
|
|
101
106
|
})
|
|
102
|
-
.then(async (
|
|
103
|
-
applyLiveRunOutcome(outcome, prepared, runnerParams.context);
|
|
107
|
+
.then(async (finalized) => {
|
|
108
|
+
applyLiveRunOutcome(finalized.outcome, prepared, runnerParams.context);
|
|
104
109
|
})
|
|
105
|
-
.catch(
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
);
|
|
110
|
+
.catch((error: unknown) => {
|
|
111
|
+
this.safeCleanup(() => discardIncompleteTools({ status: "error" }));
|
|
112
|
+
if (!liveRun.disposed) {
|
|
113
|
+
cursorLiveRuns.markError(
|
|
114
|
+
liveRun,
|
|
115
|
+
sanitizeCursorProviderError(error, this.params.resolvedApiKey() ?? runnerParams.options?.apiKey),
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
this.safeCleanup(() => sdkEventDebug?.recordWaitResult({ status: "error", error: String(error) }));
|
|
119
|
+
this.safeCleanup(() => sdkEventDebug?.recordError("run_wait", error));
|
|
115
120
|
});
|
|
116
|
-
// Mark the pooled agent busy as soon as the SDK run exists so auto-compaction summarization
|
|
121
|
+
// Mark the pooled local agent busy as soon as the SDK run exists so auto-compaction summarization
|
|
117
122
|
// (and other concurrent acquires) wait for run.wait() instead of hitting AgentBusyError.
|
|
118
|
-
prepared.
|
|
123
|
+
prepared.lifecycle.trackRunCompletion(waitCompletion);
|
|
119
124
|
return { waitCompletion, prepared };
|
|
120
125
|
}
|
|
121
126
|
|
|
122
127
|
async applyTerminalEvent(event: CursorTurnTerminalEvent): Promise<void> {
|
|
123
128
|
if (this.terminalApplied) return;
|
|
124
129
|
if (event.kind === "direct") {
|
|
125
|
-
await this.applyDirectOutcome(event.prepared, event.outcome);
|
|
130
|
+
await this.applyDirectOutcome(event.prepared, event.outcome, event.displayOnlyTraceBlock);
|
|
126
131
|
this.terminalApplied = true;
|
|
127
132
|
return;
|
|
128
133
|
}
|
|
@@ -150,6 +155,7 @@ export class CursorRunFinalizer {
|
|
|
150
155
|
.catch(() => {});
|
|
151
156
|
return;
|
|
152
157
|
}
|
|
158
|
+
await prepared?.lifecycle.dispose().catch(() => {});
|
|
153
159
|
await this.finalizeSdkEventDebugBestEffort();
|
|
154
160
|
this.safeCleanup(() => this.params.sdkProcessErrorGuard.dispose());
|
|
155
161
|
}
|
|
@@ -157,45 +163,48 @@ export class CursorRunFinalizer {
|
|
|
157
163
|
private async applyDirectOutcome(
|
|
158
164
|
prepared: CursorProviderTurnPrepareResult,
|
|
159
165
|
outcome: CursorRunOutcome,
|
|
166
|
+
displayOnlyTraceBlock: string | undefined,
|
|
160
167
|
): Promise<void> {
|
|
161
168
|
const { stream, partial, model, context } = this.params.runnerParams;
|
|
162
169
|
prepared.runtime.turnCoordinator.closeTraceBlock();
|
|
163
170
|
switch (classifyCursorRunEmission(outcome)) {
|
|
164
171
|
case "cancelled":
|
|
165
|
-
await
|
|
172
|
+
await prepared.lifecycle.abandon();
|
|
166
173
|
this.pushTerminalError(partial, "aborted", getCursorRunAbortMessage(outcome));
|
|
167
174
|
break;
|
|
168
175
|
case "failed":
|
|
169
|
-
await
|
|
176
|
+
await prepared.lifecycle.abandon();
|
|
170
177
|
this.pushTerminalError(partial, "error", outcome.kind === "error" ? outcome.errorMessage : "Cursor SDK run failed.");
|
|
171
178
|
break;
|
|
172
179
|
case "finished":
|
|
173
|
-
prepared.
|
|
180
|
+
prepared.lifecycle.commitSend(context, prepared.meta.bootstrap);
|
|
174
181
|
prepared.runtime.turnCoordinator.flushText(
|
|
175
182
|
outcome.kind === "finished" && hasUsableText(outcome.finalText) ? [outcome.finalText] : [],
|
|
176
183
|
);
|
|
177
184
|
applyCursorUsage(partial, model, context, prepared.meta.promptInputTokens, {
|
|
178
185
|
turn: prepared.runtime.turnCoordinator.lastSdkTurnUsage,
|
|
179
186
|
});
|
|
187
|
+
if (prepared.meta.resumeNotice) emitDisplayOnlyTraceBlock(stream, partial, prepared.meta.resumeNotice);
|
|
188
|
+
if (displayOnlyTraceBlock) emitDisplayOnlyTraceBlock(stream, partial, displayOnlyTraceBlock);
|
|
180
189
|
stream.push({ type: "done", reason: "stop", message: partial });
|
|
181
190
|
break;
|
|
182
191
|
}
|
|
183
192
|
}
|
|
184
193
|
|
|
185
194
|
private async applyErrorOutcome(prepared: CursorProviderTurnPrepareResult | undefined, error: unknown): Promise<void> {
|
|
186
|
-
this.
|
|
187
|
-
prepared?.runtime.turnCoordinator.discardIncompleteStartedToolCalls(
|
|
195
|
+
this.safeCleanup(() => prepared?.runtime.turnCoordinator.discardIncompleteStartedToolCalls(
|
|
188
196
|
buildIncompleteCursorToolRunOutcome({
|
|
189
197
|
status: error instanceof CursorLiveRunAbortError ? "cancelled" : "error",
|
|
190
198
|
signalAborted: error instanceof CursorLiveRunAbortError,
|
|
191
199
|
}),
|
|
192
|
-
);
|
|
200
|
+
));
|
|
193
201
|
const activeLiveRun = prepared?.runtime.liveRun;
|
|
194
202
|
if (activeLiveRun && !activeLiveRun.disposed) {
|
|
195
203
|
await cursorLiveRuns.release(activeLiveRun);
|
|
196
204
|
} else {
|
|
197
|
-
await
|
|
205
|
+
await prepared?.lifecycle.abandon();
|
|
198
206
|
}
|
|
207
|
+
this.safeCleanup(() => this.params.sdkEventDebug()?.recordError("provider_stream", error));
|
|
199
208
|
if (error instanceof CursorLiveRunAbortError) {
|
|
200
209
|
this.params.sdkProcessErrorGuard.suppressAbortErrors();
|
|
201
210
|
this.pushTerminalError(this.params.runnerParams.partial, "aborted", this.abortMessage());
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { RunResult } from "@cursor/sdk";
|
|
1
|
+
import type { RunError, RunResult } from "@cursor/sdk";
|
|
2
2
|
import { selectCursorFinalText } from "./cursor-run-final-text.js";
|
|
3
3
|
import {
|
|
4
4
|
formatCursorSdkAbortMessage,
|
|
@@ -42,6 +42,7 @@ export interface ResolveCursorRunOutcomeParams {
|
|
|
42
42
|
planTextCandidate?: string;
|
|
43
43
|
selectFinalTextOptions?: { allowPartialPrefix?: boolean };
|
|
44
44
|
runResultFallback?: string;
|
|
45
|
+
runErrorFallback?: RunError;
|
|
45
46
|
resolvedApiKey?: string;
|
|
46
47
|
optionsApiKey?: string;
|
|
47
48
|
}
|
|
@@ -91,7 +92,11 @@ export function resolveCursorRunOutcome(params: ResolveCursorRunOutcomeParams):
|
|
|
91
92
|
}
|
|
92
93
|
|
|
93
94
|
if (waitResult.status === "error") {
|
|
94
|
-
const failureDetail = formatCursorSdkRunFailureDetail(
|
|
95
|
+
const failureDetail = formatCursorSdkRunFailureDetail(
|
|
96
|
+
waitResult,
|
|
97
|
+
params.runResultFallback,
|
|
98
|
+
params.runErrorFallback,
|
|
99
|
+
);
|
|
95
100
|
return {
|
|
96
101
|
kind: "error",
|
|
97
102
|
waitResult,
|
|
@@ -1,6 +1,13 @@
|
|
|
1
|
-
import type { SDKAgent } from "@cursor/sdk";
|
|
1
|
+
import type { 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";
|
|
3
9
|
import { getCheckpointContextWindow, saveCachedContextWindow } from "./context-window-cache.js";
|
|
10
|
+
import { scrubSensitiveText } from "./cursor-sensitive-text.js";
|
|
4
11
|
import type { CursorSdkEventDebugSink } from "./cursor-sdk-event-debug.js";
|
|
5
12
|
import type { CursorSdkTurnCoordinator } from "./cursor-provider-turn-coordinator.js";
|
|
6
13
|
import {
|
|
@@ -30,6 +37,7 @@ export interface BuildCursorRunOutcomeParams {
|
|
|
30
37
|
prepared: CursorProviderTurnPrepareResult;
|
|
31
38
|
signal?: AbortSignal;
|
|
32
39
|
runResultFallback?: string;
|
|
40
|
+
runErrorFallback?: RunError;
|
|
33
41
|
resolvedApiKey?: string;
|
|
34
42
|
optionsApiKey?: string;
|
|
35
43
|
}
|
|
@@ -46,6 +54,7 @@ export function buildCursorRunOutcomeFromWait(params: BuildCursorRunOutcomeParam
|
|
|
46
54
|
planTextCandidate: turnCoordinator.planTextCandidate,
|
|
47
55
|
selectFinalTextOptions: liveRun ? undefined : { allowPartialPrefix: true },
|
|
48
56
|
runResultFallback: params.runResultFallback,
|
|
57
|
+
runErrorFallback: params.runErrorFallback,
|
|
49
58
|
resolvedApiKey: params.resolvedApiKey,
|
|
50
59
|
optionsApiKey: params.optionsApiKey,
|
|
51
60
|
});
|
|
@@ -76,6 +85,22 @@ async function replayCursorTranscriptWebToolCalls(
|
|
|
76
85
|
}
|
|
77
86
|
}
|
|
78
87
|
|
|
88
|
+
function scrubCursorCloudReportingError(error: unknown, apiKey: string | undefined): Error {
|
|
89
|
+
return new Error(scrubSensitiveText(error instanceof Error ? error.message : String(error), apiKey));
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function recordCursorCloudReportingError(
|
|
93
|
+
sdkEventDebug: CursorSdkEventDebugSink | undefined,
|
|
94
|
+
error: unknown,
|
|
95
|
+
apiKey: string | undefined,
|
|
96
|
+
): void {
|
|
97
|
+
try {
|
|
98
|
+
sdkEventDebug?.recordError("cloud_run_report", scrubCursorCloudReportingError(error, apiKey));
|
|
99
|
+
} catch {
|
|
100
|
+
// Debug reporting must never affect provider execution.
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
79
104
|
export interface AwaitFinalizeCursorRunOutcomeParams {
|
|
80
105
|
run: Awaited<ReturnType<SDKAgent["send"]>>;
|
|
81
106
|
prepared: CursorProviderTurnPrepareResult;
|
|
@@ -83,6 +108,7 @@ export interface AwaitFinalizeCursorRunOutcomeParams {
|
|
|
83
108
|
modelId: string;
|
|
84
109
|
signal?: AbortSignal;
|
|
85
110
|
runResultFallback?: string;
|
|
111
|
+
runErrorFallback?: RunError;
|
|
86
112
|
resolvedApiKey?: string;
|
|
87
113
|
optionsApiKey?: string;
|
|
88
114
|
sdkEventDebug?: CursorSdkEventDebugSink;
|
|
@@ -92,19 +118,59 @@ export interface AwaitFinalizeCursorRunOutcomeParams {
|
|
|
92
118
|
contextWindowAgentId?: string;
|
|
93
119
|
}
|
|
94
120
|
|
|
121
|
+
export interface FinalizedCursorRunOutcome {
|
|
122
|
+
outcome: CursorRunOutcome;
|
|
123
|
+
displayOnlyTraceBlock?: string;
|
|
124
|
+
}
|
|
125
|
+
|
|
95
126
|
/** Single wait/finalize path for SDK runs: wait, debug capture, transcript replay, incomplete tools, artifacts, context cache. */
|
|
96
|
-
export async function awaitFinalizeCursorRunOutcome(params: AwaitFinalizeCursorRunOutcomeParams): Promise<
|
|
127
|
+
export async function awaitFinalizeCursorRunOutcome(params: AwaitFinalizeCursorRunOutcomeParams): Promise<FinalizedCursorRunOutcome> {
|
|
128
|
+
const apiKey = params.resolvedApiKey ?? params.optionsApiKey;
|
|
97
129
|
const waitResult = params.waitResult ?? (await params.run.wait());
|
|
98
|
-
params.sdkEventDebug?.recordWaitResult(waitResult);
|
|
99
130
|
const outcome = buildCursorRunOutcomeFromWait({
|
|
100
131
|
waitResult,
|
|
101
132
|
prepared: params.prepared,
|
|
102
133
|
signal: params.signal,
|
|
103
134
|
runResultFallback: params.runResultFallback,
|
|
135
|
+
runErrorFallback: params.runErrorFallback,
|
|
104
136
|
resolvedApiKey: params.resolvedApiKey,
|
|
105
137
|
optionsApiKey: params.optionsApiKey,
|
|
106
138
|
});
|
|
107
|
-
|
|
139
|
+
let displayOnlyTraceBlock: string | undefined;
|
|
140
|
+
if (params.prepared.runtimeTarget === "cloud" && isCursorRunFinishedSuccessfully(outcome)) {
|
|
141
|
+
let report: CursorCloudRunReport = { agentId: params.run.agentId, runId: params.run.id, branches: [] };
|
|
142
|
+
try {
|
|
143
|
+
report = await collectCursorCloudRunReport({
|
|
144
|
+
agent: params.prepared.agent,
|
|
145
|
+
run: params.run,
|
|
146
|
+
waitResult,
|
|
147
|
+
apiKey,
|
|
148
|
+
});
|
|
149
|
+
} catch (error) {
|
|
150
|
+
recordCursorCloudReportingError(params.sdkEventDebug, error, apiKey);
|
|
151
|
+
}
|
|
152
|
+
try {
|
|
153
|
+
recordCursorCloudLifecycleRun(report, { apiKey });
|
|
154
|
+
} catch (error) {
|
|
155
|
+
recordCursorCloudReportingError(params.sdkEventDebug, error, apiKey);
|
|
156
|
+
}
|
|
157
|
+
try {
|
|
158
|
+
params.sdkEventDebug?.recordProviderEvent("cloud_run_report", report);
|
|
159
|
+
} catch (error) {
|
|
160
|
+
recordCursorCloudReportingError(params.sdkEventDebug, error, apiKey);
|
|
161
|
+
}
|
|
162
|
+
try {
|
|
163
|
+
displayOnlyTraceBlock = formatCursorCloudRunReport(report, { apiKey });
|
|
164
|
+
} catch (error) {
|
|
165
|
+
recordCursorCloudReportingError(params.sdkEventDebug, error, apiKey);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
try {
|
|
169
|
+
params.sdkEventDebug?.recordWaitResult(waitResult);
|
|
170
|
+
} catch {
|
|
171
|
+
// Debug reporting must never affect provider execution.
|
|
172
|
+
}
|
|
173
|
+
if (params.prepared.runtimeTarget === "local" && isCursorRunFinishedSuccessfully(outcome)) {
|
|
108
174
|
await replayCursorTranscriptWebToolCalls(
|
|
109
175
|
params.run.agentId,
|
|
110
176
|
params.prepared.cwd,
|
|
@@ -114,9 +180,13 @@ export async function awaitFinalizeCursorRunOutcome(params: AwaitFinalizeCursorR
|
|
|
114
180
|
);
|
|
115
181
|
}
|
|
116
182
|
params.prepared.runtime.turnCoordinator.discardIncompleteStartedToolCalls(outcome.incompleteTools);
|
|
117
|
-
|
|
118
|
-
|
|
183
|
+
try {
|
|
184
|
+
await params.sdkEventDebug?.captureRunArtifacts(params.run);
|
|
185
|
+
} catch {
|
|
186
|
+
// Debug artifact failures must never affect provider execution.
|
|
187
|
+
}
|
|
188
|
+
if (params.prepared.runtimeTarget === "local" && params.cacheContextWindow !== false) {
|
|
119
189
|
await cacheSdkContextWindow(params.contextWindowAgentId ?? params.run.agentId, params.modelId, params.prepared.cwd);
|
|
120
190
|
}
|
|
121
|
-
return outcome;
|
|
191
|
+
return { outcome, displayOnlyTraceBlock };
|
|
122
192
|
}
|