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
|
@@ -18,23 +18,15 @@ import {
|
|
|
18
18
|
withActivitySummaryFallback,
|
|
19
19
|
type CursorReplayActivitySummaryOverride,
|
|
20
20
|
type CursorReplayGenerateImageSummaryArgs,
|
|
21
|
-
type CursorReplayPathSummaryArgs,
|
|
22
|
-
type CursorReplayPlanSummaryArgs,
|
|
23
|
-
type CursorReplayReadLintsSummaryArgs,
|
|
24
|
-
type CursorReplayRecordScreenSummaryArgs,
|
|
25
|
-
type CursorReplaySemSearchSummaryArgs,
|
|
26
21
|
type CursorReplaySummaryArgs,
|
|
27
|
-
type CursorReplayTaskSummaryArgs,
|
|
28
|
-
type CursorReplayTodoSummaryArgs,
|
|
29
22
|
type CursorReplayWebFetchSummaryArgs,
|
|
30
23
|
type CursorReplayWebSearchSummaryArgs,
|
|
31
24
|
} from "./cursor-replay-summary-args.js";
|
|
25
|
+
import { truncateCursorDisplayLine } from "./cursor-display-text.js";
|
|
32
26
|
import {
|
|
33
|
-
buildCollapsedReplayDetailFields,
|
|
34
27
|
buildCreatePlanReplaySummaryArgs,
|
|
35
28
|
buildDeleteReplayDetailFields,
|
|
36
29
|
buildDeleteReplaySummaryArgs,
|
|
37
|
-
buildEmptyReplayDetailFields,
|
|
38
30
|
buildGenerateImageReplayDetailFields,
|
|
39
31
|
buildGenerateImageReplaySummaryArgs,
|
|
40
32
|
buildMcpReplaySummaryArgs,
|
|
@@ -55,9 +47,10 @@ import type {
|
|
|
55
47
|
|
|
56
48
|
export const CURSOR_REPLAY_ACTIVITY_TOOL_NAME = "cursor" as const;
|
|
57
49
|
|
|
58
|
-
|
|
50
|
+
const EMPTY_REPLAY_DETAIL_FIELDS = (): Record<string, never> => ({});
|
|
51
|
+
const COLLAPSED_REPLAY_DETAIL_FIELDS = (): { collapseDetailsByDefault: true } => ({ collapseDetailsByDefault: true });
|
|
59
52
|
|
|
60
|
-
export type
|
|
53
|
+
export type CursorWebToolKind = "webSearch" | "webFetch";
|
|
61
54
|
|
|
62
55
|
export type CursorToolLifecycleLabelKind =
|
|
63
56
|
| "task"
|
|
@@ -96,10 +89,6 @@ export interface CursorToolPresentationSpec {
|
|
|
96
89
|
normalizedName: CursorReplaySourceToolName;
|
|
97
90
|
/** Raw SDK/host names that resolve to this tool via {@link normalizeCursorToolName}. */
|
|
98
91
|
nameAliases?: readonly string[];
|
|
99
|
-
replayLegacyName?: string;
|
|
100
|
-
/** Human-readable SDK operation label for replay-only tool descriptions when it differs from {@link normalizedName}. */
|
|
101
|
-
replayOperationLabel?: string;
|
|
102
|
-
promptLabel: string;
|
|
103
92
|
displayLabel: string;
|
|
104
93
|
visibility: CursorToolVisibilityPolicy;
|
|
105
94
|
webKind?: CursorWebToolKind;
|
|
@@ -107,52 +96,43 @@ export interface CursorToolPresentationSpec {
|
|
|
107
96
|
webNamePatterns?: readonly RegExp[];
|
|
108
97
|
lifecycleLabelKind?: CursorToolLifecycleLabelKind;
|
|
109
98
|
replayCallSummary?: CursorReplayCallSummaryBuilder;
|
|
110
|
-
/** Short label for replay-only tool definitions (for example `edit` for `cursor_edit`). */
|
|
111
|
-
replayWrapperLabel?: string;
|
|
112
|
-
/** Whether replay-only wrappers describe file mutations or other recorded tool work. */
|
|
113
|
-
replaySideEffectCategory?: CursorReplaySideEffectCategory;
|
|
114
99
|
activityReplay?: CursorToolActivityReplaySpec;
|
|
115
100
|
generateImageReplay?: CursorToolGenerateImageReplaySpec;
|
|
116
101
|
}
|
|
117
102
|
|
|
118
103
|
const WEB_SEARCH_NAME_PATTERN =
|
|
119
|
-
/^(?:web[-_ ]?search|search[-_ ]?web|websearch|browser[-_ ]?search
|
|
104
|
+
/^(?:web[-_ ]?search|search[-_ ]?web|websearch|browser[-_ ]?search)$/i;
|
|
120
105
|
const WEB_FETCH_NAME_PATTERN =
|
|
121
|
-
/^(?:web[-_ ]?fetch|fetch[-_ ]?web|webfetch|browser[-_ ]?fetch|fetch[-_ ]?url
|
|
106
|
+
/^(?:web[-_ ]?fetch|fetch[-_ ]?web|webfetch|browser[-_ ]?fetch|fetch[-_ ]?url)$/i;
|
|
122
107
|
|
|
123
108
|
export const CURSOR_TOOL_PRESENTATION_SPECS = [
|
|
124
109
|
{
|
|
125
110
|
normalizedName: "read",
|
|
126
111
|
nameAliases: ["read_file"],
|
|
127
|
-
promptLabel: "read",
|
|
128
112
|
displayLabel: "read",
|
|
129
113
|
visibility: { incompleteTitle: "Cursor read", fastLocalDiscovery: true },
|
|
130
114
|
},
|
|
131
115
|
{
|
|
132
116
|
normalizedName: "grep",
|
|
133
117
|
nameAliases: ["grep_search", "search"],
|
|
134
|
-
promptLabel: "grep",
|
|
135
118
|
displayLabel: "grep",
|
|
136
119
|
visibility: { incompleteTitle: "Cursor grep", fastLocalDiscovery: true },
|
|
137
120
|
},
|
|
138
121
|
{
|
|
139
122
|
normalizedName: "glob",
|
|
140
123
|
nameAliases: ["file_search"],
|
|
141
|
-
promptLabel: "glob",
|
|
142
124
|
displayLabel: "glob",
|
|
143
125
|
visibility: { incompleteTitle: "Cursor find", fastLocalDiscovery: true },
|
|
144
126
|
},
|
|
145
127
|
{
|
|
146
128
|
normalizedName: "ls",
|
|
147
129
|
nameAliases: ["list_dir"],
|
|
148
|
-
promptLabel: "ls",
|
|
149
130
|
displayLabel: "ls",
|
|
150
131
|
visibility: { incompleteTitle: "Cursor ls", fastLocalDiscovery: true },
|
|
151
132
|
},
|
|
152
133
|
{
|
|
153
134
|
normalizedName: "shell",
|
|
154
135
|
nameAliases: ["run_terminal_cmd", "terminal", "bash"],
|
|
155
|
-
promptLabel: "shell",
|
|
156
136
|
displayLabel: "shell",
|
|
157
137
|
visibility: {
|
|
158
138
|
incompleteTitle: "Cursor shell",
|
|
@@ -174,29 +154,19 @@ export const CURSOR_TOOL_PRESENTATION_SPECS = [
|
|
|
174
154
|
"notebook_edit",
|
|
175
155
|
"notebookedit",
|
|
176
156
|
],
|
|
177
|
-
replayLegacyName: "cursor_edit",
|
|
178
|
-
promptLabel: "Cursor edit",
|
|
179
157
|
displayLabel: "Cursor edit",
|
|
180
158
|
visibility: {},
|
|
181
|
-
replayWrapperLabel: "edit",
|
|
182
|
-
replaySideEffectCategory: "file_mutations",
|
|
183
159
|
replayCallSummary: withActivitySummaryFallback(summarizeReplayPath),
|
|
184
160
|
},
|
|
185
161
|
{
|
|
186
162
|
normalizedName: "write",
|
|
187
163
|
nameAliases: ["write_file", "writefile"],
|
|
188
|
-
replayLegacyName: "cursor_write",
|
|
189
|
-
promptLabel: "Cursor write",
|
|
190
164
|
displayLabel: "Cursor write",
|
|
191
165
|
visibility: {},
|
|
192
|
-
replayWrapperLabel: "write",
|
|
193
|
-
replaySideEffectCategory: "file_mutations",
|
|
194
166
|
replayCallSummary: withActivitySummaryFallback(summarizeReplayPath),
|
|
195
167
|
},
|
|
196
168
|
{
|
|
197
169
|
normalizedName: "delete",
|
|
198
|
-
replayLegacyName: "cursor_delete",
|
|
199
|
-
promptLabel: "Cursor delete",
|
|
200
170
|
displayLabel: "Cursor delete",
|
|
201
171
|
visibility: {},
|
|
202
172
|
replayCallSummary: withActivitySummaryFallback(summarizeReplayPath),
|
|
@@ -207,59 +177,49 @@ export const CURSOR_TOOL_PRESENTATION_SPECS = [
|
|
|
207
177
|
},
|
|
208
178
|
{
|
|
209
179
|
normalizedName: "readLints",
|
|
210
|
-
replayLegacyName: "cursor_read_lints",
|
|
211
|
-
promptLabel: "Cursor diagnostics",
|
|
212
180
|
displayLabel: "Cursor diagnostics",
|
|
213
181
|
visibility: {},
|
|
214
182
|
replayCallSummary: withActivitySummaryFallback(summarizeReplayReadLints),
|
|
215
183
|
activityReplay: {
|
|
216
184
|
buildActivityArgs: buildReadLintsReplaySummaryArgs,
|
|
217
|
-
buildDetails:
|
|
185
|
+
buildDetails: EMPTY_REPLAY_DETAIL_FIELDS,
|
|
218
186
|
},
|
|
219
187
|
},
|
|
220
188
|
{
|
|
221
189
|
normalizedName: "updateTodos",
|
|
222
|
-
replayLegacyName: "cursor_update_todos",
|
|
223
|
-
promptLabel: "Cursor todos",
|
|
224
190
|
displayLabel: "Cursor todos",
|
|
225
191
|
visibility: { lifecycleEligible: true },
|
|
226
192
|
lifecycleLabelKind: "updateTodos",
|
|
227
193
|
replayCallSummary: withActivitySummaryFallback(summarizeReplayTodoCount),
|
|
228
194
|
activityReplay: {
|
|
229
195
|
buildActivityArgs: ({ args, result }) => buildTodoReplaySummaryArgs(args, result),
|
|
230
|
-
buildDetails:
|
|
196
|
+
buildDetails: EMPTY_REPLAY_DETAIL_FIELDS,
|
|
231
197
|
},
|
|
232
198
|
},
|
|
233
199
|
{
|
|
234
200
|
normalizedName: "createPlan",
|
|
235
|
-
replayLegacyName: "cursor_create_plan",
|
|
236
|
-
promptLabel: "Cursor plan",
|
|
237
201
|
displayLabel: "Cursor plan",
|
|
238
202
|
visibility: { lifecycleEligible: true },
|
|
239
203
|
lifecycleLabelKind: "createPlan",
|
|
240
204
|
replayCallSummary: withActivitySummaryFallback(summarizeReplayPlan),
|
|
241
205
|
activityReplay: {
|
|
242
206
|
buildActivityArgs: buildCreatePlanReplaySummaryArgs,
|
|
243
|
-
buildDetails:
|
|
207
|
+
buildDetails: EMPTY_REPLAY_DETAIL_FIELDS,
|
|
244
208
|
},
|
|
245
209
|
},
|
|
246
210
|
{
|
|
247
211
|
normalizedName: "task",
|
|
248
|
-
replayLegacyName: "cursor_task",
|
|
249
|
-
promptLabel: "Cursor task",
|
|
250
212
|
displayLabel: "Cursor task",
|
|
251
213
|
visibility: { lifecycleEligible: true },
|
|
252
214
|
lifecycleLabelKind: "task",
|
|
253
215
|
replayCallSummary: withActivitySummaryFallback(summarizeReplayTask),
|
|
254
216
|
activityReplay: {
|
|
255
217
|
buildActivityArgs: buildTaskReplaySummaryArgs,
|
|
256
|
-
buildDetails:
|
|
218
|
+
buildDetails: EMPTY_REPLAY_DETAIL_FIELDS,
|
|
257
219
|
},
|
|
258
220
|
},
|
|
259
221
|
{
|
|
260
222
|
normalizedName: "generateImage",
|
|
261
|
-
replayLegacyName: "cursor_generate_image",
|
|
262
|
-
promptLabel: "Cursor image generation",
|
|
263
223
|
displayLabel: "Cursor image generation",
|
|
264
224
|
visibility: { lifecycleEligible: true },
|
|
265
225
|
lifecycleLabelKind: "generateImage",
|
|
@@ -273,50 +233,40 @@ export const CURSOR_TOOL_PRESENTATION_SPECS = [
|
|
|
273
233
|
},
|
|
274
234
|
{
|
|
275
235
|
normalizedName: "mcp",
|
|
276
|
-
replayLegacyName: "cursor_mcp",
|
|
277
|
-
replayOperationLabel: "MCP",
|
|
278
|
-
promptLabel: "Cursor MCP",
|
|
279
236
|
displayLabel: "Cursor MCP",
|
|
280
237
|
visibility: { lifecycleEligible: true },
|
|
281
238
|
lifecycleLabelKind: "mcp",
|
|
282
239
|
replayCallSummary: withActivitySummaryFallback(summarizeReplayMcp),
|
|
283
240
|
activityReplay: {
|
|
284
241
|
buildActivityArgs: buildMcpReplaySummaryArgs,
|
|
285
|
-
buildDetails:
|
|
242
|
+
buildDetails: EMPTY_REPLAY_DETAIL_FIELDS,
|
|
286
243
|
},
|
|
287
244
|
},
|
|
288
245
|
{
|
|
289
246
|
normalizedName: "semSearch",
|
|
290
|
-
replayLegacyName: "cursor_sem_search",
|
|
291
|
-
promptLabel: "Cursor semantic search",
|
|
292
247
|
displayLabel: "Cursor semantic search",
|
|
293
248
|
visibility: { lifecycleEligible: true },
|
|
294
249
|
lifecycleLabelKind: "semSearch",
|
|
295
250
|
replayCallSummary: withActivitySummaryFallback(formatReplaySemSearchQuery),
|
|
296
251
|
activityReplay: {
|
|
297
252
|
buildActivityArgs: buildSemSearchReplaySummaryArgs,
|
|
298
|
-
buildDetails:
|
|
253
|
+
buildDetails: EMPTY_REPLAY_DETAIL_FIELDS,
|
|
299
254
|
},
|
|
300
255
|
},
|
|
301
256
|
{
|
|
302
257
|
normalizedName: "recordScreen",
|
|
303
|
-
replayLegacyName: "cursor_record_screen",
|
|
304
|
-
promptLabel: "Cursor screen recording",
|
|
305
258
|
displayLabel: "Cursor screen recording",
|
|
306
259
|
visibility: { lifecycleEligible: true },
|
|
307
260
|
lifecycleLabelKind: "recordScreen",
|
|
308
261
|
replayCallSummary: withActivitySummaryFallback(summarizeReplayRecordScreen),
|
|
309
262
|
activityReplay: {
|
|
310
263
|
buildActivityArgs: buildRecordScreenReplaySummaryArgs,
|
|
311
|
-
buildDetails:
|
|
264
|
+
buildDetails: EMPTY_REPLAY_DETAIL_FIELDS,
|
|
312
265
|
},
|
|
313
266
|
},
|
|
314
267
|
{
|
|
315
268
|
normalizedName: "webSearch",
|
|
316
269
|
nameAliases: ["websearch", "web_search", "web-search"],
|
|
317
|
-
replayLegacyName: "cursor_web_search",
|
|
318
|
-
replayOperationLabel: "web search",
|
|
319
|
-
promptLabel: "Cursor web search",
|
|
320
270
|
displayLabel: "Cursor web search",
|
|
321
271
|
visibility: { lifecycleEligible: true },
|
|
322
272
|
webKind: "webSearch",
|
|
@@ -327,15 +277,12 @@ export const CURSOR_TOOL_PRESENTATION_SPECS = [
|
|
|
327
277
|
),
|
|
328
278
|
activityReplay: {
|
|
329
279
|
buildActivityArgs: buildWebSearchReplaySummaryArgs,
|
|
330
|
-
buildDetails:
|
|
280
|
+
buildDetails: COLLAPSED_REPLAY_DETAIL_FIELDS,
|
|
331
281
|
},
|
|
332
282
|
},
|
|
333
283
|
{
|
|
334
284
|
normalizedName: "webFetch",
|
|
335
285
|
nameAliases: ["webfetch", "web_fetch", "web-fetch"],
|
|
336
|
-
replayLegacyName: "cursor_web_fetch",
|
|
337
|
-
replayOperationLabel: "web fetch",
|
|
338
|
-
promptLabel: "Cursor web fetch",
|
|
339
286
|
displayLabel: "Cursor web fetch",
|
|
340
287
|
visibility: { lifecycleEligible: true },
|
|
341
288
|
webKind: "webFetch",
|
|
@@ -346,7 +293,7 @@ export const CURSOR_TOOL_PRESENTATION_SPECS = [
|
|
|
346
293
|
),
|
|
347
294
|
activityReplay: {
|
|
348
295
|
buildActivityArgs: buildWebFetchReplaySummaryArgs,
|
|
349
|
-
buildDetails:
|
|
296
|
+
buildDetails: COLLAPSED_REPLAY_DETAIL_FIELDS,
|
|
350
297
|
},
|
|
351
298
|
},
|
|
352
299
|
] as const satisfies readonly CursorToolPresentationSpec[];
|
|
@@ -354,56 +301,19 @@ export const CURSOR_TOOL_PRESENTATION_SPECS = [
|
|
|
354
301
|
type CursorToolPresentationSpecEntry = (typeof CURSOR_TOOL_PRESENTATION_SPECS)[number];
|
|
355
302
|
|
|
356
303
|
export type CursorNormalizedToolName = CursorReplaySourceToolName;
|
|
304
|
+
export type CursorReplayToolName = typeof CURSOR_REPLAY_ACTIVITY_TOOL_NAME;
|
|
357
305
|
|
|
358
|
-
|
|
359
|
-
CursorToolPresentationSpecEntry,
|
|
360
|
-
{ readonly replayLegacyName: string }
|
|
361
|
-
>["replayLegacyName"];
|
|
362
|
-
|
|
363
|
-
export type CursorReplayToolName = typeof CURSOR_REPLAY_ACTIVITY_TOOL_NAME | CursorReplayLegacyToolName;
|
|
364
|
-
|
|
365
|
-
type CursorToolPresentationSpecWithReplayLegacy = Extract<
|
|
306
|
+
type CursorToolPresentationSpecWithNeutralActivity = Extract<
|
|
366
307
|
CursorToolPresentationSpecEntry,
|
|
367
|
-
{ readonly
|
|
368
|
-
>;
|
|
308
|
+
{ readonly activityReplay: CursorToolActivityReplaySpec } | { readonly generateImageReplay: CursorToolGenerateImageReplaySpec }
|
|
309
|
+
> | Extract<CursorToolPresentationSpecEntry, { readonly normalizedName: "edit" | "write" }>;
|
|
369
310
|
|
|
370
|
-
|
|
311
|
+
export type CursorReplayActivityToolName = CursorToolPresentationSpecWithNeutralActivity["normalizedName"];
|
|
371
312
|
|
|
372
|
-
function
|
|
373
|
-
return "
|
|
313
|
+
function hasNeutralActivityTitle(spec: CursorToolPresentationSpec): boolean {
|
|
314
|
+
return Boolean(spec.activityReplay || spec.generateImageReplay || spec.normalizedName === "edit" || spec.normalizedName === "write");
|
|
374
315
|
}
|
|
375
316
|
|
|
376
|
-
type ReplayActivityLabelKeysFromSpecs<Specs extends readonly CursorToolPresentationSpecEntry[]> = {
|
|
377
|
-
[K in Extract<Specs[number], { readonly replayLegacyName: string }>["normalizedName"]]: Extract<
|
|
378
|
-
Specs[number],
|
|
379
|
-
{ readonly replayLegacyName: string; normalizedName: K }
|
|
380
|
-
>["replayLegacyName"];
|
|
381
|
-
};
|
|
382
|
-
|
|
383
|
-
function buildReplayActivityLabelKeysByToolName<const Specs extends readonly CursorToolPresentationSpecEntry[]>(
|
|
384
|
-
specs: Specs,
|
|
385
|
-
): ReplayActivityLabelKeysFromSpecs<Specs> {
|
|
386
|
-
const labelKeys: Record<string, CursorReplayLegacyToolName> = {};
|
|
387
|
-
for (const spec of specs) {
|
|
388
|
-
if (!hasReplayLegacyName(spec)) continue;
|
|
389
|
-
labelKeys[spec.normalizedName] = spec.replayLegacyName;
|
|
390
|
-
}
|
|
391
|
-
return labelKeys as ReplayActivityLabelKeysFromSpecs<Specs>;
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
const CURSOR_REPLAY_SPECS = CURSOR_TOOL_PRESENTATION_SPECS.filter(hasReplayLegacyName);
|
|
395
|
-
|
|
396
|
-
/** Stable registration order for native replay tool wrappers (registry declaration order). */
|
|
397
|
-
export const CURSOR_REPLAY_LEGACY_TOOL_NAMES: readonly CursorReplayLegacyToolName[] = CURSOR_REPLAY_SPECS.map(
|
|
398
|
-
(spec) => spec.replayLegacyName,
|
|
399
|
-
);
|
|
400
|
-
|
|
401
|
-
export const CURSOR_REPLAY_ACTIVITY_LABEL_KEYS_BY_TOOL_NAME = buildReplayActivityLabelKeysByToolName(
|
|
402
|
-
CURSOR_TOOL_PRESENTATION_SPECS,
|
|
403
|
-
);
|
|
404
|
-
|
|
405
|
-
export type CursorReplayActivityToolName = keyof typeof CURSOR_REPLAY_ACTIVITY_LABEL_KEYS_BY_TOOL_NAME;
|
|
406
|
-
|
|
407
317
|
const SPECS_BY_NORMALIZED_NAME = new Map<string, CursorToolPresentationSpec>(
|
|
408
318
|
CURSOR_TOOL_PRESENTATION_SPECS.map((spec) => [spec.normalizedName, spec]),
|
|
409
319
|
);
|
|
@@ -412,10 +322,6 @@ const SPECS_BY_NORMALIZED_KEY = new Map<string, CursorToolPresentationSpec>(
|
|
|
412
322
|
CURSOR_TOOL_PRESENTATION_SPECS.map((spec) => [spec.normalizedName.toLowerCase(), spec]),
|
|
413
323
|
);
|
|
414
324
|
|
|
415
|
-
const SPECS_BY_REPLAY_LEGACY_NAME = new Map<CursorReplayLegacyToolName, CursorToolPresentationSpec>(
|
|
416
|
-
CURSOR_REPLAY_SPECS.map((spec) => [spec.replayLegacyName, spec]),
|
|
417
|
-
);
|
|
418
|
-
|
|
419
325
|
const ALIAS_TO_NORMALIZED_NAME = new Map<string, CursorNormalizedToolName>(
|
|
420
326
|
CURSOR_TOOL_PRESENTATION_SPECS.flatMap((spec) => {
|
|
421
327
|
const aliases = "nameAliases" in spec ? spec.nameAliases : undefined;
|
|
@@ -437,11 +343,7 @@ export function getCursorToolPresentationSpec(
|
|
|
437
343
|
): CursorToolPresentationSpec | undefined {
|
|
438
344
|
const trimmed = name.trim();
|
|
439
345
|
if (!trimmed) return undefined;
|
|
440
|
-
return (
|
|
441
|
-
SPECS_BY_NORMALIZED_NAME.get(trimmed) ??
|
|
442
|
-
SPECS_BY_NORMALIZED_KEY.get(trimmed.toLowerCase()) ??
|
|
443
|
-
(isCursorReplayLegacyToolName(trimmed) ? SPECS_BY_REPLAY_LEGACY_NAME.get(trimmed) : undefined)
|
|
444
|
-
);
|
|
346
|
+
return SPECS_BY_NORMALIZED_NAME.get(trimmed) ?? SPECS_BY_NORMALIZED_KEY.get(trimmed.toLowerCase());
|
|
445
347
|
}
|
|
446
348
|
|
|
447
349
|
export function normalizeCursorToolName(name: string): string {
|
|
@@ -464,65 +366,36 @@ export function classifyCursorWebToolKind(name: string | undefined): CursorWebTo
|
|
|
464
366
|
return spec?.webKind;
|
|
465
367
|
}
|
|
466
368
|
|
|
467
|
-
const CURSOR_REPLAY_LEGACY_TOOL_NAME_SET: ReadonlySet<string> = new Set(CURSOR_REPLAY_LEGACY_TOOL_NAMES);
|
|
468
|
-
|
|
469
|
-
export function isCursorReplayLegacyToolName(toolName: string): toolName is CursorReplayLegacyToolName {
|
|
470
|
-
return CURSOR_REPLAY_LEGACY_TOOL_NAME_SET.has(toolName);
|
|
471
|
-
}
|
|
472
|
-
|
|
473
369
|
export function isCursorReplayToolName(toolName: string): toolName is CursorReplayToolName {
|
|
474
|
-
return toolName === CURSOR_REPLAY_ACTIVITY_TOOL_NAME
|
|
370
|
+
return toolName === CURSOR_REPLAY_ACTIVITY_TOOL_NAME;
|
|
475
371
|
}
|
|
476
372
|
|
|
477
373
|
export function isExcludedFromCursorBridgeExposure(toolName: string): boolean {
|
|
478
|
-
return toolName === CURSOR_REPLAY_ACTIVITY_TOOL_NAME
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
export function getCursorReplayWrapperLabel(toolName: CursorReplayToolName): string {
|
|
482
|
-
if (toolName === CURSOR_REPLAY_ACTIVITY_TOOL_NAME) return getCursorReplayDisplayLabel(toolName);
|
|
483
|
-
const spec = SPECS_BY_REPLAY_LEGACY_NAME.get(toolName);
|
|
484
|
-
return spec?.replayWrapperLabel ?? getCursorReplayDisplayLabel(toolName);
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
export function getCursorReplaySideEffectCategory(toolName: CursorReplayToolName): CursorReplaySideEffectCategory {
|
|
488
|
-
if (toolName === CURSOR_REPLAY_ACTIVITY_TOOL_NAME) return CURSOR_REPLAY_ACTIVITY_SIDE_EFFECT_CATEGORY;
|
|
489
|
-
return SPECS_BY_REPLAY_LEGACY_NAME.get(toolName)?.replaySideEffectCategory ?? "real_tool_work";
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
export function getCursorReplaySideEffectDescription(toolName: CursorReplayToolName): string {
|
|
493
|
-
return getCursorReplaySideEffectCategory(toolName) === "file_mutations" ? "file mutations" : "real tool work";
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
export function getCursorReplayOperationLabel(toolName: CursorReplayLegacyToolName): string {
|
|
497
|
-
const spec = SPECS_BY_REPLAY_LEGACY_NAME.get(toolName);
|
|
498
|
-
return spec?.replayOperationLabel ?? spec?.normalizedName ?? toolName;
|
|
374
|
+
return toolName === CURSOR_REPLAY_ACTIVITY_TOOL_NAME;
|
|
499
375
|
}
|
|
500
376
|
|
|
501
377
|
export function getCursorReplayPromptLabel(toolName: string): string {
|
|
502
|
-
|
|
503
|
-
if (isCursorReplayLegacyToolName(toolName)) {
|
|
504
|
-
const spec = SPECS_BY_REPLAY_LEGACY_NAME.get(toolName);
|
|
505
|
-
return spec?.promptLabel ?? toolName;
|
|
506
|
-
}
|
|
507
|
-
return toolName;
|
|
378
|
+
return toolName === CURSOR_REPLAY_ACTIVITY_TOOL_NAME ? "Cursor activity" : toolName;
|
|
508
379
|
}
|
|
509
380
|
|
|
510
|
-
export function
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
return spec
|
|
381
|
+
export function getCursorReplayActivityTitle(toolName: string): string | undefined {
|
|
382
|
+
const spec = getCursorToolPresentationSpec(toolName);
|
|
383
|
+
if (!spec || !hasNeutralActivityTitle(spec)) return undefined;
|
|
384
|
+
return spec.displayLabel;
|
|
514
385
|
}
|
|
515
386
|
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
return spec.replayLegacyName;
|
|
387
|
+
function buildCursorGenericActivityTitle(displayName: string): string {
|
|
388
|
+
if (!displayName || displayName === "unknown") return "Cursor tool";
|
|
389
|
+
return `Cursor ${truncateCursorDisplayLine(displayName, 120)}`;
|
|
520
390
|
}
|
|
521
391
|
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
392
|
+
/** Canonical activity title: registry label when known, otherwise neutral fallback. */
|
|
393
|
+
export function getCursorToolActivityTitle(toolName: string): string {
|
|
394
|
+
const normalized = normalizeCursorToolName(toolName);
|
|
395
|
+
const known = getCursorReplayActivityTitle(normalized);
|
|
396
|
+
if (known) return known;
|
|
397
|
+
const label = toolName.trim() || normalized;
|
|
398
|
+
return buildCursorGenericActivityTitle(label);
|
|
526
399
|
}
|
|
527
400
|
|
|
528
401
|
function getCursorToolPresentationSpecByNormalizedKey(normalizedKey: string): CursorToolPresentationSpec | undefined {
|
|
@@ -546,11 +419,11 @@ export function getCursorToolGenerateImageReplaySpec(normalizedKey: string): Cur
|
|
|
546
419
|
}
|
|
547
420
|
|
|
548
421
|
export function getCursorReplayCallSummary(
|
|
549
|
-
toolName: CursorReplayToolName,
|
|
422
|
+
toolName: CursorReplayToolName | CursorReplaySourceToolName,
|
|
550
423
|
args: CursorReplaySummaryArgs | undefined,
|
|
551
424
|
): string | undefined {
|
|
552
425
|
if (toolName === CURSOR_REPLAY_ACTIVITY_TOOL_NAME) {
|
|
553
426
|
return readCursorReplaySummaryString(args, "activitySummary") ?? summarizeReplayGenericActivity(args);
|
|
554
427
|
}
|
|
555
|
-
return
|
|
428
|
+
return getCursorToolPresentationSpec(toolName)?.replayCallSummary?.(args);
|
|
556
429
|
}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { asRecord, getArray, getNumber, getRecord, getString, stringifyUnknown } from "./cursor-record-utils.js";
|
|
2
|
+
import { scrubSensitiveText } from "./cursor-sensitive-text.js";
|
|
3
|
+
import { firstNonEmptyLine, formatDisplayPath, truncateArg } from "./cursor-transcript-utils.js";
|
|
4
|
+
|
|
5
|
+
export interface CursorToolResultLike {
|
|
6
|
+
status: string | undefined;
|
|
7
|
+
value: unknown;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface CursorToolResultReaderOptions {
|
|
11
|
+
cwd?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface CursorTodoItem {
|
|
15
|
+
content: string;
|
|
16
|
+
status?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function getReadLintPaths(
|
|
20
|
+
args: Record<string, unknown>,
|
|
21
|
+
result: CursorToolResultLike,
|
|
22
|
+
options: CursorToolResultReaderOptions,
|
|
23
|
+
): string[] {
|
|
24
|
+
const explicitPaths = Array.isArray(args.paths)
|
|
25
|
+
? args.paths.filter((entry): entry is string => typeof entry === "string")
|
|
26
|
+
: typeof args.path === "string"
|
|
27
|
+
? [args.path]
|
|
28
|
+
: [];
|
|
29
|
+
const resultPaths = (getArray(asRecord(result.value), "fileDiagnostics") ?? [])
|
|
30
|
+
.map((file) => getString(asRecord(file), "path"))
|
|
31
|
+
.filter((entry): entry is string => Boolean(entry));
|
|
32
|
+
return [...new Set([...explicitPaths, ...resultPaths].map((entry) => formatDisplayPath(entry, options.cwd)))];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function getReadLintDiagnostics(result: CursorToolResultLike, options: CursorToolResultReaderOptions): string[] {
|
|
36
|
+
const value = asRecord(result.value);
|
|
37
|
+
const files = getArray(value, "fileDiagnostics") ?? [];
|
|
38
|
+
const lines: string[] = [];
|
|
39
|
+
for (const file of files) {
|
|
40
|
+
const fileRecord = asRecord(file);
|
|
41
|
+
const pathValue = getString(fileRecord, "path");
|
|
42
|
+
const path = pathValue ? formatDisplayPath(pathValue, options.cwd) : "unknown";
|
|
43
|
+
const diagnostics = getArray(fileRecord, "diagnostics") ?? [];
|
|
44
|
+
for (const diagnostic of diagnostics) {
|
|
45
|
+
const diagnosticRecord = asRecord(diagnostic);
|
|
46
|
+
const severity = getString(diagnosticRecord, "severity") ?? "diagnostic";
|
|
47
|
+
const message = getString(diagnosticRecord, "message") ?? "";
|
|
48
|
+
const source = getString(diagnosticRecord, "source");
|
|
49
|
+
lines.push(`${path}: ${severity}${source ? ` ${source}` : ""}: ${message}`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return lines;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function getTodoItems(args: Record<string, unknown>, result: CursorToolResultLike): CursorTodoItem[] {
|
|
56
|
+
const value = asRecord(result.value);
|
|
57
|
+
const rawTodos = getArray(value, "todos") ?? getArray(args, "todos") ?? [];
|
|
58
|
+
const todos: CursorTodoItem[] = [];
|
|
59
|
+
for (const todo of rawTodos) {
|
|
60
|
+
const record = asRecord(todo);
|
|
61
|
+
const content = getString(record, "content");
|
|
62
|
+
if (!content) continue;
|
|
63
|
+
const status = getString(record, "status");
|
|
64
|
+
todos.push(status ? { content, status } : { content });
|
|
65
|
+
}
|
|
66
|
+
return todos;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function getTodoTotalCount(args: Record<string, unknown>, result: CursorToolResultLike, todos: CursorTodoItem[]): number {
|
|
70
|
+
return getNumber(asRecord(result.value), "totalCount") ?? getNumber(args, "totalCount") ?? todos.length;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function getTaskDescription(args: Record<string, unknown>, result: CursorToolResultLike): string {
|
|
74
|
+
return getString(args, "description") ?? getString(asRecord(result.value), "description") ?? "task";
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function getNestedRecord(record: Record<string, unknown> | undefined, ...keys: string[]): Record<string, unknown> | undefined {
|
|
78
|
+
let current = record;
|
|
79
|
+
for (const key of keys) {
|
|
80
|
+
current = getRecord(current, key);
|
|
81
|
+
if (!current) return undefined;
|
|
82
|
+
}
|
|
83
|
+
return current;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function collectTaskText(result: CursorToolResultLike): string {
|
|
87
|
+
const value = asRecord(result.value);
|
|
88
|
+
const success = getNestedRecord(value, "result", "success");
|
|
89
|
+
const command = getString(success, "command");
|
|
90
|
+
const stdout = getString(success, "stdout");
|
|
91
|
+
const interleavedOutput = getString(success, "interleavedOutput");
|
|
92
|
+
const assistantMessages = (getArray(value, "conversationSteps") ?? [])
|
|
93
|
+
.map((step) => getString(getRecord(asRecord(step), "assistantMessage"), "text"))
|
|
94
|
+
.filter((entry): entry is string => Boolean(entry));
|
|
95
|
+
const parts = [command ? `$ ${command}` : undefined, stdout || interleavedOutput, ...assistantMessages].filter((part): part is string => Boolean(part));
|
|
96
|
+
return parts.join("\n");
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export function getGenerateImagePath(args: Record<string, unknown>, result: CursorToolResultLike): string | undefined {
|
|
100
|
+
const value = asRecord(result.value);
|
|
101
|
+
return getString(value, "filePath") ?? getString(args, "filePath") ?? getString(args, "path");
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function getGenerateImageDisplayPath(
|
|
105
|
+
args: Record<string, unknown>,
|
|
106
|
+
result: CursorToolResultLike,
|
|
107
|
+
options: CursorToolResultReaderOptions,
|
|
108
|
+
): string | undefined {
|
|
109
|
+
const path = getGenerateImagePath(args, result);
|
|
110
|
+
return path ? formatDisplayPath(path, options.cwd) : undefined;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export function inferImageMimeType(path: string | undefined): string | undefined {
|
|
114
|
+
const lower = path?.toLowerCase();
|
|
115
|
+
if (!lower) return undefined;
|
|
116
|
+
if (lower.endsWith(".png")) return "image/png";
|
|
117
|
+
if (lower.endsWith(".jpg") || lower.endsWith(".jpeg")) return "image/jpeg";
|
|
118
|
+
if (lower.endsWith(".gif")) return "image/gif";
|
|
119
|
+
if (lower.endsWith(".webp")) return "image/webp";
|
|
120
|
+
return undefined;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function getMcpContentText(entry: unknown): string | undefined {
|
|
124
|
+
const record = asRecord(entry);
|
|
125
|
+
const directText = getString(record, "text");
|
|
126
|
+
if (directText) return directText;
|
|
127
|
+
const nestedText = getRecord(record, "text");
|
|
128
|
+
return getString(nestedText, "text");
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function describeNonTextMcpContent(entry: unknown): string {
|
|
132
|
+
const record = asRecord(entry);
|
|
133
|
+
const type = getString(record, "type") ?? "content";
|
|
134
|
+
if (type === "image") {
|
|
135
|
+
const mimeType = getString(record, "mimeType") ?? getString(record, "mime") ?? getString(record, "mediaType");
|
|
136
|
+
return `[image${mimeType ? ` ${mimeType}` : ""} omitted]`;
|
|
137
|
+
}
|
|
138
|
+
if (type === "audio") return "[audio omitted]";
|
|
139
|
+
if (type === "resource") return "[resource omitted]";
|
|
140
|
+
return `[${type} omitted]`;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export interface McpDisplayResult {
|
|
144
|
+
isToolError: boolean;
|
|
145
|
+
text: string;
|
|
146
|
+
nonTextSummary: string;
|
|
147
|
+
body: string;
|
|
148
|
+
preview?: string;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export function readMcpDisplayResult(result: CursorToolResultLike): McpDisplayResult {
|
|
152
|
+
if (result.status === "error") {
|
|
153
|
+
return { isToolError: false, text: "", nonTextSummary: "", body: "" };
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const value = asRecord(result.value);
|
|
157
|
+
const isToolError = value?.isError === true;
|
|
158
|
+
const content = getArray(value, "content") ?? [];
|
|
159
|
+
const textParts: string[] = [];
|
|
160
|
+
const nonTextParts: string[] = [];
|
|
161
|
+
let preview: string | undefined;
|
|
162
|
+
|
|
163
|
+
for (const entry of content) {
|
|
164
|
+
const text = getMcpContentText(entry);
|
|
165
|
+
if (text) {
|
|
166
|
+
textParts.push(text);
|
|
167
|
+
const line = firstNonEmptyLine(text);
|
|
168
|
+
if (!preview && line) preview = truncateArg(scrubSensitiveText(line), 120);
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
nonTextParts.push(describeNonTextMcpContent(entry));
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (!preview) preview = nonTextParts[0];
|
|
175
|
+
const text = textParts.join("\n");
|
|
176
|
+
const nonTextSummary = nonTextParts.join("\n");
|
|
177
|
+
const body = text || nonTextSummary || scrubSensitiveText(stringifyUnknown(result.value), undefined);
|
|
178
|
+
return {
|
|
179
|
+
isToolError,
|
|
180
|
+
text,
|
|
181
|
+
nonTextSummary,
|
|
182
|
+
body: `${isToolError ? "[tool error]\n" : ""}${body}`.trim(),
|
|
183
|
+
...(preview ? { preview } : {}),
|
|
184
|
+
};
|
|
185
|
+
}
|