pi-cursor-sdk 0.1.37 → 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.
Files changed (73) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/docs/cursor-native-tool-replay.md +3 -3
  3. package/package.json +1 -1
  4. package/scripts/platform-smoke/card-detect.mjs +1 -1
  5. package/src/context-window-cache.ts +10 -14
  6. package/src/context.ts +1 -1
  7. package/src/cursor-agent-message-web-tools.ts +2 -1
  8. package/src/cursor-agents-context-registration.ts +18 -0
  9. package/src/cursor-agents-context.ts +21 -30
  10. package/src/cursor-edit-diff.ts +4 -2
  11. package/src/cursor-fallback-warning.ts +22 -0
  12. package/src/cursor-incomplete-tool-visibility.ts +5 -10
  13. package/src/cursor-live-run-coordinator.ts +1 -1
  14. package/src/cursor-mcp-timeout-override.ts +0 -2
  15. package/src/cursor-model-lifecycle.ts +72 -0
  16. package/src/cursor-native-replay-routing.ts +1 -1
  17. package/src/cursor-native-replay-trace.ts +1 -1
  18. package/src/cursor-native-tool-display-registration.ts +16 -28
  19. package/src/cursor-native-tool-display-replay.ts +4 -21
  20. package/src/cursor-native-tool-display-state.ts +1 -1
  21. package/src/cursor-native-tool-display-tools.ts +10 -17
  22. package/src/cursor-native-tool-names.ts +16 -0
  23. package/src/cursor-pi-tool-bridge-env.ts +12 -0
  24. package/src/cursor-pi-tool-bridge-mcp.ts +16 -21
  25. package/src/cursor-pi-tool-bridge-run.ts +5 -5
  26. package/src/cursor-pi-tool-bridge-server.ts +8 -3
  27. package/src/cursor-pi-tool-bridge-snapshot.ts +7 -13
  28. package/src/cursor-pi-tool-bridge.ts +7 -7
  29. package/src/cursor-provider-lazy.ts +51 -0
  30. package/src/cursor-provider-live-run-drain.ts +1 -1
  31. package/src/cursor-provider-run-finalizer.ts +5 -5
  32. package/src/cursor-provider-run-outcome.ts +0 -1
  33. package/src/cursor-provider-turn-coordinator.ts +4 -5
  34. package/src/cursor-provider-turn-display-router.ts +5 -1
  35. package/src/cursor-provider-turn-emit.ts +1 -1
  36. package/src/cursor-provider-turn-lifecycle-emitter.ts +1 -5
  37. package/src/cursor-provider-turn-prepare.ts +13 -9
  38. package/src/cursor-provider-turn-runner.ts +3 -11
  39. package/src/cursor-provider-turn-sdk-normalizer.ts +28 -5
  40. package/src/cursor-provider-turn-send.ts +7 -2
  41. package/src/cursor-provider-turn-types.ts +1 -3
  42. package/src/cursor-provider.ts +3 -2
  43. package/src/cursor-question-tool.ts +5 -18
  44. package/src/cursor-record-utils.ts +42 -0
  45. package/src/cursor-replay-activity-builders.ts +16 -122
  46. package/src/cursor-replay-tool-details.ts +52 -80
  47. package/src/cursor-sdk-event-debug.ts +6 -6
  48. package/src/cursor-sensitive-text.ts +4 -4
  49. package/src/cursor-session-agent-lifecycle.ts +47 -0
  50. package/src/cursor-session-agent.ts +9 -47
  51. package/src/cursor-session-scope.ts +23 -4
  52. package/src/cursor-setting-sources.ts +8 -8
  53. package/src/cursor-skill-tool.ts +25 -32
  54. package/src/cursor-state.ts +66 -45
  55. package/src/cursor-tool-lifecycle.ts +16 -9
  56. package/src/cursor-tool-presentation-registry.ts +27 -18
  57. package/src/cursor-tool-result-display-readers.ts +185 -0
  58. package/src/cursor-tool-transcript.ts +17 -33
  59. package/src/cursor-tool-visibility.ts +9 -1
  60. package/src/cursor-transcript-tool-formatters.ts +23 -172
  61. package/src/cursor-transcript-tool-specs.ts +16 -41
  62. package/src/cursor-transcript-utils.ts +2 -34
  63. package/src/cursor-usage-accounting.ts +0 -6
  64. package/src/cursor-web-tool-activity.ts +4 -12
  65. package/src/cursor-web-tool-args.ts +1 -9
  66. package/src/index.ts +15 -16
  67. package/src/model-discovery.ts +5 -4
  68. package/src/model-list-cache.ts +37 -38
  69. package/src/cursor-native-tool-display.ts +0 -10
  70. package/src/cursor-provider-turn-api-key.ts +0 -1
  71. package/src/cursor-provider-turn-message-offset.ts +0 -15
  72. package/src/cursor-session-cwd.ts +0 -28
  73. package/src/cursor-tool-names.ts +0 -9
