pi-cursor-sdk 0.1.37 → 0.1.39
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 +34 -0
- package/README.md +2 -2
- package/docs/cursor-model-ux-spec.md +1 -1
- package/docs/cursor-native-tool-replay.md +5 -5
- 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 -10
- 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 +4 -21
- package/src/cursor-native-tool-display-state.ts +1 -1
- package/src/cursor-native-tool-display-tools.ts +10 -17
- 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-errors.ts +11 -4
- 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 +16 -6
- 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-shell-output.ts +38 -3
- 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 +52 -80
- 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 +22 -10
- package/src/cursor-tool-presentation-registry.ts +27 -18
- 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 +16 -41
- 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 -9
|
@@ -11,16 +11,16 @@ export function resolveCursorSettingSources(raw?: string): SettingSource[] | und
|
|
|
11
11
|
return resolveCursorSettingSourcesJs(raw) as SettingSource[] | undefined;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
export function getEffectiveCursorSettingSources(
|
|
14
|
+
export function getEffectiveCursorSettingSources(
|
|
15
|
+
raw: string | undefined = process.env[CURSOR_SETTING_SOURCES_ENV],
|
|
16
|
+
): SettingSource[] | undefined {
|
|
15
17
|
return resolveCursorSettingSources(raw);
|
|
16
18
|
}
|
|
17
19
|
|
|
18
|
-
export function
|
|
20
|
+
export function cursorSettingSourcesIncludes(
|
|
21
|
+
settingSources: SettingSource[] | undefined,
|
|
22
|
+
source: Extract<SettingSource, "user" | "project">,
|
|
23
|
+
): boolean {
|
|
19
24
|
if (!settingSources?.length) return false;
|
|
20
|
-
return settingSources.includes("all") || settingSources.includes(
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export function cursorSettingSourcesLoadProjectAgentsRules(settingSources: SettingSource[] | undefined): boolean {
|
|
24
|
-
if (!settingSources?.length) return false;
|
|
25
|
-
return settingSources.includes("all") || settingSources.includes("project");
|
|
25
|
+
return settingSources.includes("all") || settingSources.includes(source);
|
|
26
26
|
}
|
package/src/cursor-skill-tool.ts
CHANGED
|
@@ -2,19 +2,15 @@ import type { Dirent } from "node:fs";
|
|
|
2
2
|
import { readdir, readFile } from "node:fs/promises";
|
|
3
3
|
import { dirname, join, relative } from "node:path";
|
|
4
4
|
import type {
|
|
5
|
-
BeforeAgentStartEvent,
|
|
6
|
-
BeforeAgentStartEventResult,
|
|
7
5
|
BuildSystemPromptOptions,
|
|
8
6
|
ExtensionAPI,
|
|
9
7
|
ExtensionContext,
|
|
10
|
-
ExtensionHandler,
|
|
11
|
-
SessionStartEvent,
|
|
12
8
|
Skill,
|
|
13
|
-
TurnStartEvent,
|
|
14
9
|
} from "@earendil-works/pi-coding-agent";
|
|
15
10
|
import { Type } from "typebox";
|
|
16
11
|
import { isCursorModel } from "./cursor-model.js";
|
|
17
|
-
import {
|
|
12
|
+
import { registerCursorModelLifecycle, type CursorModelLifecycleExtensionApi } from "./cursor-model-lifecycle.js";
|
|
13
|
+
import { resolveCursorPiToolBridgeEnabled } from "./cursor-pi-tool-bridge-env.js";
|
|
18
14
|
|
|
19
15
|
export const CURSOR_ACTIVATE_SKILL_TOOL_NAME = "cursor_activate_skill";
|
|
20
16
|
export const CURSOR_ACTIVATE_SKILL_MCP_NAME = "pi__cursor_activate_skill";
|
|
@@ -23,12 +19,7 @@ const AVAILABLE_SKILLS_SECTION_PATTERN = /\n\nThe following skills provide speci
|
|
|
23
19
|
const MAX_SKILL_RESOURCES = 80;
|
|
24
20
|
const RESOURCE_DIR_NAMES = ["scripts", "references", "assets"] as const;
|
|
25
21
|
|
|
26
|
-
type CursorSkillToolExtensionApi = Pick<ExtensionAPI, "getActiveTools" | "registerTool" | "setActiveTools"> &
|
|
27
|
-
on(event: "session_start", handler: ExtensionHandler<SessionStartEvent>): void;
|
|
28
|
-
on(event: "before_agent_start", handler: ExtensionHandler<BeforeAgentStartEvent, BeforeAgentStartEventResult>): void;
|
|
29
|
-
on(event: "turn_start", handler: ExtensionHandler<TurnStartEvent>): void;
|
|
30
|
-
on(event: "model_select", handler: (event: { model: ExtensionContext["model"] }, ctx: ExtensionContext) => Promise<void> | void): void;
|
|
31
|
-
};
|
|
22
|
+
type CursorSkillToolExtensionApi = Pick<ExtensionAPI, "getActiveTools" | "registerTool" | "setActiveTools"> & CursorModelLifecycleExtensionApi;
|
|
32
23
|
|
|
33
24
|
type CursorActivateSkillParams = {
|
|
34
25
|
name?: string;
|
|
@@ -229,26 +220,28 @@ export function registerCursorSkillTool(pi: CursorSkillToolExtensionApi): void {
|
|
|
229
220
|
syncCursorSkillToolForModel(pi, model);
|
|
230
221
|
};
|
|
231
222
|
|
|
232
|
-
pi
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
223
|
+
registerCursorModelLifecycle(pi, {
|
|
224
|
+
sessionStart: (_event, ctx) => {
|
|
225
|
+
clearSkillsAndSync(ctx.model);
|
|
226
|
+
},
|
|
227
|
+
modelSelect: (event) => {
|
|
228
|
+
clearSkillsAndSync(event.model);
|
|
229
|
+
},
|
|
230
|
+
turnStart: (_event, ctx) => {
|
|
231
|
+
if (!isCursorModel(ctx.model)) setCurrentSkills([]);
|
|
232
|
+
syncCursorSkillToolForModel(pi, ctx.model);
|
|
233
|
+
},
|
|
234
|
+
beforeAgentStart: (event, ctx) => {
|
|
235
|
+
if (isCursorModel(ctx.model)) {
|
|
236
|
+
setCurrentSkills(event.systemPromptOptions?.skills);
|
|
237
|
+
} else {
|
|
238
|
+
setCurrentSkills([]);
|
|
239
|
+
}
|
|
240
|
+
syncCursorSkillToolForModel(pi, ctx.model);
|
|
241
|
+
const resolved = resolveCursorSkillSystemPrompt(event.systemPrompt, ctx.model, event.systemPromptOptions);
|
|
242
|
+
if (resolved === event.systemPrompt) return undefined;
|
|
243
|
+
return { systemPrompt: resolved };
|
|
244
|
+
},
|
|
252
245
|
});
|
|
253
246
|
}
|
|
254
247
|
|
package/src/cursor-state.ts
CHANGED
|
@@ -15,9 +15,12 @@ import {
|
|
|
15
15
|
} from "./cursor-pi-tool-bridge-snapshot.js";
|
|
16
16
|
import {
|
|
17
17
|
CURSOR_SETTING_SOURCES_ENV,
|
|
18
|
-
|
|
18
|
+
resolveCursorSettingSources,
|
|
19
19
|
} from "./cursor-setting-sources.js";
|
|
20
20
|
import { isCursorModel } from "./cursor-model.js";
|
|
21
|
+
import { registerCursorModelLifecycle } from "./cursor-model-lifecycle.js";
|
|
22
|
+
import { asRecord } from "./cursor-record-utils.js";
|
|
23
|
+
import { getCursorSessionScopeKey } from "./cursor-session-scope.js";
|
|
21
24
|
import { getCursorModelMetadata } from "./model-discovery.js";
|
|
22
25
|
|
|
23
26
|
const FAST_ENTRY_TYPE = "cursor-fast-state";
|
|
@@ -58,6 +61,7 @@ let cliForceFast = false;
|
|
|
58
61
|
let cliForceNoFast = false;
|
|
59
62
|
let sessionCursorAgentMode: AgentModeOption | undefined;
|
|
60
63
|
let cliCursorModeState: CursorCliModeState = { kind: "unset" };
|
|
64
|
+
const invalidCursorModeNotifiedSessionScopeKeys = new Set<string>();
|
|
61
65
|
|
|
62
66
|
export function isCursorAgentMode(value: unknown): value is AgentModeOption {
|
|
63
67
|
return value === "agent" || value === "plan";
|
|
@@ -69,13 +73,10 @@ export function parseCursorAgentMode(raw: unknown): AgentModeOption | undefined
|
|
|
69
73
|
return isCursorAgentMode(mode) ? mode : undefined;
|
|
70
74
|
}
|
|
71
75
|
|
|
72
|
-
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
73
|
-
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
76
|
function isCursorFastEntryData(value: unknown): value is CursorFastEntryData {
|
|
77
|
-
|
|
78
|
-
|
|
77
|
+
const record = asRecord(value);
|
|
78
|
+
if (!record) return false;
|
|
79
|
+
return (typeof record.modelId === "string" || typeof record.baseModelId === "string") && typeof record.fast === "boolean";
|
|
79
80
|
}
|
|
80
81
|
|
|
81
82
|
function getCursorFastEntryModelId(data: CursorFastEntryData): string {
|
|
@@ -83,17 +84,19 @@ function getCursorFastEntryModelId(data: CursorFastEntryData): string {
|
|
|
83
84
|
}
|
|
84
85
|
|
|
85
86
|
function isCursorModeEntryData(value: unknown): value is CursorModeEntryData {
|
|
86
|
-
return
|
|
87
|
+
return isCursorAgentMode(asRecord(value)?.mode);
|
|
87
88
|
}
|
|
88
89
|
|
|
89
90
|
function parseCursorGlobalConfig(value: unknown): CursorGlobalConfig | undefined {
|
|
90
|
-
|
|
91
|
-
|
|
91
|
+
const record = asRecord(value);
|
|
92
|
+
if (!record) return undefined;
|
|
93
|
+
const { fastDefaults } = record;
|
|
92
94
|
if (fastDefaults === undefined) return {};
|
|
93
|
-
|
|
95
|
+
const fastDefaultsRecord = asRecord(fastDefaults);
|
|
96
|
+
if (!fastDefaultsRecord) return undefined;
|
|
94
97
|
return {
|
|
95
98
|
fastDefaults: Object.fromEntries(
|
|
96
|
-
Object.entries(
|
|
99
|
+
Object.entries(fastDefaultsRecord).filter((entry): entry is [string, boolean] => typeof entry[1] === "boolean"),
|
|
97
100
|
),
|
|
98
101
|
};
|
|
99
102
|
}
|
|
@@ -174,23 +177,38 @@ function formatInvalidCursorMode(raw: string): string {
|
|
|
174
177
|
return `Invalid --cursor-mode "${raw}". Use "agent" or "plan".`;
|
|
175
178
|
}
|
|
176
179
|
|
|
177
|
-
export
|
|
180
|
+
export type CursorAgentModeResolution =
|
|
181
|
+
| { kind: "valid"; mode: AgentModeOption }
|
|
182
|
+
| { kind: "invalid"; raw: string; message: string };
|
|
183
|
+
|
|
184
|
+
export function getStoredCursorAgentMode(): AgentModeOption {
|
|
185
|
+
return sessionCursorAgentMode ?? DEFAULT_CURSOR_AGENT_MODE;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export function resolveCursorAgentMode(): CursorAgentModeResolution {
|
|
178
189
|
switch (cliCursorModeState.kind) {
|
|
179
190
|
case "valid":
|
|
180
|
-
return cliCursorModeState.mode;
|
|
191
|
+
return { kind: "valid", mode: cliCursorModeState.mode };
|
|
181
192
|
case "invalid":
|
|
182
|
-
|
|
193
|
+
return { kind: "invalid", raw: cliCursorModeState.raw, message: cliCursorModeState.message };
|
|
183
194
|
case "unset":
|
|
184
|
-
return
|
|
195
|
+
return { kind: "valid", mode: getStoredCursorAgentMode() };
|
|
185
196
|
}
|
|
186
197
|
}
|
|
187
198
|
|
|
199
|
+
export function getCursorProviderAgentModeOrThrow(): AgentModeOption {
|
|
200
|
+
const resolution = resolveCursorAgentMode();
|
|
201
|
+
if (resolution.kind === "invalid") throw new Error(resolution.message);
|
|
202
|
+
return resolution.mode;
|
|
203
|
+
}
|
|
204
|
+
|
|
188
205
|
function formatCursorStatus(fast: boolean | undefined): string | undefined {
|
|
189
206
|
const parts: string[] = [];
|
|
207
|
+
const modeResolution = resolveCursorAgentMode();
|
|
190
208
|
if (fast === true) parts.push("fast");
|
|
191
|
-
if (
|
|
209
|
+
if (modeResolution.kind === "invalid") {
|
|
192
210
|
parts.push("mode invalid");
|
|
193
|
-
} else if (
|
|
211
|
+
} else if (modeResolution.mode === "plan") {
|
|
194
212
|
parts.push("plan");
|
|
195
213
|
}
|
|
196
214
|
return parts.length > 0 ? `cursor ${parts.join(" · ")}` : undefined;
|
|
@@ -255,7 +273,7 @@ function persistCursorModePreference(pi: Pick<ExtensionAPI, "appendEntry">, mode
|
|
|
255
273
|
}
|
|
256
274
|
}
|
|
257
275
|
|
|
258
|
-
function restoreCliCursorMode(raw: boolean | string | undefined
|
|
276
|
+
function restoreCliCursorMode(raw: boolean | string | undefined): void {
|
|
259
277
|
cliCursorModeState = { kind: "unset" };
|
|
260
278
|
if (raw === undefined || raw === "" || raw === false) return;
|
|
261
279
|
const parsed = parseCursorAgentMode(raw);
|
|
@@ -266,15 +284,19 @@ function restoreCliCursorMode(raw: boolean | string | undefined, mode: Extension
|
|
|
266
284
|
const rawText = String(raw);
|
|
267
285
|
const message = formatInvalidCursorMode(rawText);
|
|
268
286
|
cliCursorModeState = { kind: "invalid", raw: rawText, message };
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
function notifyInvalidCursorModeIfCursorActive(ctx: Pick<ExtensionContext, "hasUI" | "mode" | "ui">): void {
|
|
290
|
+
const modeResolution = resolveCursorAgentMode();
|
|
291
|
+
if (modeResolution.kind !== "invalid" || !ctx.hasUI || ctx.mode !== "tui") return;
|
|
292
|
+
const scopeKey = getCursorSessionScopeKey();
|
|
293
|
+
if (invalidCursorModeNotifiedSessionScopeKeys.has(scopeKey)) return;
|
|
294
|
+
invalidCursorModeNotifiedSessionScopeKeys.add(scopeKey);
|
|
295
|
+
ctx.ui.notify(modeResolution.message, "error");
|
|
274
296
|
}
|
|
275
297
|
|
|
276
298
|
function formatEffectiveCursorSettingSourcesLabel(raw: string | undefined = process.env[CURSOR_SETTING_SOURCES_ENV]): string {
|
|
277
|
-
const effective =
|
|
299
|
+
const effective = resolveCursorSettingSources(raw);
|
|
278
300
|
const effectiveLabel = effective === undefined ? "none" : effective.join(",");
|
|
279
301
|
const rawLabel = raw?.trim() ? raw.trim() : "(unset → all)";
|
|
280
302
|
return `${rawLabel} (effective: ${effectiveLabel})`;
|
|
@@ -395,10 +417,11 @@ export function registerCursorRuntimeControls(pi: CursorRuntimeControlsExtension
|
|
|
395
417
|
const usage = "Usage: /cursor-mode agent|plan";
|
|
396
418
|
const mode = parseCursorAgentMode(args);
|
|
397
419
|
if (!args.trim()) {
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
420
|
+
const modeResolution = resolveCursorAgentMode();
|
|
421
|
+
if (modeResolution.kind === "invalid") {
|
|
422
|
+
ctx.ui.notify(`${modeResolution.message} ${usage}`, "error");
|
|
423
|
+
} else {
|
|
424
|
+
ctx.ui.notify(`Cursor mode is ${modeResolution.mode}. ${usage}`, "info");
|
|
402
425
|
}
|
|
403
426
|
return;
|
|
404
427
|
}
|
|
@@ -429,28 +452,26 @@ export function registerCursorRuntimeControls(pi: CursorRuntimeControlsExtension
|
|
|
429
452
|
},
|
|
430
453
|
});
|
|
431
454
|
|
|
432
|
-
pi
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
pi.on("turn_start", async (_event, ctx) => {
|
|
447
|
-
updateCursorStatus(ctx);
|
|
455
|
+
registerCursorModelLifecycle(pi, {
|
|
456
|
+
sessionStart: (_event, ctx) => {
|
|
457
|
+
globalFastPreferences = loadGlobalFastPreferences();
|
|
458
|
+
cliForceFast = pi.getFlag("cursor-fast") === true;
|
|
459
|
+
cliForceNoFast = pi.getFlag("cursor-no-fast") === true;
|
|
460
|
+
restoreSessionFastPreferences(ctx);
|
|
461
|
+
restoreSessionCursorMode(ctx);
|
|
462
|
+
restoreCliCursorMode(pi.getFlag("cursor-mode"));
|
|
463
|
+
},
|
|
464
|
+
sync: (ctx) => {
|
|
465
|
+
if (isCursorModel(ctx.model)) notifyInvalidCursorModeIfCursorActive(ctx);
|
|
466
|
+
updateCursorStatus(ctx);
|
|
467
|
+
},
|
|
448
468
|
});
|
|
449
469
|
}
|
|
450
470
|
|
|
451
471
|
function resetCursorModeStateForTests(): void {
|
|
452
472
|
sessionCursorAgentMode = undefined;
|
|
453
473
|
cliCursorModeState = { kind: "unset" };
|
|
474
|
+
invalidCursorModeNotifiedSessionScopeKeys.clear();
|
|
454
475
|
}
|
|
455
476
|
|
|
456
477
|
export const __testUtils = {
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { truncateCursorDisplayLine } from "./cursor-display-text.js";
|
|
2
2
|
import { scrubSensitiveText } from "./cursor-sensitive-text.js";
|
|
3
3
|
import { getCursorToolLifecycleLabelKind } from "./cursor-tool-presentation-registry.js";
|
|
4
|
-
import { extractWebSearchQuery } from "./cursor-web-tool-
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
4
|
+
import { extractWebSearchQuery } from "./cursor-web-tool-args.js";
|
|
5
|
+
import { getArray, getString } from "./cursor-record-utils.js";
|
|
6
|
+
import { firstNonEmptyLine, truncateArg } from "./cursor-transcript-utils.js";
|
|
7
|
+
import { classifyCursorToolVisibility, type CursorToolVisibility } from "./cursor-tool-visibility.js";
|
|
7
8
|
|
|
8
9
|
/** Defer pending lifecycle lines so fast start+complete pairs coalesce into the completed replay card only. */
|
|
9
10
|
export const CURSOR_TOOL_LIFECYCLE_DEFER_MS = 75;
|
|
@@ -12,8 +13,7 @@ export function isCursorToolLifecycleEligible(toolCall: unknown): boolean {
|
|
|
12
13
|
return classifyCursorToolVisibility(toolCall).lifecycleEligible;
|
|
13
14
|
}
|
|
14
15
|
|
|
15
|
-
function getCursorToolLifecycleTitle(
|
|
16
|
-
const visibility = classifyCursorToolVisibility(toolCall);
|
|
16
|
+
function getCursorToolLifecycleTitle(visibility: CursorToolVisibility): string {
|
|
17
17
|
return visibility.lifecycleTitle ?? `Cursor ${visibility.normalizedName}`;
|
|
18
18
|
}
|
|
19
19
|
|
|
@@ -36,8 +36,15 @@ function scrubLifecycleDetail(value: string | undefined, apiKey?: string): strin
|
|
|
36
36
|
return scrubbed;
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
function scrubShellLifecycleDetail(value: string | undefined, apiKey?: string): string | undefined {
|
|
40
|
+
if (!value?.trim()) return undefined;
|
|
41
|
+
return truncateCursorDisplayLine(scrubSensitiveText(value, apiKey));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function buildCursorToolLifecycleLabelFromVisibility(
|
|
45
|
+
visibility: CursorToolVisibility,
|
|
46
|
+
apiKey?: string,
|
|
47
|
+
): string | undefined {
|
|
41
48
|
const args = visibility.args;
|
|
42
49
|
|
|
43
50
|
switch (getCursorToolLifecycleLabelKind(visibility.normalizedKey)) {
|
|
@@ -45,7 +52,7 @@ export function buildCursorToolLifecycleLabel(toolCall: unknown, apiKey?: string
|
|
|
45
52
|
return scrubLifecycleDetail(getString(args, "description"), apiKey) ?? "task";
|
|
46
53
|
}
|
|
47
54
|
case "shell": {
|
|
48
|
-
return
|
|
55
|
+
return scrubShellLifecycleDetail(getString(args, "command") ?? getString(args, "cmd"), apiKey);
|
|
49
56
|
}
|
|
50
57
|
case "mcp": {
|
|
51
58
|
return scrubLifecycleDetail(getString(args, "toolName"), apiKey) ?? "mcp";
|
|
@@ -79,8 +86,13 @@ export function buildCursorToolLifecycleLabel(toolCall: unknown, apiKey?: string
|
|
|
79
86
|
}
|
|
80
87
|
}
|
|
81
88
|
|
|
89
|
+
export function buildCursorToolLifecycleLabel(toolCall: unknown, apiKey?: string): string | undefined {
|
|
90
|
+
return buildCursorToolLifecycleLabelFromVisibility(classifyCursorToolVisibility(toolCall), apiKey);
|
|
91
|
+
}
|
|
92
|
+
|
|
82
93
|
export function formatCursorToolLifecycleProgressText(toolCall: unknown, apiKey?: string): string | undefined {
|
|
83
|
-
const
|
|
94
|
+
const visibility = classifyCursorToolVisibility(toolCall);
|
|
95
|
+
const label = buildCursorToolLifecycleLabelFromVisibility(visibility, apiKey);
|
|
84
96
|
if (!label) return undefined;
|
|
85
|
-
return `${getCursorToolLifecycleTitle(
|
|
97
|
+
return `${getCursorToolLifecycleTitle(visibility)}: ${label}\n`;
|
|
86
98
|
}
|
|
@@ -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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
}
|