pi-cursor-sdk 0.1.19 → 0.1.21
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 +52 -0
- package/README.md +72 -11
- package/docs/cursor-dogfood-checklist.md +57 -0
- package/docs/cursor-live-smoke-checklist.md +116 -10
- package/docs/cursor-model-ux-spec.md +60 -19
- package/docs/cursor-native-tool-replay.md +21 -11
- package/docs/cursor-native-tool-visual-audit.md +104 -59
- package/docs/cursor-testing-lessons.md +10 -5
- package/docs/cursor-tool-surfaces.md +69 -0
- package/package.json +37 -11
- package/scripts/debug-provider-events.d.mts +59 -0
- package/scripts/debug-provider-events.mjs +70 -175
- package/scripts/debug-sdk-events.d.mts +90 -0
- package/scripts/debug-sdk-events.mjs +36 -98
- package/scripts/fixtures/plan-strip-shim/index.ts +12 -0
- package/scripts/isolated-cursor-smoke.sh +264 -102
- package/scripts/lib/cursor-child-process.d.mts +10 -0
- package/scripts/lib/cursor-child-process.mjs +50 -0
- package/scripts/lib/cursor-cli-args.d.mts +63 -0
- package/scripts/lib/cursor-cli-args.mjs +129 -0
- package/scripts/lib/cursor-script-fail.d.mts +1 -0
- package/scripts/lib/cursor-script-fail.mjs +13 -0
- package/scripts/lib/cursor-sdk-output-filter.d.mts +5 -0
- package/scripts/lib/cursor-smoke-env.d.mts +38 -0
- package/scripts/lib/cursor-smoke-env.mjs +81 -0
- package/scripts/lib/cursor-smoke-shell.sh +174 -0
- package/scripts/lib/cursor-visual-render.d.mts +15 -0
- package/scripts/lib/cursor-visual-render.mjs +131 -0
- package/scripts/probe-mcp-coldstart.mjs +226 -0
- package/scripts/refresh-cursor-model-snapshots.mjs +29 -65
- package/scripts/steering-rpc-smoke.mjs +170 -65
- package/scripts/tmux-live-smoke.sh +152 -98
- package/scripts/visual-tui-smoke.mjs +659 -0
- package/shared/cursor-sdk-event-debug-env.d.mts +12 -0
- package/shared/cursor-sdk-event-debug-env.mjs +13 -0
- package/shared/cursor-sensitive-text.d.mts +1 -0
- package/{scripts/lib/cursor-probe-utils.mjs → shared/cursor-sensitive-text.mjs} +1 -13
- package/shared/cursor-setting-sources.d.mts +5 -0
- package/shared/cursor-setting-sources.mjs +22 -0
- package/src/context.ts +21 -12
- package/src/cursor-bridge-contract.ts +1 -3
- package/src/cursor-incomplete-tool-visibility.ts +72 -49
- package/src/cursor-mcp-timeout-override.ts +66 -11
- package/src/cursor-native-tool-display-registration.ts +63 -27
- package/src/cursor-native-tool-display-replay.ts +246 -143
- package/src/cursor-native-tool-display-state.ts +2 -0
- package/src/cursor-native-tool-display-tools.ts +149 -41
- package/src/cursor-provider-live-run-drain.ts +1 -52
- package/src/cursor-provider-run-finalizer.ts +235 -0
- package/src/cursor-provider-run-outcome.ts +149 -0
- package/src/cursor-provider-turn-api-key.ts +8 -0
- package/src/cursor-provider-turn-coordinator.ts +113 -440
- package/src/cursor-provider-turn-display-router.ts +216 -0
- package/src/cursor-provider-turn-emit.ts +59 -0
- package/src/cursor-provider-turn-finalize.ts +119 -0
- package/src/cursor-provider-turn-lifecycle-emitter.ts +97 -0
- package/src/cursor-provider-turn-message-offset.ts +15 -0
- package/src/cursor-provider-turn-prepare.ts +216 -0
- package/src/cursor-provider-turn-runner.ts +138 -0
- package/src/cursor-provider-turn-sdk-normalizer.ts +88 -0
- package/src/cursor-provider-turn-send.ts +103 -0
- package/src/cursor-provider-turn-shell-output.ts +107 -0
- package/src/cursor-provider-turn-tool-ledger.ts +126 -0
- package/src/cursor-provider-turn-types.ts +87 -0
- package/src/cursor-provider.ts +16 -482
- package/src/cursor-replay-activity-builders.ts +276 -0
- package/src/cursor-replay-source-names.ts +33 -0
- package/src/cursor-replay-summary-args.ts +191 -0
- package/src/cursor-replay-tool-details.ts +464 -0
- package/src/cursor-run-final-text.ts +56 -0
- package/src/cursor-sdk-abort-error-guard.ts +4 -0
- package/src/cursor-sdk-event-debug-constants.ts +14 -5
- package/src/cursor-sdk-event-debug.ts +8 -2
- package/src/cursor-sensitive-text.ts +3 -36
- package/src/cursor-session-agent.ts +265 -88
- package/src/cursor-setting-sources.ts +7 -10
- package/src/cursor-state.ts +232 -28
- package/src/cursor-tool-lifecycle.ts +17 -42
- package/src/cursor-tool-manifest.ts +41 -0
- package/src/cursor-tool-names.ts +18 -79
- package/src/cursor-tool-presentation-registry.ts +556 -0
- package/src/cursor-tool-transcript.ts +1 -1
- package/src/cursor-tool-visibility.ts +39 -0
- package/src/cursor-transcript-tool-formatters.ts +0 -59
- package/src/cursor-transcript-tool-specs.ts +169 -232
- package/src/cursor-transcript-utils.ts +0 -44
- package/src/cursor-web-tool-activity.ts +10 -60
- package/src/cursor-web-tool-args.ts +39 -0
- package/src/index.ts +4 -10
package/src/cursor-provider.ts
CHANGED
|
@@ -1,76 +1,26 @@
|
|
|
1
1
|
import {
|
|
2
2
|
type Api,
|
|
3
|
+
type AssistantMessage,
|
|
3
4
|
type AssistantMessageEventStream,
|
|
4
5
|
type Context,
|
|
5
6
|
createAssistantMessageEventStream,
|
|
6
7
|
type Model,
|
|
7
8
|
type SimpleStreamOptions,
|
|
8
|
-
type AssistantMessage,
|
|
9
9
|
} from "@earendil-works/pi-ai";
|
|
10
|
-
import { Agent, createAgentPlatform } from "@cursor/sdk";
|
|
11
|
-
import type { SDKAgent } from "@cursor/sdk";
|
|
12
|
-
import { installCursorMcpToolTimeoutOverride } from "./cursor-mcp-timeout-override.js";
|
|
13
|
-
import { installCursorSdkOutputFilter, suppressCursorSdkOutput } from "./cursor-sdk-output-filter.js";
|
|
14
|
-
import {
|
|
15
|
-
acquireSessionCursorAgent,
|
|
16
|
-
buildCursorSessionSendPrompt,
|
|
17
|
-
commitSessionAgentSend,
|
|
18
|
-
disposeAllSessionCursorAgents,
|
|
19
|
-
planCursorSessionSend,
|
|
20
|
-
resetSessionCursorAgent,
|
|
21
|
-
} from "./cursor-session-agent.js";
|
|
22
|
-
import {
|
|
23
|
-
type CursorPiBridgeToolRequest,
|
|
24
|
-
type CursorPiToolBridgeRun,
|
|
25
|
-
} from "./cursor-pi-tool-bridge.js";
|
|
26
10
|
import {
|
|
27
|
-
applyCursorApproximateUsage,
|
|
28
|
-
estimateCursorPromptInputTokens,
|
|
29
|
-
getCursorPromptOptions,
|
|
30
|
-
} from "./cursor-usage-accounting.js";
|
|
31
|
-
import { getCursorSessionCwd } from "./cursor-session-cwd.js";
|
|
32
|
-
import { getActiveContextToolNames } from "./cursor-context-tools.js";
|
|
33
|
-
import { CursorLiveRunAbortError, type CursorLiveRun } from "./cursor-live-run-coordinator.js";
|
|
34
|
-
import {
|
|
35
|
-
abandonSessionCursorAgent,
|
|
36
|
-
createCursorNativeReplayId,
|
|
37
|
-
cursorLiveRuns,
|
|
38
|
-
drainCursorLiveRunTurn,
|
|
39
|
-
drainExistingCursorLiveRunBeforeSend,
|
|
40
|
-
flushPendingCursorLiveRunTraceEventsToStream,
|
|
41
11
|
DEFAULT_CURSOR_NATIVE_REPLAY_IDLE_DISPOSE_MS,
|
|
42
12
|
getPendingCursorLiveRun,
|
|
43
13
|
hasTrailingUserMessagesAfterToolResults,
|
|
44
14
|
releaseAllPendingCursorLiveRunsForTests,
|
|
45
15
|
resetCursorNativeReplayIdleDisposeMs,
|
|
46
|
-
selectCursorFinalText,
|
|
47
16
|
setCursorNativeReplayIdleDisposeMs,
|
|
48
|
-
settleCursorLiveToolBatch,
|
|
49
17
|
} from "./cursor-provider-live-run-drain.js";
|
|
50
|
-
import {
|
|
51
|
-
import {
|
|
52
|
-
import {
|
|
53
|
-
import {
|
|
54
|
-
attachCursorSdkEventDebugPiStreamTap,
|
|
55
|
-
CursorSdkEventDebugSink,
|
|
56
|
-
DISCARDED_INCOMPLETE_TOOL_CALL_REASON,
|
|
57
|
-
} from "./cursor-sdk-event-debug.js";
|
|
58
|
-
import { CursorSdkTurnCoordinator } from "./cursor-provider-turn-coordinator.js";
|
|
59
|
-
import { isCursorNativeToolDisplayRuntimeEnabled } from "./cursor-native-tool-display.js";
|
|
60
|
-
import {
|
|
61
|
-
formatCursorSdkAbortMessage,
|
|
62
|
-
formatCursorSdkRunFailureDetail,
|
|
63
|
-
MISSING_CURSOR_API_KEY_MESSAGE,
|
|
64
|
-
resolveCursorSdkAbortCause,
|
|
65
|
-
sanitizeCursorProviderError,
|
|
66
|
-
} from "./cursor-provider-errors.js";
|
|
67
|
-
import { getEffectiveCursorSettingSources } from "./cursor-setting-sources.js";
|
|
68
|
-
import { hasUsableText } from "./cursor-record-utils.js";
|
|
69
|
-
import {
|
|
70
|
-
countCursorAgentMessages,
|
|
71
|
-
loadCursorTranscriptWebToolCallsAfterOffset,
|
|
72
|
-
} from "./cursor-agent-message-web-tools.js";
|
|
18
|
+
import { cursorLiveRuns } from "./cursor-provider-live-run-drain.js";
|
|
19
|
+
import { disposeAllSessionCursorAgents } from "./cursor-session-agent.js";
|
|
20
|
+
import { attachCursorSdkEventDebugPiStreamTap, type CursorSdkEventDebugSink } from "./cursor-sdk-event-debug.js";
|
|
73
21
|
import { installCursorSdkAbortErrorSuppression } from "./cursor-sdk-abort-error-guard.js";
|
|
22
|
+
import { sanitizeCursorProviderError } from "./cursor-provider-errors.js";
|
|
23
|
+
import { CursorProviderTurnRunner, resolveCursorApiKey } from "./cursor-provider-turn-runner.js";
|
|
74
24
|
|
|
75
25
|
function makeInitialMessage(model: Model<Api>): AssistantMessage {
|
|
76
26
|
return {
|
|
@@ -92,26 +42,6 @@ function makeInitialMessage(model: Model<Api>): AssistantMessage {
|
|
|
92
42
|
};
|
|
93
43
|
}
|
|
94
44
|
|
|
95
|
-
const CURSOR_API_KEY_ENV_VAR = "CURSOR_API_KEY";
|
|
96
|
-
|
|
97
|
-
function resolveCursorApiKey(apiKey?: string): string | undefined {
|
|
98
|
-
const trimmed = apiKey?.trim();
|
|
99
|
-
if (!trimmed) return undefined;
|
|
100
|
-
if (trimmed === CURSOR_API_KEY_ENV_VAR) return process.env.CURSOR_API_KEY?.trim();
|
|
101
|
-
return trimmed;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
async function cacheSdkContextWindow(agentId: string, modelId: string): Promise<void> {
|
|
105
|
-
try {
|
|
106
|
-
const platform = await createAgentPlatform();
|
|
107
|
-
const checkpoint = await platform.checkpointStore.loadLatest(agentId);
|
|
108
|
-
const contextWindow = getCheckpointContextWindow(checkpoint);
|
|
109
|
-
if (contextWindow) saveCachedContextWindow(modelId, contextWindow);
|
|
110
|
-
} catch {
|
|
111
|
-
// Context-window cache failures must not affect response streaming.
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
45
|
export function streamCursor(
|
|
116
46
|
model: Model<Api>,
|
|
117
47
|
context: Context,
|
|
@@ -123,417 +53,21 @@ export function streamCursor(
|
|
|
123
53
|
|
|
124
54
|
(async () => {
|
|
125
55
|
const partial = makeInitialMessage(model);
|
|
126
|
-
let agent: SDKAgent | null = null;
|
|
127
|
-
let activeLiveRun: CursorLiveRun | undefined;
|
|
128
|
-
let bridgeRun: CursorPiToolBridgeRun | undefined;
|
|
129
|
-
let liveRunForBridgeQueue: CursorLiveRun | undefined;
|
|
130
|
-
const queuedBridgeRequestsBeforeLiveRun: CursorPiBridgeToolRequest[] = [];
|
|
131
|
-
let resolvedApiKey: string | undefined;
|
|
132
|
-
let sessionAgentScopeKey = "";
|
|
133
|
-
let abortSignal: AbortSignal | undefined;
|
|
134
|
-
let abortListener: (() => void) | undefined;
|
|
135
|
-
let restoreCursorSdkOutputFilter: (() => void) | undefined;
|
|
136
|
-
let sdkEventDebug: CursorSdkEventDebugSink | undefined;
|
|
137
|
-
let deferSdkEventDebugFinalize = false;
|
|
138
56
|
const sdkAbortErrorSuppression = installCursorSdkAbortErrorSuppression();
|
|
139
57
|
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
};
|
|
148
|
-
|
|
149
|
-
const getCursorAgentMessageOffset = async (agentId: string, cwd: string): Promise<number | undefined> => {
|
|
150
|
-
try {
|
|
151
|
-
return await countCursorAgentMessages(agentId, cwd);
|
|
152
|
-
} catch (error) {
|
|
153
|
-
sdkEventDebug?.recordError("cursor_agent_message_count", error);
|
|
154
|
-
return undefined;
|
|
155
|
-
}
|
|
156
|
-
};
|
|
157
|
-
|
|
158
|
-
const replayCursorTranscriptWebToolCalls = async (
|
|
159
|
-
agentId: string,
|
|
160
|
-
cwd: string,
|
|
161
|
-
messageOffset: number | undefined,
|
|
162
|
-
turnCoordinator: CursorSdkTurnCoordinator,
|
|
163
|
-
): Promise<void> => {
|
|
164
|
-
try {
|
|
165
|
-
const transcriptToolCalls = await loadCursorTranscriptWebToolCallsAfterOffset({ agentId, cwd, offset: messageOffset });
|
|
166
|
-
if (transcriptToolCalls.length === 0) return;
|
|
167
|
-
sdkEventDebug?.recordCoordinatorEvent("cursor-transcript-web-tools", {
|
|
168
|
-
agentId,
|
|
169
|
-
messageOffset,
|
|
170
|
-
count: transcriptToolCalls.length,
|
|
171
|
-
});
|
|
172
|
-
turnCoordinator.handleTranscriptCompletedToolCalls(transcriptToolCalls);
|
|
173
|
-
} catch (error) {
|
|
174
|
-
sdkEventDebug?.recordError("cursor_transcript_web_tools", error);
|
|
175
|
-
}
|
|
176
|
-
};
|
|
58
|
+
const runner = new CursorProviderTurnRunner({
|
|
59
|
+
model,
|
|
60
|
+
context,
|
|
61
|
+
stream,
|
|
62
|
+
partial,
|
|
63
|
+
options,
|
|
64
|
+
sdkEventDebugRef,
|
|
65
|
+
});
|
|
177
66
|
|
|
178
67
|
try {
|
|
179
|
-
|
|
180
|
-
try {
|
|
181
|
-
const throwIfAborted = (): void => {
|
|
182
|
-
if (options?.signal?.aborted) throw new CursorLiveRunAbortError();
|
|
183
|
-
};
|
|
184
|
-
|
|
185
|
-
stream.push({ type: "start", partial });
|
|
186
|
-
throwIfAborted();
|
|
187
|
-
|
|
188
|
-
const cwd = getCursorSessionCwd();
|
|
189
|
-
sdkEventDebug = CursorSdkEventDebugSink.maybeCreate({
|
|
190
|
-
cwd,
|
|
191
|
-
modelId: model.id,
|
|
192
|
-
provider: model.provider,
|
|
193
|
-
});
|
|
194
|
-
sdkEventDebugRef.current = sdkEventDebug;
|
|
195
|
-
sdkEventDebug?.recordContextSnapshot(context);
|
|
196
|
-
|
|
197
|
-
if ((await drainExistingCursorLiveRunBeforeSend(stream, partial, model, context, options?.signal, sdkEventDebug)) === "stream_ended") {
|
|
198
|
-
sdkEventDebug?.recordFinalPartial(partial);
|
|
199
|
-
await sdkEventDebug?.finalize();
|
|
200
|
-
sdkEventDebugRef.current = undefined;
|
|
201
|
-
return;
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
const apiKey = resolveCursorApiKey(options?.apiKey);
|
|
205
|
-
if (!apiKey) throw new Error(MISSING_CURSOR_API_KEY_MESSAGE);
|
|
206
|
-
resolvedApiKey = apiKey;
|
|
207
|
-
|
|
208
|
-
const fastEnabled = getEffectiveFastForModelId(model.id);
|
|
209
|
-
const selection = buildCursorModelSelection(model.id, options?.reasoning ?? "off", fastEnabled);
|
|
210
|
-
const settingSources = getEffectiveCursorSettingSources();
|
|
211
|
-
|
|
212
|
-
installCursorMcpToolTimeoutOverride();
|
|
213
|
-
restoreCursorSdkOutputFilter = installCursorSdkOutputFilter();
|
|
214
|
-
const sessionAgentAcquireParams = {
|
|
215
|
-
apiKey,
|
|
216
|
-
cwd,
|
|
217
|
-
modelSelection: selection,
|
|
218
|
-
settingSources,
|
|
219
|
-
debugRecorder: sdkEventDebug,
|
|
220
|
-
onBridgeToolRequest: (request: CursorPiBridgeToolRequest) => {
|
|
221
|
-
if (liveRunForBridgeQueue && !liveRunForBridgeQueue.disposed) {
|
|
222
|
-
cursorLiveRuns.queueEvent(liveRunForBridgeQueue, { type: "bridge-tool", request });
|
|
223
|
-
} else {
|
|
224
|
-
queuedBridgeRequestsBeforeLiveRun.push(request);
|
|
225
|
-
}
|
|
226
|
-
},
|
|
227
|
-
createAgent: (createOptions: Parameters<typeof Agent.create>[0]) =>
|
|
228
|
-
suppressCursorSdkOutput(() => Agent.create(createOptions)),
|
|
229
|
-
};
|
|
230
|
-
let sessionAgentLease = await acquireSessionCursorAgent(sessionAgentAcquireParams);
|
|
231
|
-
sessionAgentScopeKey = sessionAgentLease.scopeKey;
|
|
232
|
-
agent = sessionAgentLease.agent;
|
|
233
|
-
bridgeRun = sessionAgentLease.bridgeRun;
|
|
234
|
-
throwIfAborted();
|
|
235
|
-
|
|
236
|
-
const promptOptions = getCursorPromptOptions(model);
|
|
237
|
-
let sendPlan = planCursorSessionSend(sessionAgentLease.sendState, context);
|
|
238
|
-
let prompt = buildCursorSessionSendPrompt(context, promptOptions, sendPlan);
|
|
239
|
-
if (sendPlan.resetAgent) {
|
|
240
|
-
await resetSessionCursorAgent(sessionAgentLease.scopeKey);
|
|
241
|
-
sessionAgentLease = await acquireSessionCursorAgent(sessionAgentAcquireParams);
|
|
242
|
-
sessionAgentScopeKey = sessionAgentLease.scopeKey;
|
|
243
|
-
agent = sessionAgentLease.agent;
|
|
244
|
-
bridgeRun = sessionAgentLease.bridgeRun;
|
|
245
|
-
sendPlan = planCursorSessionSend(sessionAgentLease.sendState, context);
|
|
246
|
-
prompt = buildCursorSessionSendPrompt(context, promptOptions, sendPlan);
|
|
247
|
-
}
|
|
248
|
-
const bootstrap = sendPlan.mode === "bootstrap";
|
|
249
|
-
const sessionBridgeRun = sessionAgentLease.bridgeRun;
|
|
250
|
-
const promptInputTokens = estimateCursorPromptInputTokens(prompt, promptOptions);
|
|
251
|
-
const useNativeToolReplay = isCursorNativeToolDisplayRuntimeEnabled();
|
|
252
|
-
const activeToolNames = getActiveContextToolNames(context);
|
|
253
|
-
sdkEventDebug?.recordProviderMeta({
|
|
254
|
-
model: {
|
|
255
|
-
id: model.id,
|
|
256
|
-
provider: model.provider,
|
|
257
|
-
api: model.api,
|
|
258
|
-
reasoning: options?.reasoning ?? "off",
|
|
259
|
-
fastEnabled,
|
|
260
|
-
selection,
|
|
261
|
-
},
|
|
262
|
-
settingSources: settingSources ?? null,
|
|
263
|
-
sendState: sessionAgentLease.sendState,
|
|
264
|
-
sendPlan,
|
|
265
|
-
promptOptions,
|
|
266
|
-
activeToolNames: activeToolNames ? [...activeToolNames] : [],
|
|
267
|
-
sessionAgentScopeKey,
|
|
268
|
-
bridgeRunId: bridgeRun?.id,
|
|
269
|
-
});
|
|
270
|
-
const nativeReplayId = createCursorNativeReplayId();
|
|
271
|
-
const textDeltas: string[] = [];
|
|
272
|
-
const useLiveRun = useNativeToolReplay || bridgeRun !== undefined;
|
|
273
|
-
const liveRun: CursorLiveRun | undefined = useLiveRun
|
|
274
|
-
? cursorLiveRuns.start({
|
|
275
|
-
id: useNativeToolReplay ? nativeReplayId : bridgeRun!.id,
|
|
276
|
-
agent,
|
|
277
|
-
bridgeRun,
|
|
278
|
-
sessionBridgeRun,
|
|
279
|
-
sessionAgentScopeKey,
|
|
280
|
-
promptInputTokens,
|
|
281
|
-
textDeltas,
|
|
282
|
-
debugRecorder: sdkEventDebug,
|
|
283
|
-
})
|
|
284
|
-
: undefined;
|
|
285
|
-
if (liveRun) {
|
|
286
|
-
activeLiveRun = liveRun;
|
|
287
|
-
liveRunForBridgeQueue = liveRun;
|
|
288
|
-
for (const request of queuedBridgeRequestsBeforeLiveRun.splice(0)) {
|
|
289
|
-
cursorLiveRuns.queueEvent(liveRun, { type: "bridge-tool", request });
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
const turnCoordinator = new CursorSdkTurnCoordinator({
|
|
293
|
-
stream,
|
|
294
|
-
partial,
|
|
295
|
-
cwd,
|
|
296
|
-
resolvedApiKey,
|
|
297
|
-
liveRun,
|
|
298
|
-
useNativeToolReplay,
|
|
299
|
-
activeToolNames,
|
|
300
|
-
nativeReplayId,
|
|
301
|
-
textDeltas,
|
|
302
|
-
debugRecorder: sdkEventDebug,
|
|
303
|
-
});
|
|
304
|
-
turnCoordinatorForCleanup = turnCoordinator;
|
|
305
|
-
|
|
306
|
-
// Handle abort signal
|
|
307
|
-
let run: Awaited<ReturnType<SDKAgent["send"]>> | null = null;
|
|
308
|
-
abortListener = () => {
|
|
309
|
-
sdkAbortErrorSuppression.suppressAbortErrors();
|
|
310
|
-
activeLiveRun?.bridgeRun?.cancel("Cursor SDK run aborted");
|
|
311
|
-
if (run) {
|
|
312
|
-
run.cancel().catch(() => {});
|
|
313
|
-
}
|
|
314
|
-
};
|
|
315
|
-
abortSignal = options?.signal;
|
|
316
|
-
abortSignal?.addEventListener("abort", abortListener, { once: true });
|
|
317
|
-
|
|
318
|
-
throwIfAborted();
|
|
319
|
-
sdkEventDebug?.recordSendMeta({
|
|
320
|
-
mode: sendPlan.mode,
|
|
321
|
-
reason: sendPlan.reason,
|
|
322
|
-
resetAgent: sendPlan.resetAgent,
|
|
323
|
-
bootstrap,
|
|
324
|
-
promptText: prompt.text,
|
|
325
|
-
imageCount: prompt.images.length,
|
|
326
|
-
useNativeToolReplay,
|
|
327
|
-
bridgeEnabled: bridgeRun !== undefined,
|
|
328
|
-
nativeReplayId,
|
|
329
|
-
promptInputTokens,
|
|
330
|
-
});
|
|
331
|
-
const sendPayload = {
|
|
332
|
-
text: prompt.text,
|
|
333
|
-
images: prompt.images.length > 0 ? prompt.images : undefined,
|
|
334
|
-
};
|
|
335
|
-
sdkEventDebug?.recordSendPayload(sendPayload);
|
|
336
|
-
const cursorAgentMessageOffset = await getCursorAgentMessageOffset(agent.agentId, cwd);
|
|
337
|
-
sdkEventDebug?.recordProviderEvent("agent_send_start", sendPayload);
|
|
338
|
-
run = await agent.send(sendPayload, {
|
|
339
|
-
onDelta: (args) => {
|
|
340
|
-
sdkEventDebug?.recordOnDelta(args.update);
|
|
341
|
-
turnCoordinator.handleDelta(args.update);
|
|
342
|
-
},
|
|
343
|
-
onStep: (args) => {
|
|
344
|
-
sdkEventDebug?.recordOnStep(args.step);
|
|
345
|
-
turnCoordinator.handleStep(args.step);
|
|
346
|
-
},
|
|
347
|
-
});
|
|
348
|
-
sdkEventDebug?.recordRunMeta({
|
|
349
|
-
runId: run.id,
|
|
350
|
-
agentId: run.agentId,
|
|
351
|
-
status: run.status,
|
|
352
|
-
});
|
|
353
|
-
sdkEventDebug?.attachRunStream(run);
|
|
354
|
-
sdkEventDebug?.recordProviderEvent("agent_send_returned", {
|
|
355
|
-
runId: run.id,
|
|
356
|
-
agentId: run.agentId,
|
|
357
|
-
status: run.status,
|
|
358
|
-
});
|
|
359
|
-
if (liveRun) cursorLiveRuns.attachSdkRun(liveRun, run);
|
|
360
|
-
if (options?.signal?.aborted) {
|
|
361
|
-
sdkAbortErrorSuppression.suppressAbortErrors();
|
|
362
|
-
liveRun?.bridgeRun?.cancel("Cursor SDK run aborted");
|
|
363
|
-
await run.cancel().catch(() => {});
|
|
364
|
-
throw new CursorLiveRunAbortError();
|
|
365
|
-
}
|
|
366
|
-
const activeRun = run;
|
|
367
|
-
|
|
368
|
-
if (liveRun) {
|
|
369
|
-
deferSdkEventDebugFinalize = true;
|
|
370
|
-
const waitCompletion = run
|
|
371
|
-
.wait()
|
|
372
|
-
.then(async (result) => {
|
|
373
|
-
sdkEventDebug?.recordWaitResult(result);
|
|
374
|
-
if (result.status === "finished" && !options?.signal?.aborted) {
|
|
375
|
-
await replayCursorTranscriptWebToolCalls(activeRun.agentId, cwd, cursorAgentMessageOffset, turnCoordinator);
|
|
376
|
-
}
|
|
377
|
-
turnCoordinator.discardIncompleteStartedToolCalls(
|
|
378
|
-
result.status === "cancelled" || options?.signal?.aborted
|
|
379
|
-
? "abort"
|
|
380
|
-
: result.status === "error"
|
|
381
|
-
? "sdk-failure"
|
|
382
|
-
: DISCARDED_INCOMPLETE_TOOL_CALL_REASON,
|
|
383
|
-
);
|
|
384
|
-
await sdkEventDebug?.captureRunArtifacts(run);
|
|
385
|
-
if (liveRun.disposed) return;
|
|
386
|
-
await cacheSdkContextWindow(liveRun.agent.agentId, model.id);
|
|
387
|
-
if (liveRun.disposed) return;
|
|
388
|
-
if (result.status === "finished" && !options?.signal?.aborted) {
|
|
389
|
-
commitSessionAgentSend(sessionAgentScopeKey, context, bootstrap);
|
|
390
|
-
cursorLiveRuns.markFinished(
|
|
391
|
-
liveRun,
|
|
392
|
-
selectCursorFinalText(result.result, liveRun.textDeltas, liveRun.emittedText, turnCoordinator.planTextCandidate),
|
|
393
|
-
);
|
|
394
|
-
} else if (result.status === "cancelled" || options?.signal?.aborted) {
|
|
395
|
-
cursorLiveRuns.markCancelled(
|
|
396
|
-
liveRun,
|
|
397
|
-
formatCursorSdkAbortMessage(
|
|
398
|
-
resolveCursorSdkAbortCause({
|
|
399
|
-
signalAborted: options?.signal?.aborted,
|
|
400
|
-
sdkStatusCancelled: result.status === "cancelled",
|
|
401
|
-
}),
|
|
402
|
-
),
|
|
403
|
-
);
|
|
404
|
-
} else {
|
|
405
|
-
const failureDetail = formatCursorSdkRunFailureDetail(result, run?.result);
|
|
406
|
-
cursorLiveRuns.markError(
|
|
407
|
-
liveRun,
|
|
408
|
-
sanitizeCursorProviderError(failureDetail, resolvedApiKey ?? options?.apiKey),
|
|
409
|
-
);
|
|
410
|
-
}
|
|
411
|
-
})
|
|
412
|
-
.catch(async (error: unknown) => {
|
|
413
|
-
sdkEventDebug?.recordWaitResult({ status: "error", error: String(error) });
|
|
414
|
-
sdkEventDebug?.recordError("run_wait", error);
|
|
415
|
-
turnCoordinatorForCleanup?.discardIncompleteStartedToolCalls("sdk-failure");
|
|
416
|
-
await sdkEventDebug?.captureRunArtifacts(run);
|
|
417
|
-
if (liveRun.disposed) return;
|
|
418
|
-
cursorLiveRuns.markError(liveRun, sanitizeCursorProviderError(error, resolvedApiKey ?? options?.apiKey));
|
|
419
|
-
});
|
|
420
|
-
|
|
421
|
-
try {
|
|
422
|
-
await cursorLiveRuns.withRunLease(liveRun, options?.signal, async () => {
|
|
423
|
-
await cursorLiveRuns.waitForProgress(liveRun, options?.signal);
|
|
424
|
-
await settleCursorLiveToolBatch(liveRun);
|
|
425
|
-
turnCoordinator.closeTraceBlock();
|
|
426
|
-
await drainCursorLiveRunTurn(stream, partial, model, context, liveRun, 0, {
|
|
427
|
-
mode: "emit",
|
|
428
|
-
signal: options?.signal,
|
|
429
|
-
debugRecorder: sdkEventDebug,
|
|
430
|
-
});
|
|
431
|
-
});
|
|
432
|
-
} catch (error) {
|
|
433
|
-
if (error instanceof CursorLiveRunAbortError) {
|
|
434
|
-
sdkAbortErrorSuppression.suppressAbortErrors();
|
|
435
|
-
turnCoordinator.discardIncompleteStartedToolCalls("abort");
|
|
436
|
-
turnCoordinator.closeTraceBlock();
|
|
437
|
-
flushPendingCursorLiveRunTraceEventsToStream(stream, partial, liveRun, {
|
|
438
|
-
includeTracesBehindQueuedTools: true,
|
|
439
|
-
});
|
|
440
|
-
await cursorLiveRuns.release(liveRun);
|
|
441
|
-
}
|
|
442
|
-
throw error;
|
|
443
|
-
} finally {
|
|
444
|
-
sdkEventDebugRef.current = undefined;
|
|
445
|
-
void waitCompletion
|
|
446
|
-
.finally(async () => {
|
|
447
|
-
try {
|
|
448
|
-
sdkEventDebug?.recordFinalPartial(partial);
|
|
449
|
-
await sdkEventDebug?.finalize();
|
|
450
|
-
} finally {
|
|
451
|
-
sdkAbortErrorSuppression.dispose();
|
|
452
|
-
}
|
|
453
|
-
})
|
|
454
|
-
.catch(() => {});
|
|
455
|
-
}
|
|
456
|
-
agent = null;
|
|
457
|
-
return;
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
const result = await run.wait();
|
|
461
|
-
sdkEventDebug?.recordWaitResult(result);
|
|
462
|
-
if (result.status === "finished" && !options?.signal?.aborted) {
|
|
463
|
-
await replayCursorTranscriptWebToolCalls(run.agentId, cwd, cursorAgentMessageOffset, turnCoordinator);
|
|
464
|
-
}
|
|
465
|
-
turnCoordinator.discardIncompleteStartedToolCalls(
|
|
466
|
-
result.status === "cancelled" || options?.signal?.aborted
|
|
467
|
-
? "abort"
|
|
468
|
-
: result.status === "error"
|
|
469
|
-
? "sdk-failure"
|
|
470
|
-
: DISCARDED_INCOMPLETE_TOOL_CALL_REASON,
|
|
471
|
-
);
|
|
472
|
-
await sdkEventDebug?.captureRunArtifacts(run);
|
|
473
|
-
await cacheSdkContextWindow(agent.agentId, model.id);
|
|
474
|
-
|
|
475
|
-
// Close any open thinking/activity trace, then use the final run result only when
|
|
476
|
-
// Cursor did not stream text deltas.
|
|
477
|
-
turnCoordinator.closeTraceBlock();
|
|
478
|
-
|
|
479
|
-
if (result.status === "cancelled") {
|
|
480
|
-
await abandonSessionCursorAgent(sessionAgentScopeKey);
|
|
481
|
-
partial.stopReason = "aborted";
|
|
482
|
-
partial.errorMessage = formatCursorSdkAbortMessage(
|
|
483
|
-
resolveCursorSdkAbortCause({
|
|
484
|
-
signalAborted: options?.signal?.aborted,
|
|
485
|
-
sdkStatusCancelled: true,
|
|
486
|
-
}),
|
|
487
|
-
);
|
|
488
|
-
stream.push({ type: "error", reason: "aborted", error: partial });
|
|
489
|
-
} else if (result.status === "error") {
|
|
490
|
-
await abandonSessionCursorAgent(sessionAgentScopeKey);
|
|
491
|
-
partial.stopReason = "error";
|
|
492
|
-
const failureDetail = formatCursorSdkRunFailureDetail(result, run.result);
|
|
493
|
-
partial.errorMessage = sanitizeCursorProviderError(failureDetail, resolvedApiKey ?? options?.apiKey);
|
|
494
|
-
stream.push({ type: "error", reason: "error", error: partial });
|
|
495
|
-
} else {
|
|
496
|
-
commitSessionAgentSend(sessionAgentScopeKey, context, bootstrap);
|
|
497
|
-
const finalCursorText = selectCursorFinalText(result.result, textDeltas, textDeltas.join(""), turnCoordinator.planTextCandidate, {
|
|
498
|
-
allowPartialPrefix: true,
|
|
499
|
-
});
|
|
500
|
-
turnCoordinator.flushText(hasUsableText(finalCursorText) ? [finalCursorText] : []);
|
|
501
|
-
applyCursorApproximateUsage(partial, model, context, promptInputTokens);
|
|
502
|
-
stream.push({ type: "done", reason: "stop", message: partial });
|
|
503
|
-
}
|
|
504
|
-
} catch (error) {
|
|
505
|
-
sdkEventDebug?.recordError("provider_stream", error);
|
|
506
|
-
turnCoordinatorForCleanup?.discardIncompleteStartedToolCalls(
|
|
507
|
-
error instanceof CursorLiveRunAbortError ? "abort" : "sdk-failure",
|
|
508
|
-
);
|
|
509
|
-
if (activeLiveRun && !activeLiveRun.disposed) await cursorLiveRuns.release(activeLiveRun);
|
|
510
|
-
else await abandonSessionCursorAgent(sessionAgentScopeKey);
|
|
511
|
-
if (error instanceof CursorLiveRunAbortError) {
|
|
512
|
-
sdkAbortErrorSuppression.suppressAbortErrors();
|
|
513
|
-
pushSanitizedStreamError(error, "aborted");
|
|
514
|
-
} else {
|
|
515
|
-
pushSanitizedStreamError(error, "error");
|
|
516
|
-
}
|
|
517
|
-
} finally {
|
|
518
|
-
if (!deferSdkEventDebugFinalize) {
|
|
519
|
-
try {
|
|
520
|
-
sdkEventDebug?.recordFinalPartial(partial);
|
|
521
|
-
await sdkEventDebug?.finalize();
|
|
522
|
-
} finally {
|
|
523
|
-
sdkAbortErrorSuppression.dispose();
|
|
524
|
-
}
|
|
525
|
-
}
|
|
526
|
-
sdkEventDebugRef.current = undefined;
|
|
527
|
-
restoreCursorSdkOutputFilter?.();
|
|
528
|
-
|
|
529
|
-
if (abortSignal && abortListener) {
|
|
530
|
-
abortSignal.removeEventListener("abort", abortListener);
|
|
531
|
-
}
|
|
532
|
-
}
|
|
68
|
+
await runner.run(sdkAbortErrorSuppression);
|
|
533
69
|
} catch (error) {
|
|
534
|
-
|
|
535
|
-
else await abandonSessionCursorAgent(sessionAgentScopeKey).catch(() => {});
|
|
536
|
-
pushSanitizedStreamError(error, error instanceof CursorLiveRunAbortError ? "aborted" : "error");
|
|
70
|
+
await runner.handleOuterCatch(error);
|
|
537
71
|
}
|
|
538
72
|
|
|
539
73
|
stream.end();
|