pi-cursor-sdk 0.1.36 → 0.1.38
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +38 -0
- package/docs/cursor-model-ux-spec.md +1 -1
- package/docs/cursor-native-tool-replay.md +9 -9
- package/package.json +1 -1
- package/scripts/platform-smoke/card-detect.mjs +1 -1
- package/src/context-window-cache.ts +10 -14
- package/src/context.ts +1 -1
- package/src/cursor-agent-message-web-tools.ts +2 -1
- package/src/cursor-agents-context-registration.ts +18 -0
- package/src/cursor-agents-context.ts +21 -30
- package/src/cursor-edit-diff.ts +4 -2
- package/src/cursor-fallback-warning.ts +22 -0
- package/src/cursor-incomplete-tool-visibility.ts +5 -11
- package/src/cursor-live-run-coordinator.ts +1 -1
- package/src/cursor-mcp-timeout-override.ts +0 -2
- package/src/cursor-model-lifecycle.ts +72 -0
- package/src/cursor-native-replay-routing.ts +1 -1
- package/src/cursor-native-replay-trace.ts +1 -1
- package/src/cursor-native-tool-display-registration.ts +16 -28
- package/src/cursor-native-tool-display-replay.ts +12 -47
- package/src/cursor-native-tool-display-state.ts +1 -1
- package/src/cursor-native-tool-display-tools.ts +10 -18
- package/src/cursor-native-tool-names.ts +16 -0
- package/src/cursor-pi-tool-bridge-env.ts +12 -0
- package/src/cursor-pi-tool-bridge-mcp.ts +16 -21
- package/src/cursor-pi-tool-bridge-run.ts +5 -5
- package/src/cursor-pi-tool-bridge-server.ts +8 -3
- package/src/cursor-pi-tool-bridge-snapshot.ts +7 -13
- package/src/cursor-pi-tool-bridge.ts +7 -7
- package/src/cursor-provider-lazy.ts +51 -0
- package/src/cursor-provider-live-run-drain.ts +1 -1
- package/src/cursor-provider-run-finalizer.ts +5 -5
- package/src/cursor-provider-run-outcome.ts +0 -1
- package/src/cursor-provider-turn-coordinator.ts +4 -5
- package/src/cursor-provider-turn-display-router.ts +5 -1
- package/src/cursor-provider-turn-emit.ts +1 -1
- package/src/cursor-provider-turn-lifecycle-emitter.ts +1 -5
- package/src/cursor-provider-turn-prepare.ts +13 -9
- package/src/cursor-provider-turn-runner.ts +3 -11
- package/src/cursor-provider-turn-sdk-normalizer.ts +28 -5
- package/src/cursor-provider-turn-send.ts +7 -2
- package/src/cursor-provider-turn-types.ts +1 -3
- package/src/cursor-provider.ts +3 -2
- package/src/cursor-question-tool.ts +5 -18
- package/src/cursor-record-utils.ts +42 -0
- package/src/cursor-replay-activity-builders.ts +16 -122
- package/src/cursor-replay-tool-details.ts +57 -197
- package/src/cursor-sdk-event-debug.ts +6 -6
- package/src/cursor-sensitive-text.ts +4 -4
- package/src/cursor-session-agent-lifecycle.ts +47 -0
- package/src/cursor-session-agent.ts +9 -47
- package/src/cursor-session-scope.ts +23 -4
- package/src/cursor-setting-sources.ts +8 -8
- package/src/cursor-skill-tool.ts +25 -32
- package/src/cursor-state.ts +66 -45
- package/src/cursor-tool-lifecycle.ts +16 -9
- package/src/cursor-tool-presentation-registry.ts +42 -169
- package/src/cursor-tool-result-display-readers.ts +185 -0
- package/src/cursor-tool-transcript.ts +17 -33
- package/src/cursor-tool-visibility.ts +9 -1
- package/src/cursor-transcript-tool-formatters.ts +23 -172
- package/src/cursor-transcript-tool-specs.ts +17 -57
- package/src/cursor-transcript-utils.ts +2 -34
- package/src/cursor-usage-accounting.ts +0 -6
- package/src/cursor-web-tool-activity.ts +4 -12
- package/src/cursor-web-tool-args.ts +1 -9
- package/src/index.ts +15 -16
- package/src/model-discovery.ts +5 -4
- package/src/model-list-cache.ts +37 -38
- package/src/cursor-native-tool-display.ts +0 -10
- package/src/cursor-provider-turn-api-key.ts +0 -1
- package/src/cursor-provider-turn-message-offset.ts +0 -15
- package/src/cursor-session-cwd.ts +0 -28
- package/src/cursor-tool-names.ts +0 -18
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
import {
|
|
3
3
|
CURSOR_MODEL_ACTIVE_REPLAY_TOOL_NAMES,
|
|
4
4
|
isNativeCursorToolName,
|
|
5
5
|
NATIVE_CURSOR_TOOL_NAMES,
|
|
6
|
-
registerNativeCursorTool,
|
|
7
6
|
type NativeCursorToolName,
|
|
8
|
-
} from "./cursor-native-tool-
|
|
7
|
+
} from "./cursor-native-tool-names.js";
|
|
9
8
|
import { isCursorModel } from "./cursor-model.js";
|
|
9
|
+
import { registerCursorModelLifecycle, type CursorModelLifecycleExtensionApi } from "./cursor-model-lifecycle.js";
|
|
10
10
|
import {
|
|
11
11
|
isCursorNativeToolDisplayRequested,
|
|
12
12
|
isCursorNativeToolRegistrationRequested,
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
registeredNativeToolNames,
|
|
16
16
|
skippedNativeToolNames,
|
|
17
17
|
} from "./cursor-native-tool-display-state.js";
|
|
18
|
-
import { isCursorReplayToolName } from "./cursor-tool-
|
|
18
|
+
import { isCursorReplayToolName } from "./cursor-tool-presentation-registry.js";
|
|
19
19
|
|
|
20
20
|
export const CURSOR_CORE_PI_REPLAY_TOOL_NAMES = ["read", "bash", "edit", "write"] as const;
|
|
21
21
|
const CORE_PI_TOOL_NAMES = new Set<string>(CURSOR_CORE_PI_REPLAY_TOOL_NAMES);
|
|
@@ -27,12 +27,7 @@ function isCursorCorePiReplayToolName(toolName: string): toolName is (typeof CUR
|
|
|
27
27
|
type CursorNativeToolActivationApi = Pick<ExtensionAPI, "getActiveTools" | "setActiveTools">;
|
|
28
28
|
type CursorNativeToolRegistryApi = CursorNativeToolActivationApi & Pick<ExtensionAPI, "getAllTools" | "registerTool">;
|
|
29
29
|
|
|
30
|
-
export interface CursorNativeToolDisplayExtensionApi extends CursorNativeToolRegistryApi {
|
|
31
|
-
on(event: "session_start", handler: ExtensionHandler<SessionStartEvent>): void;
|
|
32
|
-
on(event: "before_agent_start", handler: ExtensionHandler<BeforeAgentStartEvent>): void;
|
|
33
|
-
on(event: "turn_start", handler: ExtensionHandler<TurnStartEvent>): void;
|
|
34
|
-
on(event: "model_select", handler: (event: { model: ExtensionContext["model"] }, ctx: ExtensionContext) => Promise<void> | void): void;
|
|
35
|
-
}
|
|
30
|
+
export interface CursorNativeToolDisplayExtensionApi extends CursorNativeToolRegistryApi, CursorModelLifecycleExtensionApi {}
|
|
36
31
|
|
|
37
32
|
function hasNonBuiltinTool(pi: Pick<ExtensionAPI, "getAllTools">, toolName: NativeCursorToolName): boolean {
|
|
38
33
|
const existingTool = pi.getAllTools().find((tool) => tool.name === toolName);
|
|
@@ -43,11 +38,12 @@ type NativeRegistrationContext = Pick<ExtensionContext, "mode" | "model"> & {
|
|
|
43
38
|
ui: Pick<ExtensionContext["ui"], "notify">;
|
|
44
39
|
};
|
|
45
40
|
|
|
46
|
-
function registerNativeCursorToolsFromSet(
|
|
41
|
+
async function registerNativeCursorToolsFromSet(
|
|
47
42
|
pi: CursorNativeToolRegistryApi,
|
|
48
43
|
toolNames: readonly NativeCursorToolName[],
|
|
49
|
-
): NativeCursorToolName[] {
|
|
44
|
+
): Promise<NativeCursorToolName[]> {
|
|
50
45
|
const newlySkippedToolNames: NativeCursorToolName[] = [];
|
|
46
|
+
let registerNativeCursorTool: ((pi: CursorNativeToolRegistryApi, toolName: NativeCursorToolName) => void) | undefined;
|
|
51
47
|
for (const toolName of toolNames) {
|
|
52
48
|
if (registeredNativeToolNames.has(toolName) || skippedNativeToolNames.has(toolName)) continue;
|
|
53
49
|
if (hasNonBuiltinTool(pi, toolName)) {
|
|
@@ -55,6 +51,7 @@ function registerNativeCursorToolsFromSet(
|
|
|
55
51
|
newlySkippedToolNames.push(toolName);
|
|
56
52
|
continue;
|
|
57
53
|
}
|
|
54
|
+
registerNativeCursorTool ??= (await import("./cursor-native-tool-display-tools.js")).registerNativeCursorTool;
|
|
58
55
|
registerNativeCursorTool(pi, toolName);
|
|
59
56
|
registeredNativeToolNames.add(toolName);
|
|
60
57
|
}
|
|
@@ -97,7 +94,7 @@ export function syncRegisteredNativeCursorToolsForModel(
|
|
|
97
94
|
if (changed) pi.setActiveTools([...activeToolNames]);
|
|
98
95
|
}
|
|
99
96
|
|
|
100
|
-
function ensureNativeCursorToolsRegisteredForModel(pi: CursorNativeToolRegistryApi, ctx: NativeRegistrationContext): void {
|
|
97
|
+
async function ensureNativeCursorToolsRegisteredForModel(pi: CursorNativeToolRegistryApi, ctx: NativeRegistrationContext): Promise<void> {
|
|
101
98
|
if (!isCursorNativeToolRegistrationRequested()) {
|
|
102
99
|
registeredNativeToolNames.clear();
|
|
103
100
|
skippedNativeToolNames.clear();
|
|
@@ -107,31 +104,22 @@ function ensureNativeCursorToolsRegisteredForModel(pi: CursorNativeToolRegistryA
|
|
|
107
104
|
|
|
108
105
|
const nonCoreToolNames = NATIVE_CURSOR_TOOL_NAMES.filter((toolName) => !isCursorCorePiReplayToolName(toolName));
|
|
109
106
|
const skippedToolNames = [
|
|
110
|
-
...registerNativeCursorToolsFromSet(pi, nonCoreToolNames),
|
|
111
|
-
...registerNativeCursorToolsFromSet(pi, CURSOR_CORE_PI_REPLAY_TOOL_NAMES),
|
|
107
|
+
...(await registerNativeCursorToolsFromSet(pi, nonCoreToolNames)),
|
|
108
|
+
...(await registerNativeCursorToolsFromSet(pi, CURSOR_CORE_PI_REPLAY_TOOL_NAMES)),
|
|
112
109
|
];
|
|
113
110
|
notifySkippedNativeCursorToolsIfNeeded(ctx, skippedToolNames);
|
|
114
111
|
}
|
|
115
112
|
|
|
116
|
-
function ensureThenSyncNativeCursorToolsForModel(pi: CursorNativeToolRegistryApi, ctx: NativeRegistrationContext): void {
|
|
113
|
+
async function ensureThenSyncNativeCursorToolsForModel(pi: CursorNativeToolRegistryApi, ctx: NativeRegistrationContext): Promise<void> {
|
|
117
114
|
if (isCursorModel(ctx.model) && !hasAttemptedNativeCursorToolRegistration()) {
|
|
118
|
-
ensureNativeCursorToolsRegisteredForModel(pi, ctx);
|
|
115
|
+
await ensureNativeCursorToolsRegisteredForModel(pi, ctx);
|
|
119
116
|
}
|
|
120
117
|
syncRegisteredNativeCursorToolsForModel(pi, ctx.model);
|
|
121
118
|
}
|
|
122
119
|
|
|
123
120
|
export function registerCursorNativeToolDisplay(pi: CursorNativeToolDisplayExtensionApi): void {
|
|
124
|
-
pi
|
|
125
|
-
ensureThenSyncNativeCursorToolsForModel(pi, ctx);
|
|
126
|
-
});
|
|
127
|
-
pi.on("before_agent_start", (_event, ctx) => {
|
|
128
|
-
ensureThenSyncNativeCursorToolsForModel(pi, ctx);
|
|
129
|
-
});
|
|
130
|
-
pi.on("turn_start", (_event, ctx) => {
|
|
131
|
-
ensureThenSyncNativeCursorToolsForModel(pi, ctx);
|
|
132
|
-
});
|
|
133
|
-
pi.on("model_select", (event, ctx) => {
|
|
134
|
-
ensureThenSyncNativeCursorToolsForModel(pi, { ...ctx, model: event.model });
|
|
121
|
+
registerCursorModelLifecycle(pi, async (ctx) => {
|
|
122
|
+
await ensureThenSyncNativeCursorToolsForModel(pi, ctx);
|
|
135
123
|
});
|
|
136
124
|
}
|
|
137
125
|
|
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
import { readFileSync, statSync } from "node:fs";
|
|
2
|
-
import { basename
|
|
2
|
+
import { basename } from "node:path";
|
|
3
3
|
import { getLanguageFromPath, highlightCode, type ToolDefinition } from "@earendil-works/pi-coding-agent";
|
|
4
4
|
import { Image, Text, type Component } from "@earendil-works/pi-tui";
|
|
5
5
|
import { Type } from "typebox";
|
|
6
6
|
import { resolveCursorEditDiff } from "./cursor-edit-diff.js";
|
|
7
|
+
import { inferImageMimeType } from "./cursor-tool-result-display-readers.js";
|
|
7
8
|
import { LOCAL_READ_PREVIEW_NOTICE, isLocalReadPreviewContent } from "./cursor-transcript-utils.js";
|
|
8
9
|
import {
|
|
9
10
|
CURSOR_REPLAY_ACTIVITY_TOOL_NAME,
|
|
10
11
|
getCursorReplayCallSummary,
|
|
11
|
-
getCursorReplaySideEffectDescription,
|
|
12
|
-
getCursorReplayOperationLabel,
|
|
13
|
-
getCursorReplayWrapperLabel,
|
|
14
12
|
type CursorReplayToolName,
|
|
15
13
|
} from "./cursor-tool-presentation-registry.js";
|
|
16
14
|
import {
|
|
@@ -20,7 +18,6 @@ import {
|
|
|
20
18
|
type CursorReplayActivityDetails,
|
|
21
19
|
type CursorReplayToolDetails,
|
|
22
20
|
type CursorReplayNativeWriteDetails,
|
|
23
|
-
isCursorReplayNativeEditDetails,
|
|
24
21
|
isCursorReplayGenerateImageDetails,
|
|
25
22
|
isCursorReplayActivityDetails,
|
|
26
23
|
parseCursorReplayToolDetails,
|
|
@@ -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
|
|
|
@@ -59,22 +49,6 @@ type CursorReplayRenderCall = NonNullable<ToolDefinition<typeof cursorReplayTool
|
|
|
59
49
|
type CursorReplayRenderResult = NonNullable<ToolDefinition<typeof cursorReplayToolSchema, unknown>["renderResult"]>;
|
|
60
50
|
export type CursorReplayRenderTheme = Parameters<CursorReplayRenderCall>[1];
|
|
61
51
|
|
|
62
|
-
function inferImageMimeTypeFromPath(path: string | undefined): string | undefined {
|
|
63
|
-
switch (extname(path ?? "").toLowerCase()) {
|
|
64
|
-
case ".png":
|
|
65
|
-
return "image/png";
|
|
66
|
-
case ".jpg":
|
|
67
|
-
case ".jpeg":
|
|
68
|
-
return "image/jpeg";
|
|
69
|
-
case ".gif":
|
|
70
|
-
return "image/gif";
|
|
71
|
-
case ".webp":
|
|
72
|
-
return "image/webp";
|
|
73
|
-
default:
|
|
74
|
-
return undefined;
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
52
|
function readImageFileForReplay(path: string | undefined): string | undefined {
|
|
79
53
|
if (!path) return undefined;
|
|
80
54
|
try {
|
|
@@ -245,10 +219,8 @@ function formatCursorReplayActivityDiffPreview(
|
|
|
245
219
|
const body = (stripHeader ? stripCursorReplayHeader(text) : text).trimEnd();
|
|
246
220
|
const diffSection = body ? extractUnifiedDiffSection(body) : undefined;
|
|
247
221
|
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.
|
|
222
|
+
// Fallback for unstructured activity details that carry a unified diff only in expanded text.
|
|
223
|
+
// All actual diff coloring lives in the single `formatCursorReplayDiff` renderer.
|
|
252
224
|
return formatCursorReplayDiff(diffSection, theme, maxLines);
|
|
253
225
|
}
|
|
254
226
|
|
|
@@ -350,11 +322,11 @@ export function formatCursorReplayFilePreview(
|
|
|
350
322
|
return renderedLines.join("\n");
|
|
351
323
|
}
|
|
352
324
|
|
|
353
|
-
function
|
|
325
|
+
function getCursorReplayCardTitle(toolName: CursorReplayToolName, args: Record<string, unknown> | undefined): string {
|
|
354
326
|
if (toolName === CURSOR_REPLAY_ACTIVITY_TOOL_NAME && typeof args?.activityTitle === "string" && args.activityTitle.trim()) {
|
|
355
327
|
return args.activityTitle.trim();
|
|
356
328
|
}
|
|
357
|
-
return
|
|
329
|
+
return "Cursor activity";
|
|
358
330
|
}
|
|
359
331
|
|
|
360
332
|
export function renderCursorReplayCall(
|
|
@@ -364,7 +336,7 @@ export function renderCursorReplayCall(
|
|
|
364
336
|
isPartial: boolean,
|
|
365
337
|
): Text {
|
|
366
338
|
if (!isPartial) return new Text("", 0, 0);
|
|
367
|
-
let text = theme.fg("toolTitle", theme.bold(`${
|
|
339
|
+
let text = theme.fg("toolTitle", theme.bold(`${getCursorReplayCardTitle(toolName, args)} `));
|
|
368
340
|
const summary = getCursorReplayCallSummary(toolName, args);
|
|
369
341
|
if (summary) text += theme.fg("accent", summary);
|
|
370
342
|
return new Text(text.trimEnd(), 0, 0);
|
|
@@ -476,7 +448,7 @@ function renderExpandableCursorReplayResult(
|
|
|
476
448
|
}
|
|
477
449
|
if (details.imagePath && !isError && context.showImages) {
|
|
478
450
|
const imageData = readImageFileForReplay(details.imagePath);
|
|
479
|
-
const mimeType = details.imageMimeType ??
|
|
451
|
+
const mimeType = details.imageMimeType ?? inferImageMimeType(details.imagePath);
|
|
480
452
|
if (imageData && mimeType) return buildImageReplayComponent(rendered, imageData, mimeType, basename(details.imagePath ?? "generated-image"), theme);
|
|
481
453
|
}
|
|
482
454
|
return new Text(rendered, 0, 0);
|
|
@@ -497,7 +469,6 @@ function renderCursorReplayEditResult(
|
|
|
497
469
|
function renderCursorReplayWriteResult(
|
|
498
470
|
details: CursorReplayNativeWriteDetails,
|
|
499
471
|
result: Parameters<CursorReplayRenderResult>[0],
|
|
500
|
-
options: Parameters<CursorReplayRenderResult>[1],
|
|
501
472
|
theme: Parameters<CursorReplayRenderResult>[2],
|
|
502
473
|
): Component {
|
|
503
474
|
const text = firstContentText(result);
|
|
@@ -544,7 +515,7 @@ function renderCursorReplayDetails(
|
|
|
544
515
|
case "nativeEdit":
|
|
545
516
|
return renderCursorReplayEditResult(details, options, theme);
|
|
546
517
|
case "nativeWrite":
|
|
547
|
-
return renderCursorReplayWriteResult(details, result,
|
|
518
|
+
return renderCursorReplayWriteResult(details, result, theme);
|
|
548
519
|
case "generateImage":
|
|
549
520
|
return renderCursorGenerateImageResult(details, result, options, theme, context, isError);
|
|
550
521
|
case "activity":
|
|
@@ -602,19 +573,13 @@ export function renderNativeLookingCursorReadReplayResult(
|
|
|
602
573
|
}
|
|
603
574
|
|
|
604
575
|
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
576
|
return {
|
|
608
577
|
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
|
-
],
|
|
578
|
+
label: "Cursor activity",
|
|
579
|
+
description: "Display recorded Cursor SDK tool activity. This tool only returns recorded Cursor results and never executes work directly.",
|
|
615
580
|
parameters: cursorReplayToolSchema,
|
|
616
581
|
async execute() {
|
|
617
|
-
throw new Error(
|
|
582
|
+
throw new Error("No recorded Cursor activity result was available. This replay-only tool does not execute work directly.");
|
|
618
583
|
},
|
|
619
584
|
renderCall(args, theme, context) {
|
|
620
585
|
return renderCursorReplayCall(toolName, args as Record<string, unknown>, theme, context.isPartial);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { CursorPiToolDisplay } from "./cursor-
|
|
1
|
+
import type { CursorPiToolDisplay } from "./cursor-transcript-utils.js";
|
|
2
2
|
import { parseOptionalEnvBoolean } from "./cursor-env-boolean.js";
|
|
3
3
|
|
|
4
4
|
export interface CursorNativeToolDisplayItem extends CursorPiToolDisplay {
|
|
@@ -10,12 +10,17 @@ import {
|
|
|
10
10
|
} from "@earendil-works/pi-coding-agent";
|
|
11
11
|
import { Text } from "@earendil-works/pi-tui";
|
|
12
12
|
import type { TSchema } from "typebox";
|
|
13
|
-
import { getCursorSessionCwd } from "./cursor-session-
|
|
13
|
+
import { getCursorSessionCwd } from "./cursor-session-scope.js";
|
|
14
14
|
import {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
BUILTIN_NATIVE_CURSOR_TOOL_NAMES,
|
|
16
|
+
CURSOR_MODEL_ACTIVE_REPLAY_TOOL_NAMES,
|
|
17
|
+
CURSOR_REPLAY_TOOL_NAMES,
|
|
18
|
+
isNativeCursorToolName,
|
|
19
|
+
NATIVE_CURSOR_TOOL_NAMES,
|
|
20
|
+
type BuiltinNativeCursorToolName,
|
|
21
|
+
type NativeCursorToolName,
|
|
22
|
+
} from "./cursor-native-tool-names.js";
|
|
23
|
+
import { isCursorReplayToolName } from "./cursor-tool-presentation-registry.js";
|
|
19
24
|
import {
|
|
20
25
|
createCursorReplayOnlyToolDefinition,
|
|
21
26
|
isCursorReplayNativeEditDetails,
|
|
@@ -30,8 +35,6 @@ import {
|
|
|
30
35
|
isCursorReplayToolCallId,
|
|
31
36
|
} from "./cursor-native-tool-display-state.js";
|
|
32
37
|
|
|
33
|
-
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, ...CURSOR_REPLAY_LEGACY_TOOL_NAMES] as const;
|
|
35
38
|
|
|
36
39
|
type AnyToolDefinition = ToolDefinition<TSchema, unknown, unknown>;
|
|
37
40
|
type RenderCall = NonNullable<AnyToolDefinition["renderCall"]>;
|
|
@@ -118,8 +121,6 @@ function renderWriteReplayResult(
|
|
|
118
121
|
: renderBase();
|
|
119
122
|
}
|
|
120
123
|
|
|
121
|
-
type BuiltinNativeCursorToolName = "read" | "bash" | "edit" | "write" | "grep" | "find" | "ls";
|
|
122
|
-
|
|
123
124
|
const NATIVE_CURSOR_TOOL_STRATEGIES: Record<BuiltinNativeCursorToolName, NativeReplayStrategy> = {
|
|
124
125
|
read: {
|
|
125
126
|
createDefinition: (cwd) => createReadToolDefinition(cwd) as AnyToolDefinition,
|
|
@@ -146,12 +147,6 @@ const NATIVE_CURSOR_TOOL_STRATEGIES: Record<BuiltinNativeCursorToolName, NativeR
|
|
|
146
147
|
ls: { createDefinition: (cwd) => createLsToolDefinition(cwd) as AnyToolDefinition },
|
|
147
148
|
};
|
|
148
149
|
|
|
149
|
-
const BUILTIN_NATIVE_CURSOR_TOOL_NAMES = Object.keys(NATIVE_CURSOR_TOOL_STRATEGIES) as BuiltinNativeCursorToolName[];
|
|
150
|
-
export const NATIVE_CURSOR_TOOL_NAMES = [
|
|
151
|
-
...BUILTIN_NATIVE_CURSOR_TOOL_NAMES,
|
|
152
|
-
...CURSOR_REPLAY_TOOL_NAMES,
|
|
153
|
-
] as readonly NativeCursorToolName[];
|
|
154
|
-
export type NativeCursorToolName = BuiltinNativeCursorToolName | typeof CURSOR_REPLAY_TOOL_NAMES[number];
|
|
155
150
|
|
|
156
151
|
function getNativeReplayStrategy(toolName: string): NativeReplayStrategy | undefined {
|
|
157
152
|
return Object.hasOwn(NATIVE_CURSOR_TOOL_STRATEGIES, toolName)
|
|
@@ -159,9 +154,6 @@ function getNativeReplayStrategy(toolName: string): NativeReplayStrategy | undef
|
|
|
159
154
|
: undefined;
|
|
160
155
|
}
|
|
161
156
|
|
|
162
|
-
export function isNativeCursorToolName(toolName: string): toolName is NativeCursorToolName {
|
|
163
|
-
return NATIVE_CURSOR_TOOL_NAMES.some((nativeToolName) => nativeToolName === toolName);
|
|
164
|
-
}
|
|
165
157
|
|
|
166
158
|
export function wrapNativeCursorTool<TParams extends TSchema, TDetails, TState>(
|
|
167
159
|
definition: ToolDefinition<TParams, TDetails, TState>,
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { CURSOR_REPLAY_ACTIVITY_TOOL_NAME } from "./cursor-tool-presentation-registry.js";
|
|
2
|
+
|
|
3
|
+
export const CURSOR_MODEL_ACTIVE_REPLAY_TOOL_NAMES = [CURSOR_REPLAY_ACTIVITY_TOOL_NAME] as const;
|
|
4
|
+
export const CURSOR_REPLAY_TOOL_NAMES = [CURSOR_REPLAY_ACTIVITY_TOOL_NAME] as const;
|
|
5
|
+
export const BUILTIN_NATIVE_CURSOR_TOOL_NAMES = ["read", "bash", "edit", "write", "grep", "find", "ls"] as const;
|
|
6
|
+
export const NATIVE_CURSOR_TOOL_NAMES = [
|
|
7
|
+
...BUILTIN_NATIVE_CURSOR_TOOL_NAMES,
|
|
8
|
+
...CURSOR_REPLAY_TOOL_NAMES,
|
|
9
|
+
] as readonly NativeCursorToolName[];
|
|
10
|
+
|
|
11
|
+
export type BuiltinNativeCursorToolName = typeof BUILTIN_NATIVE_CURSOR_TOOL_NAMES[number];
|
|
12
|
+
export type NativeCursorToolName = BuiltinNativeCursorToolName | typeof CURSOR_REPLAY_TOOL_NAMES[number];
|
|
13
|
+
|
|
14
|
+
export function isNativeCursorToolName(toolName: string): toolName is NativeCursorToolName {
|
|
15
|
+
return NATIVE_CURSOR_TOOL_NAMES.some((nativeToolName) => nativeToolName === toolName);
|
|
16
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { parseEnvBoolean } from "./cursor-env-boolean.js";
|
|
2
|
+
|
|
3
|
+
export const CURSOR_PI_TOOL_BRIDGE_ENV = "PI_CURSOR_PI_TOOL_BRIDGE";
|
|
4
|
+
export const CURSOR_PI_TOOL_BRIDGE_BUILTINS_ENV = "PI_CURSOR_EXPOSE_BUILTIN_TOOLS";
|
|
5
|
+
|
|
6
|
+
export function resolveCursorPiToolBridgeEnabled(env: Record<string, string | undefined> = process.env): boolean {
|
|
7
|
+
return parseEnvBoolean(env[CURSOR_PI_TOOL_BRIDGE_ENV], true);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function resolveCursorPiToolBridgeBuiltinsEnabled(env: Record<string, string | undefined> = process.env): boolean {
|
|
11
|
+
return parseEnvBoolean(env[CURSOR_PI_TOOL_BRIDGE_BUILTINS_ENV], false);
|
|
12
|
+
}
|
|
@@ -3,19 +3,17 @@ import type { Context, ToolResultMessage } from "@earendil-works/pi-ai";
|
|
|
3
3
|
import type { CallToolResult, Tool } from "@modelcontextprotocol/sdk/types.js";
|
|
4
4
|
import { buildCursorPiBridgeMcpToolDescription, CURSOR_PI_BRIDGE_MCP_TOOL_PREFIX } from "./cursor-bridge-contract.js";
|
|
5
5
|
import type { CursorPiBridgeToolDefinition, CursorPiMcpInputSchema } from "./cursor-pi-tool-bridge-types.js";
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
export function isRecord(value: unknown): value is Record<string, unknown> {
|
|
9
|
-
return typeof value === "object" && value !== null;
|
|
10
|
-
}
|
|
6
|
+
import { asRecord, stringifyUnknown } from "./cursor-record-utils.js";
|
|
11
7
|
|
|
12
8
|
export function normalizeMcpInputSchema(schema: unknown): CursorPiMcpInputSchema {
|
|
13
|
-
|
|
9
|
+
const record = asRecord(schema);
|
|
10
|
+
if (record?.type === "object") return record as CursorPiMcpInputSchema;
|
|
14
11
|
return { type: "object", properties: {} };
|
|
15
12
|
}
|
|
16
13
|
|
|
17
14
|
export function normalizeMcpArgs(args: unknown): Record<string, unknown> {
|
|
18
|
-
|
|
15
|
+
const record = asRecord(args);
|
|
16
|
+
return record ? { ...record } : {};
|
|
19
17
|
}
|
|
20
18
|
|
|
21
19
|
export function waitForProtocolFlush(): Promise<void> {
|
|
@@ -73,21 +71,21 @@ export function snapshotToolToMcpTool(tool: CursorPiBridgeToolDefinition): Tool
|
|
|
73
71
|
|
|
74
72
|
export function convertPiContentToMcpContent(content: unknown): CallToolResult["content"] {
|
|
75
73
|
if (!Array.isArray(content)) {
|
|
76
|
-
return [{ type: "text", text:
|
|
74
|
+
return [{ type: "text", text: stringifyUnknown(content) }];
|
|
77
75
|
}
|
|
78
76
|
|
|
79
77
|
const mcpContent: CallToolResult["content"] = [];
|
|
80
78
|
for (const block of content) {
|
|
81
|
-
|
|
82
|
-
if (
|
|
83
|
-
mcpContent.push({ type: "text", text:
|
|
79
|
+
const record = asRecord(block);
|
|
80
|
+
if (record?.type === "text" && typeof record.text === "string") {
|
|
81
|
+
mcpContent.push({ type: "text", text: record.text });
|
|
84
82
|
continue;
|
|
85
83
|
}
|
|
86
|
-
if (
|
|
87
|
-
mcpContent.push({ type: "image", data:
|
|
84
|
+
if (record?.type === "image" && typeof record.data === "string" && typeof record.mimeType === "string") {
|
|
85
|
+
mcpContent.push({ type: "image", data: record.data, mimeType: record.mimeType });
|
|
88
86
|
continue;
|
|
89
87
|
}
|
|
90
|
-
mcpContent.push({ type: "text", text:
|
|
88
|
+
mcpContent.push({ type: "text", text: stringifyUnknown(block) });
|
|
91
89
|
}
|
|
92
90
|
|
|
93
91
|
return mcpContent.length > 0 ? mcpContent : [{ type: "text", text: "" }];
|
|
@@ -97,22 +95,19 @@ export function asToolResultMessage(value: Context["messages"][number]): ToolRes
|
|
|
97
95
|
return value.role === "toolResult" ? value : undefined;
|
|
98
96
|
}
|
|
99
97
|
|
|
100
|
-
export function getStringField(record: Record<string, unknown>, fields: string[]): string | undefined {
|
|
101
|
-
return getFirstStringByKeys(record, fields, { nonEmpty: true });
|
|
102
|
-
}
|
|
103
|
-
|
|
104
98
|
export function containsKnownMcpToolName(value: unknown, knownMcpToolNames: ReadonlySet<string>, depth = 0): boolean {
|
|
105
99
|
if (depth > 4) return false;
|
|
106
100
|
if (Array.isArray(value)) return value.some((entry) => containsKnownMcpToolName(entry, knownMcpToolNames, depth + 1));
|
|
107
|
-
|
|
101
|
+
const record = asRecord(value);
|
|
102
|
+
if (!record) return false;
|
|
108
103
|
|
|
109
104
|
for (const field of ["tool", "toolName", "name", "mcpToolName", "serverToolName"]) {
|
|
110
|
-
const fieldValue =
|
|
105
|
+
const fieldValue = record[field];
|
|
111
106
|
if (typeof fieldValue === "string" && knownMcpToolNames.has(fieldValue)) return true;
|
|
112
107
|
}
|
|
113
108
|
|
|
114
109
|
for (const nestedField of ["args", "arguments", "input"]) {
|
|
115
|
-
if (containsKnownMcpToolName(
|
|
110
|
+
if (containsKnownMcpToolName(record[nestedField], knownMcpToolNames, depth + 1)) return true;
|
|
116
111
|
}
|
|
117
112
|
|
|
118
113
|
return false;
|
|
@@ -27,12 +27,11 @@ import {
|
|
|
27
27
|
asToolResultMessage,
|
|
28
28
|
containsKnownMcpToolName,
|
|
29
29
|
convertPiContentToMcpContent,
|
|
30
|
-
getStringField,
|
|
31
|
-
isRecord,
|
|
32
30
|
normalizeMcpArgs,
|
|
33
31
|
snapshotToolToMcpTool,
|
|
34
32
|
waitForProtocolFlush,
|
|
35
33
|
} from "./cursor-pi-tool-bridge-mcp.js";
|
|
34
|
+
import { asRecord, getFirstStringByKeys } from "./cursor-record-utils.js";
|
|
36
35
|
|
|
37
36
|
export interface CursorPiToolBridgeRunHost {
|
|
38
37
|
registerRun(pathname: string, run: CursorPiToolBridgeRunImpl): Promise<string>;
|
|
@@ -178,12 +177,13 @@ export class CursorPiToolBridgeRunImpl implements CursorPiToolBridgeRun {
|
|
|
178
177
|
}
|
|
179
178
|
|
|
180
179
|
isBridgeMcpToolCall(toolCall: unknown): boolean {
|
|
181
|
-
|
|
182
|
-
|
|
180
|
+
const record = asRecord(toolCall);
|
|
181
|
+
if (!record) return false;
|
|
182
|
+
const toolName = getFirstStringByKeys(record, ["name", "toolName", "mcpToolName"], { nonEmpty: true });
|
|
183
183
|
if (toolName && this.knownMcpToolNames.has(toolName)) return true;
|
|
184
184
|
|
|
185
185
|
const isMcpEnvelope = toolName === "mcp" || toolName === MCP_SERVER_NAME;
|
|
186
|
-
const cursorMcpCallId =
|
|
186
|
+
const cursorMcpCallId = getFirstStringByKeys(record, ["call_id", "callId", "id", "toolCallId", "requestId"], { nonEmpty: true });
|
|
187
187
|
if (cursorMcpCallId && this.knownCursorMcpCallIds.has(cursorMcpCallId) && isMcpEnvelope) return true;
|
|
188
188
|
|
|
189
189
|
if (containsKnownMcpToolName(toolCall, this.knownMcpToolNames)) return true;
|
|
@@ -6,7 +6,7 @@ import type {
|
|
|
6
6
|
CursorPiToolBridgeRunOptions,
|
|
7
7
|
CursorPiToolBridgeSnapshotApi,
|
|
8
8
|
} from "./cursor-pi-tool-bridge-types.js";
|
|
9
|
-
import {
|
|
9
|
+
import { asRecord } from "./cursor-record-utils.js";
|
|
10
10
|
import type { CursorPiToolBridgeRunImpl } from "./cursor-pi-tool-bridge-run.js";
|
|
11
11
|
import {
|
|
12
12
|
buildCursorPiToolBridgeSnapshot,
|
|
@@ -84,8 +84,13 @@ export class CursorPiToolBridgeRegistry implements CursorPiToolBridge {
|
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
getHttpServerAddress(): AddressInfo | undefined {
|
|
87
|
-
const address = this.httpServer?.address();
|
|
88
|
-
|
|
87
|
+
const address = asRecord(this.httpServer?.address());
|
|
88
|
+
if (typeof address?.port !== "number") return undefined;
|
|
89
|
+
return {
|
|
90
|
+
address: typeof address.address === "string" ? address.address : LOOPBACK_HOST,
|
|
91
|
+
family: typeof address.family === "string" ? address.family : "IPv4",
|
|
92
|
+
port: address.port,
|
|
93
|
+
};
|
|
89
94
|
}
|
|
90
95
|
|
|
91
96
|
getEndpointCount(): number {
|
|
@@ -4,13 +4,15 @@ import type {
|
|
|
4
4
|
CursorPiToolBridgeSnapshotApi,
|
|
5
5
|
CursorPiToolBridgeSnapshotOptions,
|
|
6
6
|
} from "./cursor-pi-tool-bridge-types.js";
|
|
7
|
-
import { parseEnvBoolean } from "./cursor-env-boolean.js";
|
|
8
7
|
import { createMcpToolName, normalizeMcpInputSchema, stableNameHash } from "./cursor-pi-tool-bridge-mcp.js";
|
|
8
|
+
export {
|
|
9
|
+
CURSOR_PI_TOOL_BRIDGE_BUILTINS_ENV,
|
|
10
|
+
CURSOR_PI_TOOL_BRIDGE_ENV,
|
|
11
|
+
resolveCursorPiToolBridgeBuiltinsEnabled,
|
|
12
|
+
resolveCursorPiToolBridgeEnabled,
|
|
13
|
+
} from "./cursor-pi-tool-bridge-env.js";
|
|
9
14
|
import { isRegisteredCursorNativeToolName } from "./cursor-native-tool-display-state.js";
|
|
10
|
-
import { isExcludedFromCursorBridgeExposure } from "./cursor-tool-
|
|
11
|
-
|
|
12
|
-
export const CURSOR_PI_TOOL_BRIDGE_ENV = "PI_CURSOR_PI_TOOL_BRIDGE";
|
|
13
|
-
export const CURSOR_PI_TOOL_BRIDGE_BUILTINS_ENV = "PI_CURSOR_EXPOSE_BUILTIN_TOOLS";
|
|
15
|
+
import { isExcludedFromCursorBridgeExposure } from "./cursor-tool-presentation-registry.js";
|
|
14
16
|
|
|
15
17
|
const OVERLAPPING_CURSOR_NATIVE_PI_BUILTIN_TOOL_NAMES = new Set(["read", "bash", "write", "edit", "grep", "find", "ls"]);
|
|
16
18
|
|
|
@@ -22,14 +24,6 @@ export function createEmptySnapshot(): CursorPiToolBridgeSnapshot {
|
|
|
22
24
|
};
|
|
23
25
|
}
|
|
24
26
|
|
|
25
|
-
export function resolveCursorPiToolBridgeEnabled(env: Record<string, string | undefined> = process.env): boolean {
|
|
26
|
-
return parseEnvBoolean(env[CURSOR_PI_TOOL_BRIDGE_ENV], true);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export function resolveCursorPiToolBridgeBuiltinsEnabled(env: Record<string, string | undefined> = process.env): boolean {
|
|
30
|
-
return parseEnvBoolean(env[CURSOR_PI_TOOL_BRIDGE_BUILTINS_ENV], false);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
27
|
function isOverlappingCursorNativePiToolName(toolName: string): boolean {
|
|
34
28
|
return OVERLAPPING_CURSOR_NATIVE_PI_BUILTIN_TOOL_NAMES.has(toolName);
|
|
35
29
|
}
|
|
@@ -8,11 +8,7 @@ import {
|
|
|
8
8
|
import {
|
|
9
9
|
CURSOR_PI_TOOL_BRIDGE_BUILTINS_ENV,
|
|
10
10
|
CURSOR_PI_TOOL_BRIDGE_ENV,
|
|
11
|
-
|
|
12
|
-
buildCursorPiToolBridgeSurfaceSignature,
|
|
13
|
-
resolveCursorPiToolBridgeBuiltinsEnabled,
|
|
14
|
-
resolveCursorPiToolBridgeEnabled,
|
|
15
|
-
} from "./cursor-pi-tool-bridge-snapshot.js";
|
|
11
|
+
} from "./cursor-pi-tool-bridge-env.js";
|
|
16
12
|
import { bridgeToolExecutionAbortTracker } from "./cursor-pi-tool-bridge-abort.js";
|
|
17
13
|
import { MCP_SERVER_NAME } from "./cursor-pi-tool-bridge-constants.js";
|
|
18
14
|
import { LOOPBACK_HOST, CursorPiToolBridgeRegistry } from "./cursor-pi-tool-bridge-server.js";
|
|
@@ -37,10 +33,14 @@ export type {
|
|
|
37
33
|
export type { CursorPiToolBridgeDiagnosticEvent } from "./cursor-pi-tool-bridge-diagnostics.js";
|
|
38
34
|
export { resolveCursorPiToolBridgeDebugEnabled } from "./cursor-pi-tool-bridge-diagnostics.js";
|
|
39
35
|
export {
|
|
40
|
-
|
|
41
|
-
|
|
36
|
+
CURSOR_PI_TOOL_BRIDGE_BUILTINS_ENV,
|
|
37
|
+
CURSOR_PI_TOOL_BRIDGE_ENV,
|
|
42
38
|
resolveCursorPiToolBridgeBuiltinsEnabled,
|
|
43
39
|
resolveCursorPiToolBridgeEnabled,
|
|
40
|
+
} from "./cursor-pi-tool-bridge-env.js";
|
|
41
|
+
export {
|
|
42
|
+
buildCursorPiToolBridgeSnapshot,
|
|
43
|
+
buildCursorPiToolBridgeSurfaceSignature,
|
|
44
44
|
} from "./cursor-pi-tool-bridge-snapshot.js";
|
|
45
45
|
|
|
46
46
|
let registeredCursorPiToolBridge: CursorPiToolBridgeRegistry | undefined;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createAssistantMessageEventStream,
|
|
3
|
+
type Api,
|
|
4
|
+
type AssistantMessage,
|
|
5
|
+
type AssistantMessageEventStream,
|
|
6
|
+
type Context,
|
|
7
|
+
type Model,
|
|
8
|
+
type SimpleStreamOptions,
|
|
9
|
+
} from "@earendil-works/pi-ai";
|
|
10
|
+
|
|
11
|
+
function makeProviderLoadErrorMessage(model: Model<Api>, error: unknown): AssistantMessage {
|
|
12
|
+
return {
|
|
13
|
+
role: "assistant",
|
|
14
|
+
content: [],
|
|
15
|
+
api: model.api,
|
|
16
|
+
provider: model.provider,
|
|
17
|
+
model: model.id,
|
|
18
|
+
usage: {
|
|
19
|
+
input: 0,
|
|
20
|
+
output: 0,
|
|
21
|
+
cacheRead: 0,
|
|
22
|
+
cacheWrite: 0,
|
|
23
|
+
totalTokens: 0,
|
|
24
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
|
|
25
|
+
},
|
|
26
|
+
stopReason: "error",
|
|
27
|
+
timestamp: Date.now(),
|
|
28
|
+
errorMessage: `Failed to load Cursor provider runtime: ${error instanceof Error ? error.message : String(error)}`,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function streamCursorLazy(
|
|
33
|
+
model: Model<Api>,
|
|
34
|
+
context: Context,
|
|
35
|
+
options?: SimpleStreamOptions,
|
|
36
|
+
): AssistantMessageEventStream {
|
|
37
|
+
const outer = createAssistantMessageEventStream();
|
|
38
|
+
queueMicrotask(async () => {
|
|
39
|
+
try {
|
|
40
|
+
const { streamCursor } = await import("./cursor-provider.js");
|
|
41
|
+
for await (const event of streamCursor(model, context, options)) {
|
|
42
|
+
outer.push(event);
|
|
43
|
+
}
|
|
44
|
+
} catch (error) {
|
|
45
|
+
const message = makeProviderLoadErrorMessage(model, error);
|
|
46
|
+
outer.push({ type: "error", reason: "error", error: message });
|
|
47
|
+
outer.end(message);
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
return outer;
|
|
51
|
+
}
|
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
deleteCursorNativeToolDisplay,
|
|
18
18
|
recordCursorNativeToolDisplay,
|
|
19
19
|
type CursorNativeToolDisplayItem,
|
|
20
|
-
} from "./cursor-native-tool-display.js";
|
|
20
|
+
} from "./cursor-native-tool-display-state.js";
|
|
21
21
|
import { type CursorPiBridgeToolRequest } from "./cursor-pi-tool-bridge.js";
|
|
22
22
|
import { resetSessionCursorAgent } from "./cursor-session-agent.js";
|
|
23
23
|
import { applyCursorApproximateUsage } from "./cursor-usage-accounting.js";
|