@@ -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,6 +47,9 @@ import type {
55
47
 
56
48
  export const CURSOR_REPLAY_ACTIVITY_TOOL_NAME = "cursor" as const;
57
49
 
50
+ const EMPTY_REPLAY_DETAIL_FIELDS = (): Record<string, never> => ({});
51
+ const COLLAPSED_REPLAY_DETAIL_FIELDS = (): { collapseDetailsByDefault: true } => ({ collapseDetailsByDefault: true });
52
+
58
53
  export type CursorWebToolKind = "webSearch" | "webFetch";
59
54
 
60
55
  export type CursorToolLifecycleLabelKind =
@@ -187,7 +182,7 @@ export const CURSOR_TOOL_PRESENTATION_SPECS = [
187
182
  replayCallSummary: withActivitySummaryFallback(summarizeReplayReadLints),
188
183
  activityReplay: {
189
184
  buildActivityArgs: buildReadLintsReplaySummaryArgs,
190
- buildDetails: buildEmptyReplayDetailFields,
185
+ buildDetails: EMPTY_REPLAY_DETAIL_FIELDS,
191
186
  },
192
187
  },
193
188
  {
@@ -198,7 +193,7 @@ export const CURSOR_TOOL_PRESENTATION_SPECS = [
198
193
  replayCallSummary: withActivitySummaryFallback(summarizeReplayTodoCount),
199
194
  activityReplay: {
200
195
  buildActivityArgs: ({ args, result }) => buildTodoReplaySummaryArgs(args, result),
201
- buildDetails: buildEmptyReplayDetailFields,
196
+ buildDetails: EMPTY_REPLAY_DETAIL_FIELDS,
202
197
  },
203
198
  },
204
199
  {
@@ -209,7 +204,7 @@ export const CURSOR_TOOL_PRESENTATION_SPECS = [
209
204
  replayCallSummary: withActivitySummaryFallback(summarizeReplayPlan),
210
205
  activityReplay: {
211
206
  buildActivityArgs: buildCreatePlanReplaySummaryArgs,
212
- buildDetails: buildEmptyReplayDetailFields,
207
+ buildDetails: EMPTY_REPLAY_DETAIL_FIELDS,
213
208
  },
214
209
  },
215
210
  {
@@ -220,7 +215,7 @@ export const CURSOR_TOOL_PRESENTATION_SPECS = [
220
215
  replayCallSummary: withActivitySummaryFallback(summarizeReplayTask),
221
216
  activityReplay: {
222
217
  buildActivityArgs: buildTaskReplaySummaryArgs,
223
- buildDetails: buildEmptyReplayDetailFields,
218
+ buildDetails: EMPTY_REPLAY_DETAIL_FIELDS,
224
219
  },
225
220
  },
226
221
  {
@@ -244,7 +239,7 @@ export const CURSOR_TOOL_PRESENTATION_SPECS = [
244
239
  replayCallSummary: withActivitySummaryFallback(summarizeReplayMcp),
245
240
  activityReplay: {
246
241
  buildActivityArgs: buildMcpReplaySummaryArgs,
247
- buildDetails: buildEmptyReplayDetailFields,
242
+ buildDetails: EMPTY_REPLAY_DETAIL_FIELDS,
248
243
  },
249
244
  },
250
245
  {
@@ -255,7 +250,7 @@ export const CURSOR_TOOL_PRESENTATION_SPECS = [
255
250
  replayCallSummary: withActivitySummaryFallback(formatReplaySemSearchQuery),
256
251
  activityReplay: {
257
252
  buildActivityArgs: buildSemSearchReplaySummaryArgs,
258
- buildDetails: buildEmptyReplayDetailFields,
253
+ buildDetails: EMPTY_REPLAY_DETAIL_FIELDS,
259
254
  },
260
255
  },
261
256
  {
@@ -266,7 +261,7 @@ export const CURSOR_TOOL_PRESENTATION_SPECS = [
266
261
  replayCallSummary: withActivitySummaryFallback(summarizeReplayRecordScreen),
267
262
  activityReplay: {
268
263
  buildActivityArgs: buildRecordScreenReplaySummaryArgs,
269
- buildDetails: buildEmptyReplayDetailFields,
264
+ buildDetails: EMPTY_REPLAY_DETAIL_FIELDS,
270
265
  },
271
266
  },
272
267
  {
@@ -282,7 +277,7 @@ export const CURSOR_TOOL_PRESENTATION_SPECS = [
282
277
  ),
283
278
  activityReplay: {
284
279
  buildActivityArgs: buildWebSearchReplaySummaryArgs,
285
- buildDetails: buildCollapsedReplayDetailFields,
280
+ buildDetails: COLLAPSED_REPLAY_DETAIL_FIELDS,
286
281
  },
287
282
  },
288
283
  {
@@ -298,7 +293,7 @@ export const CURSOR_TOOL_PRESENTATION_SPECS = [
298
293
  ),
299
294
  activityReplay: {
300
295
  buildActivityArgs: buildWebFetchReplaySummaryArgs,
301
- buildDetails: buildCollapsedReplayDetailFields,
296
+ buildDetails: COLLAPSED_REPLAY_DETAIL_FIELDS,
302
297
  },
303
298
  },
304
299
  ] as const satisfies readonly CursorToolPresentationSpec[];
@@ -389,6 +384,20 @@ export function getCursorReplayActivityTitle(toolName: string): string | undefin
389
384
  return spec.displayLabel;
390
385
  }
391
386
 
387
+ function buildCursorGenericActivityTitle(displayName: string): string {
388
+ if (!displayName || displayName === "unknown") return "Cursor tool";
389
+ return `Cursor ${truncateCursorDisplayLine(displayName, 120)}`;
390
+ }
391
+
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);
399
+ }
400
+
392
401
  function getCursorToolPresentationSpecByNormalizedKey(normalizedKey: string): CursorToolPresentationSpec | undefined {
393
402
  return SPECS_BY_NORMALIZED_KEY.get(normalizedKey.toLowerCase());
394
403
  }
@@ -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
+ }
@@ -1,6 +1,11 @@
1
+ import { asRecord, getString } from "./cursor-record-utils.js";
2
+ import { normalizeCursorToolName } from "./cursor-tool-presentation-registry.js";
3
+ import {
4
+ buildCursorPiToolDisplayFromSpec,
5
+ formatCursorToolTranscriptFromSpec,
6
+ type ToolDisplayContext,
7
+ } from "./cursor-transcript-tool-specs.js";
1
8
  import {
2
- asRecord,
3
- getString,
4
9
  getToolArgs,
5
10
  getToolName,
6
11
  getToolResult,
@@ -8,27 +13,12 @@ import {
8
13
  type CursorPiToolDisplay,
9
14
  type TranscriptOptions,
10
15
  } from "./cursor-transcript-utils.js";
11
- import { normalizeCursorToolName as normalizeToolName } from "./cursor-tool-presentation-registry.js";
12
- import {
13
- buildCursorPiToolDisplayFromSpec,
14
- formatCursorToolTranscriptFromSpec,
15
- type ToolDisplayContext,
16
- } from "./cursor-transcript-tool-specs.js";
17
16
  import { resolveTranscriptToolName } from "./cursor-web-tool-activity.js";
18
17
 
19
18
  export type { CursorPiToolDisplay } from "./cursor-transcript-utils.js";
19
+ export type { ToolDisplayContext } from "./cursor-transcript-tool-specs.js";
20
20
 
21
- export function getCursorCreatePlanText(toolCall: unknown): string | undefined {
22
- const name = normalizeToolName(getToolName(toolCall));
23
- if (name !== "createPlan") return undefined;
24
- const args = getToolArgs(toolCall);
25
- const result = normalizeResult(getToolResult(toolCall));
26
- const plan = getString(args, "plan") ?? getString(asRecord(result.value), "plan");
27
- const trimmed = plan?.trim();
28
- return trimmed || undefined;
29
- }
30
-
31
- function buildToolDisplayContext(toolCall: unknown, options: TranscriptOptions): ToolDisplayContext {
21
+ function buildToolDisplayContext(toolCall: unknown, options: TranscriptOptions = {}): ToolDisplayContext {
32
22
  const rawName = getToolName(toolCall);
33
23
  const args = getToolArgs(toolCall);
34
24
  return {
@@ -48,18 +38,12 @@ export function buildCursorPiToolDisplay(toolCall: unknown, options: TranscriptO
48
38
  return buildCursorPiToolDisplayFromSpec(buildToolDisplayContext(toolCall, options));
49
39
  }
50
40
 
51
- export function mergeCursorToolCalls(startedToolCall: unknown, completedToolCall: unknown): unknown {
52
- const started = asRecord(startedToolCall);
53
- const completed = asRecord(completedToolCall);
54
- if (!started) return completedToolCall;
55
- if (!completed) return startedToolCall;
56
- return {
57
- ...started,
58
- ...completed,
59
- name: completed.name ?? started.name,
60
- type: completed.type ?? started.type,
61
- args: completed.args ?? started.args,
62
- input: completed.input ?? started.input,
63
- result: completed.result ?? started.result,
64
- };
41
+ export function getCursorCreatePlanText(toolCall: unknown): string | undefined {
42
+ const name = normalizeCursorToolName(getToolName(toolCall));
43
+ if (name !== "createPlan") return undefined;
44
+ const args = getToolArgs(toolCall);
45
+ const result = normalizeResult(getToolResult(toolCall));
46
+ const plan = getString(args, "plan") ?? getString(asRecord(result.value), "plan");
47
+ const trimmed = plan?.trim();
48
+ return trimmed || undefined;
65
49
  }
@@ -1,4 +1,8 @@
1
- import { getCursorReplayActivityTitle, getCursorToolVisibilityPolicy, normalizeCursorToolName as normalizeToolName } from "./cursor-tool-presentation-registry.js";
1
+ import {
2
+ getCursorReplayActivityTitle,
3
+ getCursorToolVisibilityPolicy,
4
+ normalizeCursorToolName as normalizeToolName,
5
+ } from "./cursor-tool-presentation-registry.js";
2
6
  import { getToolArgs, getToolName } from "./cursor-transcript-utils.js";
3
7
  import { resolveTranscriptToolName } from "./cursor-web-tool-activity.js";
4
8
 
@@ -14,6 +18,10 @@ export interface CursorToolVisibility {
14
18
  fastLocalDiscovery: boolean;
15
19
  }
16
20
 
21
+ export function getNormalizedCursorToolName(toolCall: unknown): string {
22
+ return classifyCursorToolVisibility(toolCall).normalizedName;
23
+ }
24
+
17
25
  export function classifyCursorToolVisibility(toolCall: unknown): CursorToolVisibility {
18
26
  const args = getToolArgs(toolCall);
19
27
  const displayName = resolveTranscriptToolName(getToolName(toolCall), args);