pi-cursor-sdk 0.1.35 → 0.1.37
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 +17 -0
- package/docs/cursor-model-ux-spec.md +1 -1
- package/docs/cursor-native-tool-replay.md +6 -6
- package/package.json +1 -1
- package/src/cursor-incomplete-tool-visibility.ts +0 -1
- package/src/cursor-native-tool-display-replay.ts +8 -26
- package/src/cursor-native-tool-display-tools.ts +1 -2
- package/src/cursor-provider-errors.ts +12 -3
- package/src/cursor-replay-tool-details.ts +9 -121
- package/src/cursor-tool-names.ts +0 -9
- package/src/cursor-tool-presentation-registry.ts +16 -152
- package/src/cursor-transcript-tool-specs.ts +7 -22
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,23 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.1.37 - 2026-06-06
|
|
6
|
+
|
|
7
|
+
### Changed
|
|
8
|
+
|
|
9
|
+
- Cut Cursor native replay over to one canonical replay surface: neutral `cursor` activity cards plus native-compatible `read`, `bash`, `grep`, `find`, `ls`, `edit`, and `write` cards. Legacy `cursor_*` replay wrapper names, alias exports, old `cursorToolName` replay-detail parsing, and replay-only prompt metadata are removed instead of preserved behind compatibility shims (#123).
|
|
10
|
+
- Keep edit/write replay previews on the shared structured diff/file preview renderers, while retaining unstructured `expandedText` unified-diff extraction only as a fallback for current SDK payloads that do not include structured diff fields.
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- Stop registering duplicate replay-only prompt snippets/guidelines for every Cursor SDK activity wrapper, eliminating the inflated prompt metadata reported by issue #123 while preserving current TUI replay card titles, summaries, typed details, and `sourceToolName` display metadata.
|
|
15
|
+
|
|
16
|
+
## 0.1.36 - 2026-06-05
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
|
|
20
|
+
- Classify Cursor backend `ConnectError: [unavailable] Error` failures with code 14 and `aiserver.v1.ErrorDetails` as recoverable network/service errors, preventing duplicate process-level uncaught exceptions from crashing pi while still surfacing scrubbed retry guidance.
|
|
21
|
+
|
|
5
22
|
## 0.1.35 - 2026-06-05
|
|
6
23
|
|
|
7
24
|
### Changed
|
|
@@ -30,7 +30,7 @@ Current implementation notes:
|
|
|
30
30
|
- Bridge diagnostics are opt-in only: `PI_CURSOR_PI_TOOL_BRIDGE_DEBUG=1` writes typed, allowlisted, scrubbed single-line JSONL records to `process.stderr` with prefix `[pi-cursor-sdk:bridge]`. Diagnostics are scrubbed operational logs, not anonymous telemetry. They intentionally include tool names, safe correlation IDs, run lifecycle, exposed pi↔MCP name pairs, queued requests, result resolution, rejection, cancellation, and pending counts. Correlation IDs are generated independently from the tokenized endpoint path, and Cursor MCP call IDs are hashed before serialization. Diagnostics must not include endpoint paths/URLs/path components/tokens, API keys, bearer tokens, cookies, session credentials, raw args/results, stdout/stderr payloads, file contents, Cursor settings output, or local private session paths in tracked docs, and they must not call pi UI status, notification, or footer APIs. If tool names themselves are unacceptable for a release target, bridge debug diagnostics are not safe for shared logs under the current contract.
|
|
31
31
|
- This repo does not provide a generic desktop-automation, browser-driver, or CDP recipe. Provider docs should describe pi-cursor-sdk's Cursor provider/bridge contract only.
|
|
32
32
|
- Cursor internal tool activity is recorded from SDK events and scrubbed. Maintainer reference for all 16 `@cursor/sdk@1.0.17` `ToolType` values, runtime alias normalization, and intentional mapping/fallback rules: [Cursor native tool replay — SDK ToolType replay matrix](./cursor-native-tool-replay.md#sdk-tooltype-replay-matrix) (official SDK docs: https://cursor.com/docs/sdk/typescript). In interactive TTY sessions, supported completed `read`, `bash`, `grep`, `find`, `ls`, `edit`, `write`, diagnostics, delete, todo/plan, task, image generation, MCP, semantic search, and screen recording activity is replayed through pi's native tool-call rendering path with recorded Cursor results, so the TUI can show native-looking cards without rerunning Cursor's reads/shell commands/file edits. Cursor `glob` activity is replayed through native `find` cards. Cursor write activity is replayed through native-looking `write` cards, and Cursor StrReplace/edit activity uses native-looking `edit` only when recorded arguments truthfully satisfy pi's `edit` schema; path-only Cursor edit and notebook edit replay falls back to neutral Cursor activity before pi validation. Diagnostics, delete, todos/plans, task, image, and MCP activity use neutral Cursor activity cards with pi's default success/error shell. Neutral Cursor activity calls include `activityTitle` and, when available, `activitySummary` so partial/collapsed cards preserve identity such as `Cursor plan`, `Cursor todos`, `Cursor MCP`, or `Cursor edit`. For long-running or externally meaningful Cursor tools (`task`, `shell`, `mcp`, `generateImage`, `recordScreen`, `semSearch`, web search/fetch, plan/todo), the provider may surface one low-noise deferred in-progress thinking line such as `Cursor MCP: external_search` from bounded, scrubbed SDK args; fast local tools (`read`, `grep`, `glob`, and similar) skip lifecycle lines when completion follows immediately, and pi bridge MCP calls are excluded because pi already shows real pi tool execution ([lifecycle visibility](./cursor-native-tool-replay.md#low-noise-tool-lifecycle-visibility)). Replay-only tools display recorded Cursor results, normalize workspace-local paths/diff headers for display, use pi diff colors for edit previews and path-inferred syntax highlighting for write previews, and fail closed if called without a recorded result. Native replay wrappers are registered only for tool names not already owned by another extension; conflicting tools use the bounded scrubbed transcript fallback. Cursor workflow tools such as mode/task/todo/plan activity are not pi workflow controls; reported todo/plan events are displayed as Cursor activity only. Plan/todo replay cards can be followed by Cursor's final plan text, selected from `run.wait().result` when Cursor provides one and trimmed against already-emitted text. Started Cursor SDK tool calls that never receive a completion event are surfaced with bounded user-visible labels/traces (neutral activity cards when native replay routing allows, otherwise the same inactive or transcript trace fallbacks used for completed replay) instead of being silently discarded when the run failed/aborted, produced no assistant text, or involved external/side-effectful tools; incomplete fast local discovery starts (`read`, `grep`, `glob`, `ls`) remain maintainer-debug-only after successful text-producing runs so stale SDK start events do not create red post-answer cards. Explicit failures remain visible when Cursor reports them through completed tool calls or step results. Pi bridge MCP starts remain excluded from duplicate incomplete Cursor cards because pi already shows real pi tool execution. `PI_CURSOR_NATIVE_TOOL_DISPLAY=0` disables native replay, and `PI_CURSOR_REGISTER_NATIVE_TOOLS=0` is a registration-only opt-out that keeps the transcript fallback without shadowing pi tool names. When bridge or native replay cards are emitted, the provider mirrors Codex's turn shape as Cursor SDK activity arrives: assistant `toolUse`, pi `toolResult`s, live post-tool Cursor thinking/text, any later tool batches as further `toolUse` turns, then Cursor's final assistant answer. For shell replay, completed `stdout` / `stderr` are primary; unambiguous `shell-output-delta` data is used only as display-only fallback for empty successful shell completions, and overlapping shell calls drop ambiguous deltas instead of guessing. Non-interactive runs keep bounded scrubbed transcript output instead, preserving `pi -p` assistant text output. Cursor text deltas stream live when no live-run turn split is active.
|
|
33
|
-
-
|
|
33
|
+
- Cursor native replay uses one neutral replay tool name, `cursor`, plus native-compatible card names when renderer-compatible (`read`, `bash`, `grep`, `find`, `ls`, `edit`, `write`). Neutral replay identity lives in `activityTitle`, `activitySummary`, and typed replay details, not in extra registered tool names. Bridge MCP names such as `pi__sem_reindex` are MCP-only; pi session output uses real pi tool names.
|
|
34
34
|
- Cursor SDK usage events report cumulative internal agent/tool/cache work, not the replayable pi prompt context. The extension does not copy raw Cursor SDK usage into pi usage or compaction. For Cursor assistant messages, `usage.input`/`usage.output` are approximate pi session activity components: initial Cursor prompt input is counted once, consumed split-run tool results are counted as deduped input on the following assistant turn, and assistant output includes visible text/thinking/tool-call content. `usage.totalTokens` is the replayable Cursor prompt/context estimate derived from the same `buildCursorPrompt()` path used for `Agent.send`; it may differ from `input + output` and is the context-safe value for display/compaction. `src/cursor-usage-accounting.ts` owns this usage policy, and `src/cursor-live-run-accounting.ts` owns prompt-once and consumed-tool-result accounting so provider usage and bridge result resolution share the same matched tool-result boundary.
|
|
35
35
|
- Audit observation, 2026-05-19, superseded by the 2026-05-21 replay pass and #68 incomplete visibility, then narrowed by the 2026-05-26 fast-local suppression: a missing-file read with Composer 2.5 emitted `tool-call-started` for Cursor `read`, then streamed final text `Error: File not found`, but did not emit `tool-call-completed` or an `onStep` `toolCall` error result. Leftover external/side-effectful started calls are surfaced at run completion through the same native replay routing as completed tools (activity cards when allowed, otherwise inactive/transcript traces), while fast local discovery starts are debug-only after a successful text-producing run. Cursor-reported completed/step errors remain visible.
|
|
36
36
|
- Maintainer visual verification for replay-card changes should follow [Cursor Native Tool Visual Audit Workflow](./cursor-native-tool-visual-audit.md): offscreen PTY-driven pi run, xterm.js/Playwright screenshot rendering, and JSONL inspection before accepting commits or PRs.
|
|
@@ -72,7 +72,7 @@ Source of truth for SDK tool names: `@cursor/sdk@1.0.17` conversation `ToolType`
|
|
|
72
72
|
|
|
73
73
|
Implementation owners: `src/cursor-tool-presentation-registry.ts` (canonical names, labels, visibility, replay policy, bridge exclusions for internal replay wrappers, and display-spec key completeness), `src/cursor-transcript-tool-specs.ts` (registry-keyed `TOOL_DISPLAY_SPECS` formatters/builders), `src/cursor-native-tool-display-replay.ts` (replay card rendering derived from registry replay metadata), and `src/cursor-transcript-utils.ts` (`normalizeToolName()` delegating to the registry).
|
|
74
74
|
|
|
75
|
-
**Maintainer invariants — edit/write replay previews:** All colored diff rendering (native `edit` cards and `Cursor edit` activity fallbacks) flows through the single `formatCursorReplayDiff()` in `src/cursor-native-tool-display-replay.ts`. Activity write fallbacks with structured `fileContentAfterWrite` use the same `formatCursorReplayFilePreview()` path as native `write` cards. Structured `diffString` (and `diff`/`lines*`) or `fileContentAfterWrite` on `CursorReplay*Details` (including activity variants) is the source of truth for TUI preview coloring/highlighting. `expandedText` on activity details is for summary/expansion and
|
|
75
|
+
**Maintainer invariants — edit/write replay previews:** All colored diff rendering (native `edit` cards and `Cursor edit` activity fallbacks) flows through the single `formatCursorReplayDiff()` in `src/cursor-native-tool-display-replay.ts`. Activity write fallbacks with structured `fileContentAfterWrite` use the same `formatCursorReplayFilePreview()` path as native `write` cards. Structured `diffString` (and `diff`/`lines*`) or `fileContentAfterWrite` on `CursorReplay*Details` (including activity variants) is the source of truth for TUI preview coloring/highlighting. `expandedText` on activity details is for summary/expansion and as a fallback when the current SDK reports a unified diff only in text; it is never the primary preview source when structured fields are present. No parallel +/- coloring loops exist.
|
|
76
76
|
|
|
77
77
|
This matrix covers **Cursor native tool replay only**. It does not describe the [live pi MCP bridge](#live-bridge-vs-replay) or Cursor-native host tools, settings, plugins, and configured MCP servers from the Cursor SDK local-agent path.
|
|
78
78
|
|
|
@@ -100,9 +100,9 @@ This matrix covers **Cursor native tool replay only**. It does not describe the
|
|
|
100
100
|
|
|
101
101
|
**Unknown/future fallback path:** SDK tool names with no registry-backed `TOOL_DISPLAY_SPECS` entry (future or unknown types) use `buildGenericPiToolDisplay()` in `src/cursor-transcript-tool-specs.ts` with bounded `formatFallback()` content from `src/cursor-transcript-tool-formatters.ts`. Lookup uses `Object.hasOwn(TOOL_DISPLAY_SPECS, name)` so inherited object keys such as `constructor` or `toString` cannot accidentally match a registry spec. When native replay is enabled, those completions queue through neutral pi tool name `cursor` (not native pi `read`/`bash`/… cards). Collapsed labels read like **Cursor futureSemSearchWidget** (title `Cursor` plus the SDK tool name) with optional bounded `activitySummary` from scrubbed args/result lines. Errors keep `details.summary` undefined so unbounded raw errors do not leak into replay cards (#52). Known explicit specs still win over this path; real pi bridge tool names such as `edit` and `write` are not suppressed by internal replay-wrapper exclusions.
|
|
102
102
|
|
|
103
|
-
**Replay detail disposition model:** `src/cursor-replay-tool-details.ts` stores replay card disposition separately from SDK source tool identity. Variants are `nativeEdit`, `nativeWrite`, `activity` (`sourceToolName` + display `title`), `generateImage`, and `genericFallback`. Path-only or notebook edit/write fallbacks produce `activity` details (neutral `cursor` cards) instead of structured edit/write variants with optional `title` escape hatches. Native edit/write cards use `nativeEdit` / `nativeWrite` only when pi-facing replay args satisfy the matching schema. The renderer dispatches on `variant` only
|
|
103
|
+
**Replay detail disposition model:** `src/cursor-replay-tool-details.ts` stores replay card disposition separately from SDK source tool identity. Variants are `nativeEdit`, `nativeWrite`, `activity` (`sourceToolName` + display `title`), `generateImage`, and `genericFallback`. Path-only or notebook edit/write fallbacks produce `activity` details (neutral `cursor` cards) instead of structured edit/write variants with optional `title` escape hatches. Native edit/write cards use `nativeEdit` / `nativeWrite` only when pi-facing replay args satisfy the matching schema. The renderer dispatches on `variant` only.
|
|
104
104
|
|
|
105
|
-
Neutral activity rows use pi tool name `cursor` with `activityTitle` / `activitySummary` metadata.
|
|
105
|
+
Neutral activity rows use pi tool name `cursor` with `activityTitle` / `activitySummary` metadata. User-visible collapsed cards use labels like **Cursor semantic search**.
|
|
106
106
|
|
|
107
107
|
## Runtime alias normalization
|
|
108
108
|
|
|
@@ -184,15 +184,15 @@ For shell replay, completed `stdout` / `stderr` remain the primary source. If a
|
|
|
184
184
|
|
|
185
185
|
Non-interactive and session consumers still receive bounded scrubbed transcript data so `pi -p` keeps printing normal assistant text.
|
|
186
186
|
|
|
187
|
-
##
|
|
187
|
+
## Replay-name policy
|
|
188
188
|
|
|
189
|
-
|
|
189
|
+
Cursor native replay has one neutral replay tool name, `cursor`, plus native-compatible card names when renderer-compatible: `read`, `bash`, `grep`, `find`, `ls`, `edit`, and `write`. Neutral replay identity lives in `activityTitle`, `activitySummary`, and typed replay details, not in extra registered tool names.
|
|
190
190
|
|
|
191
191
|
Bridge MCP names are also not pi tool names. Cursor may see names such as `pi__sem_reindex` inside the local MCP bridge, but pi session output uses the real pi tool name.
|
|
192
192
|
|
|
193
193
|
## Conflicts and opt out
|
|
194
194
|
|
|
195
|
-
Native replay wrappers are registered only for tool names not already owned by another extension. If another extension already owns a wrapper name needed for replay, pi-cursor-sdk skips only the conflicting wrapper and uses the scrubbed Cursor activity transcript for that tool instead.
|
|
195
|
+
Native replay wrappers are registered only for tool names not already owned by another extension. If another extension already owns a wrapper name needed for replay, pi-cursor-sdk skips only the conflicting wrapper and uses the scrubbed Cursor activity transcript for that tool instead.
|
|
196
196
|
|
|
197
197
|
Disable native replay registration entirely:
|
|
198
198
|
|
package/package.json
CHANGED
|
@@ -8,9 +8,6 @@ import { LOCAL_READ_PREVIEW_NOTICE, isLocalReadPreviewContent } from "./cursor-t
|
|
|
8
8
|
import {
|
|
9
9
|
CURSOR_REPLAY_ACTIVITY_TOOL_NAME,
|
|
10
10
|
getCursorReplayCallSummary,
|
|
11
|
-
getCursorReplaySideEffectDescription,
|
|
12
|
-
getCursorReplayOperationLabel,
|
|
13
|
-
getCursorReplayWrapperLabel,
|
|
14
11
|
type CursorReplayToolName,
|
|
15
12
|
} from "./cursor-tool-presentation-registry.js";
|
|
16
13
|
import {
|
|
@@ -28,24 +25,17 @@ import {
|
|
|
28
25
|
|
|
29
26
|
export type {
|
|
30
27
|
CursorReplayNativeEditDetails,
|
|
31
|
-
CursorReplayEditDetails,
|
|
32
28
|
CursorReplayGenerateImageDetails,
|
|
33
29
|
CursorReplayGenericFallbackDetails,
|
|
34
30
|
CursorReplayActivityDetails,
|
|
35
|
-
CursorReplayTitledActivityDetails,
|
|
36
31
|
CursorReplayToolDetails,
|
|
37
32
|
CursorReplayNativeWriteDetails,
|
|
38
|
-
CursorReplayWriteDetails,
|
|
39
33
|
} from "./cursor-replay-tool-details.js";
|
|
40
34
|
export {
|
|
41
|
-
asCursorReplayToolDetails,
|
|
42
35
|
isCursorReplayNativeEditDetails,
|
|
43
|
-
isCursorReplayEditDetails,
|
|
44
36
|
isCursorReplayGenerateImageDetails,
|
|
45
37
|
isCursorReplayActivityDetails,
|
|
46
|
-
isCursorReplayTitledActivityDetails,
|
|
47
38
|
isCursorReplayNativeWriteDetails,
|
|
48
|
-
isCursorReplayWriteDetails,
|
|
49
39
|
parseCursorReplayToolDetails,
|
|
50
40
|
} from "./cursor-replay-tool-details.js";
|
|
51
41
|
|
|
@@ -245,10 +235,8 @@ function formatCursorReplayActivityDiffPreview(
|
|
|
245
235
|
const body = (stripHeader ? stripCursorReplayHeader(text) : text).trimEnd();
|
|
246
236
|
const diffSection = body ? extractUnifiedDiffSection(body) : undefined;
|
|
247
237
|
if (!diffSection || !hasUnifiedDiffHunk(diffSection)) return undefined;
|
|
248
|
-
//
|
|
249
|
-
// All actual diff coloring
|
|
250
|
-
// This path exists solely so pre-structured sessions still get colored diffs via the same
|
|
251
|
-
// high-quality implementation used for nativeEdit and new structured activity cases.
|
|
238
|
+
// Fallback for unstructured activity details that carry a unified diff only in expanded text.
|
|
239
|
+
// All actual diff coloring lives in the single `formatCursorReplayDiff` renderer.
|
|
252
240
|
return formatCursorReplayDiff(diffSection, theme, maxLines);
|
|
253
241
|
}
|
|
254
242
|
|
|
@@ -350,11 +338,11 @@ export function formatCursorReplayFilePreview(
|
|
|
350
338
|
return renderedLines.join("\n");
|
|
351
339
|
}
|
|
352
340
|
|
|
353
|
-
function
|
|
341
|
+
function getCursorReplayCardTitle(toolName: CursorReplayToolName, args: Record<string, unknown> | undefined): string {
|
|
354
342
|
if (toolName === CURSOR_REPLAY_ACTIVITY_TOOL_NAME && typeof args?.activityTitle === "string" && args.activityTitle.trim()) {
|
|
355
343
|
return args.activityTitle.trim();
|
|
356
344
|
}
|
|
357
|
-
return
|
|
345
|
+
return "Cursor activity";
|
|
358
346
|
}
|
|
359
347
|
|
|
360
348
|
export function renderCursorReplayCall(
|
|
@@ -364,7 +352,7 @@ export function renderCursorReplayCall(
|
|
|
364
352
|
isPartial: boolean,
|
|
365
353
|
): Text {
|
|
366
354
|
if (!isPartial) return new Text("", 0, 0);
|
|
367
|
-
let text = theme.fg("toolTitle", theme.bold(`${
|
|
355
|
+
let text = theme.fg("toolTitle", theme.bold(`${getCursorReplayCardTitle(toolName, args)} `));
|
|
368
356
|
const summary = getCursorReplayCallSummary(toolName, args);
|
|
369
357
|
if (summary) text += theme.fg("accent", summary);
|
|
370
358
|
return new Text(text.trimEnd(), 0, 0);
|
|
@@ -602,19 +590,13 @@ export function renderNativeLookingCursorReadReplayResult(
|
|
|
602
590
|
}
|
|
603
591
|
|
|
604
592
|
export function createCursorReplayOnlyToolDefinition(toolName: CursorReplayToolName): ToolDefinition<typeof cursorReplayToolSchema, unknown> {
|
|
605
|
-
const cursorToolName = toolName === CURSOR_REPLAY_ACTIVITY_TOOL_NAME ? "activity" : getCursorReplayOperationLabel(toolName);
|
|
606
|
-
const sideEffectDescription = getCursorReplaySideEffectDescription(toolName);
|
|
607
593
|
return {
|
|
608
594
|
name: toolName,
|
|
609
|
-
label:
|
|
610
|
-
description:
|
|
611
|
-
promptSnippet: `Render a recorded Cursor SDK ${cursorToolName} operation without executing ${sideEffectDescription}.`,
|
|
612
|
-
promptGuidelines: [
|
|
613
|
-
`Use this tool only for replaying Cursor SDK ${cursorToolName} results that were already produced by Cursor; it does not execute ${sideEffectDescription}.`,
|
|
614
|
-
],
|
|
595
|
+
label: "Cursor activity",
|
|
596
|
+
description: "Display recorded Cursor SDK tool activity. This tool only returns recorded Cursor results and never executes work directly.",
|
|
615
597
|
parameters: cursorReplayToolSchema,
|
|
616
598
|
async execute() {
|
|
617
|
-
throw new Error(
|
|
599
|
+
throw new Error("No recorded Cursor activity result was available. This replay-only tool does not execute work directly.");
|
|
618
600
|
},
|
|
619
601
|
renderCall(args, theme, context) {
|
|
620
602
|
return renderCursorReplayCall(toolName, args as Record<string, unknown>, theme, context.isPartial);
|
|
@@ -13,7 +13,6 @@ import type { TSchema } from "typebox";
|
|
|
13
13
|
import { getCursorSessionCwd } from "./cursor-session-cwd.js";
|
|
14
14
|
import {
|
|
15
15
|
CURSOR_REPLAY_ACTIVITY_TOOL_NAME,
|
|
16
|
-
CURSOR_REPLAY_LEGACY_TOOL_NAMES,
|
|
17
16
|
isCursorReplayToolName,
|
|
18
17
|
} from "./cursor-tool-names.js";
|
|
19
18
|
import {
|
|
@@ -31,7 +30,7 @@ import {
|
|
|
31
30
|
} from "./cursor-native-tool-display-state.js";
|
|
32
31
|
|
|
33
32
|
const CURSOR_MODEL_ACTIVE_REPLAY_TOOL_NAMES = [CURSOR_REPLAY_ACTIVITY_TOOL_NAME] as const;
|
|
34
|
-
const CURSOR_REPLAY_TOOL_NAMES = [CURSOR_REPLAY_ACTIVITY_TOOL_NAME
|
|
33
|
+
const CURSOR_REPLAY_TOOL_NAMES = [CURSOR_REPLAY_ACTIVITY_TOOL_NAME] as const;
|
|
35
34
|
|
|
36
35
|
type AnyToolDefinition = ToolDefinition<TSchema, unknown, unknown>;
|
|
37
36
|
type RenderCall = NonNullable<AnyToolDefinition["renderCall"]>;
|
|
@@ -9,7 +9,7 @@ const GENERIC_CURSOR_SDK_ERROR_MESSAGE =
|
|
|
9
9
|
const AUTH_CURSOR_SDK_ERROR_MESSAGE =
|
|
10
10
|
"Cursor SDK request failed because the Cursor SDK API key may be invalid or unauthorized. Cursor Agent CLI/Desktop login is not reused. Run /login -> Use an API key -> Cursor, verify CURSOR_API_KEY, or pass --api-key, then retry.";
|
|
11
11
|
const NETWORK_CURSOR_SDK_ERROR_MESSAGE =
|
|
12
|
-
"Cursor SDK request
|
|
12
|
+
"Cursor SDK request failed during network or service I/O. Check your connection and retry; if this keeps happening, try again later or verify Cursor service availability.";
|
|
13
13
|
|
|
14
14
|
const GENERIC_CURSOR_RUN_FAILURE_TEXT = "cursor sdk run failed";
|
|
15
15
|
|
|
@@ -47,6 +47,10 @@ function isUnauthenticatedConnectCode(code: unknown): boolean {
|
|
|
47
47
|
return code === 16 || (typeof code === "string" && /^(?:16|unauthenticated)$/i.test(code));
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
+
function isUnavailableConnectCode(code: unknown): boolean {
|
|
51
|
+
return code === 14 || (typeof code === "string" && /^(?:14|unavailable)$/i.test(code));
|
|
52
|
+
}
|
|
53
|
+
|
|
50
54
|
function isCursorExtensionConnectStack(stack: string): boolean {
|
|
51
55
|
return stack.includes("@connectrpc/connect-node") && /(?:^|[\\/])pi-cursor-sdk(?:[\\/]|$)/.test(stack);
|
|
52
56
|
}
|
|
@@ -101,6 +105,10 @@ export function classifyCursorConnectError(error: unknown): CursorConnectErrorCl
|
|
|
101
105
|
return { kind: "unauthenticated", source: getCursorConnectSource(error, record) };
|
|
102
106
|
}
|
|
103
107
|
|
|
108
|
+
if (isUnavailableConnectCode(code)) {
|
|
109
|
+
return { kind: "network", source: getCursorConnectSource(error, record) };
|
|
110
|
+
}
|
|
111
|
+
|
|
104
112
|
const causeCode = getErrorStringField(cause, "code");
|
|
105
113
|
const causeSyscall = getErrorStringField(cause, "syscall");
|
|
106
114
|
if (isLikelyNetworkTimeout(`${message}\n${rawMessage}\n${causeCode ?? ""}\n${causeSyscall ?? ""}`)) {
|
|
@@ -179,8 +187,9 @@ export function sanitizeCursorProviderError(error: unknown, apiKey?: string): st
|
|
|
179
187
|
const message = error instanceof Error ? error.message : typeof error === "string" ? error : "";
|
|
180
188
|
if (message === MISSING_CURSOR_API_KEY_MESSAGE) return MISSING_CURSOR_API_KEY_MESSAGE;
|
|
181
189
|
const scrubbed = scrubSensitiveText(message, apiKey).trim();
|
|
182
|
-
|
|
190
|
+
const connectClassification = classifyCursorConnectError(error);
|
|
191
|
+
if (connectClassification?.kind === "unauthenticated" || isLikelyAuthError(scrubbed)) return AUTH_CURSOR_SDK_ERROR_MESSAGE;
|
|
192
|
+
if (connectClassification?.kind === "network" || isLikelyNetworkTimeout(scrubbed)) return NETWORK_CURSOR_SDK_ERROR_MESSAGE;
|
|
183
193
|
if (isGenericErrorMessage(scrubbed)) return GENERIC_CURSOR_SDK_ERROR_MESSAGE;
|
|
184
|
-
if (isLikelyNetworkTimeout(scrubbed)) return NETWORK_CURSOR_SDK_ERROR_MESSAGE;
|
|
185
194
|
return scrubbed || GENERIC_CURSOR_SDK_ERROR_MESSAGE;
|
|
186
195
|
}
|
|
@@ -55,8 +55,6 @@ export interface CursorReplayGenerateImageDetails {
|
|
|
55
55
|
imageMimeType?: string;
|
|
56
56
|
summary?: string;
|
|
57
57
|
expandedText?: string;
|
|
58
|
-
/** Legacy parsed title retained on older payloads; display always uses `Cursor generateImage`. */
|
|
59
|
-
title?: string;
|
|
60
58
|
collapseDetailsByDefault?: boolean;
|
|
61
59
|
}
|
|
62
60
|
|
|
@@ -79,7 +77,7 @@ export interface CursorReplayActivityDetails {
|
|
|
79
77
|
fileContentAfterWrite?: string;
|
|
80
78
|
}
|
|
81
79
|
|
|
82
|
-
/** Parsed replay details without a display title
|
|
80
|
+
/** Parsed replay details without a display title. */
|
|
83
81
|
export interface CursorReplayGenericFallbackDetails {
|
|
84
82
|
variant: "genericFallback";
|
|
85
83
|
sourceToolName: CursorReplayUnknownSourceToolName;
|
|
@@ -94,21 +92,6 @@ export type CursorReplayToolDetails =
|
|
|
94
92
|
| CursorReplayActivityDetails
|
|
95
93
|
| CursorReplayGenericFallbackDetails;
|
|
96
94
|
|
|
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
95
|
export type CursorReplayActivityDetailFields = Pick<
|
|
113
96
|
CursorReplayActivityDetails,
|
|
114
97
|
| "summary"
|
|
@@ -147,19 +130,12 @@ function readOptionalBoolean(record: Record<string, unknown>, key: string): bool
|
|
|
147
130
|
return typeof value === "boolean" ? value : undefined;
|
|
148
131
|
}
|
|
149
132
|
|
|
150
|
-
function
|
|
133
|
+
function readSourceToolName(record: Record<string, unknown>): string | undefined {
|
|
151
134
|
const sourceToolName = readOptionalString(record, "sourceToolName");
|
|
152
135
|
return sourceToolName?.trim() ? sourceToolName.trim() : undefined;
|
|
153
136
|
}
|
|
154
137
|
|
|
155
|
-
function
|
|
156
|
-
const sourceToolName = readCurrentSourceToolName(record);
|
|
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 {
|
|
138
|
+
function readVariant(record: Record<string, unknown>): string | undefined {
|
|
163
139
|
const variant = readOptionalString(record, "variant");
|
|
164
140
|
return variant?.trim() ? variant.trim() : undefined;
|
|
165
141
|
}
|
|
@@ -191,7 +167,6 @@ function parseCursorReplayNativeWriteDetails(record: Record<string, unknown>): C
|
|
|
191
167
|
}
|
|
192
168
|
|
|
193
169
|
function parseCursorReplayGenerateImageDetails(record: Record<string, unknown>): CursorReplayGenerateImageDetails {
|
|
194
|
-
const title = readOptionalString(record, "title");
|
|
195
170
|
const collapseDetailsByDefault = readOptionalBoolean(record, "collapseDetailsByDefault");
|
|
196
171
|
return {
|
|
197
172
|
variant: "generateImage",
|
|
@@ -200,7 +175,6 @@ function parseCursorReplayGenerateImageDetails(record: Record<string, unknown>):
|
|
|
200
175
|
imageMimeType: readOptionalString(record, "imageMimeType"),
|
|
201
176
|
summary: readOptionalString(record, "summary"),
|
|
202
177
|
expandedText: readOptionalString(record, "expandedText"),
|
|
203
|
-
...(title !== undefined ? { title } : {}),
|
|
204
178
|
...(collapseDetailsByDefault !== undefined ? { collapseDetailsByDefault } : {}),
|
|
205
179
|
};
|
|
206
180
|
}
|
|
@@ -263,37 +237,9 @@ export function resolveIncompleteReplayActivitySourceToolName(
|
|
|
263
237
|
return resolveParseActivitySourceToolName(sourceToolName);
|
|
264
238
|
}
|
|
265
239
|
|
|
266
|
-
function hasNativeEditChanges(record: Record<string, unknown>): boolean {
|
|
267
|
-
return Boolean(
|
|
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
240
|
type CursorReplayVariantParser = (record: Record<string, unknown>) => CursorReplayToolDetails | undefined;
|
|
292
241
|
|
|
293
|
-
function parseActivityVariantDetails(
|
|
294
|
-
record: Record<string, unknown>,
|
|
295
|
-
readSourceToolName: (record: Record<string, unknown>) => string | undefined,
|
|
296
|
-
): CursorReplayActivityDetails | undefined {
|
|
242
|
+
function parseActivityVariantDetails(record: Record<string, unknown>): CursorReplayActivityDetails | undefined {
|
|
297
243
|
const title = readOptionalString(record, "title")?.trim();
|
|
298
244
|
if (!title) return undefined;
|
|
299
245
|
return parseCursorReplayActivityDetails(
|
|
@@ -307,75 +253,29 @@ const CURRENT_REPLAY_VARIANT_PARSERS: Readonly<Record<CursorReplayToolDetailsVar
|
|
|
307
253
|
nativeEdit: parseCursorReplayNativeEditDetails,
|
|
308
254
|
nativeWrite: parseCursorReplayNativeWriteDetails,
|
|
309
255
|
generateImage: parseCursorReplayGenerateImageDetails,
|
|
310
|
-
activity:
|
|
311
|
-
|
|
312
|
-
return parseActivityVariantDetails(record, readCurrentSourceToolName);
|
|
313
|
-
},
|
|
314
|
-
genericFallback: (record) => parseCursorReplayGenericFallbackDetails(record, readCurrentSourceToolName(record) ?? "tool"),
|
|
256
|
+
activity: parseActivityVariantDetails,
|
|
257
|
+
genericFallback: (record) => parseCursorReplayGenericFallbackDetails(record, readSourceToolName(record) ?? "tool"),
|
|
315
258
|
};
|
|
316
259
|
|
|
317
|
-
|
|
318
|
-
edit: parseLegacyEditDetails,
|
|
319
|
-
write: parseLegacyWriteDetails,
|
|
320
|
-
titledActivity: (record) => parseActivityVariantDetails(record, readLegacySourceToolName),
|
|
321
|
-
};
|
|
322
|
-
|
|
323
|
-
export function parseStrictCurrentCursorReplayToolDetails(value: unknown): CursorReplayToolDetails | undefined {
|
|
260
|
+
export function parseCursorReplayToolDetails(value: unknown): CursorReplayToolDetails | undefined {
|
|
324
261
|
if (!isRecord(value)) return undefined;
|
|
325
|
-
const variant =
|
|
262
|
+
const variant = readVariant(value);
|
|
326
263
|
if (!variant) return undefined;
|
|
327
264
|
return CURRENT_REPLAY_VARIANT_PARSERS[variant as CursorReplayToolDetailsVariant]?.(value);
|
|
328
265
|
}
|
|
329
266
|
|
|
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
|
-
export function parseCursorReplayToolDetails(value: unknown): CursorReplayToolDetails | undefined {
|
|
355
|
-
return parseStrictCurrentCursorReplayToolDetails(value) ?? upgradeLegacyCursorReplayToolDetails(value);
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
/** @deprecated Prefer {@link parseCursorReplayToolDetails} for validated narrowing. */
|
|
359
|
-
export const asCursorReplayToolDetails = parseCursorReplayToolDetails;
|
|
360
|
-
|
|
361
267
|
export function buildCursorReplayNativeEditDetails(
|
|
362
268
|
fields: Omit<CursorReplayNativeEditDetails, "variant">,
|
|
363
269
|
): CursorReplayNativeEditDetails {
|
|
364
270
|
return { variant: "nativeEdit", ...fields };
|
|
365
271
|
}
|
|
366
272
|
|
|
367
|
-
/** @deprecated Prefer {@link buildCursorReplayNativeEditDetails}. */
|
|
368
|
-
export const buildCursorReplayEditDetails = buildCursorReplayNativeEditDetails;
|
|
369
|
-
|
|
370
273
|
export function buildCursorReplayNativeWriteDetails(
|
|
371
274
|
fields: Omit<CursorReplayNativeWriteDetails, "variant">,
|
|
372
275
|
): CursorReplayNativeWriteDetails {
|
|
373
276
|
return { variant: "nativeWrite", ...fields };
|
|
374
277
|
}
|
|
375
278
|
|
|
376
|
-
/** @deprecated Prefer {@link buildCursorReplayNativeWriteDetails}. */
|
|
377
|
-
export const buildCursorReplayWriteDetails = buildCursorReplayNativeWriteDetails;
|
|
378
|
-
|
|
379
279
|
export function assembleCursorReplayActivityDetails(
|
|
380
280
|
sourceToolName: CursorReplayActivitySourceToolName,
|
|
381
281
|
title: string,
|
|
@@ -402,10 +302,7 @@ export function assembleCursorReplayActivityDetails(
|
|
|
402
302
|
};
|
|
403
303
|
}
|
|
404
304
|
|
|
405
|
-
|
|
406
|
-
export const assembleCursorReplayTitledActivityDetails = assembleCursorReplayActivityDetails;
|
|
407
|
-
|
|
408
|
-
export const CURSOR_REPLAY_GENERATE_IMAGE_RESULT_TITLE = "Cursor generateImage" as const;
|
|
305
|
+
export const CURSOR_REPLAY_GENERATE_IMAGE_RESULT_TITLE = "Cursor image generation" as const;
|
|
409
306
|
|
|
410
307
|
export function assembleCursorReplayGenerateImageDetails(
|
|
411
308
|
fields: CursorReplayGenerateImageDetailFields,
|
|
@@ -430,18 +327,12 @@ export function isCursorReplayNativeEditDetails(
|
|
|
430
327
|
return details.variant === "nativeEdit";
|
|
431
328
|
}
|
|
432
329
|
|
|
433
|
-
/** @deprecated Prefer {@link isCursorReplayNativeEditDetails}. */
|
|
434
|
-
export const isCursorReplayEditDetails = isCursorReplayNativeEditDetails;
|
|
435
|
-
|
|
436
330
|
export function isCursorReplayNativeWriteDetails(
|
|
437
331
|
details: CursorReplayToolDetails,
|
|
438
332
|
): details is CursorReplayNativeWriteDetails {
|
|
439
333
|
return details.variant === "nativeWrite";
|
|
440
334
|
}
|
|
441
335
|
|
|
442
|
-
/** @deprecated Prefer {@link isCursorReplayNativeWriteDetails}. */
|
|
443
|
-
export const isCursorReplayWriteDetails = isCursorReplayNativeWriteDetails;
|
|
444
|
-
|
|
445
336
|
export function isCursorReplayGenerateImageDetails(
|
|
446
337
|
details: CursorReplayToolDetails,
|
|
447
338
|
): details is CursorReplayGenerateImageDetails {
|
|
@@ -454,9 +345,6 @@ export function isCursorReplayActivityDetails(
|
|
|
454
345
|
return details.variant === "activity";
|
|
455
346
|
}
|
|
456
347
|
|
|
457
|
-
/** @deprecated Prefer {@link isCursorReplayActivityDetails}. */
|
|
458
|
-
export const isCursorReplayTitledActivityDetails = isCursorReplayActivityDetails;
|
|
459
|
-
|
|
460
348
|
export function isCursorReplayGenericFallbackDetails(
|
|
461
349
|
details: CursorReplayToolDetails,
|
|
462
350
|
): details is CursorReplayGenericFallbackDetails {
|
package/src/cursor-tool-names.ts
CHANGED
|
@@ -1,18 +1,9 @@
|
|
|
1
1
|
export {
|
|
2
|
-
CURSOR_REPLAY_ACTIVITY_LABEL_KEYS_BY_TOOL_NAME,
|
|
3
2
|
CURSOR_REPLAY_ACTIVITY_TOOL_NAME,
|
|
4
|
-
CURSOR_REPLAY_LEGACY_TOOL_NAMES,
|
|
5
|
-
getCursorReplayActivityLabelKey,
|
|
6
3
|
getCursorReplayActivityTitle,
|
|
7
|
-
getCursorReplayDisplayLabel,
|
|
8
4
|
getCursorReplayPromptLabel,
|
|
9
|
-
getCursorReplaySideEffectDescription,
|
|
10
|
-
getCursorReplayOperationLabel,
|
|
11
|
-
getCursorReplayWrapperLabel,
|
|
12
|
-
isCursorReplayLegacyToolName,
|
|
13
5
|
isCursorReplayToolName,
|
|
14
6
|
isExcludedFromCursorBridgeExposure,
|
|
15
7
|
type CursorReplayActivityToolName,
|
|
16
|
-
type CursorReplayLegacyToolName,
|
|
17
8
|
type CursorReplayToolName,
|
|
18
9
|
} from "./cursor-tool-presentation-registry.js";
|
|
@@ -57,8 +57,6 @@ export const CURSOR_REPLAY_ACTIVITY_TOOL_NAME = "cursor" as const;
|
|
|
57
57
|
|
|
58
58
|
export type CursorWebToolKind = "webSearch" | "webFetch";
|
|
59
59
|
|
|
60
|
-
export type CursorReplaySideEffectCategory = "file_mutations" | "real_tool_work";
|
|
61
|
-
|
|
62
60
|
export type CursorToolLifecycleLabelKind =
|
|
63
61
|
| "task"
|
|
64
62
|
| "shell"
|
|
@@ -96,10 +94,6 @@ export interface CursorToolPresentationSpec {
|
|
|
96
94
|
normalizedName: CursorReplaySourceToolName;
|
|
97
95
|
/** Raw SDK/host names that resolve to this tool via {@link normalizeCursorToolName}. */
|
|
98
96
|
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
97
|
displayLabel: string;
|
|
104
98
|
visibility: CursorToolVisibilityPolicy;
|
|
105
99
|
webKind?: CursorWebToolKind;
|
|
@@ -107,52 +101,43 @@ export interface CursorToolPresentationSpec {
|
|
|
107
101
|
webNamePatterns?: readonly RegExp[];
|
|
108
102
|
lifecycleLabelKind?: CursorToolLifecycleLabelKind;
|
|
109
103
|
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
104
|
activityReplay?: CursorToolActivityReplaySpec;
|
|
115
105
|
generateImageReplay?: CursorToolGenerateImageReplaySpec;
|
|
116
106
|
}
|
|
117
107
|
|
|
118
108
|
const WEB_SEARCH_NAME_PATTERN =
|
|
119
|
-
/^(?:web[-_ ]?search|search[-_ ]?web|websearch|browser[-_ ]?search
|
|
109
|
+
/^(?:web[-_ ]?search|search[-_ ]?web|websearch|browser[-_ ]?search)$/i;
|
|
120
110
|
const WEB_FETCH_NAME_PATTERN =
|
|
121
|
-
/^(?:web[-_ ]?fetch|fetch[-_ ]?web|webfetch|browser[-_ ]?fetch|fetch[-_ ]?url
|
|
111
|
+
/^(?:web[-_ ]?fetch|fetch[-_ ]?web|webfetch|browser[-_ ]?fetch|fetch[-_ ]?url)$/i;
|
|
122
112
|
|
|
123
113
|
export const CURSOR_TOOL_PRESENTATION_SPECS = [
|
|
124
114
|
{
|
|
125
115
|
normalizedName: "read",
|
|
126
116
|
nameAliases: ["read_file"],
|
|
127
|
-
promptLabel: "read",
|
|
128
117
|
displayLabel: "read",
|
|
129
118
|
visibility: { incompleteTitle: "Cursor read", fastLocalDiscovery: true },
|
|
130
119
|
},
|
|
131
120
|
{
|
|
132
121
|
normalizedName: "grep",
|
|
133
122
|
nameAliases: ["grep_search", "search"],
|
|
134
|
-
promptLabel: "grep",
|
|
135
123
|
displayLabel: "grep",
|
|
136
124
|
visibility: { incompleteTitle: "Cursor grep", fastLocalDiscovery: true },
|
|
137
125
|
},
|
|
138
126
|
{
|
|
139
127
|
normalizedName: "glob",
|
|
140
128
|
nameAliases: ["file_search"],
|
|
141
|
-
promptLabel: "glob",
|
|
142
129
|
displayLabel: "glob",
|
|
143
130
|
visibility: { incompleteTitle: "Cursor find", fastLocalDiscovery: true },
|
|
144
131
|
},
|
|
145
132
|
{
|
|
146
133
|
normalizedName: "ls",
|
|
147
134
|
nameAliases: ["list_dir"],
|
|
148
|
-
promptLabel: "ls",
|
|
149
135
|
displayLabel: "ls",
|
|
150
136
|
visibility: { incompleteTitle: "Cursor ls", fastLocalDiscovery: true },
|
|
151
137
|
},
|
|
152
138
|
{
|
|
153
139
|
normalizedName: "shell",
|
|
154
140
|
nameAliases: ["run_terminal_cmd", "terminal", "bash"],
|
|
155
|
-
promptLabel: "shell",
|
|
156
141
|
displayLabel: "shell",
|
|
157
142
|
visibility: {
|
|
158
143
|
incompleteTitle: "Cursor shell",
|
|
@@ -174,29 +159,19 @@ export const CURSOR_TOOL_PRESENTATION_SPECS = [
|
|
|
174
159
|
"notebook_edit",
|
|
175
160
|
"notebookedit",
|
|
176
161
|
],
|
|
177
|
-
replayLegacyName: "cursor_edit",
|
|
178
|
-
promptLabel: "Cursor edit",
|
|
179
162
|
displayLabel: "Cursor edit",
|
|
180
163
|
visibility: {},
|
|
181
|
-
replayWrapperLabel: "edit",
|
|
182
|
-
replaySideEffectCategory: "file_mutations",
|
|
183
164
|
replayCallSummary: withActivitySummaryFallback(summarizeReplayPath),
|
|
184
165
|
},
|
|
185
166
|
{
|
|
186
167
|
normalizedName: "write",
|
|
187
168
|
nameAliases: ["write_file", "writefile"],
|
|
188
|
-
replayLegacyName: "cursor_write",
|
|
189
|
-
promptLabel: "Cursor write",
|
|
190
169
|
displayLabel: "Cursor write",
|
|
191
170
|
visibility: {},
|
|
192
|
-
replayWrapperLabel: "write",
|
|
193
|
-
replaySideEffectCategory: "file_mutations",
|
|
194
171
|
replayCallSummary: withActivitySummaryFallback(summarizeReplayPath),
|
|
195
172
|
},
|
|
196
173
|
{
|
|
197
174
|
normalizedName: "delete",
|
|
198
|
-
replayLegacyName: "cursor_delete",
|
|
199
|
-
promptLabel: "Cursor delete",
|
|
200
175
|
displayLabel: "Cursor delete",
|
|
201
176
|
visibility: {},
|
|
202
177
|
replayCallSummary: withActivitySummaryFallback(summarizeReplayPath),
|
|
@@ -207,8 +182,6 @@ export const CURSOR_TOOL_PRESENTATION_SPECS = [
|
|
|
207
182
|
},
|
|
208
183
|
{
|
|
209
184
|
normalizedName: "readLints",
|
|
210
|
-
replayLegacyName: "cursor_read_lints",
|
|
211
|
-
promptLabel: "Cursor diagnostics",
|
|
212
185
|
displayLabel: "Cursor diagnostics",
|
|
213
186
|
visibility: {},
|
|
214
187
|
replayCallSummary: withActivitySummaryFallback(summarizeReplayReadLints),
|
|
@@ -219,8 +192,6 @@ export const CURSOR_TOOL_PRESENTATION_SPECS = [
|
|
|
219
192
|
},
|
|
220
193
|
{
|
|
221
194
|
normalizedName: "updateTodos",
|
|
222
|
-
replayLegacyName: "cursor_update_todos",
|
|
223
|
-
promptLabel: "Cursor todos",
|
|
224
195
|
displayLabel: "Cursor todos",
|
|
225
196
|
visibility: { lifecycleEligible: true },
|
|
226
197
|
lifecycleLabelKind: "updateTodos",
|
|
@@ -232,8 +203,6 @@ export const CURSOR_TOOL_PRESENTATION_SPECS = [
|
|
|
232
203
|
},
|
|
233
204
|
{
|
|
234
205
|
normalizedName: "createPlan",
|
|
235
|
-
replayLegacyName: "cursor_create_plan",
|
|
236
|
-
promptLabel: "Cursor plan",
|
|
237
206
|
displayLabel: "Cursor plan",
|
|
238
207
|
visibility: { lifecycleEligible: true },
|
|
239
208
|
lifecycleLabelKind: "createPlan",
|
|
@@ -245,8 +214,6 @@ export const CURSOR_TOOL_PRESENTATION_SPECS = [
|
|
|
245
214
|
},
|
|
246
215
|
{
|
|
247
216
|
normalizedName: "task",
|
|
248
|
-
replayLegacyName: "cursor_task",
|
|
249
|
-
promptLabel: "Cursor task",
|
|
250
217
|
displayLabel: "Cursor task",
|
|
251
218
|
visibility: { lifecycleEligible: true },
|
|
252
219
|
lifecycleLabelKind: "task",
|
|
@@ -258,8 +225,6 @@ export const CURSOR_TOOL_PRESENTATION_SPECS = [
|
|
|
258
225
|
},
|
|
259
226
|
{
|
|
260
227
|
normalizedName: "generateImage",
|
|
261
|
-
replayLegacyName: "cursor_generate_image",
|
|
262
|
-
promptLabel: "Cursor image generation",
|
|
263
228
|
displayLabel: "Cursor image generation",
|
|
264
229
|
visibility: { lifecycleEligible: true },
|
|
265
230
|
lifecycleLabelKind: "generateImage",
|
|
@@ -273,9 +238,6 @@ export const CURSOR_TOOL_PRESENTATION_SPECS = [
|
|
|
273
238
|
},
|
|
274
239
|
{
|
|
275
240
|
normalizedName: "mcp",
|
|
276
|
-
replayLegacyName: "cursor_mcp",
|
|
277
|
-
replayOperationLabel: "MCP",
|
|
278
|
-
promptLabel: "Cursor MCP",
|
|
279
241
|
displayLabel: "Cursor MCP",
|
|
280
242
|
visibility: { lifecycleEligible: true },
|
|
281
243
|
lifecycleLabelKind: "mcp",
|
|
@@ -287,8 +249,6 @@ export const CURSOR_TOOL_PRESENTATION_SPECS = [
|
|
|
287
249
|
},
|
|
288
250
|
{
|
|
289
251
|
normalizedName: "semSearch",
|
|
290
|
-
replayLegacyName: "cursor_sem_search",
|
|
291
|
-
promptLabel: "Cursor semantic search",
|
|
292
252
|
displayLabel: "Cursor semantic search",
|
|
293
253
|
visibility: { lifecycleEligible: true },
|
|
294
254
|
lifecycleLabelKind: "semSearch",
|
|
@@ -300,8 +260,6 @@ export const CURSOR_TOOL_PRESENTATION_SPECS = [
|
|
|
300
260
|
},
|
|
301
261
|
{
|
|
302
262
|
normalizedName: "recordScreen",
|
|
303
|
-
replayLegacyName: "cursor_record_screen",
|
|
304
|
-
promptLabel: "Cursor screen recording",
|
|
305
263
|
displayLabel: "Cursor screen recording",
|
|
306
264
|
visibility: { lifecycleEligible: true },
|
|
307
265
|
lifecycleLabelKind: "recordScreen",
|
|
@@ -314,9 +272,6 @@ export const CURSOR_TOOL_PRESENTATION_SPECS = [
|
|
|
314
272
|
{
|
|
315
273
|
normalizedName: "webSearch",
|
|
316
274
|
nameAliases: ["websearch", "web_search", "web-search"],
|
|
317
|
-
replayLegacyName: "cursor_web_search",
|
|
318
|
-
replayOperationLabel: "web search",
|
|
319
|
-
promptLabel: "Cursor web search",
|
|
320
275
|
displayLabel: "Cursor web search",
|
|
321
276
|
visibility: { lifecycleEligible: true },
|
|
322
277
|
webKind: "webSearch",
|
|
@@ -333,9 +288,6 @@ export const CURSOR_TOOL_PRESENTATION_SPECS = [
|
|
|
333
288
|
{
|
|
334
289
|
normalizedName: "webFetch",
|
|
335
290
|
nameAliases: ["webfetch", "web_fetch", "web-fetch"],
|
|
336
|
-
replayLegacyName: "cursor_web_fetch",
|
|
337
|
-
replayOperationLabel: "web fetch",
|
|
338
|
-
promptLabel: "Cursor web fetch",
|
|
339
291
|
displayLabel: "Cursor web fetch",
|
|
340
292
|
visibility: { lifecycleEligible: true },
|
|
341
293
|
webKind: "webFetch",
|
|
@@ -354,56 +306,19 @@ export const CURSOR_TOOL_PRESENTATION_SPECS = [
|
|
|
354
306
|
type CursorToolPresentationSpecEntry = (typeof CURSOR_TOOL_PRESENTATION_SPECS)[number];
|
|
355
307
|
|
|
356
308
|
export type CursorNormalizedToolName = CursorReplaySourceToolName;
|
|
309
|
+
export type CursorReplayToolName = typeof CURSOR_REPLAY_ACTIVITY_TOOL_NAME;
|
|
357
310
|
|
|
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<
|
|
311
|
+
type CursorToolPresentationSpecWithNeutralActivity = Extract<
|
|
366
312
|
CursorToolPresentationSpecEntry,
|
|
367
|
-
{ readonly
|
|
368
|
-
>;
|
|
313
|
+
{ readonly activityReplay: CursorToolActivityReplaySpec } | { readonly generateImageReplay: CursorToolGenerateImageReplaySpec }
|
|
314
|
+
> | Extract<CursorToolPresentationSpecEntry, { readonly normalizedName: "edit" | "write" }>;
|
|
369
315
|
|
|
370
|
-
|
|
316
|
+
export type CursorReplayActivityToolName = CursorToolPresentationSpecWithNeutralActivity["normalizedName"];
|
|
371
317
|
|
|
372
|
-
function
|
|
373
|
-
return "
|
|
318
|
+
function hasNeutralActivityTitle(spec: CursorToolPresentationSpec): boolean {
|
|
319
|
+
return Boolean(spec.activityReplay || spec.generateImageReplay || spec.normalizedName === "edit" || spec.normalizedName === "write");
|
|
374
320
|
}
|
|
375
321
|
|
|
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
322
|
const SPECS_BY_NORMALIZED_NAME = new Map<string, CursorToolPresentationSpec>(
|
|
408
323
|
CURSOR_TOOL_PRESENTATION_SPECS.map((spec) => [spec.normalizedName, spec]),
|
|
409
324
|
);
|
|
@@ -412,10 +327,6 @@ const SPECS_BY_NORMALIZED_KEY = new Map<string, CursorToolPresentationSpec>(
|
|
|
412
327
|
CURSOR_TOOL_PRESENTATION_SPECS.map((spec) => [spec.normalizedName.toLowerCase(), spec]),
|
|
413
328
|
);
|
|
414
329
|
|
|
415
|
-
const SPECS_BY_REPLAY_LEGACY_NAME = new Map<CursorReplayLegacyToolName, CursorToolPresentationSpec>(
|
|
416
|
-
CURSOR_REPLAY_SPECS.map((spec) => [spec.replayLegacyName, spec]),
|
|
417
|
-
);
|
|
418
|
-
|
|
419
330
|
const ALIAS_TO_NORMALIZED_NAME = new Map<string, CursorNormalizedToolName>(
|
|
420
331
|
CURSOR_TOOL_PRESENTATION_SPECS.flatMap((spec) => {
|
|
421
332
|
const aliases = "nameAliases" in spec ? spec.nameAliases : undefined;
|
|
@@ -437,11 +348,7 @@ export function getCursorToolPresentationSpec(
|
|
|
437
348
|
): CursorToolPresentationSpec | undefined {
|
|
438
349
|
const trimmed = name.trim();
|
|
439
350
|
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
|
-
);
|
|
351
|
+
return SPECS_BY_NORMALIZED_NAME.get(trimmed) ?? SPECS_BY_NORMALIZED_KEY.get(trimmed.toLowerCase());
|
|
445
352
|
}
|
|
446
353
|
|
|
447
354
|
export function normalizeCursorToolName(name: string): string {
|
|
@@ -464,64 +371,21 @@ export function classifyCursorWebToolKind(name: string | undefined): CursorWebTo
|
|
|
464
371
|
return spec?.webKind;
|
|
465
372
|
}
|
|
466
373
|
|
|
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
374
|
export function isCursorReplayToolName(toolName: string): toolName is CursorReplayToolName {
|
|
474
|
-
return toolName === CURSOR_REPLAY_ACTIVITY_TOOL_NAME
|
|
375
|
+
return toolName === CURSOR_REPLAY_ACTIVITY_TOOL_NAME;
|
|
475
376
|
}
|
|
476
377
|
|
|
477
378
|
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;
|
|
379
|
+
return toolName === CURSOR_REPLAY_ACTIVITY_TOOL_NAME;
|
|
499
380
|
}
|
|
500
381
|
|
|
501
382
|
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;
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
export function getCursorReplayDisplayLabel(toolName: CursorReplayToolName): string {
|
|
511
|
-
if (toolName === CURSOR_REPLAY_ACTIVITY_TOOL_NAME) return "Cursor activity";
|
|
512
|
-
const spec = SPECS_BY_REPLAY_LEGACY_NAME.get(toolName);
|
|
513
|
-
return spec?.displayLabel ?? toolName;
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
export function getCursorReplayActivityLabelKey(toolName: string): CursorReplayLegacyToolName | undefined {
|
|
517
|
-
const spec = getCursorToolPresentationSpec(toolName);
|
|
518
|
-
if (!spec?.replayLegacyName || !isCursorReplayLegacyToolName(spec.replayLegacyName)) return undefined;
|
|
519
|
-
return spec.replayLegacyName;
|
|
383
|
+
return toolName === CURSOR_REPLAY_ACTIVITY_TOOL_NAME ? "Cursor activity" : toolName;
|
|
520
384
|
}
|
|
521
385
|
|
|
522
386
|
export function getCursorReplayActivityTitle(toolName: string): string | undefined {
|
|
523
387
|
const spec = getCursorToolPresentationSpec(toolName);
|
|
524
|
-
if (!spec
|
|
388
|
+
if (!spec || !hasNeutralActivityTitle(spec)) return undefined;
|
|
525
389
|
return spec.displayLabel;
|
|
526
390
|
}
|
|
527
391
|
|
|
@@ -546,11 +410,11 @@ export function getCursorToolGenerateImageReplaySpec(normalizedKey: string): Cur
|
|
|
546
410
|
}
|
|
547
411
|
|
|
548
412
|
export function getCursorReplayCallSummary(
|
|
549
|
-
toolName: CursorReplayToolName,
|
|
413
|
+
toolName: CursorReplayToolName | CursorReplaySourceToolName,
|
|
550
414
|
args: CursorReplaySummaryArgs | undefined,
|
|
551
415
|
): string | undefined {
|
|
552
416
|
if (toolName === CURSOR_REPLAY_ACTIVITY_TOOL_NAME) {
|
|
553
417
|
return readCursorReplaySummaryString(args, "activitySummary") ?? summarizeReplayGenericActivity(args);
|
|
554
418
|
}
|
|
555
|
-
return
|
|
419
|
+
return getCursorToolPresentationSpec(toolName)?.replayCallSummary?.(args);
|
|
556
420
|
}
|
|
@@ -1,15 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
CURSOR_KNOWN_NORMALIZED_TOOL_NAMES,
|
|
3
3
|
CURSOR_REPLAY_ACTIVITY_TOOL_NAME,
|
|
4
|
-
getCursorReplayActivityLabelKey,
|
|
5
4
|
getCursorReplayActivityTitle,
|
|
6
5
|
getCursorReplayCallSummary,
|
|
7
|
-
getCursorReplayDisplayLabel,
|
|
8
6
|
getCursorToolActivityReplaySpec,
|
|
9
7
|
getCursorToolGenerateImageReplaySpec,
|
|
10
8
|
type CursorNormalizedToolName,
|
|
11
9
|
type CursorReplayActivityToolName,
|
|
12
|
-
type CursorReplayLegacyToolName,
|
|
13
10
|
} from "./cursor-tool-presentation-registry.js";
|
|
14
11
|
import { resolveCursorEditDiff } from "./cursor-edit-diff.js";
|
|
15
12
|
import {
|
|
@@ -88,12 +85,6 @@ interface ToolDisplaySpec {
|
|
|
88
85
|
buildPiToolDisplay: (context: ToolDisplayContext) => CursorPiToolDisplay;
|
|
89
86
|
}
|
|
90
87
|
|
|
91
|
-
function requireReplayActivityLabelKey(normalizedName: CursorReplayActivityToolName): CursorReplayLegacyToolName {
|
|
92
|
-
const labelKey = getCursorReplayActivityLabelKey(normalizedName);
|
|
93
|
-
if (!labelKey) throw new Error(`Missing replay activity label for ${normalizedName}`);
|
|
94
|
-
return labelKey;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
88
|
function textToolResult(text: string, details?: unknown): PiToolDisplayResult {
|
|
98
89
|
return { content: [{ type: "text", text }], details };
|
|
99
90
|
}
|
|
@@ -112,10 +103,10 @@ function buildCursorActivityDisplayArgs(
|
|
|
112
103
|
}
|
|
113
104
|
|
|
114
105
|
function buildRegistryReplaySummary(
|
|
115
|
-
|
|
106
|
+
sourceToolName: CursorReplayActivityToolName,
|
|
116
107
|
args: CursorReplaySummaryArgs,
|
|
117
108
|
): string | undefined {
|
|
118
|
-
return getCursorReplayCallSummary(
|
|
109
|
+
return getCursorReplayCallSummary(sourceToolName, args);
|
|
119
110
|
}
|
|
120
111
|
|
|
121
112
|
function buildReplaySummaryDisplay(
|
|
@@ -151,10 +142,9 @@ function buildActivityReplayDisplay(
|
|
|
151
142
|
const spec = TOOL_DISPLAY_SPECS[sourceToolName];
|
|
152
143
|
const activity = getCursorToolActivityReplaySpec(sourceToolName);
|
|
153
144
|
if (!activity) throw new Error(`Missing activity replay spec for ${sourceToolName}`);
|
|
154
|
-
const
|
|
155
|
-
const activityTitle = getCursorReplayDisplayLabel(labelKey);
|
|
145
|
+
const activityTitle = getCursorReplayActivityTitle(sourceToolName) ?? buildGenericUnknownToolActivityTitle(sourceToolName);
|
|
156
146
|
const replayArgs = activity.buildActivityArgs(context);
|
|
157
|
-
const activitySummary = buildRegistryReplaySummary(
|
|
147
|
+
const activitySummary = buildRegistryReplaySummary(sourceToolName, replayArgs);
|
|
158
148
|
const activityArgs = buildCursorActivityDisplayArgs({ ...replayArgs }, activityTitle, activitySummary);
|
|
159
149
|
const contentText = spec.formatTranscript(context).trimEnd();
|
|
160
150
|
const activityFields = activity.buildDetails(context, contentText);
|
|
@@ -173,10 +163,9 @@ function buildGenerateImageReplayDisplay(context: ToolDisplayContext): CursorPiT
|
|
|
173
163
|
const spec = TOOL_DISPLAY_SPECS.generateImage;
|
|
174
164
|
const replay = getCursorToolGenerateImageReplaySpec("generateImage");
|
|
175
165
|
if (!replay) throw new Error("Missing generate image replay spec");
|
|
176
|
-
const
|
|
177
|
-
const activityTitle = getCursorReplayDisplayLabel(labelKey);
|
|
166
|
+
const activityTitle = getCursorReplayActivityTitle("generateImage") ?? "Cursor image generation";
|
|
178
167
|
const replayArgs = replay.buildActivityArgs(context);
|
|
179
|
-
const activitySummary = buildRegistryReplaySummary(
|
|
168
|
+
const activitySummary = buildRegistryReplaySummary("generateImage", replayArgs);
|
|
180
169
|
const activityArgs = buildCursorActivityDisplayArgs({ ...replayArgs }, activityTitle, activitySummary);
|
|
181
170
|
const contentText = spec.formatTranscript(context).trimEnd();
|
|
182
171
|
const details = assembleCursorReplayGenerateImageDetails(
|
|
@@ -201,11 +190,7 @@ function buildGenericPiToolDisplay(context: ToolDisplayContext): CursorPiToolDis
|
|
|
201
190
|
const fallbackBody = contentText.includes("\n\n") ? contentText.slice(contentText.indexOf("\n\n") + 2) : "";
|
|
202
191
|
const activitySummary =
|
|
203
192
|
result.status === "error" ? undefined : firstNonEmptyLine(fallbackBody);
|
|
204
|
-
const activityArgs = buildCursorActivityDisplayArgs(
|
|
205
|
-
{ cursorToolName: displayName === "unknown" ? "tool" : displayName },
|
|
206
|
-
activityTitle,
|
|
207
|
-
activitySummary,
|
|
208
|
-
);
|
|
193
|
+
const activityArgs = buildCursorActivityDisplayArgs({}, activityTitle, activitySummary);
|
|
209
194
|
const summary =
|
|
210
195
|
result.status === "error"
|
|
211
196
|
? undefined
|