pi-cursor-sdk 0.1.37 → 0.1.38
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +27 -0
- package/docs/cursor-native-tool-replay.md +3 -3
- 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-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 +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 +16 -9
- 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
|
@@ -10,11 +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
|
-
|
|
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";
|
|
18
24
|
import {
|
|
19
25
|
createCursorReplayOnlyToolDefinition,
|
|
20
26
|
isCursorReplayNativeEditDetails,
|
|
@@ -29,8 +35,6 @@ import {
|
|
|
29
35
|
isCursorReplayToolCallId,
|
|
30
36
|
} from "./cursor-native-tool-display-state.js";
|
|
31
37
|
|
|
32
|
-
const CURSOR_MODEL_ACTIVE_REPLAY_TOOL_NAMES = [CURSOR_REPLAY_ACTIVITY_TOOL_NAME] as const;
|
|
33
|
-
const CURSOR_REPLAY_TOOL_NAMES = [CURSOR_REPLAY_ACTIVITY_TOOL_NAME] as const;
|
|
34
38
|
|
|
35
39
|
type AnyToolDefinition = ToolDefinition<TSchema, unknown, unknown>;
|
|
36
40
|
type RenderCall = NonNullable<AnyToolDefinition["renderCall"]>;
|
|
@@ -117,8 +121,6 @@ function renderWriteReplayResult(
|
|
|
117
121
|
: renderBase();
|
|
118
122
|
}
|
|
119
123
|
|
|
120
|
-
type BuiltinNativeCursorToolName = "read" | "bash" | "edit" | "write" | "grep" | "find" | "ls";
|
|
121
|
-
|
|
122
124
|
const NATIVE_CURSOR_TOOL_STRATEGIES: Record<BuiltinNativeCursorToolName, NativeReplayStrategy> = {
|
|
123
125
|
read: {
|
|
124
126
|
createDefinition: (cwd) => createReadToolDefinition(cwd) as AnyToolDefinition,
|
|
@@ -145,12 +147,6 @@ const NATIVE_CURSOR_TOOL_STRATEGIES: Record<BuiltinNativeCursorToolName, NativeR
|
|
|
145
147
|
ls: { createDefinition: (cwd) => createLsToolDefinition(cwd) as AnyToolDefinition },
|
|
146
148
|
};
|
|
147
149
|
|
|
148
|
-
const BUILTIN_NATIVE_CURSOR_TOOL_NAMES = Object.keys(NATIVE_CURSOR_TOOL_STRATEGIES) as BuiltinNativeCursorToolName[];
|
|
149
|
-
export const NATIVE_CURSOR_TOOL_NAMES = [
|
|
150
|
-
...BUILTIN_NATIVE_CURSOR_TOOL_NAMES,
|
|
151
|
-
...CURSOR_REPLAY_TOOL_NAMES,
|
|
152
|
-
] as readonly NativeCursorToolName[];
|
|
153
|
-
export type NativeCursorToolName = BuiltinNativeCursorToolName | typeof CURSOR_REPLAY_TOOL_NAMES[number];
|
|
154
150
|
|
|
155
151
|
function getNativeReplayStrategy(toolName: string): NativeReplayStrategy | undefined {
|
|
156
152
|
return Object.hasOwn(NATIVE_CURSOR_TOOL_STRATEGIES, toolName)
|
|
@@ -158,9 +154,6 @@ function getNativeReplayStrategy(toolName: string): NativeReplayStrategy | undef
|
|
|
158
154
|
: undefined;
|
|
159
155
|
}
|
|
160
156
|
|
|
161
|
-
export function isNativeCursorToolName(toolName: string): toolName is NativeCursorToolName {
|
|
162
|
-
return NATIVE_CURSOR_TOOL_NAMES.some((nativeToolName) => nativeToolName === toolName);
|
|
163
|
-
}
|
|
164
157
|
|
|
165
158
|
export function wrapNativeCursorTool<TParams extends TSchema, TDetails, TState>(
|
|
166
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";
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import type { AssistantMessage } from "@earendil-works/pi-ai";
|
|
2
|
-
import { cursorLiveRuns } from "./cursor-provider-live-run-drain.js";
|
|
3
|
-
import { abandonSessionCursorAgent } from "./cursor-provider-live-run-drain.js";
|
|
2
|
+
import { abandonSessionCursorAgent, cursorLiveRuns } from "./cursor-provider-live-run-drain.js";
|
|
4
3
|
import {
|
|
5
4
|
classifyCursorRunEmission,
|
|
6
5
|
getCursorRunAbortMessage,
|
|
@@ -12,7 +11,10 @@ import {
|
|
|
12
11
|
sanitizeCursorProviderError,
|
|
13
12
|
} from "./cursor-provider-errors.js";
|
|
14
13
|
import { CursorLiveRunAbortError } from "./cursor-live-run-coordinator.js";
|
|
15
|
-
import
|
|
14
|
+
import {
|
|
15
|
+
buildIncompleteCursorToolRunOutcome,
|
|
16
|
+
type IncompleteCursorToolRunOutcomeInput,
|
|
17
|
+
} from "./cursor-incomplete-tool-visibility.js";
|
|
16
18
|
import type { installCursorSdkProcessErrorGuard } from "./cursor-sdk-process-error-guard.js";
|
|
17
19
|
import type { CursorSdkEventDebugSink } from "./cursor-sdk-event-debug.js";
|
|
18
20
|
import { awaitFinalizeCursorRunOutcome } from "./cursor-provider-turn-finalize.js";
|
|
@@ -24,8 +26,6 @@ import type {
|
|
|
24
26
|
} from "./cursor-provider-turn-types.js";
|
|
25
27
|
import { applyCursorApproximateUsage } from "./cursor-usage-accounting.js";
|
|
26
28
|
import { hasUsableText } from "./cursor-record-utils.js";
|
|
27
|
-
import { buildIncompleteCursorToolRunOutcome } from "./cursor-incomplete-tool-visibility.js";
|
|
28
|
-
|
|
29
29
|
export type CursorTurnTerminalEvent =
|
|
30
30
|
| {
|
|
31
31
|
kind: "direct";
|
|
@@ -10,7 +10,6 @@ import { hasUsableText } from "./cursor-record-utils.js";
|
|
|
10
10
|
import {
|
|
11
11
|
buildIncompleteCursorToolRunOutcome,
|
|
12
12
|
type IncompleteCursorToolRunOutcome,
|
|
13
|
-
type IncompleteCursorToolRunOutcomeInput,
|
|
14
13
|
} from "./cursor-incomplete-tool-visibility.js";
|
|
15
14
|
|
|
16
15
|
/** Unified SDK wait() facts consumed by live and direct emission strategies. */
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
type IncompleteCursorToolRunOutcome,
|
|
14
14
|
} from "./cursor-incomplete-tool-visibility.js";
|
|
15
15
|
import { getToolName } from "./cursor-transcript-utils.js";
|
|
16
|
-
import {
|
|
16
|
+
import { getNormalizedCursorToolName } from "./cursor-tool-visibility.js";
|
|
17
17
|
import { buildCursorPiToolDisplay } from "./cursor-tool-transcript.js";
|
|
18
18
|
import { getField } from "./cursor-record-utils.js";
|
|
19
19
|
import { CursorTurnDisplayRouter } from "./cursor-provider-turn-display-router.js";
|
|
@@ -32,10 +32,6 @@ import {
|
|
|
32
32
|
getToolFingerprint,
|
|
33
33
|
} from "./cursor-provider-turn-tool-ledger.js";
|
|
34
34
|
|
|
35
|
-
function getNormalizedCursorToolName(toolCall: unknown): string {
|
|
36
|
-
return classifyCursorToolVisibility(toolCall).normalizedName;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
35
|
export interface CursorSdkTurnCoordinatorOptions {
|
|
40
36
|
stream: AssistantMessageEventStream;
|
|
41
37
|
partial: AssistantMessage;
|
|
@@ -207,6 +203,9 @@ export class CursorSdkTurnCoordinator {
|
|
|
207
203
|
identity: resolution.identity,
|
|
208
204
|
source: resolution.source,
|
|
209
205
|
});
|
|
206
|
+
if (resolution.matchedStartedCallId && resolution.matchedStartedCallId !== update.callId) {
|
|
207
|
+
this.ledger.recordCompletedIdentity(`cursor-tool:${resolution.matchedStartedCallId}`);
|
|
208
|
+
}
|
|
210
209
|
return;
|
|
211
210
|
}
|
|
212
211
|
if (update.type === "shell-output-delta") {
|
|
@@ -10,7 +10,11 @@ import {
|
|
|
10
10
|
type IncompleteCursorToolDiscardReason,
|
|
11
11
|
} from "./cursor-incomplete-tool-visibility.js";
|
|
12
12
|
import { scrubPiToolDisplay, scrubSensitiveText } from "./cursor-sensitive-text.js";
|
|
13
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
buildCursorPiToolDisplay,
|
|
15
|
+
formatCursorToolTranscript,
|
|
16
|
+
getCursorCreatePlanText,
|
|
17
|
+
} from "./cursor-tool-transcript.js";
|
|
14
18
|
import { getToolName } from "./cursor-transcript-utils.js";
|
|
15
19
|
import type { CursorPartialContentEmitter } from "./cursor-partial-content-emitter.js";
|
|
16
20
|
import type { CursorToolDisplaySource } from "./cursor-provider-turn-tool-ledger.js";
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { CursorLiveRunAbortError } from "./cursor-live-run-coordinator.js";
|
|
2
2
|
import {
|
|
3
|
+
cursorLiveRuns,
|
|
3
4
|
drainCursorLiveRunTurn,
|
|
4
5
|
flushPendingCursorLiveRunTraceEventsToStream,
|
|
5
6
|
settleCursorLiveToolBatch,
|
|
6
7
|
} from "./cursor-provider-live-run-drain.js";
|
|
7
|
-
import { cursorLiveRuns } from "./cursor-provider-live-run-drain.js";
|
|
8
8
|
import {
|
|
9
9
|
buildIncompleteCursorToolRunOutcome,
|
|
10
10
|
type IncompleteCursorToolRunOutcomeInput,
|
|
@@ -8,13 +8,9 @@ import {
|
|
|
8
8
|
formatCursorToolLifecycleProgressText,
|
|
9
9
|
isCursorToolLifecycleEligible,
|
|
10
10
|
} from "./cursor-tool-lifecycle.js";
|
|
11
|
-
import {
|
|
11
|
+
import { getNormalizedCursorToolName } from "./cursor-tool-visibility.js";
|
|
12
12
|
import { getStartedToolCallFingerprint } from "./cursor-provider-turn-tool-ledger.js";
|
|
13
13
|
|
|
14
|
-
function getNormalizedCursorToolName(toolCall: unknown): string {
|
|
15
|
-
return classifyCursorToolVisibility(toolCall).normalizedName;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
14
|
export interface CursorToolLifecycleEmitterOptions {
|
|
19
15
|
liveRun?: CursorLiveRun;
|
|
20
16
|
resolvedApiKey?: string;
|
|
@@ -8,23 +8,27 @@ import {
|
|
|
8
8
|
resetSessionCursorAgent,
|
|
9
9
|
} from "./cursor-session-agent.js";
|
|
10
10
|
import type { CursorPiBridgeToolRequest } from "./cursor-pi-tool-bridge.js";
|
|
11
|
-
import {
|
|
11
|
+
import { estimateCursorPromptTokens } from "./context.js";
|
|
12
|
+
import { getCursorPromptOptions } from "./cursor-usage-accounting.js";
|
|
12
13
|
import { getActiveContextToolNames } from "./cursor-context-tools.js";
|
|
13
14
|
import type { CursorLiveRun } from "./cursor-live-run-coordinator.js";
|
|
14
|
-
import {
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
import {
|
|
16
|
+
abandonSessionCursorAgent,
|
|
17
|
+
createCursorNativeReplayId,
|
|
18
|
+
cursorLiveRuns,
|
|
19
|
+
} from "./cursor-provider-live-run-drain.js";
|
|
20
|
+
import { getCursorProviderAgentModeOrThrow, getEffectiveFastForModelId } from "./cursor-state.js";
|
|
17
21
|
import { buildCursorModelSelection } from "./model-discovery.js";
|
|
18
22
|
import { getEffectiveCursorSettingSources } from "./cursor-setting-sources.js";
|
|
19
|
-
import { resolveCursorPiToolBridgeEnabled } from "./cursor-pi-tool-bridge-
|
|
23
|
+
import { resolveCursorPiToolBridgeEnabled } from "./cursor-pi-tool-bridge-env.js";
|
|
20
24
|
import {
|
|
21
25
|
buildCursorToolManifestText,
|
|
22
26
|
resolveCursorToolManifestEnabled,
|
|
23
27
|
} from "./cursor-tool-manifest.js";
|
|
24
|
-
import { isCursorNativeToolDisplayRuntimeEnabled } from "./cursor-native-tool-display.js";
|
|
28
|
+
import { isCursorNativeToolDisplayRuntimeEnabled } from "./cursor-native-tool-display-state.js";
|
|
25
29
|
import { MISSING_CURSOR_API_KEY_MESSAGE } from "./cursor-provider-errors.js";
|
|
26
30
|
import { CursorSdkTurnCoordinator } from "./cursor-provider-turn-coordinator.js";
|
|
27
|
-
import { resolveCursorApiKey } from "./cursor-
|
|
31
|
+
import { resolveCursorApiKey } from "./cursor-api-key.js";
|
|
28
32
|
import { loadCursorSdk } from "./cursor-sdk-runtime.js";
|
|
29
33
|
import type {
|
|
30
34
|
CursorProviderTurnPrepareResult,
|
|
@@ -53,7 +57,7 @@ export async function prepareCursorProviderTurn(
|
|
|
53
57
|
|
|
54
58
|
try {
|
|
55
59
|
const fastEnabled = getEffectiveFastForModelId(model.id);
|
|
56
|
-
const agentMode =
|
|
60
|
+
const agentMode = getCursorProviderAgentModeOrThrow();
|
|
57
61
|
const selection = buildCursorModelSelection(model.id, options?.reasoning ?? "off", fastEnabled);
|
|
58
62
|
const settingSources = getEffectiveCursorSettingSources();
|
|
59
63
|
const { Agent } = await loadCursorSdk();
|
|
@@ -116,7 +120,7 @@ export async function prepareCursorProviderTurn(
|
|
|
116
120
|
images: prompt.images.length > 0 ? prompt.images : undefined,
|
|
117
121
|
};
|
|
118
122
|
const sessionBridgeRun = bridgeRun;
|
|
119
|
-
const promptInputTokens =
|
|
123
|
+
const promptInputTokens = estimateCursorPromptTokens(prompt, promptOptions);
|
|
120
124
|
const useNativeToolReplay = isCursorNativeToolDisplayRuntimeEnabled();
|
|
121
125
|
const activeToolNames = getActiveContextToolNames(context);
|
|
122
126
|
sdkEventDebug?.recordProviderMeta({
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { CursorLiveRunAbortError } from "./cursor-live-run-coordinator.js";
|
|
2
2
|
import { drainExistingCursorLiveRunBeforeSend } from "./cursor-provider-live-run-drain.js";
|
|
3
|
-
import { getCursorSessionCwd } from "./cursor-session-
|
|
3
|
+
import { getCursorSessionCwd } from "./cursor-session-scope.js";
|
|
4
4
|
import { installCursorSdkProcessErrorGuard } from "./cursor-sdk-process-error-guard.js";
|
|
5
5
|
import { CursorSdkEventDebugSink } from "./cursor-sdk-event-debug.js";
|
|
6
6
|
import { awaitFinalizeCursorRunOutcome } from "./cursor-provider-turn-finalize.js";
|
|
@@ -17,7 +17,6 @@ import type {
|
|
|
17
17
|
CursorProviderTurnSendResult,
|
|
18
18
|
} from "./cursor-provider-turn-types.js";
|
|
19
19
|
|
|
20
|
-
export { resolveCursorApiKey } from "./cursor-provider-turn-api-key.js";
|
|
21
20
|
export type { CursorProviderTurnRunnerParams } from "./cursor-provider-turn-types.js";
|
|
22
21
|
|
|
23
22
|
export class CursorProviderTurnRunner {
|
|
@@ -34,13 +33,6 @@ export class CursorProviderTurnRunner {
|
|
|
34
33
|
if (this.options?.signal?.aborted) throw new CursorLiveRunAbortError();
|
|
35
34
|
}
|
|
36
35
|
|
|
37
|
-
private discardIncompleteTools(
|
|
38
|
-
prepared: CursorProviderTurnPrepareResult | undefined,
|
|
39
|
-
outcome: import("./cursor-incomplete-tool-visibility.js").IncompleteCursorToolRunOutcomeInput,
|
|
40
|
-
): void {
|
|
41
|
-
discardIncompleteToolsFromPrepared(prepared, outcome);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
36
|
async run(sdkProcessErrorGuard: ReturnType<typeof installCursorSdkProcessErrorGuard>): Promise<void> {
|
|
45
37
|
const { stream, partial, model, context, options, sdkEventDebugRef } = this.params;
|
|
46
38
|
let prepared: CursorProviderTurnPrepareResult | undefined;
|
|
@@ -94,13 +86,13 @@ export class CursorProviderTurnRunner {
|
|
|
94
86
|
send,
|
|
95
87
|
prepared,
|
|
96
88
|
modelId: model.id,
|
|
97
|
-
discardIncompleteTools: (outcome) =>
|
|
89
|
+
discardIncompleteTools: (outcome) => discardIncompleteToolsFromPrepared(prepared, outcome),
|
|
98
90
|
});
|
|
99
91
|
await emitCursorLiveTurn({
|
|
100
92
|
params: this.params,
|
|
101
93
|
prepared,
|
|
102
94
|
sdkEventDebug: this.sdkEventDebug,
|
|
103
|
-
discardIncompleteTools: (outcome) =>
|
|
95
|
+
discardIncompleteTools: (outcome) => discardIncompleteToolsFromPrepared(prepared, outcome),
|
|
104
96
|
});
|
|
105
97
|
return;
|
|
106
98
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { asRecord } from "./cursor-record-utils.js";
|
|
2
2
|
import type { CursorLiveRun } from "./cursor-live-run-coordinator.js";
|
|
3
3
|
import {
|
|
4
4
|
mergeShellOutputDeltasIntoCursorToolCall,
|
|
@@ -8,6 +8,22 @@ import type { CursorToolCompletionLedger } from "./cursor-provider-turn-tool-led
|
|
|
8
8
|
|
|
9
9
|
export type CursorToolCompletionSource = "delta" | "step";
|
|
10
10
|
|
|
11
|
+
function mergeCursorToolCalls(startedToolCall: unknown, completedToolCall: unknown): unknown {
|
|
12
|
+
const started = asRecord(startedToolCall);
|
|
13
|
+
const completed = asRecord(completedToolCall);
|
|
14
|
+
if (!started) return completedToolCall;
|
|
15
|
+
if (!completed) return startedToolCall;
|
|
16
|
+
return {
|
|
17
|
+
...started,
|
|
18
|
+
...completed,
|
|
19
|
+
name: completed.name ?? started.name,
|
|
20
|
+
type: completed.type ?? started.type,
|
|
21
|
+
args: completed.args ?? started.args,
|
|
22
|
+
input: completed.input ?? started.input,
|
|
23
|
+
result: completed.result ?? started.result,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
11
27
|
export type ToolCompletionResolution =
|
|
12
28
|
| { action: "ignore-bridge"; identity?: string }
|
|
13
29
|
| {
|
|
@@ -47,15 +63,22 @@ export function resolveCursorToolCompletion(options: ResolveCursorToolCompletion
|
|
|
47
63
|
const callId = options.callId;
|
|
48
64
|
identity = typeof callId === "string" ? `cursor-tool:${callId}` : undefined;
|
|
49
65
|
resolvedToolCall = mergeCursorToolCalls(options.startedToolCall, options.toolCall);
|
|
50
|
-
if (typeof callId === "string") {
|
|
66
|
+
if (typeof callId === "string" && options.ledger.hasStartedToolCall(callId)) {
|
|
51
67
|
options.onClearStartedCallId?.(callId);
|
|
52
68
|
options.ledger.clearStartedToolCall(callId);
|
|
69
|
+
} else {
|
|
70
|
+
matchedStartedCallId = options.ledger.removeStartedToolCallForStep(options.toolCall, callId);
|
|
71
|
+
if (matchedStartedCallId) options.onClearStartedCallId?.(matchedStartedCallId);
|
|
53
72
|
}
|
|
54
73
|
resolvedToolCall = mergeShellOutputDeltasIntoCursorToolCall(
|
|
55
74
|
resolvedToolCall,
|
|
56
|
-
|
|
75
|
+
matchedStartedCallId
|
|
76
|
+
? options.shellOutput.takeDeltasForCall(matchedStartedCallId)
|
|
77
|
+
: typeof callId === "string"
|
|
78
|
+
? options.shellOutput.takeDeltasForCall(callId)
|
|
79
|
+
: undefined,
|
|
57
80
|
);
|
|
58
|
-
source = identity ? "started" : "fallback";
|
|
81
|
+
source = identity || matchedStartedCallId ? "started" : "fallback";
|
|
59
82
|
} else {
|
|
60
83
|
matchedStartedCallId = options.ledger.removeStartedToolCallForStep(options.toolCall, options.callId);
|
|
61
84
|
if (matchedStartedCallId) {
|
|
@@ -77,7 +100,7 @@ export function resolveCursorToolCompletion(options: ResolveCursorToolCompletion
|
|
|
77
100
|
}
|
|
78
101
|
|
|
79
102
|
if (options.source === "delta") {
|
|
80
|
-
return { action: "handle", toolCall: resolvedToolCall, identity, source };
|
|
103
|
+
return { action: "handle", toolCall: resolvedToolCall, identity, source, matchedStartedCallId };
|
|
81
104
|
}
|
|
82
105
|
return {
|
|
83
106
|
action: "handle",
|