pi-cursor-sdk 0.2.0 → 0.3.6
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 +62 -0
- package/README.md +54 -50
- package/dist/bundled-context-windows.js +39 -0
- package/dist/context-window-cache.js +92 -0
- package/dist/context.js +338 -0
- package/dist/cursor-active-tools.js +3 -0
- package/dist/cursor-agent-message-web-tools.js +165 -0
- package/dist/cursor-agents-context-registration.js +17 -0
- package/dist/cursor-agents-context.js +113 -0
- package/dist/cursor-api-key.js +34 -0
- package/dist/cursor-bridge-contract.js +27 -0
- package/dist/cursor-cloud-lifecycle.js +650 -0
- package/dist/cursor-cloud-local-state.js +460 -0
- package/dist/cursor-cloud-options.js +145 -0
- package/dist/cursor-cloud-reporting.js +222 -0
- package/dist/cursor-compact-tool-summary.js +75 -0
- package/dist/cursor-config.js +613 -0
- package/dist/cursor-context-tools.js +4 -0
- package/dist/cursor-display-only-trace.js +11 -0
- package/dist/cursor-display-text.js +18 -0
- package/dist/cursor-durable-fs.js +53 -0
- package/dist/cursor-edit-diff.js +13 -0
- package/dist/cursor-env-boolean.js +19 -0
- package/dist/cursor-fallback-models.generated.js +7760 -0
- package/dist/cursor-fallback-warning.js +15 -0
- package/dist/cursor-http1.js +45 -0
- package/dist/cursor-incomplete-tool-visibility.js +83 -0
- package/dist/cursor-live-run-accounting.js +55 -0
- package/dist/cursor-live-run-coordinator.js +406 -0
- package/dist/cursor-mcp-timeout-override.js +116 -0
- package/dist/cursor-model-lifecycle.js +32 -0
- package/dist/cursor-model.js +5 -0
- package/dist/cursor-native-replay-routing.js +35 -0
- package/dist/cursor-native-replay-trace.js +28 -0
- package/dist/cursor-native-tool-display-registration.js +102 -0
- package/dist/cursor-native-tool-display-replay.js +428 -0
- package/dist/cursor-native-tool-display-state.js +70 -0
- package/dist/cursor-native-tool-display-tools.js +123 -0
- package/dist/cursor-native-tool-names.js +11 -0
- package/dist/cursor-partial-content-emitter.js +118 -0
- package/dist/cursor-pi-tool-bridge-abort.js +114 -0
- package/dist/cursor-pi-tool-bridge-constants.js +6 -0
- package/dist/cursor-pi-tool-bridge-diagnostics.js +125 -0
- package/dist/cursor-pi-tool-bridge-env.js +18 -0
- package/dist/cursor-pi-tool-bridge-mcp.js +100 -0
- package/dist/cursor-pi-tool-bridge-run.js +355 -0
- package/dist/cursor-pi-tool-bridge-server.js +163 -0
- package/dist/cursor-pi-tool-bridge-snapshot.js +63 -0
- package/dist/cursor-pi-tool-bridge-types.js +1 -0
- package/dist/cursor-pi-tool-bridge.js +123 -0
- package/dist/cursor-provider-errors.js +311 -0
- package/dist/cursor-provider-lazy.js +39 -0
- package/dist/cursor-provider-live-run-drain.js +412 -0
- package/dist/cursor-provider-overflow.js +90 -0
- package/dist/cursor-provider-run-finalizer.js +176 -0
- package/dist/cursor-provider-run-outcome.js +76 -0
- package/dist/cursor-provider-turn-coordinator.js +275 -0
- package/dist/cursor-provider-turn-display-router.js +158 -0
- package/dist/cursor-provider-turn-emit.js +35 -0
- package/dist/cursor-provider-turn-finalize.js +154 -0
- package/dist/cursor-provider-turn-lifecycle-emitter.js +126 -0
- package/dist/cursor-provider-turn-prepare.js +374 -0
- package/dist/cursor-provider-turn-runner.js +146 -0
- package/dist/cursor-provider-turn-sdk-normalizer.js +74 -0
- package/dist/cursor-provider-turn-send.js +138 -0
- package/dist/cursor-provider-turn-shell-output.js +117 -0
- package/dist/cursor-provider-turn-tool-ledger.js +99 -0
- package/dist/cursor-provider-turn-types.js +1 -0
- package/dist/cursor-provider.js +72 -0
- package/dist/cursor-question-tool.js +194 -0
- package/dist/cursor-record-utils.js +61 -0
- package/dist/cursor-replay-activity-builders.js +114 -0
- package/dist/cursor-replay-source-names.js +27 -0
- package/dist/cursor-replay-summary-args.js +105 -0
- package/dist/cursor-replay-tool-details.js +167 -0
- package/dist/cursor-ripgrep-path.js +27 -0
- package/dist/cursor-run-final-text.js +63 -0
- package/dist/cursor-runtime-state.js +380 -0
- package/dist/cursor-sdk-billed-usage.js +86 -0
- package/dist/cursor-sdk-event-debug-constants.js +33 -0
- package/dist/cursor-sdk-event-debug-session.js +107 -0
- package/dist/cursor-sdk-event-debug.js +461 -0
- package/dist/cursor-sdk-output-filter.js +1 -0
- package/dist/cursor-sdk-process-error-guard.js +221 -0
- package/dist/cursor-sdk-runtime.js +3 -0
- package/dist/cursor-sensitive-text.js +23 -0
- package/dist/cursor-session-agent-cleanup.js +311 -0
- package/dist/cursor-session-agent-lifecycle.js +32 -0
- package/dist/cursor-session-agent-lineage.js +103 -0
- package/dist/cursor-session-agent-resume.js +417 -0
- package/dist/cursor-session-agent.js +507 -0
- package/dist/cursor-session-compaction-prep.js +21 -0
- package/dist/cursor-session-scope.js +113 -0
- package/dist/cursor-session-send-policy.js +18 -0
- package/dist/cursor-session-store.js +105 -0
- package/dist/cursor-session-turn-queue.js +50 -0
- package/dist/cursor-setting-sources.js +15 -0
- package/dist/cursor-skill-tool.js +230 -0
- package/dist/cursor-state.js +519 -0
- package/dist/cursor-task-presentation.js +59 -0
- package/dist/cursor-tool-lifecycle.js +94 -0
- package/dist/cursor-tool-manifest.js +34 -0
- package/dist/cursor-tool-presentation-registry.js +305 -0
- package/dist/cursor-tool-result-display-readers.js +158 -0
- package/dist/cursor-tool-transcript.js +32 -0
- package/dist/cursor-tool-visibility.js +28 -0
- package/dist/cursor-transcript-tool-formatters.js +618 -0
- package/dist/cursor-transcript-tool-specs.js +284 -0
- package/dist/cursor-transcript-utils.js +170 -0
- package/dist/cursor-usage-accounting.js +166 -0
- package/dist/cursor-web-tool-activity.js +23 -0
- package/dist/cursor-web-tool-args.js +12 -0
- package/dist/index.js +82 -0
- package/dist/model-discovery.js +305 -0
- package/dist/model-list-cache.js +156 -0
- package/docs/cursor-dogfood-checklist.md +2 -1
- package/docs/cursor-live-smoke-checklist.md +13 -12
- package/docs/cursor-model-ux-spec.md +25 -6
- package/docs/cursor-native-tool-replay.md +13 -13
- package/docs/cursor-native-tool-visual-audit.md +6 -3
- package/docs/cursor-testing-lessons.md +7 -7
- package/docs/cursor-tool-surfaces.md +5 -5
- package/docs/platform-smoke.md +3 -3
- package/package.json +19 -7
- package/platform-smoke.config.mjs +1 -1
- package/scripts/build.mjs +126 -0
- package/scripts/cloud-runtime-smoke.mjs +3 -1
- package/scripts/debug-provider-events.mjs +3 -1
- package/scripts/debug-sdk-events.mjs +1 -1
- package/scripts/isolated-cursor-smoke.sh +5 -4
- package/scripts/lib/cloud-smoke-cleanup-evidence.mjs +1 -1
- package/scripts/lib/cursor-visual-render.mjs +1 -1
- package/scripts/lib/ensure-built.mjs +16 -0
- package/scripts/lib/local-resume-smoke-harness.mjs +1 -1
- package/scripts/local-resume-smoke.mjs +3 -1
- package/scripts/platform-smoke/card-detect.mjs +1 -1
- package/scripts/platform-smoke/live-suite-runner.mjs +1 -1
- package/scripts/platform-smoke/render-ansi.mjs +1 -1
- package/scripts/platform-smoke/targets.mjs +2 -2
- package/scripts/prepare.mjs +102 -0
- package/scripts/steering-rpc-smoke.mjs +4 -2
- package/scripts/tmux-live-smoke.sh +11 -11
- package/scripts/visual-tui-smoke-self-test.mjs +4 -1
- package/scripts/visual-tui-smoke.mjs +51 -7
- package/src/bundled-context-windows.ts +4 -1
- package/src/cursor-agents-context-registration.ts +2 -2
- package/src/cursor-agents-context.ts +0 -2
- package/src/cursor-api-key.ts +5 -4
- package/src/cursor-cloud-reporting.ts +23 -2
- package/src/cursor-fallback-models.generated.ts +592 -204
- package/src/cursor-live-run-coordinator.ts +1 -0
- package/src/cursor-native-tool-display-registration.ts +10 -11
- package/src/cursor-provider-errors.ts +1 -1
- package/src/cursor-provider-lazy.ts +5 -4
- package/src/cursor-provider-live-run-drain.ts +1 -0
- package/src/cursor-provider-run-finalizer.ts +1 -0
- package/src/cursor-provider-turn-finalize.ts +12 -0
- package/src/cursor-provider-turn-types.ts +2 -0
- package/src/cursor-sdk-billed-usage.ts +107 -0
- package/src/cursor-sdk-process-error-guard.ts +6 -4
- package/src/cursor-session-agent-lifecycle.ts +8 -9
- package/src/cursor-session-agent-resume.ts +23 -0
- package/src/cursor-session-compaction-prep.ts +4 -0
- package/src/cursor-tool-manifest.ts +1 -1
- package/src/cursor-usage-accounting.ts +70 -9
- package/src/index.ts +1 -1
- package/tsconfig.build.json +8 -0
- package/tsconfig.json +16 -0
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { loadCursorTranscriptWebToolCallsAfterOffset } from "./cursor-agent-message-web-tools.js";
|
|
2
|
+
import { collectCursorCloudRunReport, formatCursorCloudRunReport, } from "./cursor-cloud-reporting.js";
|
|
3
|
+
import { recordCursorCloudLifecycleRun } from "./cursor-cloud-lifecycle.js";
|
|
4
|
+
import { getCheckpointContextWindow, saveCachedContextWindow } from "./context-window-cache.js";
|
|
5
|
+
import { scrubSensitiveText } from "./cursor-sensitive-text.js";
|
|
6
|
+
import { isCursorRunFinishedSuccessfully, resolveCursorRunOutcome, } from "./cursor-provider-run-outcome.js";
|
|
7
|
+
import { loadCursorSdk } from "./cursor-sdk-runtime.js";
|
|
8
|
+
import { attachCursorSdkBilledTurnUsage } from "./cursor-sdk-billed-usage.js";
|
|
9
|
+
export async function cacheSdkContextWindow(agentId, modelId, cwd, store) {
|
|
10
|
+
try {
|
|
11
|
+
const { createAgentPlatform } = await loadCursorSdk();
|
|
12
|
+
const platform = await createAgentPlatform(cwd || store
|
|
13
|
+
? {
|
|
14
|
+
...(cwd ? { workspaceRef: cwd, scopedWorkspaceRef: cwd } : {}),
|
|
15
|
+
...(store ? { localStore: store } : {}),
|
|
16
|
+
}
|
|
17
|
+
: undefined);
|
|
18
|
+
const checkpoint = await platform.checkpointStore.loadLatest(agentId);
|
|
19
|
+
const contextWindow = getCheckpointContextWindow(checkpoint);
|
|
20
|
+
if (contextWindow)
|
|
21
|
+
saveCachedContextWindow(modelId, contextWindow);
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
// Context-window cache failures must not affect response streaming.
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
export function buildCursorRunOutcomeFromWait(params) {
|
|
28
|
+
const { waitResult, prepared } = params;
|
|
29
|
+
const { turnCoordinator, liveRun } = prepared.runtime;
|
|
30
|
+
const { textDeltas } = prepared;
|
|
31
|
+
return resolveCursorRunOutcome({
|
|
32
|
+
waitResult,
|
|
33
|
+
signalAborted: params.signal?.aborted,
|
|
34
|
+
textDeltas: liveRun?.textDeltas ?? textDeltas,
|
|
35
|
+
emittedText: liveRun?.emittedText ?? textDeltas.join(""),
|
|
36
|
+
planTextCandidate: turnCoordinator.planTextCandidate,
|
|
37
|
+
selectFinalTextOptions: liveRun ? undefined : { allowPartialPrefix: true },
|
|
38
|
+
runResultFallback: params.runResultFallback,
|
|
39
|
+
runErrorFallback: params.runErrorFallback,
|
|
40
|
+
resolvedApiKey: params.resolvedApiKey,
|
|
41
|
+
optionsApiKey: params.optionsApiKey,
|
|
42
|
+
runtimeTarget: prepared.runtimeTarget,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
async function replayCursorTranscriptWebToolCalls(agentId, cwd, messageOffset, turnStore, turnCoordinator, sdkEventDebug) {
|
|
46
|
+
try {
|
|
47
|
+
const transcriptToolCalls = await loadCursorTranscriptWebToolCallsAfterOffset({
|
|
48
|
+
agentId,
|
|
49
|
+
cwd,
|
|
50
|
+
offset: messageOffset,
|
|
51
|
+
store: turnStore,
|
|
52
|
+
});
|
|
53
|
+
if (transcriptToolCalls.length === 0)
|
|
54
|
+
return;
|
|
55
|
+
sdkEventDebug?.recordCoordinatorEvent("cursor-transcript-web-tools", {
|
|
56
|
+
agentId,
|
|
57
|
+
messageOffset,
|
|
58
|
+
count: transcriptToolCalls.length,
|
|
59
|
+
});
|
|
60
|
+
turnCoordinator.handleTranscriptCompletedToolCalls(transcriptToolCalls);
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
sdkEventDebug?.recordError("cursor_transcript_web_tools", error);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
function scrubCursorCloudReportingError(error, apiKey) {
|
|
67
|
+
return new Error(scrubSensitiveText(error instanceof Error ? error.message : String(error), apiKey));
|
|
68
|
+
}
|
|
69
|
+
function recordCursorCloudReportingError(sdkEventDebug, error, apiKey) {
|
|
70
|
+
try {
|
|
71
|
+
sdkEventDebug?.recordError("cloud_run_report", scrubCursorCloudReportingError(error, apiKey));
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
// Debug reporting must never affect provider execution.
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/** Single wait/finalize path for SDK runs: wait, debug capture, transcript replay, incomplete tools, artifacts, context cache. */
|
|
78
|
+
export async function awaitFinalizeCursorRunOutcome(params) {
|
|
79
|
+
const apiKey = params.resolvedApiKey ?? params.optionsApiKey;
|
|
80
|
+
const waitResult = params.waitResult ?? (await params.run.wait());
|
|
81
|
+
const outcome = buildCursorRunOutcomeFromWait({
|
|
82
|
+
waitResult,
|
|
83
|
+
prepared: params.prepared,
|
|
84
|
+
signal: params.signal,
|
|
85
|
+
runResultFallback: params.runResultFallback,
|
|
86
|
+
runErrorFallback: params.runErrorFallback,
|
|
87
|
+
resolvedApiKey: params.resolvedApiKey,
|
|
88
|
+
optionsApiKey: params.optionsApiKey,
|
|
89
|
+
});
|
|
90
|
+
const billed = await attachCursorSdkBilledTurnUsage({
|
|
91
|
+
agent: params.prepared.agent,
|
|
92
|
+
agentId: params.run.agentId,
|
|
93
|
+
runtime: params.prepared.runtimeTarget,
|
|
94
|
+
runId: params.run.id,
|
|
95
|
+
});
|
|
96
|
+
params.prepared.runtime.billedTurnUsage = billed.turn;
|
|
97
|
+
if (params.prepared.runtime.liveRun) {
|
|
98
|
+
params.prepared.runtime.liveRun.billedTurnUsage = billed.turn;
|
|
99
|
+
}
|
|
100
|
+
let displayOnlyTraceBlock;
|
|
101
|
+
if (params.prepared.runtimeTarget === "cloud" && isCursorRunFinishedSuccessfully(outcome)) {
|
|
102
|
+
let report = { agentId: params.run.agentId, runId: params.run.id, branches: [] };
|
|
103
|
+
try {
|
|
104
|
+
report = await collectCursorCloudRunReport({
|
|
105
|
+
agent: params.prepared.agent,
|
|
106
|
+
run: params.run,
|
|
107
|
+
waitResult,
|
|
108
|
+
apiKey,
|
|
109
|
+
agentUsage: billed.agentUsage,
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
catch (error) {
|
|
113
|
+
recordCursorCloudReportingError(params.sdkEventDebug, error, apiKey);
|
|
114
|
+
}
|
|
115
|
+
try {
|
|
116
|
+
recordCursorCloudLifecycleRun(report, { apiKey });
|
|
117
|
+
}
|
|
118
|
+
catch (error) {
|
|
119
|
+
recordCursorCloudReportingError(params.sdkEventDebug, error, apiKey);
|
|
120
|
+
}
|
|
121
|
+
try {
|
|
122
|
+
params.sdkEventDebug?.recordProviderEvent("cloud_run_report", report);
|
|
123
|
+
}
|
|
124
|
+
catch (error) {
|
|
125
|
+
recordCursorCloudReportingError(params.sdkEventDebug, error, apiKey);
|
|
126
|
+
}
|
|
127
|
+
try {
|
|
128
|
+
displayOnlyTraceBlock = formatCursorCloudRunReport(report, { apiKey });
|
|
129
|
+
}
|
|
130
|
+
catch (error) {
|
|
131
|
+
recordCursorCloudReportingError(params.sdkEventDebug, error, apiKey);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
try {
|
|
135
|
+
params.sdkEventDebug?.recordWaitResult(waitResult);
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
// Debug reporting must never affect provider execution.
|
|
139
|
+
}
|
|
140
|
+
if (params.prepared.runtimeTarget === "local" && isCursorRunFinishedSuccessfully(outcome)) {
|
|
141
|
+
await replayCursorTranscriptWebToolCalls(params.run.agentId, params.prepared.cwd, params.cursorAgentMessageOffset, params.prepared.sessionAgentLease.store, params.prepared.runtime.turnCoordinator, params.sdkEventDebug);
|
|
142
|
+
}
|
|
143
|
+
params.prepared.runtime.turnCoordinator.discardIncompleteStartedToolCalls(outcome.incompleteTools);
|
|
144
|
+
try {
|
|
145
|
+
await params.sdkEventDebug?.captureRunArtifacts(params.run);
|
|
146
|
+
}
|
|
147
|
+
catch {
|
|
148
|
+
// Debug artifact failures must never affect provider execution.
|
|
149
|
+
}
|
|
150
|
+
if (params.prepared.runtimeTarget === "local" && params.cacheContextWindow !== false) {
|
|
151
|
+
await cacheSdkContextWindow(params.contextWindowAgentId ?? params.run.agentId, params.modelId, params.prepared.cwd, params.prepared.sessionAgentLease.store);
|
|
152
|
+
}
|
|
153
|
+
return { outcome, displayOnlyTraceBlock };
|
|
154
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { cursorLiveRuns } from "./cursor-provider-live-run-drain.js";
|
|
2
|
+
import { CursorPartialContentEmitter } from "./cursor-partial-content-emitter.js";
|
|
3
|
+
import { CURSOR_TOOL_LIFECYCLE_DEFER_MS, formatCursorToolLifecycleProgressText, isCursorToolLifecycleEligible, } from "./cursor-tool-lifecycle.js";
|
|
4
|
+
import { getNormalizedCursorToolName } from "./cursor-tool-visibility.js";
|
|
5
|
+
import { getStartedToolCallFingerprint } from "./cursor-provider-turn-tool-ledger.js";
|
|
6
|
+
export class CursorToolLifecycleEmitter {
|
|
7
|
+
liveRun;
|
|
8
|
+
resolvedApiKey;
|
|
9
|
+
contentEmitter;
|
|
10
|
+
debugRecorder;
|
|
11
|
+
hasStartedToolCall;
|
|
12
|
+
isBridgeMcpToolCall;
|
|
13
|
+
emittedLifecycleCallIds = new Set();
|
|
14
|
+
lifecycleTimers = new Map();
|
|
15
|
+
activeLifecycleFingerprintOwners = new Map();
|
|
16
|
+
lifecycleFingerprintByCallId = new Map();
|
|
17
|
+
activeLifecycleProgressTextOwners = new Map();
|
|
18
|
+
lifecycleProgressTextByCallId = new Map();
|
|
19
|
+
constructor(options) {
|
|
20
|
+
this.liveRun = options.liveRun;
|
|
21
|
+
this.resolvedApiKey = options.resolvedApiKey;
|
|
22
|
+
this.contentEmitter = options.contentEmitter;
|
|
23
|
+
this.debugRecorder = options.debugRecorder;
|
|
24
|
+
this.hasStartedToolCall = options.hasStartedToolCall;
|
|
25
|
+
this.isBridgeMcpToolCall = options.isBridgeMcpToolCall;
|
|
26
|
+
}
|
|
27
|
+
maybeSchedule(callId, toolCall) {
|
|
28
|
+
if (typeof callId !== "string" || this.emittedLifecycleCallIds.has(callId))
|
|
29
|
+
return;
|
|
30
|
+
if (this.isBridgeMcpToolCall(toolCall))
|
|
31
|
+
return;
|
|
32
|
+
if (!isCursorToolLifecycleEligible(toolCall))
|
|
33
|
+
return;
|
|
34
|
+
const progressText = formatCursorToolLifecycleProgressText(toolCall, this.resolvedApiKey);
|
|
35
|
+
if (!progressText)
|
|
36
|
+
return;
|
|
37
|
+
const fingerprint = getStartedToolCallFingerprint(toolCall);
|
|
38
|
+
const existingOwner = this.activeLifecycleFingerprintOwners.get(fingerprint);
|
|
39
|
+
if (existingOwner && existingOwner !== callId) {
|
|
40
|
+
this.debugRecorder?.recordCoordinatorEvent("tool_lifecycle_skip", {
|
|
41
|
+
callId,
|
|
42
|
+
ownerCallId: existingOwner,
|
|
43
|
+
toolName: getNormalizedCursorToolName(toolCall),
|
|
44
|
+
reason: "duplicate-active-fingerprint",
|
|
45
|
+
});
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
this.cancel(callId);
|
|
49
|
+
this.activeLifecycleFingerprintOwners.set(fingerprint, callId);
|
|
50
|
+
this.lifecycleFingerprintByCallId.set(callId, fingerprint);
|
|
51
|
+
if (!this.activeLifecycleProgressTextOwners.has(progressText)) {
|
|
52
|
+
this.activeLifecycleProgressTextOwners.set(progressText, callId);
|
|
53
|
+
}
|
|
54
|
+
this.lifecycleProgressTextByCallId.set(callId, progressText);
|
|
55
|
+
const timer = setTimeout(() => {
|
|
56
|
+
this.lifecycleTimers.delete(callId);
|
|
57
|
+
if (!this.hasStartedToolCall(callId)) {
|
|
58
|
+
this.clearLifecycleIdentity(callId);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
if (this.emittedLifecycleCallIds.has(callId))
|
|
62
|
+
return;
|
|
63
|
+
const progressOwner = this.activeLifecycleProgressTextOwners.get(progressText);
|
|
64
|
+
if (progressOwner && progressOwner !== callId && this.hasStartedToolCall(progressOwner)) {
|
|
65
|
+
this.debugRecorder?.recordCoordinatorEvent("tool_lifecycle_skip", {
|
|
66
|
+
callId,
|
|
67
|
+
ownerCallId: progressOwner,
|
|
68
|
+
toolName: getNormalizedCursorToolName(toolCall),
|
|
69
|
+
reason: "duplicate-active-progress-text",
|
|
70
|
+
});
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
this.activeLifecycleProgressTextOwners.set(progressText, callId);
|
|
74
|
+
this.emit(callId, toolCall, progressText);
|
|
75
|
+
}, CURSOR_TOOL_LIFECYCLE_DEFER_MS);
|
|
76
|
+
timer.unref?.();
|
|
77
|
+
this.lifecycleTimers.set(callId, timer);
|
|
78
|
+
}
|
|
79
|
+
cancel(callId) {
|
|
80
|
+
const timer = this.lifecycleTimers.get(callId);
|
|
81
|
+
if (timer) {
|
|
82
|
+
clearTimeout(timer);
|
|
83
|
+
this.lifecycleTimers.delete(callId);
|
|
84
|
+
}
|
|
85
|
+
this.clearLifecycleIdentity(callId);
|
|
86
|
+
}
|
|
87
|
+
clear() {
|
|
88
|
+
this.emittedLifecycleCallIds.clear();
|
|
89
|
+
for (const timer of this.lifecycleTimers.values())
|
|
90
|
+
clearTimeout(timer);
|
|
91
|
+
this.lifecycleTimers.clear();
|
|
92
|
+
this.activeLifecycleFingerprintOwners.clear();
|
|
93
|
+
this.lifecycleFingerprintByCallId.clear();
|
|
94
|
+
this.activeLifecycleProgressTextOwners.clear();
|
|
95
|
+
this.lifecycleProgressTextByCallId.clear();
|
|
96
|
+
}
|
|
97
|
+
clearLifecycleIdentity(callId) {
|
|
98
|
+
const fingerprint = this.lifecycleFingerprintByCallId.get(callId);
|
|
99
|
+
if (fingerprint && this.activeLifecycleFingerprintOwners.get(fingerprint) === callId) {
|
|
100
|
+
this.activeLifecycleFingerprintOwners.delete(fingerprint);
|
|
101
|
+
}
|
|
102
|
+
this.lifecycleFingerprintByCallId.delete(callId);
|
|
103
|
+
const progressText = this.lifecycleProgressTextByCallId.get(callId);
|
|
104
|
+
if (progressText && this.activeLifecycleProgressTextOwners.get(progressText) === callId) {
|
|
105
|
+
this.activeLifecycleProgressTextOwners.delete(progressText);
|
|
106
|
+
}
|
|
107
|
+
this.lifecycleProgressTextByCallId.delete(callId);
|
|
108
|
+
}
|
|
109
|
+
emit(callId, toolCall, progressText) {
|
|
110
|
+
this.emittedLifecycleCallIds.add(callId);
|
|
111
|
+
this.debugRecorder?.recordCoordinatorEvent("tool_lifecycle", {
|
|
112
|
+
callId,
|
|
113
|
+
toolName: getNormalizedCursorToolName(toolCall),
|
|
114
|
+
progressText,
|
|
115
|
+
liveRun: this.liveRun !== undefined,
|
|
116
|
+
});
|
|
117
|
+
if (this.liveRun) {
|
|
118
|
+
cursorLiveRuns.queueEvent(this.liveRun, { type: "thinking-delta", text: progressText });
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
this.contentEmitter.appendThinkingDelta(progressText);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
export function createTurnCoordinatorContentEmitter(stream, partial) {
|
|
125
|
+
return new CursorPartialContentEmitter(stream, partial, undefined, false);
|
|
126
|
+
}
|
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
import { configureCursorSdkHttp1 } from "./cursor-http1.js";
|
|
2
|
+
import { installCursorMcpToolTimeoutOverride } from "./cursor-mcp-timeout-override.js";
|
|
3
|
+
import { ensureCursorRipgrepPath } from "./cursor-ripgrep-path.js";
|
|
4
|
+
import { installCursorSdkOutputFilter, suppressCursorSdkOutput } from "./cursor-sdk-output-filter.js";
|
|
5
|
+
import { acquireSessionCursorAgent, buildCursorSessionSendPrompt, planCursorSessionSend, resetSessionCursorAgent, } from "./cursor-session-agent.js";
|
|
6
|
+
import { buildCursorPrompt, estimateCursorPromptTokens } from "./context.js";
|
|
7
|
+
import { getCursorPromptOptions } from "./cursor-usage-accounting.js";
|
|
8
|
+
import { getActiveContextToolNames } from "./cursor-context-tools.js";
|
|
9
|
+
import { abandonSessionCursorAgent, createCursorNativeReplayId, cursorLiveRuns, getActiveCursorLiveRunForCurrentScope, getPendingCursorLiveRun, } from "./cursor-provider-live-run-drain.js";
|
|
10
|
+
import { getCursorProviderAgentModeOrThrow, getEffectiveFastForModelId, } from "./cursor-state.js";
|
|
11
|
+
import { resolveEffectiveCursorConfig } from "./cursor-runtime-state.js";
|
|
12
|
+
import { buildCursorModelSelection } from "./model-discovery.js";
|
|
13
|
+
import { getEffectiveCursorSettingSources } from "./cursor-setting-sources.js";
|
|
14
|
+
import { formatCursorCloudPreflightError, buildCursorCloudAgentOptions, preflightCursorCloudRuntime, } from "./cursor-cloud-options.js";
|
|
15
|
+
import { inspectCursorCloudLocalState } from "./cursor-cloud-local-state.js";
|
|
16
|
+
import { getCursorSessionName, getCursorSessionProjectTrusted } from "./cursor-session-scope.js";
|
|
17
|
+
import { resolveCursorPiToolBridgeEnabled } from "./cursor-pi-tool-bridge-env.js";
|
|
18
|
+
import { buildCursorToolManifestText, resolveCursorToolManifestEnabled, } from "./cursor-tool-manifest.js";
|
|
19
|
+
import { isCursorNativeToolDisplayRuntimeEnabled } from "./cursor-native-tool-display-state.js";
|
|
20
|
+
import { createCursorCloudLifecyclePersistenceError, recordCursorCloudLifecycleSafely, } from "./cursor-cloud-lifecycle.js";
|
|
21
|
+
import { MISSING_CURSOR_API_KEY_MESSAGE } from "./cursor-provider-errors.js";
|
|
22
|
+
import { CursorSdkTurnCoordinator } from "./cursor-provider-turn-coordinator.js";
|
|
23
|
+
import { resolveCursorApiKey } from "./cursor-api-key.js";
|
|
24
|
+
import { loadCursorSdk } from "./cursor-sdk-runtime.js";
|
|
25
|
+
function buildCursorCloudPromptContext(context, handoff) {
|
|
26
|
+
if (handoff === "bootstrap")
|
|
27
|
+
return context;
|
|
28
|
+
for (let index = context.messages.length - 1; index >= 0; index -= 1) {
|
|
29
|
+
const message = context.messages[index];
|
|
30
|
+
if (message.role === "user")
|
|
31
|
+
return { ...context, messages: [message] };
|
|
32
|
+
}
|
|
33
|
+
return { ...context, messages: context.messages.slice(-1) };
|
|
34
|
+
}
|
|
35
|
+
const CLOUD_SEND_PLAN = { mode: "bootstrap", resetAgent: false, reason: "initial" };
|
|
36
|
+
export function resolveCursorProviderTurnConfig(cwd) {
|
|
37
|
+
return resolveEffectiveCursorConfig({ cwd, projectTrusted: getCursorSessionProjectTrusted() });
|
|
38
|
+
}
|
|
39
|
+
function buildCloudCursorProviderTurnLifecycle(agent) {
|
|
40
|
+
return {
|
|
41
|
+
trackRunCompletion: () => { },
|
|
42
|
+
commitSend: () => { },
|
|
43
|
+
abandon: async () => { },
|
|
44
|
+
dispose: async () => {
|
|
45
|
+
await agent[Symbol.asyncDispose]?.();
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
function buildLocalCursorProviderTurnLifecycle(lease, scopeKey) {
|
|
50
|
+
return {
|
|
51
|
+
trackRunCompletion: (completion) => lease.trackRunCompletion(completion),
|
|
52
|
+
commitSend: (context, bootstrapped) => lease.commitSend(context, bootstrapped),
|
|
53
|
+
abandon: () => abandonSessionCursorAgent(scopeKey),
|
|
54
|
+
dispose: async () => { },
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
async function prepareCursorCloudProviderTurn(prepareParams) {
|
|
58
|
+
const { params, cwd, resolvedApiKey, sdkEventDebug, throwIfAborted, resolvedConfig, agentMode, selection, fastEnabled } = prepareParams;
|
|
59
|
+
const { model, context, options } = params;
|
|
60
|
+
let restoreCursorSdkOutputFilter;
|
|
61
|
+
let cloudAgentForCleanup;
|
|
62
|
+
let completed = false;
|
|
63
|
+
try {
|
|
64
|
+
const preflight = preflightCursorCloudRuntime({
|
|
65
|
+
resolvedConfig,
|
|
66
|
+
localState: resolvedConfig.cloud.allowLocalState.value
|
|
67
|
+
? { insideGitRepo: false }
|
|
68
|
+
: inspectCursorCloudLocalState(cwd, {
|
|
69
|
+
repo: resolvedConfig.cloud.repo.value,
|
|
70
|
+
branch: resolvedConfig.cloud.branch.value,
|
|
71
|
+
}),
|
|
72
|
+
hasPriorContext: context.messages.length > 1,
|
|
73
|
+
});
|
|
74
|
+
if (!preflight.ok)
|
|
75
|
+
throw new Error(formatCursorCloudPreflightError(preflight));
|
|
76
|
+
if (getPendingCursorLiveRun(context) || getActiveCursorLiveRunForCurrentScope()) {
|
|
77
|
+
throw new Error("Cursor cloud runtime cannot start while a local Cursor live run is pending; finish or abort the local run, then retry.");
|
|
78
|
+
}
|
|
79
|
+
const { Agent } = await loadCursorSdk();
|
|
80
|
+
restoreCursorSdkOutputFilter = installCursorSdkOutputFilter();
|
|
81
|
+
const promptOptions = {
|
|
82
|
+
...getCursorPromptOptions(model),
|
|
83
|
+
agentMode,
|
|
84
|
+
includePiBridgeGuidance: false,
|
|
85
|
+
includePiAskQuestionGuidance: false,
|
|
86
|
+
};
|
|
87
|
+
const prompt = buildCursorPrompt(buildCursorCloudPromptContext(context, resolvedConfig.cloud.contextHandoff.value), promptOptions);
|
|
88
|
+
const promptInputTokens = estimateCursorPromptTokens(prompt, promptOptions);
|
|
89
|
+
const agent = await suppressCursorSdkOutput(() => Agent.create(buildCursorCloudAgentOptions({
|
|
90
|
+
apiKey: resolvedApiKey,
|
|
91
|
+
modelSelection: selection,
|
|
92
|
+
agentMode,
|
|
93
|
+
resolvedConfig,
|
|
94
|
+
name: getCursorSessionName(),
|
|
95
|
+
})));
|
|
96
|
+
cloudAgentForCleanup = agent;
|
|
97
|
+
if (!recordCursorCloudLifecycleSafely({ agentId: agent.agentId }, resolvedApiKey)) {
|
|
98
|
+
throw createCursorCloudLifecyclePersistenceError(agent.agentId, "intent", undefined, resolvedApiKey);
|
|
99
|
+
}
|
|
100
|
+
sdkEventDebug?.recordProviderMeta({ runtime: "cloud", cloudAgentId: agent.agentId, phase: "agent_created" });
|
|
101
|
+
throwIfAborted();
|
|
102
|
+
const textDeltas = [];
|
|
103
|
+
const nativeReplayId = createCursorNativeReplayId();
|
|
104
|
+
const turnCoordinator = new CursorSdkTurnCoordinator({
|
|
105
|
+
stream: params.stream,
|
|
106
|
+
partial: params.partial,
|
|
107
|
+
cwd,
|
|
108
|
+
resolvedApiKey,
|
|
109
|
+
useNativeToolReplay: false,
|
|
110
|
+
nativeReplayId,
|
|
111
|
+
textDeltas,
|
|
112
|
+
debugRecorder: sdkEventDebug,
|
|
113
|
+
});
|
|
114
|
+
sdkEventDebug?.recordProviderMeta({
|
|
115
|
+
runtime: "cloud",
|
|
116
|
+
cloudAgentId: agent.agentId,
|
|
117
|
+
model: {
|
|
118
|
+
id: model.id,
|
|
119
|
+
provider: model.provider,
|
|
120
|
+
api: model.api,
|
|
121
|
+
reasoning: options?.reasoning ?? "off",
|
|
122
|
+
fastEnabled,
|
|
123
|
+
selection,
|
|
124
|
+
},
|
|
125
|
+
contextHandoff: resolvedConfig.cloud.contextHandoff.value,
|
|
126
|
+
sendPlan: CLOUD_SEND_PLAN,
|
|
127
|
+
promptOptions,
|
|
128
|
+
agentMode,
|
|
129
|
+
localForce: false,
|
|
130
|
+
});
|
|
131
|
+
completed = true;
|
|
132
|
+
cloudAgentForCleanup = undefined;
|
|
133
|
+
return {
|
|
134
|
+
runtimeTarget: "cloud",
|
|
135
|
+
agent,
|
|
136
|
+
cwd,
|
|
137
|
+
payload: {
|
|
138
|
+
text: prompt.text,
|
|
139
|
+
images: prompt.images.length > 0 ? prompt.images : undefined,
|
|
140
|
+
},
|
|
141
|
+
meta: {
|
|
142
|
+
sendPlan: CLOUD_SEND_PLAN,
|
|
143
|
+
prompt,
|
|
144
|
+
bootstrap: true,
|
|
145
|
+
promptInputTokens,
|
|
146
|
+
useNativeToolReplay: false,
|
|
147
|
+
bridgeEnabled: false,
|
|
148
|
+
nativeReplayId,
|
|
149
|
+
agentMode,
|
|
150
|
+
modelSelection: selection,
|
|
151
|
+
},
|
|
152
|
+
contextWindowAgentId: agent.agentId,
|
|
153
|
+
textDeltas,
|
|
154
|
+
restoreCursorSdkOutputFilter,
|
|
155
|
+
lifecycle: buildCloudCursorProviderTurnLifecycle(agent),
|
|
156
|
+
runtime: { kind: "direct", turnCoordinator },
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
finally {
|
|
160
|
+
if (!completed) {
|
|
161
|
+
await cloudAgentForCleanup?.[Symbol.asyncDispose]?.().catch(() => { });
|
|
162
|
+
restoreCursorSdkOutputFilter?.();
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
async function prepareCursorLocalProviderTurn(prepareParams) {
|
|
167
|
+
const { params, cwd, resolvedApiKey, sdkEventDebug, throwIfAborted, resolvedConfig, agentMode, selection, fastEnabled } = prepareParams;
|
|
168
|
+
const { model, context, options } = params;
|
|
169
|
+
let restoreCursorSdkOutputFilter;
|
|
170
|
+
let sessionAgentScopeKey;
|
|
171
|
+
let liveRun;
|
|
172
|
+
let completed = false;
|
|
173
|
+
try {
|
|
174
|
+
ensureCursorRipgrepPath();
|
|
175
|
+
const localSafety = {
|
|
176
|
+
autoReview: resolvedConfig.local.autoReview.value,
|
|
177
|
+
sandboxEnabled: resolvedConfig.local.sandboxEnabled.value,
|
|
178
|
+
};
|
|
179
|
+
const sdk = await loadCursorSdk();
|
|
180
|
+
const { Agent } = sdk;
|
|
181
|
+
const useHttp1ForAgent = configureCursorSdkHttp1(sdk, resolvedConfig.local.useHttp1ForAgent);
|
|
182
|
+
installCursorMcpToolTimeoutOverride();
|
|
183
|
+
restoreCursorSdkOutputFilter = installCursorSdkOutputFilter();
|
|
184
|
+
const settingSources = getEffectiveCursorSettingSources();
|
|
185
|
+
const queuedBridgeRequestsBeforeLiveRun = [];
|
|
186
|
+
let liveRunForBridgeQueue;
|
|
187
|
+
const sessionAgentAcquireParams = {
|
|
188
|
+
apiKey: resolvedApiKey,
|
|
189
|
+
agentMode,
|
|
190
|
+
cwd,
|
|
191
|
+
modelSelection: selection,
|
|
192
|
+
settingSources,
|
|
193
|
+
localSafety,
|
|
194
|
+
localResume: resolvedConfig.local.resume.value,
|
|
195
|
+
useHttp1ForAgent,
|
|
196
|
+
debugRecorder: sdkEventDebug,
|
|
197
|
+
onBridgeToolRequest: (request) => {
|
|
198
|
+
if (liveRunForBridgeQueue && !liveRunForBridgeQueue.disposed) {
|
|
199
|
+
cursorLiveRuns.queueEvent(liveRunForBridgeQueue, { type: "bridge-tool", request });
|
|
200
|
+
}
|
|
201
|
+
else {
|
|
202
|
+
queuedBridgeRequestsBeforeLiveRun.push(request);
|
|
203
|
+
}
|
|
204
|
+
},
|
|
205
|
+
createAgent: (createOptions) => suppressCursorSdkOutput(() => Agent.create(createOptions)),
|
|
206
|
+
};
|
|
207
|
+
let sessionAgentLease = await acquireSessionCursorAgent(sessionAgentAcquireParams);
|
|
208
|
+
sessionAgentScopeKey = sessionAgentLease.scopeKey;
|
|
209
|
+
throwIfAborted();
|
|
210
|
+
let bridgeToolNames = new Set(sessionAgentLease.bridgeRun?.snapshot.tools.map((tool) => tool.mcpToolName) ?? []);
|
|
211
|
+
let includePiBridgeGuidance = bridgeToolNames.size > 0;
|
|
212
|
+
const buildPromptOptions = (plan) => {
|
|
213
|
+
const promptOptions = {
|
|
214
|
+
...getCursorPromptOptions(model),
|
|
215
|
+
agentMode,
|
|
216
|
+
includePiBridgeGuidance,
|
|
217
|
+
includePiAskQuestionGuidance: bridgeToolNames.has("pi__cursor_ask_question"),
|
|
218
|
+
};
|
|
219
|
+
if (plan.mode !== "bootstrap" || !resolveCursorToolManifestEnabled()) {
|
|
220
|
+
return promptOptions;
|
|
221
|
+
}
|
|
222
|
+
return {
|
|
223
|
+
...promptOptions,
|
|
224
|
+
toolManifest: buildCursorToolManifestText({
|
|
225
|
+
bridgeSnapshot: sessionAgentLease.bridgeRun?.snapshot,
|
|
226
|
+
piBridgeEnabled: resolveCursorPiToolBridgeEnabled(),
|
|
227
|
+
includePiBridgeGuidance,
|
|
228
|
+
}),
|
|
229
|
+
};
|
|
230
|
+
};
|
|
231
|
+
let sendPlan = planCursorSessionSend(sessionAgentLease.sendState, context);
|
|
232
|
+
if (sessionAgentLease.created && sessionAgentLease.resumed && sendPlan.mode === "incremental") {
|
|
233
|
+
sendPlan = { mode: "bootstrap", resetAgent: false, reason: "process_resume" };
|
|
234
|
+
}
|
|
235
|
+
let promptOptions = buildPromptOptions(sendPlan);
|
|
236
|
+
let prompt = buildCursorSessionSendPrompt(context, promptOptions, sendPlan);
|
|
237
|
+
if (sendPlan.resetAgent) {
|
|
238
|
+
await resetSessionCursorAgent(sessionAgentScopeKey);
|
|
239
|
+
sessionAgentLease = await acquireSessionCursorAgent({ ...sessionAgentAcquireParams, forceCreate: true });
|
|
240
|
+
sessionAgentScopeKey = sessionAgentLease.scopeKey;
|
|
241
|
+
bridgeToolNames = new Set(sessionAgentLease.bridgeRun?.snapshot.tools.map((tool) => tool.mcpToolName) ?? []);
|
|
242
|
+
includePiBridgeGuidance = bridgeToolNames.size > 0;
|
|
243
|
+
sendPlan = planCursorSessionSend(sessionAgentLease.sendState, context);
|
|
244
|
+
promptOptions = buildPromptOptions(sendPlan);
|
|
245
|
+
prompt = buildCursorSessionSendPrompt(context, promptOptions, sendPlan);
|
|
246
|
+
}
|
|
247
|
+
const bootstrap = sendPlan.mode === "bootstrap";
|
|
248
|
+
const agent = sessionAgentLease.agent;
|
|
249
|
+
const bridgeRun = sessionAgentLease.bridgeRun;
|
|
250
|
+
const sendPayload = {
|
|
251
|
+
text: prompt.text,
|
|
252
|
+
images: prompt.images.length > 0 ? prompt.images : undefined,
|
|
253
|
+
};
|
|
254
|
+
const sessionBridgeRun = bridgeRun;
|
|
255
|
+
const promptInputTokens = estimateCursorPromptTokens(prompt, promptOptions);
|
|
256
|
+
const useNativeToolReplay = isCursorNativeToolDisplayRuntimeEnabled();
|
|
257
|
+
const activeToolNames = getActiveContextToolNames(context);
|
|
258
|
+
sdkEventDebug?.recordProviderMeta({
|
|
259
|
+
model: {
|
|
260
|
+
id: model.id,
|
|
261
|
+
provider: model.provider,
|
|
262
|
+
api: model.api,
|
|
263
|
+
reasoning: options?.reasoning ?? "off",
|
|
264
|
+
fastEnabled,
|
|
265
|
+
selection,
|
|
266
|
+
},
|
|
267
|
+
settingSources: settingSources ?? null,
|
|
268
|
+
sendState: sessionAgentLease.sendState,
|
|
269
|
+
sendPlan,
|
|
270
|
+
promptOptions,
|
|
271
|
+
toolManifestEnabled: resolveCursorToolManifestEnabled(),
|
|
272
|
+
agentMode,
|
|
273
|
+
localForce: resolvedConfig.local.force.value,
|
|
274
|
+
localResume: resolvedConfig.local.resume.value,
|
|
275
|
+
resumedAgent: sessionAgentLease.resumed,
|
|
276
|
+
activeToolNames: activeToolNames ? [...activeToolNames] : [],
|
|
277
|
+
sessionAgentScopeKey,
|
|
278
|
+
bridgeRunId: bridgeRun?.id,
|
|
279
|
+
});
|
|
280
|
+
const nativeReplayId = createCursorNativeReplayId();
|
|
281
|
+
const textDeltas = [];
|
|
282
|
+
const useLiveRun = useNativeToolReplay || bridgeRun !== undefined;
|
|
283
|
+
liveRun = useLiveRun
|
|
284
|
+
? cursorLiveRuns.start({
|
|
285
|
+
id: useNativeToolReplay ? nativeReplayId : bridgeRun?.id ?? nativeReplayId,
|
|
286
|
+
agent,
|
|
287
|
+
bridgeRun,
|
|
288
|
+
sessionBridgeRun,
|
|
289
|
+
sessionAgentScopeKey,
|
|
290
|
+
promptInputTokens,
|
|
291
|
+
textDeltas,
|
|
292
|
+
debugRecorder: sdkEventDebug,
|
|
293
|
+
})
|
|
294
|
+
: undefined;
|
|
295
|
+
if (liveRun) {
|
|
296
|
+
liveRunForBridgeQueue = liveRun;
|
|
297
|
+
for (const request of queuedBridgeRequestsBeforeLiveRun.splice(0)) {
|
|
298
|
+
cursorLiveRuns.queueEvent(liveRun, { type: "bridge-tool", request });
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
const turnCoordinator = new CursorSdkTurnCoordinator({
|
|
302
|
+
stream: params.stream,
|
|
303
|
+
partial: params.partial,
|
|
304
|
+
cwd,
|
|
305
|
+
resolvedApiKey,
|
|
306
|
+
liveRun,
|
|
307
|
+
useNativeToolReplay,
|
|
308
|
+
activeToolNames,
|
|
309
|
+
nativeReplayId,
|
|
310
|
+
textDeltas,
|
|
311
|
+
debugRecorder: sdkEventDebug,
|
|
312
|
+
});
|
|
313
|
+
completed = true;
|
|
314
|
+
return {
|
|
315
|
+
runtimeTarget: "local",
|
|
316
|
+
agent,
|
|
317
|
+
cwd,
|
|
318
|
+
payload: sendPayload,
|
|
319
|
+
meta: {
|
|
320
|
+
sendPlan,
|
|
321
|
+
prompt,
|
|
322
|
+
bootstrap,
|
|
323
|
+
promptInputTokens,
|
|
324
|
+
useNativeToolReplay,
|
|
325
|
+
bridgeEnabled: bridgeRun !== undefined,
|
|
326
|
+
nativeReplayId,
|
|
327
|
+
agentMode,
|
|
328
|
+
modelSelection: selection,
|
|
329
|
+
...(sessionAgentLease.resumeNotice ? { resumeNotice: sessionAgentLease.resumeNotice } : {}),
|
|
330
|
+
},
|
|
331
|
+
contextWindowAgentId: agent.agentId,
|
|
332
|
+
textDeltas,
|
|
333
|
+
sessionAgentScopeKey,
|
|
334
|
+
sessionAgentLease,
|
|
335
|
+
localForce: resolvedConfig.local.force,
|
|
336
|
+
restoreCursorSdkOutputFilter,
|
|
337
|
+
lifecycle: buildLocalCursorProviderTurnLifecycle(sessionAgentLease, sessionAgentScopeKey),
|
|
338
|
+
runtime: liveRun
|
|
339
|
+
? { kind: "live", liveRun, turnCoordinator }
|
|
340
|
+
: { kind: "direct", turnCoordinator },
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
finally {
|
|
344
|
+
if (!completed) {
|
|
345
|
+
if (liveRun && !liveRun.disposed) {
|
|
346
|
+
await cursorLiveRuns
|
|
347
|
+
.release(liveRun)
|
|
348
|
+
.catch(() => abandonSessionCursorAgent(sessionAgentScopeKey).catch(() => { }));
|
|
349
|
+
}
|
|
350
|
+
else {
|
|
351
|
+
await abandonSessionCursorAgent(sessionAgentScopeKey).catch(() => { });
|
|
352
|
+
}
|
|
353
|
+
restoreCursorSdkOutputFilter?.();
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
/** Resolves the runtime target from the caller-supplied snapshot and dispatches to the owning branch. */
|
|
358
|
+
export async function prepareCursorProviderTurn(prepareParams) {
|
|
359
|
+
const { params, resolvedConfig } = prepareParams;
|
|
360
|
+
const { model, options } = params;
|
|
361
|
+
const agentMode = getCursorProviderAgentModeOrThrow();
|
|
362
|
+
const fastEnabled = resolvedConfig.runtime.value === "cloud" ? undefined : getEffectiveFastForModelId(model.id);
|
|
363
|
+
const selection = buildCursorModelSelection(model.id, options?.reasoning ?? "off", fastEnabled);
|
|
364
|
+
const context = { ...prepareParams, agentMode, selection, fastEnabled };
|
|
365
|
+
return resolvedConfig.runtime.value === "cloud"
|
|
366
|
+
? prepareCursorCloudProviderTurn(context)
|
|
367
|
+
: prepareCursorLocalProviderTurn(context);
|
|
368
|
+
}
|
|
369
|
+
export function requireCursorApiKey(options) {
|
|
370
|
+
const apiKey = resolveCursorApiKey(options?.apiKey);
|
|
371
|
+
if (!apiKey)
|
|
372
|
+
throw new Error(MISSING_CURSOR_API_KEY_MESSAGE);
|
|
373
|
+
return apiKey;
|
|
374
|
+
}
|