pi-cursor-sdk 0.1.36 → 0.1.38
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 +38 -0
- package/docs/cursor-model-ux-spec.md +1 -1
- package/docs/cursor-native-tool-replay.md +9 -9
- package/package.json +1 -1
- package/scripts/platform-smoke/card-detect.mjs +1 -1
- package/src/context-window-cache.ts +10 -14
- package/src/context.ts +1 -1
- package/src/cursor-agent-message-web-tools.ts +2 -1
- package/src/cursor-agents-context-registration.ts +18 -0
- package/src/cursor-agents-context.ts +21 -30
- package/src/cursor-edit-diff.ts +4 -2
- package/src/cursor-fallback-warning.ts +22 -0
- package/src/cursor-incomplete-tool-visibility.ts +5 -11
- package/src/cursor-live-run-coordinator.ts +1 -1
- package/src/cursor-mcp-timeout-override.ts +0 -2
- package/src/cursor-model-lifecycle.ts +72 -0
- package/src/cursor-native-replay-routing.ts +1 -1
- package/src/cursor-native-replay-trace.ts +1 -1
- package/src/cursor-native-tool-display-registration.ts +16 -28
- package/src/cursor-native-tool-display-replay.ts +12 -47
- package/src/cursor-native-tool-display-state.ts +1 -1
- package/src/cursor-native-tool-display-tools.ts +10 -18
- package/src/cursor-native-tool-names.ts +16 -0
- package/src/cursor-pi-tool-bridge-env.ts +12 -0
- package/src/cursor-pi-tool-bridge-mcp.ts +16 -21
- package/src/cursor-pi-tool-bridge-run.ts +5 -5
- package/src/cursor-pi-tool-bridge-server.ts +8 -3
- package/src/cursor-pi-tool-bridge-snapshot.ts +7 -13
- package/src/cursor-pi-tool-bridge.ts +7 -7
- package/src/cursor-provider-lazy.ts +51 -0
- package/src/cursor-provider-live-run-drain.ts +1 -1
- package/src/cursor-provider-run-finalizer.ts +5 -5
- package/src/cursor-provider-run-outcome.ts +0 -1
- package/src/cursor-provider-turn-coordinator.ts +4 -5
- package/src/cursor-provider-turn-display-router.ts +5 -1
- package/src/cursor-provider-turn-emit.ts +1 -1
- package/src/cursor-provider-turn-lifecycle-emitter.ts +1 -5
- package/src/cursor-provider-turn-prepare.ts +13 -9
- package/src/cursor-provider-turn-runner.ts +3 -11
- package/src/cursor-provider-turn-sdk-normalizer.ts +28 -5
- package/src/cursor-provider-turn-send.ts +7 -2
- package/src/cursor-provider-turn-types.ts +1 -3
- package/src/cursor-provider.ts +3 -2
- package/src/cursor-question-tool.ts +5 -18
- package/src/cursor-record-utils.ts +42 -0
- package/src/cursor-replay-activity-builders.ts +16 -122
- package/src/cursor-replay-tool-details.ts +57 -197
- package/src/cursor-sdk-event-debug.ts +6 -6
- package/src/cursor-sensitive-text.ts +4 -4
- package/src/cursor-session-agent-lifecycle.ts +47 -0
- package/src/cursor-session-agent.ts +9 -47
- package/src/cursor-session-scope.ts +23 -4
- package/src/cursor-setting-sources.ts +8 -8
- package/src/cursor-skill-tool.ts +25 -32
- package/src/cursor-state.ts +66 -45
- package/src/cursor-tool-lifecycle.ts +16 -9
- package/src/cursor-tool-presentation-registry.ts +42 -169
- package/src/cursor-tool-result-display-readers.ts +185 -0
- package/src/cursor-tool-transcript.ts +17 -33
- package/src/cursor-tool-visibility.ts +9 -1
- package/src/cursor-transcript-tool-formatters.ts +23 -172
- package/src/cursor-transcript-tool-specs.ts +17 -57
- package/src/cursor-transcript-utils.ts +2 -34
- package/src/cursor-usage-accounting.ts +0 -6
- package/src/cursor-web-tool-activity.ts +4 -12
- package/src/cursor-web-tool-args.ts +1 -9
- package/src/index.ts +15 -16
- package/src/model-discovery.ts +5 -4
- package/src/model-list-cache.ts +37 -38
- package/src/cursor-native-tool-display.ts +0 -10
- package/src/cursor-provider-turn-api-key.ts +0 -1
- package/src/cursor-provider-turn-message-offset.ts +0 -15
- package/src/cursor-session-cwd.ts +0 -28
- package/src/cursor-tool-names.ts +0 -18
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { asRecord, getBoolean, getNumber, getString } from "./cursor-record-utils.js";
|
|
1
2
|
import { isCursorReplayActivitySourceName, type CursorReplayActivitySourceName } from "./cursor-replay-source-names.js";
|
|
2
3
|
|
|
3
4
|
/** Replay detail variants keyed by replay card disposition, not SDK source tool alone. */
|
|
@@ -55,8 +56,6 @@ export interface CursorReplayGenerateImageDetails {
|
|
|
55
56
|
imageMimeType?: string;
|
|
56
57
|
summary?: string;
|
|
57
58
|
expandedText?: string;
|
|
58
|
-
/** Legacy parsed title retained on older payloads; display always uses `Cursor generateImage`. */
|
|
59
|
-
title?: string;
|
|
60
59
|
collapseDetailsByDefault?: boolean;
|
|
61
60
|
}
|
|
62
61
|
|
|
@@ -79,7 +78,7 @@ export interface CursorReplayActivityDetails {
|
|
|
79
78
|
fileContentAfterWrite?: string;
|
|
80
79
|
}
|
|
81
80
|
|
|
82
|
-
/** Parsed replay details without a display title
|
|
81
|
+
/** Parsed replay details without a display title. */
|
|
83
82
|
export interface CursorReplayGenericFallbackDetails {
|
|
84
83
|
variant: "genericFallback";
|
|
85
84
|
sourceToolName: CursorReplayUnknownSourceToolName;
|
|
@@ -94,21 +93,6 @@ export type CursorReplayToolDetails =
|
|
|
94
93
|
| CursorReplayActivityDetails
|
|
95
94
|
| CursorReplayGenericFallbackDetails;
|
|
96
95
|
|
|
97
|
-
/** @deprecated Use {@link CursorReplayNativeEditDetails}. */
|
|
98
|
-
export type CursorReplayEditDetails = CursorReplayNativeEditDetails;
|
|
99
|
-
|
|
100
|
-
/** @deprecated Use {@link CursorReplayNativeWriteDetails}. */
|
|
101
|
-
export type CursorReplayWriteDetails = CursorReplayNativeWriteDetails;
|
|
102
|
-
|
|
103
|
-
/** @deprecated Use {@link CursorReplayActivityDetails}. */
|
|
104
|
-
export type CursorReplayTitledActivityDetails = CursorReplayActivityDetails;
|
|
105
|
-
|
|
106
|
-
/** @deprecated Use {@link CursorReplayActivitySourceToolName}. */
|
|
107
|
-
export type CursorReplayActivityCursorToolName = CursorReplayActivitySourceToolName;
|
|
108
|
-
|
|
109
|
-
/** @deprecated Use {@link CursorReplayUnknownSourceToolName}. */
|
|
110
|
-
export type CursorReplayUnknownCursorToolName = CursorReplayUnknownSourceToolName;
|
|
111
|
-
|
|
112
96
|
export type CursorReplayActivityDetailFields = Pick<
|
|
113
97
|
CursorReplayActivityDetails,
|
|
114
98
|
| "summary"
|
|
@@ -128,79 +112,51 @@ export type CursorReplayGenerateImageDetailFields = Pick<
|
|
|
128
112
|
"summary" | "expandedText" | "imagePath" | "imageDisplayPath" | "imageMimeType"
|
|
129
113
|
>;
|
|
130
114
|
|
|
131
|
-
function
|
|
132
|
-
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
function readOptionalString(record: Record<string, unknown>, key: string): string | undefined {
|
|
136
|
-
const value = record[key];
|
|
137
|
-
return typeof value === "string" ? value : undefined;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
function readOptionalNumber(record: Record<string, unknown>, key: string): number | undefined {
|
|
141
|
-
const value = record[key];
|
|
142
|
-
return typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
function readOptionalBoolean(record: Record<string, unknown>, key: string): boolean | undefined {
|
|
146
|
-
const value = record[key];
|
|
147
|
-
return typeof value === "boolean" ? value : undefined;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
function readCurrentSourceToolName(record: Record<string, unknown>): string | undefined {
|
|
151
|
-
const sourceToolName = readOptionalString(record, "sourceToolName");
|
|
115
|
+
function readSourceToolName(record: Record<string, unknown>): string | undefined {
|
|
116
|
+
const sourceToolName = getString(record, "sourceToolName");
|
|
152
117
|
return sourceToolName?.trim() ? sourceToolName.trim() : undefined;
|
|
153
118
|
}
|
|
154
119
|
|
|
155
|
-
function
|
|
156
|
-
const
|
|
157
|
-
if (sourceToolName) return sourceToolName;
|
|
158
|
-
const cursorToolName = readOptionalString(record, "cursorToolName");
|
|
159
|
-
return cursorToolName?.trim() ? cursorToolName.trim() : undefined;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
function readLegacyVariant(record: Record<string, unknown>): string | undefined {
|
|
163
|
-
const variant = readOptionalString(record, "variant");
|
|
120
|
+
function readVariant(record: Record<string, unknown>): string | undefined {
|
|
121
|
+
const variant = getString(record, "variant");
|
|
164
122
|
return variant?.trim() ? variant.trim() : undefined;
|
|
165
123
|
}
|
|
166
124
|
|
|
167
125
|
function parseCursorReplayNativeEditDetails(record: Record<string, unknown>): CursorReplayNativeEditDetails {
|
|
168
126
|
return {
|
|
169
127
|
variant: "nativeEdit",
|
|
170
|
-
path:
|
|
171
|
-
linesAdded:
|
|
172
|
-
linesRemoved:
|
|
173
|
-
diffString:
|
|
174
|
-
diff:
|
|
175
|
-
firstChangedLine:
|
|
176
|
-
summary:
|
|
177
|
-
expandedText:
|
|
128
|
+
path: getString(record, "path"),
|
|
129
|
+
linesAdded: getNumber(record, "linesAdded"),
|
|
130
|
+
linesRemoved: getNumber(record, "linesRemoved"),
|
|
131
|
+
diffString: getString(record, "diffString"),
|
|
132
|
+
diff: getString(record, "diff"),
|
|
133
|
+
firstChangedLine: getNumber(record, "firstChangedLine"),
|
|
134
|
+
summary: getString(record, "summary"),
|
|
135
|
+
expandedText: getString(record, "expandedText"),
|
|
178
136
|
};
|
|
179
137
|
}
|
|
180
138
|
|
|
181
139
|
function parseCursorReplayNativeWriteDetails(record: Record<string, unknown>): CursorReplayNativeWriteDetails {
|
|
182
140
|
return {
|
|
183
141
|
variant: "nativeWrite",
|
|
184
|
-
path:
|
|
185
|
-
linesCreated:
|
|
186
|
-
fileSize:
|
|
187
|
-
fileContentAfterWrite:
|
|
188
|
-
expandedText:
|
|
189
|
-
summary:
|
|
142
|
+
path: getString(record, "path"),
|
|
143
|
+
linesCreated: getNumber(record, "linesCreated"),
|
|
144
|
+
fileSize: getNumber(record, "fileSize"),
|
|
145
|
+
fileContentAfterWrite: getString(record, "fileContentAfterWrite"),
|
|
146
|
+
expandedText: getString(record, "expandedText"),
|
|
147
|
+
summary: getString(record, "summary"),
|
|
190
148
|
};
|
|
191
149
|
}
|
|
192
150
|
|
|
193
151
|
function parseCursorReplayGenerateImageDetails(record: Record<string, unknown>): CursorReplayGenerateImageDetails {
|
|
194
|
-
const
|
|
195
|
-
const collapseDetailsByDefault = readOptionalBoolean(record, "collapseDetailsByDefault");
|
|
152
|
+
const collapseDetailsByDefault = getBoolean(record, "collapseDetailsByDefault");
|
|
196
153
|
return {
|
|
197
154
|
variant: "generateImage",
|
|
198
|
-
imagePath:
|
|
199
|
-
imageDisplayPath:
|
|
200
|
-
imageMimeType:
|
|
201
|
-
summary:
|
|
202
|
-
expandedText:
|
|
203
|
-
...(title !== undefined ? { title } : {}),
|
|
155
|
+
imagePath: getString(record, "imagePath"),
|
|
156
|
+
imageDisplayPath: getString(record, "imageDisplayPath"),
|
|
157
|
+
imageMimeType: getString(record, "imageMimeType"),
|
|
158
|
+
summary: getString(record, "summary"),
|
|
159
|
+
expandedText: getString(record, "expandedText"),
|
|
204
160
|
...(collapseDetailsByDefault !== undefined ? { collapseDetailsByDefault } : {}),
|
|
205
161
|
};
|
|
206
162
|
}
|
|
@@ -214,16 +170,16 @@ function parseCursorReplayActivityDetails(
|
|
|
214
170
|
variant: "activity",
|
|
215
171
|
sourceToolName,
|
|
216
172
|
title,
|
|
217
|
-
summary:
|
|
218
|
-
expandedText:
|
|
219
|
-
collapseDetailsByDefault:
|
|
220
|
-
path:
|
|
221
|
-
fileSize:
|
|
222
|
-
diffString:
|
|
223
|
-
diff:
|
|
224
|
-
linesAdded:
|
|
225
|
-
linesRemoved:
|
|
226
|
-
fileContentAfterWrite:
|
|
173
|
+
summary: getString(record, "summary"),
|
|
174
|
+
expandedText: getString(record, "expandedText"),
|
|
175
|
+
collapseDetailsByDefault: getBoolean(record, "collapseDetailsByDefault"),
|
|
176
|
+
path: getString(record, "path"),
|
|
177
|
+
fileSize: getNumber(record, "fileSize"),
|
|
178
|
+
diffString: getString(record, "diffString"),
|
|
179
|
+
diff: getString(record, "diff"),
|
|
180
|
+
linesAdded: getNumber(record, "linesAdded"),
|
|
181
|
+
linesRemoved: getNumber(record, "linesRemoved"),
|
|
182
|
+
fileContentAfterWrite: getString(record, "fileContentAfterWrite"),
|
|
227
183
|
};
|
|
228
184
|
}
|
|
229
185
|
|
|
@@ -238,8 +194,8 @@ function parseCursorReplayGenericFallbackDetails(
|
|
|
238
194
|
return {
|
|
239
195
|
variant: "genericFallback",
|
|
240
196
|
sourceToolName: brandCursorReplayUnknownSourceToolName(sourceToolName),
|
|
241
|
-
summary:
|
|
242
|
-
expandedText:
|
|
197
|
+
summary: getString(record, "summary"),
|
|
198
|
+
expandedText: getString(record, "expandedText"),
|
|
243
199
|
};
|
|
244
200
|
}
|
|
245
201
|
|
|
@@ -263,38 +219,8 @@ export function resolveIncompleteReplayActivitySourceToolName(
|
|
|
263
219
|
return resolveParseActivitySourceToolName(sourceToolName);
|
|
264
220
|
}
|
|
265
221
|
|
|
266
|
-
function
|
|
267
|
-
|
|
268
|
-
readOptionalString(record, "diffString")?.trim()
|
|
269
|
-
|| readOptionalString(record, "diff")?.trim()
|
|
270
|
-
|| readOptionalNumber(record, "linesAdded")
|
|
271
|
-
|| readOptionalNumber(record, "linesRemoved"),
|
|
272
|
-
);
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
function parseLegacyEditDetails(record: Record<string, unknown>): CursorReplayToolDetails {
|
|
276
|
-
const title = readOptionalString(record, "title")?.trim();
|
|
277
|
-
if (title) {
|
|
278
|
-
return parseCursorReplayActivityDetails(record, resolveParseActivitySourceToolName("edit"), title);
|
|
279
|
-
}
|
|
280
|
-
return parseCursorReplayNativeEditDetails(record);
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
function parseLegacyWriteDetails(record: Record<string, unknown>): CursorReplayToolDetails {
|
|
284
|
-
const title = readOptionalString(record, "title")?.trim();
|
|
285
|
-
if (title) {
|
|
286
|
-
return parseCursorReplayActivityDetails(record, resolveParseActivitySourceToolName("write"), title);
|
|
287
|
-
}
|
|
288
|
-
return parseCursorReplayNativeWriteDetails(record);
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
type CursorReplayVariantParser = (record: Record<string, unknown>) => CursorReplayToolDetails | undefined;
|
|
292
|
-
|
|
293
|
-
function parseActivityVariantDetails(
|
|
294
|
-
record: Record<string, unknown>,
|
|
295
|
-
readSourceToolName: (record: Record<string, unknown>) => string | undefined,
|
|
296
|
-
): CursorReplayActivityDetails | undefined {
|
|
297
|
-
const title = readOptionalString(record, "title")?.trim();
|
|
222
|
+
function parseActivityVariantDetails(record: Record<string, unknown>): CursorReplayActivityDetails | undefined {
|
|
223
|
+
const title = getString(record, "title")?.trim();
|
|
298
224
|
if (!title) return undefined;
|
|
299
225
|
return parseCursorReplayActivityDetails(
|
|
300
226
|
record,
|
|
@@ -303,79 +229,25 @@ function parseActivityVariantDetails(
|
|
|
303
229
|
);
|
|
304
230
|
}
|
|
305
231
|
|
|
306
|
-
const CURRENT_REPLAY_VARIANT_PARSERS: Readonly<Record<CursorReplayToolDetailsVariant, CursorReplayVariantParser>> = {
|
|
307
|
-
nativeEdit: parseCursorReplayNativeEditDetails,
|
|
308
|
-
nativeWrite: parseCursorReplayNativeWriteDetails,
|
|
309
|
-
generateImage: parseCursorReplayGenerateImageDetails,
|
|
310
|
-
activity: (record) => {
|
|
311
|
-
if (!readCurrentSourceToolName(record) && readOptionalString(record, "cursorToolName")?.trim()) return undefined;
|
|
312
|
-
return parseActivityVariantDetails(record, readCurrentSourceToolName);
|
|
313
|
-
},
|
|
314
|
-
genericFallback: (record) => parseCursorReplayGenericFallbackDetails(record, readCurrentSourceToolName(record) ?? "tool"),
|
|
315
|
-
};
|
|
316
|
-
|
|
317
|
-
const LEGACY_REPLAY_VARIANT_UPGRADERS: Readonly<Record<string, CursorReplayVariantParser>> = {
|
|
318
|
-
edit: parseLegacyEditDetails,
|
|
319
|
-
write: parseLegacyWriteDetails,
|
|
320
|
-
titledActivity: (record) => parseActivityVariantDetails(record, readLegacySourceToolName),
|
|
321
|
-
};
|
|
322
|
-
|
|
323
|
-
export function parseStrictCurrentCursorReplayToolDetails(value: unknown): CursorReplayToolDetails | undefined {
|
|
324
|
-
if (!isRecord(value)) return undefined;
|
|
325
|
-
const variant = readLegacyVariant(value);
|
|
326
|
-
if (!variant) return undefined;
|
|
327
|
-
return CURRENT_REPLAY_VARIANT_PARSERS[variant as CursorReplayToolDetailsVariant]?.(value);
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
export function upgradeLegacyCursorReplayToolDetails(value: unknown): CursorReplayToolDetails | undefined {
|
|
331
|
-
if (!isRecord(value)) return undefined;
|
|
332
|
-
const explicitVariant = readLegacyVariant(value);
|
|
333
|
-
if (explicitVariant) {
|
|
334
|
-
return LEGACY_REPLAY_VARIANT_UPGRADERS[explicitVariant]?.(value);
|
|
335
|
-
}
|
|
336
|
-
const sourceToolName = readLegacySourceToolName(value);
|
|
337
|
-
if (sourceToolName === "edit") return parseLegacyEditDetails(value);
|
|
338
|
-
if (sourceToolName === "write") return parseLegacyWriteDetails(value);
|
|
339
|
-
if (sourceToolName === "generateImage") return parseCursorReplayGenerateImageDetails(value);
|
|
340
|
-
const title = readOptionalString(value, "title")?.trim();
|
|
341
|
-
if (title) {
|
|
342
|
-
return parseCursorReplayActivityDetails(
|
|
343
|
-
value,
|
|
344
|
-
resolveParseActivitySourceToolName(sourceToolName ?? CURSOR_REPLAY_UNREGISTERED_ACTIVITY_TOOL_NAME),
|
|
345
|
-
title,
|
|
346
|
-
);
|
|
347
|
-
}
|
|
348
|
-
if (sourceToolName === undefined && hasNativeEditChanges(value)) {
|
|
349
|
-
return parseCursorReplayNativeEditDetails(value);
|
|
350
|
-
}
|
|
351
|
-
return undefined;
|
|
352
|
-
}
|
|
353
|
-
|
|
354
232
|
export function parseCursorReplayToolDetails(value: unknown): CursorReplayToolDetails | undefined {
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
)
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
fields: Omit<CursorReplayNativeWriteDetails, "variant">,
|
|
372
|
-
): CursorReplayNativeWriteDetails {
|
|
373
|
-
return { variant: "nativeWrite", ...fields };
|
|
233
|
+
const record = asRecord(value);
|
|
234
|
+
if (!record) return undefined;
|
|
235
|
+
switch (readVariant(record)) {
|
|
236
|
+
case "nativeEdit":
|
|
237
|
+
return parseCursorReplayNativeEditDetails(record);
|
|
238
|
+
case "nativeWrite":
|
|
239
|
+
return parseCursorReplayNativeWriteDetails(record);
|
|
240
|
+
case "generateImage":
|
|
241
|
+
return parseCursorReplayGenerateImageDetails(record);
|
|
242
|
+
case "activity":
|
|
243
|
+
return parseActivityVariantDetails(record);
|
|
244
|
+
case "genericFallback":
|
|
245
|
+
return parseCursorReplayGenericFallbackDetails(record, readSourceToolName(record) ?? "tool");
|
|
246
|
+
default:
|
|
247
|
+
return undefined;
|
|
248
|
+
}
|
|
374
249
|
}
|
|
375
250
|
|
|
376
|
-
/** @deprecated Prefer {@link buildCursorReplayNativeWriteDetails}. */
|
|
377
|
-
export const buildCursorReplayWriteDetails = buildCursorReplayNativeWriteDetails;
|
|
378
|
-
|
|
379
251
|
export function assembleCursorReplayActivityDetails(
|
|
380
252
|
sourceToolName: CursorReplayActivitySourceToolName,
|
|
381
253
|
title: string,
|
|
@@ -402,10 +274,7 @@ export function assembleCursorReplayActivityDetails(
|
|
|
402
274
|
};
|
|
403
275
|
}
|
|
404
276
|
|
|
405
|
-
|
|
406
|
-
export const assembleCursorReplayTitledActivityDetails = assembleCursorReplayActivityDetails;
|
|
407
|
-
|
|
408
|
-
export const CURSOR_REPLAY_GENERATE_IMAGE_RESULT_TITLE = "Cursor generateImage" as const;
|
|
277
|
+
export const CURSOR_REPLAY_GENERATE_IMAGE_RESULT_TITLE = "Cursor image generation" as const;
|
|
409
278
|
|
|
410
279
|
export function assembleCursorReplayGenerateImageDetails(
|
|
411
280
|
fields: CursorReplayGenerateImageDetailFields,
|
|
@@ -430,18 +299,12 @@ export function isCursorReplayNativeEditDetails(
|
|
|
430
299
|
return details.variant === "nativeEdit";
|
|
431
300
|
}
|
|
432
301
|
|
|
433
|
-
/** @deprecated Prefer {@link isCursorReplayNativeEditDetails}. */
|
|
434
|
-
export const isCursorReplayEditDetails = isCursorReplayNativeEditDetails;
|
|
435
|
-
|
|
436
302
|
export function isCursorReplayNativeWriteDetails(
|
|
437
303
|
details: CursorReplayToolDetails,
|
|
438
304
|
): details is CursorReplayNativeWriteDetails {
|
|
439
305
|
return details.variant === "nativeWrite";
|
|
440
306
|
}
|
|
441
307
|
|
|
442
|
-
/** @deprecated Prefer {@link isCursorReplayNativeWriteDetails}. */
|
|
443
|
-
export const isCursorReplayWriteDetails = isCursorReplayNativeWriteDetails;
|
|
444
|
-
|
|
445
308
|
export function isCursorReplayGenerateImageDetails(
|
|
446
309
|
details: CursorReplayToolDetails,
|
|
447
310
|
): details is CursorReplayGenerateImageDetails {
|
|
@@ -454,9 +317,6 @@ export function isCursorReplayActivityDetails(
|
|
|
454
317
|
return details.variant === "activity";
|
|
455
318
|
}
|
|
456
319
|
|
|
457
|
-
/** @deprecated Prefer {@link isCursorReplayActivityDetails}. */
|
|
458
|
-
export const isCursorReplayTitledActivityDetails = isCursorReplayActivityDetails;
|
|
459
|
-
|
|
460
320
|
export function isCursorReplayGenericFallbackDetails(
|
|
461
321
|
details: CursorReplayToolDetails,
|
|
462
322
|
): details is CursorReplayGenericFallbackDetails {
|
|
@@ -7,6 +7,7 @@ import type { CursorPiToolBridgeDiagnosticEvent } from "./cursor-pi-tool-bridge-
|
|
|
7
7
|
import { serializeCursorPiToolBridgeDiagnostic } from "./cursor-pi-tool-bridge-diagnostics.js";
|
|
8
8
|
import type { CursorPiBridgeToolRequest } from "./cursor-pi-tool-bridge-types.js";
|
|
9
9
|
import type { CursorLiveQueuedEvent } from "./cursor-live-run-coordinator.js";
|
|
10
|
+
import { asRecord } from "./cursor-record-utils.js";
|
|
10
11
|
import { getCursorSessionFile } from "./cursor-session-scope.js";
|
|
11
12
|
import { parseEnvBoolean } from "./cursor-env-boolean.js";
|
|
12
13
|
import {
|
|
@@ -93,11 +94,10 @@ interface CursorSdkRunLike {
|
|
|
93
94
|
}
|
|
94
95
|
|
|
95
96
|
function eventType(value: unknown): string {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
}
|
|
97
|
+
const record = asRecord(value);
|
|
98
|
+
if (typeof record?.type === "string") return record.type;
|
|
99
|
+
if (typeof record?.event === "string") return record.event;
|
|
100
|
+
if (typeof record?.kind === "string") return record.kind;
|
|
101
101
|
return "unknown";
|
|
102
102
|
}
|
|
103
103
|
|
|
@@ -106,7 +106,7 @@ function resolveCursorSdkEventDebugStderrEnabled(env: Record<string, string | un
|
|
|
106
106
|
}
|
|
107
107
|
|
|
108
108
|
function isNodeErrorWithCode(error: unknown, code: string): boolean {
|
|
109
|
-
return
|
|
109
|
+
return asRecord(error)?.code === code;
|
|
110
110
|
}
|
|
111
111
|
|
|
112
112
|
function snapshotCursorSdkEventDebugRecord(record: unknown): unknown {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { asRecord } from "./cursor-record-utils.js";
|
|
1
2
|
import type { CursorPiToolDisplay } from "./cursor-transcript-utils.js";
|
|
2
3
|
/** Provider-facing wrapper; canonical scrubbing lives in shared/cursor-sensitive-text.mjs. */
|
|
3
4
|
import { scrubSensitiveText as scrubSensitiveTextJs } from "../shared/cursor-sensitive-text.mjs";
|
|
@@ -9,10 +10,9 @@ export function scrubSensitiveText(text: string, apiKey?: string): string {
|
|
|
9
10
|
function scrubDisplayValue(value: unknown, apiKey?: string): unknown {
|
|
10
11
|
if (typeof value === "string") return scrubSensitiveText(value, apiKey);
|
|
11
12
|
if (Array.isArray(value)) return value.map((entry) => scrubDisplayValue(entry, apiKey));
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
return value;
|
|
13
|
+
const record = asRecord(value);
|
|
14
|
+
if (!record) return value;
|
|
15
|
+
return Object.fromEntries(Object.entries(record).map(([key, entry]) => [key, scrubDisplayValue(entry, apiKey)]));
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
export function scrubPiToolDisplay(display: CursorPiToolDisplay, apiKey?: string): CursorPiToolDisplay {
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ExtensionHandler,
|
|
3
|
+
SessionBeforeTreeEvent,
|
|
4
|
+
SessionCompactEvent,
|
|
5
|
+
SessionShutdownEvent,
|
|
6
|
+
SessionTreeEvent,
|
|
7
|
+
} from "@earendil-works/pi-coding-agent";
|
|
8
|
+
import { onCursorSessionScopeKeyChange } from "./cursor-session-scope.js";
|
|
9
|
+
|
|
10
|
+
export interface CursorSessionAgentLifecycleExtensionApi {
|
|
11
|
+
on(event: "session_shutdown", handler: ExtensionHandler<SessionShutdownEvent>): void;
|
|
12
|
+
on(event: "session_compact", handler: ExtensionHandler<SessionCompactEvent>): void;
|
|
13
|
+
on(event: "session_before_tree", handler: ExtensionHandler<SessionBeforeTreeEvent>): void;
|
|
14
|
+
on(event: "session_tree", handler: ExtensionHandler<SessionTreeEvent>): void;
|
|
15
|
+
on(event: "model_select", handler: () => Promise<void> | void): void;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function registerCursorSessionAgentLifecycle(pi: CursorSessionAgentLifecycleExtensionApi): void {
|
|
19
|
+
onCursorSessionScopeKeyChange(async (previousScopeKey) => {
|
|
20
|
+
const { disposeSessionCursorAgent } = await import("./cursor-session-agent.js");
|
|
21
|
+
await disposeSessionCursorAgent(previousScopeKey);
|
|
22
|
+
});
|
|
23
|
+
pi.on("session_shutdown", async (event) => {
|
|
24
|
+
const { disposeSessionCursorAgent, resetSessionCursorAgent } = await import("./cursor-session-agent.js");
|
|
25
|
+
if (event.reason === "reload") {
|
|
26
|
+
await resetSessionCursorAgent();
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
await disposeSessionCursorAgent();
|
|
30
|
+
});
|
|
31
|
+
pi.on("session_compact", async () => {
|
|
32
|
+
const { invalidateSessionAgent } = await import("./cursor-session-agent.js");
|
|
33
|
+
invalidateSessionAgent();
|
|
34
|
+
});
|
|
35
|
+
pi.on("session_before_tree", async () => {
|
|
36
|
+
const { invalidateSessionAgent } = await import("./cursor-session-agent.js");
|
|
37
|
+
invalidateSessionAgent();
|
|
38
|
+
});
|
|
39
|
+
pi.on("session_tree", async () => {
|
|
40
|
+
const { resetSessionCursorAgent } = await import("./cursor-session-agent.js");
|
|
41
|
+
await resetSessionCursorAgent();
|
|
42
|
+
});
|
|
43
|
+
pi.on("model_select", async () => {
|
|
44
|
+
const { invalidateSessionAgent } = await import("./cursor-session-agent.js");
|
|
45
|
+
invalidateSessionAgent();
|
|
46
|
+
});
|
|
47
|
+
}
|
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
ExtensionHandler,
|
|
3
|
-
SessionBeforeTreeEvent,
|
|
4
|
-
SessionCompactEvent,
|
|
5
|
-
SessionShutdownEvent,
|
|
6
|
-
SessionTreeEvent,
|
|
7
|
-
} from "@earendil-works/pi-coding-agent";
|
|
8
1
|
import { createHash } from "node:crypto";
|
|
9
2
|
import type { AgentModeOption, ModelSelection, SDKAgent, SettingSource } from "@cursor/sdk";
|
|
10
3
|
import type { Context } from "@earendil-works/pi-ai";
|
|
@@ -14,7 +7,7 @@ import {
|
|
|
14
7
|
type CursorPiToolBridgeRun,
|
|
15
8
|
} from "./cursor-pi-tool-bridge.js";
|
|
16
9
|
import { computeCursorContextFingerprint } from "./context.js";
|
|
17
|
-
import {
|
|
10
|
+
import { getCursorSessionScopeGeneration, getCursorSessionScopeKey } from "./cursor-session-scope.js";
|
|
18
11
|
import type { CursorSdkEventDebugRecorder } from "./cursor-sdk-event-debug.js";
|
|
19
12
|
import { loadCursorSdk, type CursorSdkModule } from "./cursor-sdk-runtime.js";
|
|
20
13
|
|
|
@@ -88,9 +81,12 @@ export class SessionCursorAgentScopeClosedError extends Error {
|
|
|
88
81
|
}
|
|
89
82
|
|
|
90
83
|
function assertScopeAcceptsAcquire(scopeKey: string): void {
|
|
91
|
-
|
|
84
|
+
const terminalGeneration = terminalDisposedScopeGenerations.get(scopeKey);
|
|
85
|
+
if (terminalGeneration === undefined) return;
|
|
86
|
+
if (terminalGeneration >= getCursorSessionScopeGeneration(scopeKey)) {
|
|
92
87
|
throw new SessionCursorAgentScopeClosedError();
|
|
93
88
|
}
|
|
89
|
+
terminalDisposedScopeGenerations.delete(scopeKey);
|
|
94
90
|
}
|
|
95
91
|
|
|
96
92
|
function rethrowSupersededWhenReplacedByDifferentPoolKey(scopeKey: string, poolKey: string, error: unknown): void {
|
|
@@ -112,17 +108,9 @@ interface SessionCursorAgentCreateParams {
|
|
|
112
108
|
createAgent?: CursorSdkModule["Agent"]["create"];
|
|
113
109
|
}
|
|
114
110
|
|
|
115
|
-
interface CursorSessionAgentExtensionApi {
|
|
116
|
-
on(event: "session_shutdown", handler: ExtensionHandler<SessionShutdownEvent>): void;
|
|
117
|
-
on(event: "session_compact", handler: ExtensionHandler<SessionCompactEvent>): void;
|
|
118
|
-
on(event: "session_before_tree", handler: ExtensionHandler<SessionBeforeTreeEvent>): void;
|
|
119
|
-
on(event: "session_tree", handler: ExtensionHandler<SessionTreeEvent>): void;
|
|
120
|
-
on(event: "model_select", handler: ExtensionHandler<{ model: unknown }>): void;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
111
|
const sessionAgentsByScope = new Map<string, SessionCursorAgentPoolEntry>();
|
|
124
112
|
const invalidatedScopeKeys = new Set<string>();
|
|
125
|
-
const
|
|
113
|
+
const terminalDisposedScopeGenerations = new Map<string, number>();
|
|
126
114
|
const scopeCreationGenerations = new Map<string, number>();
|
|
127
115
|
const EMPTY_POOL_STATE: SessionCursorAgentPoolState = { status: "empty" };
|
|
128
116
|
let nextSessionAgentInstanceId = 1;
|
|
@@ -194,7 +182,7 @@ async function disposePoolEntry(entry: SessionCursorAgentPoolEntry): Promise<voi
|
|
|
194
182
|
async function disposePoolEntryForScope(scopeKey: string, options?: { terminal?: boolean }): Promise<void> {
|
|
195
183
|
invalidateScopeCreations(scopeKey);
|
|
196
184
|
if (options?.terminal) {
|
|
197
|
-
|
|
185
|
+
terminalDisposedScopeGenerations.set(scopeKey, getCursorSessionScopeGeneration(scopeKey));
|
|
198
186
|
}
|
|
199
187
|
const entry = sessionAgentsByScope.get(scopeKey);
|
|
200
188
|
invalidatedScopeKeys.delete(scopeKey);
|
|
@@ -416,7 +404,6 @@ export {
|
|
|
416
404
|
planCursorSessionSend,
|
|
417
405
|
type CursorSessionSendPlan,
|
|
418
406
|
} from "./cursor-session-send-policy.js";
|
|
419
|
-
export { shouldBootstrapCursorContext, shouldBootstrapCursorSend } from "./context.js";
|
|
420
407
|
|
|
421
408
|
export function invalidateSessionAgent(scopeKey: string = getCursorSessionScopeKey()): void {
|
|
422
409
|
invalidatedScopeKeys.add(scopeKey);
|
|
@@ -526,35 +513,10 @@ export async function disposeSessionCursorAgent(scopeKey: string = getCursorSess
|
|
|
526
513
|
}
|
|
527
514
|
|
|
528
515
|
export async function disposeAllSessionCursorAgents(): Promise<void> {
|
|
529
|
-
const scopeKeys = [...new Set([...sessionAgentsByScope.keys(), ...
|
|
516
|
+
const scopeKeys = [...new Set([...sessionAgentsByScope.keys(), ...terminalDisposedScopeGenerations.keys()])];
|
|
530
517
|
await Promise.all(scopeKeys.map((scopeKey) => disposePoolEntryForScope(scopeKey, { terminal: true })));
|
|
531
518
|
invalidatedScopeKeys.clear();
|
|
532
|
-
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
export function registerCursorSessionAgent(_pi: CursorSessionAgentExtensionApi): void {
|
|
536
|
-
onCursorSessionScopeKeyChange((previousScopeKey) => {
|
|
537
|
-
void disposePoolEntryForScope(previousScopeKey, { terminal: true });
|
|
538
|
-
});
|
|
539
|
-
_pi.on("session_shutdown", async (event) => {
|
|
540
|
-
if (event.reason === "reload") {
|
|
541
|
-
await resetSessionCursorAgent();
|
|
542
|
-
return;
|
|
543
|
-
}
|
|
544
|
-
await disposeSessionCursorAgent();
|
|
545
|
-
});
|
|
546
|
-
_pi.on("session_compact", () => {
|
|
547
|
-
invalidateSessionAgent();
|
|
548
|
-
});
|
|
549
|
-
_pi.on("session_before_tree", () => {
|
|
550
|
-
invalidateSessionAgent();
|
|
551
|
-
});
|
|
552
|
-
_pi.on("session_tree", async () => {
|
|
553
|
-
await resetSessionCursorAgent();
|
|
554
|
-
});
|
|
555
|
-
_pi.on("model_select", () => {
|
|
556
|
-
invalidateSessionAgent();
|
|
557
|
-
});
|
|
519
|
+
terminalDisposedScopeGenerations.clear();
|
|
558
520
|
}
|
|
559
521
|
|
|
560
522
|
export const __testUtils = {
|
|
@@ -7,14 +7,17 @@ interface CursorSessionScopeExtensionApi {
|
|
|
7
7
|
const ANONYMOUS_SESSION_SCOPE_KEY = "__anonymous__";
|
|
8
8
|
const EPHEMERAL_SESSION_SCOPE_PREFIX = "__ephemeral__:";
|
|
9
9
|
|
|
10
|
-
type CursorSessionScopeChangeHandler = (previousScopeKey: string) => void;
|
|
10
|
+
type CursorSessionScopeChangeHandler = (previousScopeKey: string) => Promise<void> | void;
|
|
11
11
|
|
|
12
12
|
const state = {
|
|
13
13
|
sessionCwd: process.cwd(),
|
|
14
14
|
sessionFile: undefined as string | undefined,
|
|
15
15
|
sessionId: undefined as string | undefined,
|
|
16
|
+
sessionGeneration: 0,
|
|
16
17
|
};
|
|
17
18
|
|
|
19
|
+
const scopeGenerations = new Map<string, number>([[ANONYMOUS_SESSION_SCOPE_KEY, state.sessionGeneration]]);
|
|
20
|
+
let nextSessionGeneration = 1;
|
|
18
21
|
let scopeChangeHandler: CursorSessionScopeChangeHandler | undefined;
|
|
19
22
|
|
|
20
23
|
/**
|
|
@@ -34,7 +37,16 @@ export function getCursorSessionScopeKey(): string {
|
|
|
34
37
|
return ANONYMOUS_SESSION_SCOPE_KEY;
|
|
35
38
|
}
|
|
36
39
|
|
|
37
|
-
export function
|
|
40
|
+
export function getCursorSessionScopeGeneration(scopeKey: string = getCursorSessionScopeKey()): number {
|
|
41
|
+
return scopeGenerations.get(scopeKey) ?? 0;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Pi session cwd when known; falls back to process.cwd() before session_start.
|
|
46
|
+
* Updated on session_start only until pi threads cwd into streamSimple—mid-session cwd
|
|
47
|
+
* changes without a new session_start event are not reflected here.
|
|
48
|
+
*/
|
|
49
|
+
export function getCursorSessionCwd(): string {
|
|
38
50
|
return state.sessionCwd;
|
|
39
51
|
}
|
|
40
52
|
|
|
@@ -42,12 +54,19 @@ function setCursorSessionScope(cwd: string, sessionFile: string | undefined, ses
|
|
|
42
54
|
state.sessionCwd = cwd;
|
|
43
55
|
state.sessionFile = sessionFile;
|
|
44
56
|
state.sessionId = sessionId;
|
|
57
|
+
state.sessionGeneration = nextSessionGeneration;
|
|
58
|
+
nextSessionGeneration += 1;
|
|
59
|
+
scopeGenerations.set(getCursorSessionScopeKey(), state.sessionGeneration);
|
|
45
60
|
}
|
|
46
61
|
|
|
47
62
|
function resetCursorSessionScope(): void {
|
|
48
63
|
state.sessionCwd = process.cwd();
|
|
49
64
|
state.sessionFile = undefined;
|
|
50
65
|
state.sessionId = undefined;
|
|
66
|
+
state.sessionGeneration = 0;
|
|
67
|
+
nextSessionGeneration = 1;
|
|
68
|
+
scopeGenerations.clear();
|
|
69
|
+
scopeGenerations.set(ANONYMOUS_SESSION_SCOPE_KEY, state.sessionGeneration);
|
|
51
70
|
}
|
|
52
71
|
|
|
53
72
|
export function onCursorSessionScopeKeyChange(handler: CursorSessionScopeChangeHandler): void {
|
|
@@ -55,7 +74,7 @@ export function onCursorSessionScopeKeyChange(handler: CursorSessionScopeChangeH
|
|
|
55
74
|
}
|
|
56
75
|
|
|
57
76
|
export function registerCursorSessionScope(pi: CursorSessionScopeExtensionApi): void {
|
|
58
|
-
pi.on("session_start", (_event, ctx) => {
|
|
77
|
+
pi.on("session_start", async (_event, ctx) => {
|
|
59
78
|
const previousScopeKey = getCursorSessionScopeKey();
|
|
60
79
|
setCursorSessionScope(
|
|
61
80
|
ctx.cwd,
|
|
@@ -63,7 +82,7 @@ export function registerCursorSessionScope(pi: CursorSessionScopeExtensionApi):
|
|
|
63
82
|
ctx.sessionManager?.getSessionId?.() ?? undefined,
|
|
64
83
|
);
|
|
65
84
|
if (previousScopeKey !== getCursorSessionScopeKey()) {
|
|
66
|
-
scopeChangeHandler?.(previousScopeKey);
|
|
85
|
+
await scopeChangeHandler?.(previousScopeKey);
|
|
67
86
|
}
|
|
68
87
|
});
|
|
69
88
|
}
|
|
@@ -11,16 +11,16 @@ export function resolveCursorSettingSources(raw?: string): SettingSource[] | und
|
|
|
11
11
|
return resolveCursorSettingSourcesJs(raw) as SettingSource[] | undefined;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
export function getEffectiveCursorSettingSources(
|
|
14
|
+
export function getEffectiveCursorSettingSources(
|
|
15
|
+
raw: string | undefined = process.env[CURSOR_SETTING_SOURCES_ENV],
|
|
16
|
+
): SettingSource[] | undefined {
|
|
15
17
|
return resolveCursorSettingSources(raw);
|
|
16
18
|
}
|
|
17
19
|
|
|
18
|
-
export function
|
|
20
|
+
export function cursorSettingSourcesIncludes(
|
|
21
|
+
settingSources: SettingSource[] | undefined,
|
|
22
|
+
source: Extract<SettingSource, "user" | "project">,
|
|
23
|
+
): boolean {
|
|
19
24
|
if (!settingSources?.length) return false;
|
|
20
|
-
return settingSources.includes("all") || settingSources.includes(
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export function cursorSettingSourcesLoadProjectAgentsRules(settingSources: SettingSource[] | undefined): boolean {
|
|
24
|
-
if (!settingSources?.length) return false;
|
|
25
|
-
return settingSources.includes("all") || settingSources.includes("project");
|
|
25
|
+
return settingSources.includes("all") || settingSources.includes(source);
|
|
26
26
|
}
|