pi-cursor-sdk 0.2.0 → 0.3.7

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