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