pi-cursor-sdk 0.1.56 → 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 +45 -1
- package/README.md +100 -28
- package/docs/cursor-live-smoke-checklist.md +7 -7
- package/docs/cursor-model-ux-spec.md +45 -38
- package/docs/cursor-native-tool-replay.md +2 -2
- 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 +38 -6
- 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-live-run-drain.ts +5 -0
- package/src/cursor-provider-run-finalizer.ts +36 -28
- package/src/cursor-provider-turn-finalize.ts +72 -6
- package/src/cursor-provider-turn-prepare.ts +228 -12
- package/src/cursor-provider-turn-runner.ts +41 -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-usage-accounting.ts +14 -4
- package/src/index.ts +11 -2
- package/src/model-discovery.ts +10 -56
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type { SimpleStreamOptions } from "@earendil-works/pi-ai/compat";
|
|
1
|
+
import type { Context, SimpleStreamOptions } from "@earendil-works/pi-ai/compat";
|
|
2
|
+
import type { AgentModeOption, ModelSelection, SDKAgent } from "@cursor/sdk";
|
|
2
3
|
import { installCursorMcpToolTimeoutOverride } from "./cursor-mcp-timeout-override.js";
|
|
3
4
|
import { installCursorSdkOutputFilter, suppressCursorSdkOutput } from "./cursor-sdk-output-filter.js";
|
|
4
5
|
import {
|
|
@@ -6,9 +7,10 @@ import {
|
|
|
6
7
|
buildCursorSessionSendPrompt,
|
|
7
8
|
planCursorSessionSend,
|
|
8
9
|
resetSessionCursorAgent,
|
|
10
|
+
type CursorSessionSendPlan,
|
|
9
11
|
} from "./cursor-session-agent.js";
|
|
10
12
|
import type { CursorPiBridgeToolRequest } from "./cursor-pi-tool-bridge.js";
|
|
11
|
-
import { estimateCursorPromptTokens } from "./context.js";
|
|
13
|
+
import { buildCursorPrompt, estimateCursorPromptTokens } from "./context.js";
|
|
12
14
|
import { getCursorPromptOptions } from "./cursor-usage-accounting.js";
|
|
13
15
|
import { getActiveContextToolNames } from "./cursor-context-tools.js";
|
|
14
16
|
import type { CursorLiveRun } from "./cursor-live-run-coordinator.js";
|
|
@@ -16,25 +18,47 @@ import {
|
|
|
16
18
|
abandonSessionCursorAgent,
|
|
17
19
|
createCursorNativeReplayId,
|
|
18
20
|
cursorLiveRuns,
|
|
21
|
+
getActiveCursorLiveRunForCurrentScope,
|
|
22
|
+
getPendingCursorLiveRun,
|
|
19
23
|
} from "./cursor-provider-live-run-drain.js";
|
|
20
|
-
import {
|
|
24
|
+
import {
|
|
25
|
+
getCursorProviderAgentModeOrThrow,
|
|
26
|
+
getEffectiveFastForModelId,
|
|
27
|
+
} from "./cursor-state.js";
|
|
28
|
+
import { resolveEffectiveCursorConfig } from "./cursor-runtime-state.js";
|
|
29
|
+
import type { CursorResolvedSdkConfig } from "./cursor-config.js";
|
|
21
30
|
import { buildCursorModelSelection } from "./model-discovery.js";
|
|
22
31
|
import { getEffectiveCursorSettingSources } from "./cursor-setting-sources.js";
|
|
32
|
+
import {
|
|
33
|
+
formatCursorCloudPreflightError,
|
|
34
|
+
buildCursorCloudAgentOptions,
|
|
35
|
+
inspectCursorCloudLocalState,
|
|
36
|
+
preflightCursorCloudRuntime,
|
|
37
|
+
} from "./cursor-cloud-options.js";
|
|
38
|
+
import { getCursorSessionName, getCursorSessionProjectTrusted } from "./cursor-session-scope.js";
|
|
23
39
|
import { resolveCursorPiToolBridgeEnabled } from "./cursor-pi-tool-bridge-env.js";
|
|
24
40
|
import {
|
|
25
41
|
buildCursorToolManifestText,
|
|
26
42
|
resolveCursorToolManifestEnabled,
|
|
27
43
|
} from "./cursor-tool-manifest.js";
|
|
28
44
|
import { isCursorNativeToolDisplayRuntimeEnabled } from "./cursor-native-tool-display-state.js";
|
|
45
|
+
import {
|
|
46
|
+
createCursorCloudLifecyclePersistenceError,
|
|
47
|
+
recordCursorCloudLifecycleSafely,
|
|
48
|
+
} from "./cursor-cloud-lifecycle.js";
|
|
29
49
|
import { MISSING_CURSOR_API_KEY_MESSAGE } from "./cursor-provider-errors.js";
|
|
30
50
|
import { CursorSdkTurnCoordinator } from "./cursor-provider-turn-coordinator.js";
|
|
31
51
|
import { resolveCursorApiKey } from "./cursor-api-key.js";
|
|
32
52
|
import { loadCursorSdk } from "./cursor-sdk-runtime.js";
|
|
33
53
|
import type {
|
|
54
|
+
CloudCursorProviderTurnPrepareResult,
|
|
55
|
+
CursorProviderTurnLifecycle,
|
|
34
56
|
CursorProviderTurnPrepareResult,
|
|
35
57
|
CursorProviderTurnRunnerParams,
|
|
58
|
+
LocalCursorProviderTurnPrepareResult,
|
|
36
59
|
} from "./cursor-provider-turn-types.js";
|
|
37
60
|
import type { CursorSdkEventDebugSink } from "./cursor-sdk-event-debug.js";
|
|
61
|
+
import type { SessionCursorAgentLease } from "./cursor-session-agent.js";
|
|
38
62
|
|
|
39
63
|
export interface PrepareCursorProviderTurnParams {
|
|
40
64
|
params: CursorProviderTurnRunnerParams;
|
|
@@ -42,12 +66,173 @@ export interface PrepareCursorProviderTurnParams {
|
|
|
42
66
|
resolvedApiKey: string;
|
|
43
67
|
sdkEventDebug: CursorSdkEventDebugSink | undefined;
|
|
44
68
|
throwIfAborted: () => void;
|
|
69
|
+
/** Snapshot resolved once by the runner before draining; reused unchanged through prepare. */
|
|
70
|
+
resolvedConfig: CursorResolvedSdkConfig;
|
|
45
71
|
}
|
|
46
72
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
73
|
+
interface PrepareCursorProviderTurnContext extends PrepareCursorProviderTurnParams {
|
|
74
|
+
agentMode: AgentModeOption;
|
|
75
|
+
selection: ModelSelection;
|
|
76
|
+
fastEnabled: boolean | undefined;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function buildCursorCloudPromptContext(context: Context, handoff: "fresh" | "bootstrap" | "never"): Context {
|
|
80
|
+
if (handoff === "bootstrap") return context;
|
|
81
|
+
for (let index = context.messages.length - 1; index >= 0; index -= 1) {
|
|
82
|
+
const message = context.messages[index];
|
|
83
|
+
if (message.role === "user") return { ...context, messages: [message] };
|
|
84
|
+
}
|
|
85
|
+
return { ...context, messages: context.messages.slice(-1) };
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const CLOUD_SEND_PLAN: CursorSessionSendPlan = { mode: "bootstrap", resetAgent: false, reason: "initial" };
|
|
89
|
+
|
|
90
|
+
export function resolveCursorProviderTurnConfig(cwd: string) {
|
|
91
|
+
return resolveEffectiveCursorConfig({ cwd, projectTrusted: getCursorSessionProjectTrusted() });
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function buildCloudCursorProviderTurnLifecycle(agent: SDKAgent): CursorProviderTurnLifecycle {
|
|
95
|
+
return {
|
|
96
|
+
trackRunCompletion: () => {},
|
|
97
|
+
commitSend: () => {},
|
|
98
|
+
abandon: async () => {},
|
|
99
|
+
dispose: async () => {
|
|
100
|
+
await agent[Symbol.asyncDispose]?.();
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function buildLocalCursorProviderTurnLifecycle(
|
|
106
|
+
lease: SessionCursorAgentLease,
|
|
107
|
+
scopeKey: string,
|
|
108
|
+
): CursorProviderTurnLifecycle {
|
|
109
|
+
return {
|
|
110
|
+
trackRunCompletion: (completion) => lease.trackRunCompletion(completion),
|
|
111
|
+
commitSend: (context, bootstrapped) => lease.commitSend(context, bootstrapped),
|
|
112
|
+
abandon: () => abandonSessionCursorAgent(scopeKey),
|
|
113
|
+
dispose: async () => {},
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
async function prepareCursorCloudProviderTurn(
|
|
118
|
+
prepareParams: PrepareCursorProviderTurnContext,
|
|
119
|
+
): Promise<CloudCursorProviderTurnPrepareResult> {
|
|
120
|
+
const { params, cwd, resolvedApiKey, sdkEventDebug, throwIfAborted, resolvedConfig, agentMode, selection, fastEnabled } = prepareParams;
|
|
121
|
+
const { model, context, options } = params;
|
|
122
|
+
|
|
123
|
+
let restoreCursorSdkOutputFilter: (() => void) | undefined;
|
|
124
|
+
let cloudAgentForCleanup: SDKAgent | undefined;
|
|
125
|
+
let completed = false;
|
|
126
|
+
|
|
127
|
+
try {
|
|
128
|
+
const preflight = preflightCursorCloudRuntime({
|
|
129
|
+
resolvedConfig,
|
|
130
|
+
localState: inspectCursorCloudLocalState(cwd),
|
|
131
|
+
hasPriorContext: context.messages.length > 1,
|
|
132
|
+
});
|
|
133
|
+
if (!preflight.ok) throw new Error(formatCursorCloudPreflightError(preflight));
|
|
134
|
+
if (getPendingCursorLiveRun(context) || getActiveCursorLiveRunForCurrentScope()) {
|
|
135
|
+
throw new Error("Cursor cloud runtime cannot start while a local Cursor live run is pending; finish or abort the local run, then retry.");
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const { Agent } = await loadCursorSdk();
|
|
139
|
+
restoreCursorSdkOutputFilter = installCursorSdkOutputFilter();
|
|
140
|
+
const promptOptions = {
|
|
141
|
+
...getCursorPromptOptions(model),
|
|
142
|
+
agentMode,
|
|
143
|
+
includePiBridgeGuidance: false,
|
|
144
|
+
includePiAskQuestionGuidance: false,
|
|
145
|
+
};
|
|
146
|
+
const prompt = buildCursorPrompt(
|
|
147
|
+
buildCursorCloudPromptContext(context, resolvedConfig.cloud.contextHandoff.value),
|
|
148
|
+
promptOptions,
|
|
149
|
+
);
|
|
150
|
+
const promptInputTokens = estimateCursorPromptTokens(prompt, promptOptions);
|
|
151
|
+
const agent = await suppressCursorSdkOutput(() =>
|
|
152
|
+
Agent.create(buildCursorCloudAgentOptions({
|
|
153
|
+
apiKey: resolvedApiKey,
|
|
154
|
+
modelSelection: selection,
|
|
155
|
+
agentMode,
|
|
156
|
+
resolvedConfig,
|
|
157
|
+
name: getCursorSessionName(),
|
|
158
|
+
})),
|
|
159
|
+
);
|
|
160
|
+
cloudAgentForCleanup = agent;
|
|
161
|
+
if (!recordCursorCloudLifecycleSafely({ agentId: agent.agentId }, resolvedApiKey)) {
|
|
162
|
+
throw createCursorCloudLifecyclePersistenceError(agent.agentId, "intent", undefined, resolvedApiKey);
|
|
163
|
+
}
|
|
164
|
+
sdkEventDebug?.recordProviderMeta({ runtime: "cloud", cloudAgentId: agent.agentId, phase: "agent_created" });
|
|
165
|
+
throwIfAborted();
|
|
166
|
+
|
|
167
|
+
const textDeltas: string[] = [];
|
|
168
|
+
const nativeReplayId = createCursorNativeReplayId();
|
|
169
|
+
const turnCoordinator = new CursorSdkTurnCoordinator({
|
|
170
|
+
stream: params.stream,
|
|
171
|
+
partial: params.partial,
|
|
172
|
+
cwd,
|
|
173
|
+
resolvedApiKey,
|
|
174
|
+
useNativeToolReplay: false,
|
|
175
|
+
nativeReplayId,
|
|
176
|
+
textDeltas,
|
|
177
|
+
debugRecorder: sdkEventDebug,
|
|
178
|
+
});
|
|
179
|
+
sdkEventDebug?.recordProviderMeta({
|
|
180
|
+
runtime: "cloud",
|
|
181
|
+
cloudAgentId: agent.agentId,
|
|
182
|
+
model: {
|
|
183
|
+
id: model.id,
|
|
184
|
+
provider: model.provider,
|
|
185
|
+
api: model.api,
|
|
186
|
+
reasoning: options?.reasoning ?? "off",
|
|
187
|
+
fastEnabled,
|
|
188
|
+
selection,
|
|
189
|
+
},
|
|
190
|
+
contextHandoff: resolvedConfig.cloud.contextHandoff.value,
|
|
191
|
+
sendPlan: CLOUD_SEND_PLAN,
|
|
192
|
+
promptOptions,
|
|
193
|
+
agentMode,
|
|
194
|
+
localForce: false,
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
completed = true;
|
|
198
|
+
cloudAgentForCleanup = undefined;
|
|
199
|
+
return {
|
|
200
|
+
runtimeTarget: "cloud",
|
|
201
|
+
agent,
|
|
202
|
+
cwd,
|
|
203
|
+
payload: {
|
|
204
|
+
text: prompt.text,
|
|
205
|
+
images: prompt.images.length > 0 ? prompt.images : undefined,
|
|
206
|
+
},
|
|
207
|
+
meta: {
|
|
208
|
+
sendPlan: CLOUD_SEND_PLAN,
|
|
209
|
+
prompt,
|
|
210
|
+
bootstrap: true,
|
|
211
|
+
promptInputTokens,
|
|
212
|
+
useNativeToolReplay: false,
|
|
213
|
+
bridgeEnabled: false,
|
|
214
|
+
nativeReplayId,
|
|
215
|
+
agentMode,
|
|
216
|
+
modelSelection: selection,
|
|
217
|
+
},
|
|
218
|
+
contextWindowAgentId: agent.agentId,
|
|
219
|
+
textDeltas,
|
|
220
|
+
restoreCursorSdkOutputFilter,
|
|
221
|
+
lifecycle: buildCloudCursorProviderTurnLifecycle(agent),
|
|
222
|
+
runtime: { kind: "direct", turnCoordinator },
|
|
223
|
+
};
|
|
224
|
+
} finally {
|
|
225
|
+
if (!completed) {
|
|
226
|
+
await cloudAgentForCleanup?.[Symbol.asyncDispose]?.().catch(() => {});
|
|
227
|
+
restoreCursorSdkOutputFilter?.();
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
async function prepareCursorLocalProviderTurn(
|
|
233
|
+
prepareParams: PrepareCursorProviderTurnContext,
|
|
234
|
+
): Promise<LocalCursorProviderTurnPrepareResult> {
|
|
235
|
+
const { params, cwd, resolvedApiKey, sdkEventDebug, throwIfAborted, resolvedConfig, agentMode, selection, fastEnabled } = prepareParams;
|
|
51
236
|
const { model, context, options } = params;
|
|
52
237
|
|
|
53
238
|
let restoreCursorSdkOutputFilter: (() => void) | undefined;
|
|
@@ -56,14 +241,15 @@ export async function prepareCursorProviderTurn(
|
|
|
56
241
|
let completed = false;
|
|
57
242
|
|
|
58
243
|
try {
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
244
|
+
const localSafety = {
|
|
245
|
+
autoReview: resolvedConfig.local.autoReview.value,
|
|
246
|
+
sandboxEnabled: resolvedConfig.local.sandboxEnabled.value,
|
|
247
|
+
};
|
|
63
248
|
const { Agent } = await loadCursorSdk();
|
|
64
249
|
|
|
65
250
|
installCursorMcpToolTimeoutOverride();
|
|
66
251
|
restoreCursorSdkOutputFilter = installCursorSdkOutputFilter();
|
|
252
|
+
const settingSources = getEffectiveCursorSettingSources();
|
|
67
253
|
const queuedBridgeRequestsBeforeLiveRun: CursorPiBridgeToolRequest[] = [];
|
|
68
254
|
let liveRunForBridgeQueue: CursorLiveRun | undefined;
|
|
69
255
|
|
|
@@ -73,6 +259,8 @@ export async function prepareCursorProviderTurn(
|
|
|
73
259
|
cwd,
|
|
74
260
|
modelSelection: selection,
|
|
75
261
|
settingSources,
|
|
262
|
+
localSafety,
|
|
263
|
+
localResume: resolvedConfig.local.resume.value,
|
|
76
264
|
debugRecorder: sdkEventDebug,
|
|
77
265
|
onBridgeToolRequest: (request: CursorPiBridgeToolRequest) => {
|
|
78
266
|
if (liveRunForBridgeQueue && !liveRunForBridgeQueue.disposed) {
|
|
@@ -110,11 +298,14 @@ export async function prepareCursorProviderTurn(
|
|
|
110
298
|
};
|
|
111
299
|
};
|
|
112
300
|
let sendPlan = planCursorSessionSend(sessionAgentLease.sendState, context);
|
|
301
|
+
if (sessionAgentLease.created && sessionAgentLease.resumed && sendPlan.mode === "incremental") {
|
|
302
|
+
sendPlan = { mode: "bootstrap", resetAgent: false, reason: "process_resume" };
|
|
303
|
+
}
|
|
113
304
|
let promptOptions = buildPromptOptions(sendPlan);
|
|
114
305
|
let prompt = buildCursorSessionSendPrompt(context, promptOptions, sendPlan);
|
|
115
306
|
if (sendPlan.resetAgent) {
|
|
116
307
|
await resetSessionCursorAgent(sessionAgentScopeKey);
|
|
117
|
-
sessionAgentLease = await acquireSessionCursorAgent(sessionAgentAcquireParams);
|
|
308
|
+
sessionAgentLease = await acquireSessionCursorAgent({ ...sessionAgentAcquireParams, forceCreate: true });
|
|
118
309
|
sessionAgentScopeKey = sessionAgentLease.scopeKey;
|
|
119
310
|
bridgeToolNames = new Set(sessionAgentLease.bridgeRun?.snapshot.tools.map((tool) => tool.mcpToolName) ?? []);
|
|
120
311
|
includePiBridgeGuidance = bridgeToolNames.size > 0;
|
|
@@ -148,6 +339,9 @@ export async function prepareCursorProviderTurn(
|
|
|
148
339
|
promptOptions,
|
|
149
340
|
toolManifestEnabled: resolveCursorToolManifestEnabled(),
|
|
150
341
|
agentMode,
|
|
342
|
+
localForce: resolvedConfig.local.force.value,
|
|
343
|
+
localResume: resolvedConfig.local.resume.value,
|
|
344
|
+
resumedAgent: sessionAgentLease.resumed,
|
|
151
345
|
activeToolNames: activeToolNames ? [...activeToolNames] : [],
|
|
152
346
|
sessionAgentScopeKey,
|
|
153
347
|
bridgeRunId: bridgeRun?.id,
|
|
@@ -188,6 +382,7 @@ export async function prepareCursorProviderTurn(
|
|
|
188
382
|
|
|
189
383
|
completed = true;
|
|
190
384
|
return {
|
|
385
|
+
runtimeTarget: "local",
|
|
191
386
|
agent,
|
|
192
387
|
cwd,
|
|
193
388
|
payload: sendPayload,
|
|
@@ -200,12 +395,16 @@ export async function prepareCursorProviderTurn(
|
|
|
200
395
|
bridgeEnabled: bridgeRun !== undefined,
|
|
201
396
|
nativeReplayId,
|
|
202
397
|
agentMode,
|
|
398
|
+
modelSelection: selection,
|
|
399
|
+
...(sessionAgentLease.resumeNotice ? { resumeNotice: sessionAgentLease.resumeNotice } : {}),
|
|
203
400
|
},
|
|
204
401
|
contextWindowAgentId: agent.agentId,
|
|
205
402
|
textDeltas,
|
|
206
403
|
sessionAgentScopeKey,
|
|
207
404
|
sessionAgentLease,
|
|
405
|
+
localForce: resolvedConfig.local.force,
|
|
208
406
|
restoreCursorSdkOutputFilter,
|
|
407
|
+
lifecycle: buildLocalCursorProviderTurnLifecycle(sessionAgentLease, sessionAgentScopeKey),
|
|
209
408
|
runtime: liveRun
|
|
210
409
|
? { kind: "live", liveRun, turnCoordinator }
|
|
211
410
|
: { kind: "direct", turnCoordinator },
|
|
@@ -224,6 +423,23 @@ export async function prepareCursorProviderTurn(
|
|
|
224
423
|
}
|
|
225
424
|
}
|
|
226
425
|
|
|
426
|
+
/** Resolves the runtime target from the caller-supplied snapshot and dispatches to the owning branch. */
|
|
427
|
+
export async function prepareCursorProviderTurn(
|
|
428
|
+
prepareParams: PrepareCursorProviderTurnParams,
|
|
429
|
+
): Promise<CursorProviderTurnPrepareResult> {
|
|
430
|
+
const { params, resolvedConfig } = prepareParams;
|
|
431
|
+
const { model, options } = params;
|
|
432
|
+
|
|
433
|
+
const agentMode = getCursorProviderAgentModeOrThrow();
|
|
434
|
+
const fastEnabled = resolvedConfig.runtime.value === "cloud" ? undefined : getEffectiveFastForModelId(model.id);
|
|
435
|
+
const selection = buildCursorModelSelection(model.id, options?.reasoning ?? "off", fastEnabled);
|
|
436
|
+
const context: PrepareCursorProviderTurnContext = { ...prepareParams, agentMode, selection, fastEnabled };
|
|
437
|
+
|
|
438
|
+
return resolvedConfig.runtime.value === "cloud"
|
|
439
|
+
? prepareCursorCloudProviderTurn(context)
|
|
440
|
+
: prepareCursorLocalProviderTurn(context);
|
|
441
|
+
}
|
|
442
|
+
|
|
227
443
|
export function requireCursorApiKey(options: SimpleStreamOptions | undefined): string {
|
|
228
444
|
const apiKey = resolveCursorApiKey(options?.apiKey);
|
|
229
445
|
if (!apiKey) throw new Error(MISSING_CURSOR_API_KEY_MESSAGE);
|
|
@@ -9,16 +9,31 @@ import {
|
|
|
9
9
|
emitCursorLiveTurn,
|
|
10
10
|
} from "./cursor-provider-turn-emit.js";
|
|
11
11
|
import { CursorRunFinalizer, type CursorLiveRunCompletion } from "./cursor-provider-run-finalizer.js";
|
|
12
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
prepareCursorProviderTurn,
|
|
14
|
+
requireCursorApiKey,
|
|
15
|
+
resolveCursorProviderTurnConfig,
|
|
16
|
+
} from "./cursor-provider-turn-prepare.js";
|
|
13
17
|
import { sendCursorProviderTurn } from "./cursor-provider-turn-send.js";
|
|
14
18
|
import type {
|
|
15
19
|
CursorProviderTurnPrepareResult,
|
|
16
20
|
CursorProviderTurnRunnerParams,
|
|
17
21
|
CursorProviderTurnSendResult,
|
|
22
|
+
LiveCursorProviderTurnRuntime,
|
|
23
|
+
LocalCursorProviderTurnPrepareResult,
|
|
18
24
|
} from "./cursor-provider-turn-types.js";
|
|
19
25
|
|
|
20
26
|
export type { CursorProviderTurnRunnerParams } from "./cursor-provider-turn-types.js";
|
|
21
27
|
|
|
28
|
+
type LocalLivePreparedTurn = LocalCursorProviderTurnPrepareResult & { runtime: LiveCursorProviderTurnRuntime };
|
|
29
|
+
|
|
30
|
+
function requireLocalLivePreparedTurn(prepared: CursorProviderTurnPrepareResult): LocalLivePreparedTurn {
|
|
31
|
+
if (prepared.runtimeTarget !== "local" || prepared.runtime.kind !== "live") {
|
|
32
|
+
throw new Error("Cursor live run requires a local live prepared turn");
|
|
33
|
+
}
|
|
34
|
+
return prepared as LocalLivePreparedTurn;
|
|
35
|
+
}
|
|
36
|
+
|
|
22
37
|
export class CursorProviderTurnRunner {
|
|
23
38
|
private sdkEventDebug: CursorSdkEventDebugSink | undefined;
|
|
24
39
|
private resolvedApiKey: string | undefined;
|
|
@@ -55,11 +70,16 @@ export class CursorProviderTurnRunner {
|
|
|
55
70
|
});
|
|
56
71
|
sdkEventDebugRef.current = this.sdkEventDebug;
|
|
57
72
|
this.sdkEventDebug?.recordContextSnapshot(context);
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
) {
|
|
62
|
-
|
|
73
|
+
// Resolved once here, before any drain await, so the drain decision and the
|
|
74
|
+
// prepare dispatch below always act on the same config snapshot.
|
|
75
|
+
const resolvedConfig = resolveCursorProviderTurnConfig(cwd);
|
|
76
|
+
if (resolvedConfig.runtime.value === "local") {
|
|
77
|
+
if (
|
|
78
|
+
(await drainExistingCursorLiveRunBeforeSend(stream, partial, model, context, options?.signal, this.sdkEventDebug)) ===
|
|
79
|
+
"stream_ended"
|
|
80
|
+
) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
63
83
|
}
|
|
64
84
|
this.throwIfAborted();
|
|
65
85
|
|
|
@@ -70,6 +90,7 @@ export class CursorProviderTurnRunner {
|
|
|
70
90
|
resolvedApiKey: this.resolvedApiKey,
|
|
71
91
|
sdkEventDebug: this.sdkEventDebug,
|
|
72
92
|
throwIfAborted: () => this.throwIfAborted(),
|
|
93
|
+
resolvedConfig,
|
|
73
94
|
});
|
|
74
95
|
|
|
75
96
|
sendResult = await sendCursorProviderTurn({
|
|
@@ -78,21 +99,23 @@ export class CursorProviderTurnRunner {
|
|
|
78
99
|
sdkEventDebug: this.sdkEventDebug,
|
|
79
100
|
sdkProcessErrorGuard,
|
|
80
101
|
throwIfAborted: () => this.throwIfAborted(),
|
|
102
|
+
resolvedApiKey: this.resolvedApiKey,
|
|
81
103
|
});
|
|
82
104
|
const { send } = sendResult;
|
|
83
105
|
|
|
84
106
|
if (prepared.runtime.kind === "live") {
|
|
107
|
+
const livePrepared = requireLocalLivePreparedTurn(prepared);
|
|
85
108
|
liveCompletion = runFinalizer.startLiveRunCompletion({
|
|
86
109
|
send,
|
|
87
|
-
prepared,
|
|
110
|
+
prepared: livePrepared,
|
|
88
111
|
modelId: model.id,
|
|
89
|
-
discardIncompleteTools: (outcome) => discardIncompleteToolsFromPrepared(
|
|
112
|
+
discardIncompleteTools: (outcome) => discardIncompleteToolsFromPrepared(livePrepared, outcome),
|
|
90
113
|
});
|
|
91
114
|
await emitCursorLiveTurn({
|
|
92
115
|
params: this.params,
|
|
93
|
-
prepared,
|
|
116
|
+
prepared: livePrepared,
|
|
94
117
|
sdkEventDebug: this.sdkEventDebug,
|
|
95
|
-
discardIncompleteTools: (outcome) => discardIncompleteToolsFromPrepared(
|
|
118
|
+
discardIncompleteTools: (outcome) => discardIncompleteToolsFromPrepared(livePrepared, outcome),
|
|
96
119
|
});
|
|
97
120
|
return;
|
|
98
121
|
}
|
|
@@ -110,9 +133,14 @@ export class CursorProviderTurnRunner {
|
|
|
110
133
|
sdkEventDebug: this.sdkEventDebug,
|
|
111
134
|
contextWindowAgentId: prepared.contextWindowAgentId,
|
|
112
135
|
});
|
|
113
|
-
prepared.
|
|
114
|
-
const
|
|
115
|
-
await runFinalizer.applyTerminalEvent({
|
|
136
|
+
prepared.lifecycle.trackRunCompletion(outcomePromise);
|
|
137
|
+
const finalized = await outcomePromise;
|
|
138
|
+
await runFinalizer.applyTerminalEvent({
|
|
139
|
+
kind: "direct",
|
|
140
|
+
prepared,
|
|
141
|
+
outcome: finalized.outcome,
|
|
142
|
+
displayOnlyTraceBlock: finalized.displayOnlyTraceBlock,
|
|
143
|
+
});
|
|
116
144
|
} catch (error) {
|
|
117
145
|
await runFinalizer.applyTerminalEvent({ kind: "error", prepared, error });
|
|
118
146
|
} finally {
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import type { SendOptions } from "@cursor/sdk";
|
|
2
2
|
import { countCursorAgentMessages } from "./cursor-agent-message-web-tools.js";
|
|
3
|
+
import {
|
|
4
|
+
createCursorCloudLifecyclePersistenceError,
|
|
5
|
+
recordCursorCloudLifecycleSafely,
|
|
6
|
+
} from "./cursor-cloud-lifecycle.js";
|
|
3
7
|
import { CursorLiveRunAbortError } from "./cursor-live-run-coordinator.js";
|
|
4
8
|
import { cursorLiveRuns } from "./cursor-provider-live-run-drain.js";
|
|
9
|
+
import { consumeCursorLocalForceOverride } from "./cursor-runtime-state.js";
|
|
5
10
|
import type { installCursorSdkProcessErrorGuard } from "./cursor-sdk-process-error-guard.js";
|
|
6
11
|
import type {
|
|
7
12
|
CursorProviderTurnRunnerParams,
|
|
@@ -16,10 +21,37 @@ export interface SendCursorProviderTurnParams {
|
|
|
16
21
|
sdkEventDebug: CursorSdkEventDebugSink | undefined;
|
|
17
22
|
sdkProcessErrorGuard: ReturnType<typeof installCursorSdkProcessErrorGuard>;
|
|
18
23
|
throwIfAborted: () => void;
|
|
24
|
+
resolvedApiKey?: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const CLOUD_LEDGER_CANCEL_TIMEOUT_MS = 5000;
|
|
28
|
+
|
|
29
|
+
async function requestBoundedCloudRunCancellation(run: Awaited<ReturnType<CursorProviderTurnPrepareResult["agent"]["send"]>>): Promise<boolean> {
|
|
30
|
+
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
31
|
+
const timeout = new Promise<false>((resolve) => {
|
|
32
|
+
timer = setTimeout(() => resolve(false), CLOUD_LEDGER_CANCEL_TIMEOUT_MS);
|
|
33
|
+
timer.unref?.();
|
|
34
|
+
});
|
|
35
|
+
try {
|
|
36
|
+
return await Promise.race([
|
|
37
|
+
Promise.resolve().then(() => run.cancel()).then(() => true, () => false),
|
|
38
|
+
timeout,
|
|
39
|
+
]);
|
|
40
|
+
} finally {
|
|
41
|
+
if (timer) clearTimeout(timer);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function recordDebug(action: () => void): void {
|
|
46
|
+
try {
|
|
47
|
+
action();
|
|
48
|
+
} catch {
|
|
49
|
+
// Debug capture is optional and must never change provider execution.
|
|
50
|
+
}
|
|
19
51
|
}
|
|
20
52
|
|
|
21
53
|
export async function sendCursorProviderTurn(sendParams: SendCursorProviderTurnParams): Promise<CursorProviderTurnSendResult> {
|
|
22
|
-
const { params, prepared, sdkEventDebug, sdkProcessErrorGuard, throwIfAborted } = sendParams;
|
|
54
|
+
const { params, prepared, sdkEventDebug, sdkProcessErrorGuard, throwIfAborted, resolvedApiKey } = sendParams;
|
|
23
55
|
const { options } = params;
|
|
24
56
|
const { agent, cwd, payload, meta, runtime } = prepared;
|
|
25
57
|
const { turnCoordinator, liveRun } = runtime;
|
|
@@ -42,13 +74,15 @@ export async function sendCursorProviderTurn(sendParams: SendCursorProviderTurnP
|
|
|
42
74
|
abortRegistration?.signal.addEventListener("abort", abortListener, { once: true });
|
|
43
75
|
throwIfAborted();
|
|
44
76
|
let cursorAgentMessageOffset: number | undefined;
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
77
|
+
if (prepared.runtimeTarget === "local") {
|
|
78
|
+
try {
|
|
79
|
+
cursorAgentMessageOffset = await countCursorAgentMessages(agent.agentId, cwd);
|
|
80
|
+
} catch (error) {
|
|
81
|
+
recordDebug(() => sdkEventDebug?.recordError("cursor_agent_message_count", error));
|
|
82
|
+
}
|
|
49
83
|
}
|
|
50
84
|
throwIfAborted();
|
|
51
|
-
sdkEventDebug?.recordSendMeta({
|
|
85
|
+
recordDebug(() => sdkEventDebug?.recordSendMeta({
|
|
52
86
|
mode: meta.sendPlan.mode,
|
|
53
87
|
reason: meta.sendPlan.reason,
|
|
54
88
|
resetAgent: meta.sendPlan.resetAgent,
|
|
@@ -60,35 +94,44 @@ export async function sendCursorProviderTurn(sendParams: SendCursorProviderTurnP
|
|
|
60
94
|
nativeReplayId: meta.nativeReplayId,
|
|
61
95
|
promptInputTokens: meta.promptInputTokens,
|
|
62
96
|
agentMode: meta.agentMode,
|
|
63
|
-
});
|
|
64
|
-
sdkEventDebug?.recordSendPayload(payload);
|
|
65
|
-
sdkEventDebug?.recordProviderEvent("agent_send_start", payload);
|
|
97
|
+
}));
|
|
98
|
+
recordDebug(() => sdkEventDebug?.recordSendPayload(payload));
|
|
99
|
+
recordDebug(() => sdkEventDebug?.recordProviderEvent("agent_send_start", payload));
|
|
66
100
|
const sendOptions: SendOptions = {
|
|
67
101
|
mode: meta.agentMode,
|
|
102
|
+
model: meta.modelSelection,
|
|
68
103
|
onDelta: (args) => {
|
|
69
|
-
sdkEventDebug?.recordOnDelta(args.update);
|
|
104
|
+
recordDebug(() => sdkEventDebug?.recordOnDelta(args.update));
|
|
70
105
|
turnCoordinator.handleDelta(args.update);
|
|
71
106
|
},
|
|
72
107
|
onStep: (args) => {
|
|
73
|
-
sdkEventDebug?.recordOnStep(args.step);
|
|
108
|
+
recordDebug(() => sdkEventDebug?.recordOnStep(args.step));
|
|
74
109
|
turnCoordinator.handleStep(args.step);
|
|
75
110
|
},
|
|
76
111
|
};
|
|
112
|
+
throwIfAborted();
|
|
113
|
+
if (prepared.runtimeTarget === "local" && consumeCursorLocalForceOverride(prepared.localForce)) {
|
|
114
|
+
sendOptions.local = { force: true };
|
|
115
|
+
}
|
|
77
116
|
const run = await agent.send(payload, sendOptions);
|
|
78
117
|
sdkRun = run;
|
|
79
|
-
|
|
118
|
+
if (prepared.runtimeTarget === "cloud" && !recordCursorCloudLifecycleSafely({ agentId: run.agentId, runId: run.id }, resolvedApiKey)) {
|
|
119
|
+
const cancellationConfirmed = await requestBoundedCloudRunCancellation(run);
|
|
120
|
+
throw createCursorCloudLifecyclePersistenceError(run.agentId, "run", cancellationConfirmed, resolvedApiKey);
|
|
121
|
+
}
|
|
122
|
+
recordDebug(() => sdkEventDebug?.recordRunMeta({
|
|
80
123
|
runId: run.id,
|
|
81
124
|
requestId: run.requestId,
|
|
82
125
|
agentId: run.agentId,
|
|
83
126
|
status: run.status,
|
|
84
|
-
});
|
|
85
|
-
sdkEventDebug?.attachRunStream(run);
|
|
86
|
-
sdkEventDebug?.recordProviderEvent("agent_send_returned", {
|
|
127
|
+
}));
|
|
128
|
+
recordDebug(() => sdkEventDebug?.attachRunStream(run));
|
|
129
|
+
recordDebug(() => sdkEventDebug?.recordProviderEvent("agent_send_returned", {
|
|
87
130
|
runId: run.id,
|
|
88
131
|
requestId: run.requestId,
|
|
89
132
|
agentId: run.agentId,
|
|
90
133
|
status: run.status,
|
|
91
|
-
});
|
|
134
|
+
}));
|
|
92
135
|
if (liveRun) cursorLiveRuns.attachSdkRun(liveRun, run);
|
|
93
136
|
if (options?.signal?.aborted) {
|
|
94
137
|
sdkProcessErrorGuard.suppressAbortErrors();
|
|
@@ -6,13 +6,14 @@ import type {
|
|
|
6
6
|
Model,
|
|
7
7
|
SimpleStreamOptions,
|
|
8
8
|
} from "@earendil-works/pi-ai/compat";
|
|
9
|
-
import type { AgentModeOption, SDKAgent, SDKImage } from "@cursor/sdk";
|
|
9
|
+
import type { AgentModeOption, ModelSelection, SDKAgent, SDKImage } from "@cursor/sdk";
|
|
10
10
|
import type { CursorLiveRun } from "./cursor-live-run-coordinator.js";
|
|
11
11
|
import type { SessionCursorAgentLease } from "./cursor-session-agent.js";
|
|
12
12
|
import type { planCursorSessionSend } from "./cursor-session-agent.js";
|
|
13
13
|
import type { CursorSdkEventDebugSink } from "./cursor-sdk-event-debug.js";
|
|
14
14
|
import type { CursorSdkTurnCoordinator } from "./cursor-provider-turn-coordinator.js";
|
|
15
15
|
import type { CursorPrompt } from "./context.js";
|
|
16
|
+
import type { CursorResolvedSetting } from "./cursor-config.js";
|
|
16
17
|
|
|
17
18
|
export interface CursorProviderTurnRunnerParams {
|
|
18
19
|
model: Model<Api>;
|
|
@@ -37,12 +38,28 @@ export interface CursorProviderTurnSendMeta {
|
|
|
37
38
|
bridgeEnabled: boolean;
|
|
38
39
|
nativeReplayId: string;
|
|
39
40
|
agentMode: AgentModeOption;
|
|
41
|
+
modelSelection: ModelSelection;
|
|
42
|
+
resumeNotice?: string;
|
|
40
43
|
}
|
|
41
44
|
|
|
42
45
|
interface CursorProviderTurnRuntimeBase {
|
|
43
46
|
turnCoordinator: CursorSdkTurnCoordinator;
|
|
44
47
|
}
|
|
45
48
|
|
|
49
|
+
/**
|
|
50
|
+
* Runtime-agnostic lifecycle operations for a prepared turn.
|
|
51
|
+
*
|
|
52
|
+
* Local implementations delegate to the session agent lease; cloud
|
|
53
|
+
* implementations no-op the local-only operations (commitSend,
|
|
54
|
+
* trackRunCompletion, abandon) and dispose the cloud agent instead.
|
|
55
|
+
*/
|
|
56
|
+
export interface CursorProviderTurnLifecycle {
|
|
57
|
+
trackRunCompletion(completion: Promise<unknown>): void;
|
|
58
|
+
commitSend(context: Context, bootstrapped: boolean): void;
|
|
59
|
+
abandon(): Promise<void>;
|
|
60
|
+
dispose(): Promise<void>;
|
|
61
|
+
}
|
|
62
|
+
|
|
46
63
|
export interface DirectCursorProviderTurnRuntime extends CursorProviderTurnRuntimeBase {
|
|
47
64
|
kind: "direct";
|
|
48
65
|
liveRun?: undefined;
|
|
@@ -55,25 +72,42 @@ export interface LiveCursorProviderTurnRuntime extends CursorProviderTurnRuntime
|
|
|
55
72
|
|
|
56
73
|
export type CursorProviderTurnRuntime = DirectCursorProviderTurnRuntime | LiveCursorProviderTurnRuntime;
|
|
57
74
|
|
|
58
|
-
|
|
59
|
-
* Single owned model for a prepared provider turn.
|
|
60
|
-
*
|
|
61
|
-
* Send, finalize, and cleanup phases receive this immutable object instead of
|
|
62
|
-
* keeping parallel liveRun/turnCoordinator/resource bags in sync by convention.
|
|
63
|
-
*/
|
|
64
|
-
export interface CursorProviderTurnPrepareResult {
|
|
75
|
+
interface CursorProviderTurnPrepareResultBase {
|
|
65
76
|
agent: SDKAgent;
|
|
66
77
|
cwd: string;
|
|
67
78
|
payload: CursorProviderTurnSendPayload;
|
|
68
79
|
meta: CursorProviderTurnSendMeta;
|
|
69
80
|
contextWindowAgentId: string;
|
|
70
81
|
textDeltas: string[];
|
|
71
|
-
sessionAgentScopeKey: string;
|
|
72
|
-
sessionAgentLease: SessionCursorAgentLease;
|
|
73
82
|
restoreCursorSdkOutputFilter: () => void;
|
|
83
|
+
lifecycle: CursorProviderTurnLifecycle;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export interface LocalCursorProviderTurnPrepareResult extends CursorProviderTurnPrepareResultBase {
|
|
87
|
+
runtimeTarget: "local";
|
|
74
88
|
runtime: CursorProviderTurnRuntime;
|
|
89
|
+
sessionAgentScopeKey: string;
|
|
90
|
+
sessionAgentLease: SessionCursorAgentLease;
|
|
91
|
+
localForce: CursorResolvedSetting<boolean>;
|
|
75
92
|
}
|
|
76
93
|
|
|
94
|
+
export interface CloudCursorProviderTurnPrepareResult extends CursorProviderTurnPrepareResultBase {
|
|
95
|
+
runtimeTarget: "cloud";
|
|
96
|
+
runtime: DirectCursorProviderTurnRuntime;
|
|
97
|
+
sessionAgentScopeKey?: undefined;
|
|
98
|
+
sessionAgentLease?: undefined;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Single owned model for a prepared provider turn.
|
|
103
|
+
*
|
|
104
|
+
* Send, finalize, and cleanup phases receive this immutable object instead of
|
|
105
|
+
* keeping parallel liveRun/turnCoordinator/resource bags in sync by convention.
|
|
106
|
+
*/
|
|
107
|
+
export type CursorProviderTurnPrepareResult =
|
|
108
|
+
| LocalCursorProviderTurnPrepareResult
|
|
109
|
+
| CloudCursorProviderTurnPrepareResult;
|
|
110
|
+
|
|
77
111
|
export interface CursorProviderTurnSend {
|
|
78
112
|
run: Awaited<ReturnType<SDKAgent["send"]>>;
|
|
79
113
|
cursorAgentMessageOffset: number | undefined;
|