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
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
CURSOR_KNOWN_NORMALIZED_TOOL_NAMES,
|
|
3
3
|
CURSOR_REPLAY_ACTIVITY_TOOL_NAME,
|
|
4
|
-
getCursorReplayActivityTitle,
|
|
5
4
|
getCursorReplayCallSummary,
|
|
5
|
+
getCursorToolActivityTitle,
|
|
6
6
|
getCursorToolActivityReplaySpec,
|
|
7
7
|
getCursorToolGenerateImageReplaySpec,
|
|
8
8
|
type CursorNormalizedToolName,
|
|
@@ -12,21 +12,16 @@ import { resolveCursorEditDiff } from "./cursor-edit-diff.js";
|
|
|
12
12
|
import {
|
|
13
13
|
assembleCursorReplayActivityDetails,
|
|
14
14
|
assembleCursorReplayGenerateImageDetails,
|
|
15
|
-
buildCursorReplayNativeEditDetails,
|
|
16
|
-
buildCursorReplayNativeWriteDetails,
|
|
17
15
|
CURSOR_REPLAY_UNREGISTERED_ACTIVITY_TOOL_NAME,
|
|
18
16
|
type CursorReplayToolDetails,
|
|
19
17
|
} from "./cursor-replay-tool-details.js";
|
|
18
|
+
import { asRecord, getNumber, getString } from "./cursor-record-utils.js";
|
|
20
19
|
import {
|
|
21
|
-
asRecord,
|
|
22
20
|
firstNonEmptyLine,
|
|
23
21
|
formatDisplayPath,
|
|
24
22
|
formatDiffString,
|
|
25
23
|
formatError,
|
|
26
|
-
getNumber,
|
|
27
|
-
getString,
|
|
28
24
|
limitText,
|
|
29
|
-
stringifyUnknown,
|
|
30
25
|
truncateArg,
|
|
31
26
|
type CursorPiToolDisplay,
|
|
32
27
|
type NormalizedResult,
|
|
@@ -68,8 +63,6 @@ import {
|
|
|
68
63
|
getShellOutput,
|
|
69
64
|
usesLocalReadPreview,
|
|
70
65
|
} from "./cursor-transcript-tool-formatters.js";
|
|
71
|
-
import type { CursorReplaySummaryArgs } from "./cursor-replay-summary-args.js";
|
|
72
|
-
|
|
73
66
|
export interface ToolDisplayContext {
|
|
74
67
|
rawName: string;
|
|
75
68
|
name: string;
|
|
@@ -102,13 +95,6 @@ function buildCursorActivityDisplayArgs(
|
|
|
102
95
|
};
|
|
103
96
|
}
|
|
104
97
|
|
|
105
|
-
function buildRegistryReplaySummary(
|
|
106
|
-
sourceToolName: CursorReplayActivityToolName,
|
|
107
|
-
args: CursorReplaySummaryArgs,
|
|
108
|
-
): string | undefined {
|
|
109
|
-
return getCursorReplayCallSummary(sourceToolName, args);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
98
|
function buildReplaySummaryDisplay(
|
|
113
99
|
toolName: string,
|
|
114
100
|
args: Record<string, unknown>,
|
|
@@ -131,20 +117,16 @@ function buildReplaySummaryDisplay(
|
|
|
131
117
|
};
|
|
132
118
|
}
|
|
133
119
|
|
|
134
|
-
function getCursorToolActivityTitle(toolName: string): string {
|
|
135
|
-
return getCursorReplayActivityTitle(toolName) ?? buildGenericUnknownToolActivityTitle(toolName);
|
|
136
|
-
}
|
|
137
|
-
|
|
138
120
|
function buildActivityReplayDisplay(
|
|
139
121
|
sourceToolName: NeutralActivityReplayToolName,
|
|
140
122
|
context: ToolDisplayContext,
|
|
141
123
|
): CursorPiToolDisplay {
|
|
142
|
-
const spec =
|
|
124
|
+
const spec = TOOL_DISPLAY_IMPLEMENTATIONS[sourceToolName];
|
|
143
125
|
const activity = getCursorToolActivityReplaySpec(sourceToolName);
|
|
144
126
|
if (!activity) throw new Error(`Missing activity replay spec for ${sourceToolName}`);
|
|
145
|
-
const activityTitle =
|
|
127
|
+
const activityTitle = getCursorToolActivityTitle(sourceToolName);
|
|
146
128
|
const replayArgs = activity.buildActivityArgs(context);
|
|
147
|
-
const activitySummary =
|
|
129
|
+
const activitySummary = getCursorReplayCallSummary(sourceToolName, replayArgs);
|
|
148
130
|
const activityArgs = buildCursorActivityDisplayArgs({ ...replayArgs }, activityTitle, activitySummary);
|
|
149
131
|
const contentText = spec.formatTranscript(context).trimEnd();
|
|
150
132
|
const activityFields = activity.buildDetails(context, contentText);
|
|
@@ -160,12 +142,12 @@ function buildActivityReplayDisplay(
|
|
|
160
142
|
}
|
|
161
143
|
|
|
162
144
|
function buildGenerateImageReplayDisplay(context: ToolDisplayContext): CursorPiToolDisplay {
|
|
163
|
-
const spec =
|
|
145
|
+
const spec = TOOL_DISPLAY_IMPLEMENTATIONS.generateImage;
|
|
164
146
|
const replay = getCursorToolGenerateImageReplaySpec("generateImage");
|
|
165
147
|
if (!replay) throw new Error("Missing generate image replay spec");
|
|
166
|
-
const activityTitle =
|
|
148
|
+
const activityTitle = getCursorToolActivityTitle("generateImage");
|
|
167
149
|
const replayArgs = replay.buildActivityArgs(context);
|
|
168
|
-
const activitySummary =
|
|
150
|
+
const activitySummary = getCursorReplayCallSummary("generateImage", replayArgs);
|
|
169
151
|
const activityArgs = buildCursorActivityDisplayArgs({ ...replayArgs }, activityTitle, activitySummary);
|
|
170
152
|
const contentText = spec.formatTranscript(context).trimEnd();
|
|
171
153
|
const details = assembleCursorReplayGenerateImageDetails(
|
|
@@ -177,15 +159,10 @@ function buildGenerateImageReplayDisplay(context: ToolDisplayContext): CursorPiT
|
|
|
177
159
|
return buildReplaySummaryDisplay(CURSOR_REPLAY_ACTIVITY_TOOL_NAME, activityArgs, context.result, contentText, details);
|
|
178
160
|
}
|
|
179
161
|
|
|
180
|
-
function buildGenericUnknownToolActivityTitle(displayName: string): string {
|
|
181
|
-
if (displayName === "unknown") return "Cursor tool";
|
|
182
|
-
return `Cursor ${truncateArg(displayName)}`;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
162
|
function buildGenericPiToolDisplay(context: ToolDisplayContext): CursorPiToolDisplay {
|
|
186
163
|
const { rawName, name, args, result, options } = context;
|
|
187
164
|
const displayName = rawName.trim() || name;
|
|
188
|
-
const activityTitle =
|
|
165
|
+
const activityTitle = getCursorToolActivityTitle(displayName);
|
|
189
166
|
const contentText = formatFallback(name, args, result, options);
|
|
190
167
|
const fallbackBody = contentText.includes("\n\n") ? contentText.slice(contentText.indexOf("\n\n") + 2) : "";
|
|
191
168
|
const activitySummary =
|
|
@@ -237,14 +214,15 @@ function buildEditPiToolDisplay(context: ToolDisplayContext): CursorPiToolDispla
|
|
|
237
214
|
const activityTitle = getCursorToolActivityTitle("edit");
|
|
238
215
|
const activityArgs = buildCursorActivityDisplayArgs(baseActivityArgs, activityTitle, displayPath);
|
|
239
216
|
const contentText = formatEdit(activityArgs, result, options);
|
|
240
|
-
const details =
|
|
217
|
+
const details: CursorReplayToolDetails = {
|
|
218
|
+
variant: "nativeEdit",
|
|
241
219
|
path: displayPath,
|
|
242
220
|
linesAdded: getNumber(value, "linesAdded"),
|
|
243
221
|
linesRemoved: getNumber(value, "linesRemoved"),
|
|
244
222
|
diffString: normalizedDiff,
|
|
245
223
|
diff: normalizedDiff,
|
|
246
224
|
firstChangedLine: getNumber(value, "firstChangedLine"),
|
|
247
|
-
}
|
|
225
|
+
};
|
|
248
226
|
if (nativeEditArgs) {
|
|
249
227
|
return {
|
|
250
228
|
toolName: "edit",
|
|
@@ -284,13 +262,14 @@ function buildWritePiToolDisplay(context: ToolDisplayContext): CursorPiToolDispl
|
|
|
284
262
|
const displayArgs = buildWriteDisplayArgs(args, options);
|
|
285
263
|
const displayPath = typeof args.path === "string" ? formatDisplayPath(args.path, options.cwd) : undefined;
|
|
286
264
|
const contentText = formatWrite(args, result, options).trimEnd();
|
|
287
|
-
const details =
|
|
265
|
+
const details: CursorReplayToolDetails = {
|
|
266
|
+
variant: "nativeWrite",
|
|
288
267
|
path: displayPath,
|
|
289
268
|
linesCreated: getNumber(value, "linesCreated"),
|
|
290
269
|
fileSize: getNumber(value, "fileSize"),
|
|
291
270
|
fileContentAfterWrite: getString(value, "fileContentAfterWrite"),
|
|
292
271
|
expandedText: contentText,
|
|
293
|
-
}
|
|
272
|
+
};
|
|
294
273
|
if (content === undefined) {
|
|
295
274
|
const activityTitle = getCursorToolActivityTitle("write");
|
|
296
275
|
return buildReplaySummaryDisplay(
|
|
@@ -440,12 +419,8 @@ const TOOL_DISPLAY_IMPLEMENTATIONS: Record<CursorNormalizedToolName, ToolDisplay
|
|
|
440
419
|
|
|
441
420
|
export const CURSOR_TOOL_DISPLAY_SPEC_KEYS = CURSOR_KNOWN_NORMALIZED_TOOL_NAMES;
|
|
442
421
|
|
|
443
|
-
const TOOL_DISPLAY_SPECS = Object.fromEntries(
|
|
444
|
-
CURSOR_KNOWN_NORMALIZED_TOOL_NAMES.map((name) => [name, TOOL_DISPLAY_IMPLEMENTATIONS[name]]),
|
|
445
|
-
) as Record<CursorNormalizedToolName, ToolDisplaySpec>;
|
|
446
|
-
|
|
447
422
|
function getToolDisplaySpec(name: string): ToolDisplaySpec | undefined {
|
|
448
|
-
if (Object.hasOwn(
|
|
423
|
+
if (Object.hasOwn(TOOL_DISPLAY_IMPLEMENTATIONS, name)) return TOOL_DISPLAY_IMPLEMENTATIONS[name as CursorNormalizedToolName];
|
|
449
424
|
return undefined;
|
|
450
425
|
}
|
|
451
426
|
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import { closeSync, openSync, readSync, realpathSync, statSync } from "node:fs";
|
|
2
2
|
import { isAbsolute, relative, resolve } from "node:path";
|
|
3
|
-
import { asRecord,
|
|
4
|
-
|
|
5
|
-
export { asRecord, getFirstStringByKeys } from "./cursor-record-utils.js";
|
|
3
|
+
import { asRecord, getArray, getRecord, getString, stringifyUnknown as stringifyUnknownValue } from "./cursor-record-utils.js";
|
|
6
4
|
|
|
7
5
|
export interface TranscriptOptions {
|
|
8
6
|
maxChars?: number;
|
|
@@ -47,30 +45,6 @@ export function isLocalReadPreviewContent(text: string): boolean {
|
|
|
47
45
|
return text.startsWith(LOCAL_READ_PREVIEW_NOTICE);
|
|
48
46
|
}
|
|
49
47
|
|
|
50
|
-
export function getString(record: Record<string, unknown> | undefined, key: string): string | undefined {
|
|
51
|
-
const value = record?.[key];
|
|
52
|
-
return typeof value === "string" ? value : undefined;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export function getNumber(record: Record<string, unknown> | undefined, key: string): number | undefined {
|
|
56
|
-
const value = record?.[key];
|
|
57
|
-
return typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
export function getBoolean(record: Record<string, unknown> | undefined, key: string): boolean | undefined {
|
|
61
|
-
const value = record?.[key];
|
|
62
|
-
return typeof value === "boolean" ? value : undefined;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
export function getRecord(record: Record<string, unknown> | undefined, key: string): Record<string, unknown> | undefined {
|
|
66
|
-
return asRecord(record?.[key]);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
export function getArray(record: Record<string, unknown> | undefined, key: string): unknown[] | undefined {
|
|
70
|
-
const value = record?.[key];
|
|
71
|
-
return Array.isArray(value) ? value : undefined;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
48
|
export function getToolName(toolCall: unknown): string {
|
|
75
49
|
const record = asRecord(toolCall);
|
|
76
50
|
return getString(record, "name") ?? getString(record, "type") ?? getString(record, "toolName") ?? "unknown";
|
|
@@ -96,13 +70,7 @@ export function normalizeResult(result: unknown): NormalizedResult {
|
|
|
96
70
|
}
|
|
97
71
|
|
|
98
72
|
export function stringifyUnknown(value: unknown): string {
|
|
99
|
-
|
|
100
|
-
if (typeof value === "string") return value;
|
|
101
|
-
try {
|
|
102
|
-
return JSON.stringify(value, null, 2) ?? String(value);
|
|
103
|
-
} catch {
|
|
104
|
-
return String(value);
|
|
105
|
-
}
|
|
73
|
+
return stringifyUnknownValue(value, { pretty: true });
|
|
106
74
|
}
|
|
107
75
|
|
|
108
76
|
export function limitText(text: string, options: TranscriptOptions = {}, knownTotalLines?: number): string {
|
|
@@ -3,9 +3,7 @@ import {
|
|
|
3
3
|
CURSOR_APPROX_CHARS_PER_TOKEN,
|
|
4
4
|
CURSOR_IMAGE_TOKEN_ESTIMATE,
|
|
5
5
|
estimateCursorContextTokens,
|
|
6
|
-
estimateCursorPromptTokens,
|
|
7
6
|
estimateCursorTextTokens,
|
|
8
|
-
type CursorPrompt,
|
|
9
7
|
type CursorPromptOptions,
|
|
10
8
|
} from "./context.js";
|
|
11
9
|
|
|
@@ -28,10 +26,6 @@ export function getCursorPromptOptions(model: Model<Api>): CursorUsagePromptOpti
|
|
|
28
26
|
};
|
|
29
27
|
}
|
|
30
28
|
|
|
31
|
-
export function estimateCursorPromptInputTokens(prompt: CursorPrompt, options: Pick<CursorPromptOptions, "charsPerToken" | "imageTokenEstimate">): number {
|
|
32
|
-
return estimateCursorPromptTokens(prompt, options);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
29
|
function stringifyUsageValue(value: unknown): string {
|
|
36
30
|
try {
|
|
37
31
|
return JSON.stringify(value) ?? "";
|
|
@@ -1,23 +1,15 @@
|
|
|
1
|
+
import { getFirstStringByKeys } from "./cursor-record-utils.js";
|
|
1
2
|
import {
|
|
2
|
-
classifyCursorWebToolKind
|
|
3
|
-
|
|
3
|
+
classifyCursorWebToolKind,
|
|
4
|
+
normalizeCursorToolName as normalizeToolName,
|
|
4
5
|
} from "./cursor-tool-presentation-registry.js";
|
|
5
|
-
import { normalizeCursorToolName as normalizeToolName } from "./cursor-tool-presentation-registry.js";
|
|
6
|
-
import { extractWebFetchTarget, extractWebSearchQuery } from "./cursor-web-tool-args.js";
|
|
7
|
-
|
|
8
|
-
export type { CursorWebToolKind } from "./cursor-tool-presentation-registry.js";
|
|
9
|
-
export { extractWebFetchTarget, extractWebSearchQuery } from "./cursor-web-tool-args.js";
|
|
10
6
|
|
|
11
7
|
function getMcpToolName(args: Record<string, unknown>): string | undefined {
|
|
12
|
-
const toolName =
|
|
8
|
+
const toolName = getFirstStringByKeys(args, ["toolName", "tool_name"]);
|
|
13
9
|
const trimmed = toolName?.trim();
|
|
14
10
|
return trimmed || undefined;
|
|
15
11
|
}
|
|
16
12
|
|
|
17
|
-
export function classifyCursorWebToolKind(name: string | undefined): CursorWebToolKind | undefined {
|
|
18
|
-
return classifyCursorWebToolKindFromRegistry(name);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
13
|
/**
|
|
22
14
|
* Maps SDK/host/MCP tool names to transcript display keys.
|
|
23
15
|
* Web search/fetch often arrives as MCP `toolName` values, not dedicated SDK ToolTypes.
|
|
@@ -1,17 +1,9 @@
|
|
|
1
|
-
import { asRecord } from "./cursor-record-utils.js";
|
|
1
|
+
import { asRecord, firstNonEmptyString } from "./cursor-record-utils.js";
|
|
2
2
|
|
|
3
3
|
function getNestedMcpArgs(args: Record<string, unknown>): Record<string, unknown> {
|
|
4
4
|
return asRecord(args.args) ?? {};
|
|
5
5
|
}
|
|
6
6
|
|
|
7
|
-
function firstNonEmptyString(...values: Array<string | undefined>): string | undefined {
|
|
8
|
-
for (const value of values) {
|
|
9
|
-
const trimmed = value?.trim();
|
|
10
|
-
if (trimmed) return trimmed;
|
|
11
|
-
}
|
|
12
|
-
return undefined;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
7
|
export function extractWebSearchQuery(args: Record<string, unknown>): string | undefined {
|
|
16
8
|
const nested = getNestedMcpArgs(args);
|
|
17
9
|
return firstNonEmptyString(
|
package/src/index.ts
CHANGED
|
@@ -1,26 +1,27 @@
|
|
|
1
|
-
import type { ExtensionAPI,
|
|
1
|
+
import type { ExtensionAPI, ProviderConfig, ProviderModelConfig } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
import { discoverModels, type CursorModelFallbackIssue } from "./model-discovery.js";
|
|
3
3
|
import { registerCursorRuntimeControls } from "./cursor-state.js";
|
|
4
|
-
import { registerCursorNativeToolDisplay } from "./cursor-native-tool-display.js";
|
|
4
|
+
import { registerCursorNativeToolDisplay } from "./cursor-native-tool-display-registration.js";
|
|
5
5
|
import { registerCursorPiToolBridge } from "./cursor-pi-tool-bridge.js";
|
|
6
6
|
import { registerCursorQuestionTool } from "./cursor-question-tool.js";
|
|
7
7
|
import { registerCursorSkillTool } from "./cursor-skill-tool.js";
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import { prepareCursorSessionForCompaction } from "./cursor-session-compaction-prep.js";
|
|
12
|
-
import { streamCursor } from "./cursor-provider.js";
|
|
8
|
+
import { registerCursorSessionScope } from "./cursor-session-scope.js";
|
|
9
|
+
import { registerCursorSessionAgentLifecycle } from "./cursor-session-agent-lifecycle.js";
|
|
10
|
+
import { streamCursorLazy } from "./cursor-provider-lazy.js";
|
|
13
11
|
import { CURSOR_API_KEY_CONFIG_VALUE } from "./cursor-api-key.js";
|
|
12
|
+
import { registerCursorFallbackIssueWarning } from "./cursor-fallback-warning.js";
|
|
13
|
+
import { registerCursorAgentsContextDedup } from "./cursor-agents-context-registration.js";
|
|
14
14
|
|
|
15
15
|
type CursorExtensionApi =
|
|
16
16
|
& Pick<ExtensionAPI, "registerProvider" | "registerCommand" | "on">
|
|
17
|
-
& Parameters<typeof
|
|
18
|
-
& Parameters<typeof
|
|
17
|
+
& Parameters<typeof registerCursorSessionScope>[0]
|
|
18
|
+
& Parameters<typeof registerCursorSessionAgentLifecycle>[0]
|
|
19
19
|
& Parameters<typeof registerCursorRuntimeControls>[0]
|
|
20
20
|
& Parameters<typeof registerCursorNativeToolDisplay>[0]
|
|
21
21
|
& Parameters<typeof registerCursorQuestionTool>[0]
|
|
22
22
|
& Parameters<typeof registerCursorSkillTool>[0]
|
|
23
23
|
& Parameters<typeof registerCursorPiToolBridge>[0]
|
|
24
|
+
& Parameters<typeof registerCursorFallbackIssueWarning>[0]
|
|
24
25
|
& Parameters<typeof registerCursorAgentsContextDedup>[0];
|
|
25
26
|
|
|
26
27
|
function createCursorProviderConfig(models: ProviderModelConfig[]): ProviderConfig {
|
|
@@ -30,7 +31,7 @@ function createCursorProviderConfig(models: ProviderModelConfig[]): ProviderConf
|
|
|
30
31
|
apiKey: CURSOR_API_KEY_CONFIG_VALUE,
|
|
31
32
|
api: "cursor-sdk",
|
|
32
33
|
models,
|
|
33
|
-
streamSimple:
|
|
34
|
+
streamSimple: streamCursorLazy,
|
|
34
35
|
};
|
|
35
36
|
}
|
|
36
37
|
|
|
@@ -40,9 +41,10 @@ function registerCursorProvider(pi: Pick<ExtensionAPI, "registerProvider">, mode
|
|
|
40
41
|
|
|
41
42
|
export default async function (pi: CursorExtensionApi) {
|
|
42
43
|
// Session cwd must register before other session_start listeners that depend on it.
|
|
43
|
-
|
|
44
|
-
|
|
44
|
+
registerCursorSessionScope(pi);
|
|
45
|
+
registerCursorSessionAgentLifecycle(pi);
|
|
45
46
|
pi.on("session_before_compact", async () => {
|
|
47
|
+
const { prepareCursorSessionForCompaction } = await import("./cursor-session-compaction-prep.js");
|
|
46
48
|
await prepareCursorSessionForCompaction();
|
|
47
49
|
});
|
|
48
50
|
registerCursorRuntimeControls(pi);
|
|
@@ -59,10 +61,7 @@ export default async function (pi: CursorExtensionApi) {
|
|
|
59
61
|
});
|
|
60
62
|
|
|
61
63
|
if (fallbackIssue) {
|
|
62
|
-
|
|
63
|
-
pi.on("session_start", async (_event, ctx) => {
|
|
64
|
-
if (ctx.hasUI) ctx.ui.notify(issue.message, "warning");
|
|
65
|
-
});
|
|
64
|
+
registerCursorFallbackIssueWarning(pi, fallbackIssue);
|
|
66
65
|
}
|
|
67
66
|
|
|
68
67
|
pi.registerCommand("cursor-refresh-models", {
|
package/src/model-discovery.ts
CHANGED
|
@@ -4,12 +4,11 @@ import type {
|
|
|
4
4
|
ModelParameterValue,
|
|
5
5
|
ModelSelection,
|
|
6
6
|
} from "@cursor/sdk";
|
|
7
|
-
import {
|
|
7
|
+
import type { ProviderModelConfig } from "@earendil-works/pi-coding-agent";
|
|
8
8
|
import type { ModelThinkingLevel, ThinkingLevelMap } from "@earendil-works/pi-ai";
|
|
9
9
|
import { loadContextWindowCache } from "./context-window-cache.js";
|
|
10
10
|
import { loadCursorSdk } from "./cursor-sdk-runtime.js";
|
|
11
|
-
import {
|
|
12
|
-
import { FALLBACK_MODEL_ITEMS } from "./cursor-fallback-models.generated.js";
|
|
11
|
+
import { resolveCursorApiKey } from "./cursor-api-key.js";
|
|
13
12
|
import {
|
|
14
13
|
fingerprintApiKey,
|
|
15
14
|
loadAnyCachedModelCatalog,
|
|
@@ -62,6 +61,7 @@ function getCliApiKeyFromArgv(argv: string[] = process.argv): string | undefined
|
|
|
62
61
|
|
|
63
62
|
async function getStoredCursorApiKey(): Promise<string | undefined> {
|
|
64
63
|
try {
|
|
64
|
+
const { AuthStorage } = await import("@earendil-works/pi-coding-agent");
|
|
65
65
|
return resolveCursorApiKey(await AuthStorage.create().getApiKey(CURSOR_PROVIDER_ID, { includeFallback: false }));
|
|
66
66
|
} catch {
|
|
67
67
|
return undefined;
|
|
@@ -462,8 +462,9 @@ function sanitizeDiscoveryError(error: unknown, apiKey: string): string | undefi
|
|
|
462
462
|
return scrubbed || undefined;
|
|
463
463
|
}
|
|
464
464
|
|
|
465
|
-
function useFallbackModels(options: DiscoverModelsOptions, issue: CursorModelFallbackIssue): ProviderModelConfig[] {
|
|
465
|
+
async function useFallbackModels(options: DiscoverModelsOptions, issue: CursorModelFallbackIssue): Promise<ProviderModelConfig[]> {
|
|
466
466
|
options.onFallback?.(issue);
|
|
467
|
+
const { FALLBACK_MODEL_ITEMS } = await import("./cursor-fallback-models.generated.js");
|
|
467
468
|
return registerModelItems(FALLBACK_MODEL_ITEMS);
|
|
468
469
|
}
|
|
469
470
|
|
package/src/model-list-cache.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { dirname, join } from "node:path";
|
|
|
4
4
|
import { getAgentDir } from "@earendil-works/pi-coding-agent";
|
|
5
5
|
import type { ModelListItem } from "@cursor/sdk";
|
|
6
6
|
import { parseEnvBoolean } from "./cursor-env-boolean.js";
|
|
7
|
+
import { asRecord } from "./cursor-record-utils.js";
|
|
7
8
|
|
|
8
9
|
const MODEL_LIST_CACHE_FILE = "cursor-sdk-model-list.json";
|
|
9
10
|
const MODEL_LIST_CACHE_VERSION = 1;
|
|
@@ -46,52 +47,53 @@ export function fingerprintApiKey(apiKey: string): string {
|
|
|
46
47
|
return createHash("sha256").update(apiKey).digest("hex").slice(0, 16);
|
|
47
48
|
}
|
|
48
49
|
|
|
49
|
-
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
50
|
-
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
50
|
function isStringArray(value: unknown): value is string[] {
|
|
54
51
|
return Array.isArray(value) && value.every((entry) => typeof entry === "string");
|
|
55
52
|
}
|
|
56
53
|
|
|
57
54
|
function isModelParameterValue(value: unknown): value is NonNullable<ModelListItem["variants"]>[number]["params"][number] {
|
|
58
|
-
|
|
55
|
+
const record = asRecord(value);
|
|
56
|
+
return record !== undefined && typeof record.id === "string" && typeof record.value === "string";
|
|
59
57
|
}
|
|
60
58
|
|
|
61
59
|
function isModelParameterDefinitionValue(value: unknown): value is NonNullable<ModelListItem["parameters"]>[number]["values"][number] {
|
|
62
|
-
|
|
60
|
+
const record = asRecord(value);
|
|
61
|
+
return record !== undefined && typeof record.value === "string" && (record.displayName === undefined || typeof record.displayName === "string");
|
|
63
62
|
}
|
|
64
63
|
|
|
65
64
|
function isModelParameterDefinition(value: unknown): value is NonNullable<ModelListItem["parameters"]>[number] {
|
|
66
|
-
|
|
65
|
+
const record = asRecord(value);
|
|
66
|
+
if (!record) return false;
|
|
67
67
|
return (
|
|
68
|
-
typeof
|
|
69
|
-
(
|
|
70
|
-
Array.isArray(
|
|
71
|
-
|
|
68
|
+
typeof record.id === "string" &&
|
|
69
|
+
(record.displayName === undefined || typeof record.displayName === "string") &&
|
|
70
|
+
Array.isArray(record.values) &&
|
|
71
|
+
record.values.every(isModelParameterDefinitionValue)
|
|
72
72
|
);
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
function isModelVariant(value: unknown): value is NonNullable<ModelListItem["variants"]>[number] {
|
|
76
|
-
|
|
76
|
+
const record = asRecord(value);
|
|
77
|
+
if (!record) return false;
|
|
77
78
|
return (
|
|
78
|
-
Array.isArray(
|
|
79
|
-
|
|
80
|
-
typeof
|
|
81
|
-
(
|
|
82
|
-
(
|
|
79
|
+
Array.isArray(record.params) &&
|
|
80
|
+
record.params.every(isModelParameterValue) &&
|
|
81
|
+
typeof record.displayName === "string" &&
|
|
82
|
+
(record.description === undefined || typeof record.description === "string") &&
|
|
83
|
+
(record.isDefault === undefined || typeof record.isDefault === "boolean")
|
|
83
84
|
);
|
|
84
85
|
}
|
|
85
86
|
|
|
86
87
|
function isModelListItem(value: unknown): value is ModelListItem {
|
|
87
|
-
|
|
88
|
+
const record = asRecord(value);
|
|
89
|
+
if (!record) return false;
|
|
88
90
|
return (
|
|
89
|
-
typeof
|
|
90
|
-
typeof
|
|
91
|
-
(
|
|
92
|
-
(
|
|
93
|
-
(
|
|
94
|
-
(
|
|
91
|
+
typeof record.id === "string" &&
|
|
92
|
+
typeof record.displayName === "string" &&
|
|
93
|
+
(record.description === undefined || typeof record.description === "string") &&
|
|
94
|
+
(record.aliases === undefined || isStringArray(record.aliases)) &&
|
|
95
|
+
(record.parameters === undefined || (Array.isArray(record.parameters) && record.parameters.every(isModelParameterDefinition))) &&
|
|
96
|
+
(record.variants === undefined || (Array.isArray(record.variants) && record.variants.every(isModelVariant)))
|
|
95
97
|
);
|
|
96
98
|
}
|
|
97
99
|
|
|
@@ -100,21 +102,22 @@ function isValidFetchedAt(value: unknown): value is number {
|
|
|
100
102
|
}
|
|
101
103
|
|
|
102
104
|
function parseModelListCacheFile(value: unknown): ModelListCacheFile | undefined {
|
|
103
|
-
|
|
105
|
+
const record = asRecord(value);
|
|
106
|
+
if (!record) return undefined;
|
|
104
107
|
if (
|
|
105
|
-
|
|
106
|
-
!isValidFetchedAt(
|
|
107
|
-
typeof
|
|
108
|
-
!Array.isArray(
|
|
109
|
-
!
|
|
108
|
+
record.version !== MODEL_LIST_CACHE_VERSION ||
|
|
109
|
+
!isValidFetchedAt(record.fetchedAt) ||
|
|
110
|
+
typeof record.keyFingerprint !== "string" ||
|
|
111
|
+
!Array.isArray(record.models) ||
|
|
112
|
+
!record.models.every(isModelListItem)
|
|
110
113
|
) {
|
|
111
114
|
return undefined;
|
|
112
115
|
}
|
|
113
116
|
return {
|
|
114
|
-
version:
|
|
115
|
-
fetchedAt:
|
|
116
|
-
keyFingerprint:
|
|
117
|
-
models:
|
|
117
|
+
version: record.version,
|
|
118
|
+
fetchedAt: record.fetchedAt,
|
|
119
|
+
keyFingerprint: record.keyFingerprint,
|
|
120
|
+
models: record.models,
|
|
118
121
|
};
|
|
119
122
|
}
|
|
120
123
|
|
|
@@ -149,10 +152,6 @@ export function loadAnyCachedModelCatalog(keyFingerprint: string): CachedModelLi
|
|
|
149
152
|
return { fetchedAt: cache.fetchedAt, models: cache.models };
|
|
150
153
|
}
|
|
151
154
|
|
|
152
|
-
export function loadAnyCachedModels(keyFingerprint: string): ModelListItem[] | undefined {
|
|
153
|
-
return loadAnyCachedModelCatalog(keyFingerprint)?.models;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
155
|
export function saveModelListCache(keyFingerprint: string, models: ModelListItem[]): boolean {
|
|
157
156
|
if (isModelCacheDisabled()) return false;
|
|
158
157
|
try {
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
export type { CursorNativeToolDisplayItem } from "./cursor-native-tool-display-state.js";
|
|
2
|
-
export {
|
|
3
|
-
canRenderCursorToolNatively,
|
|
4
|
-
deleteCursorNativeToolDisplay,
|
|
5
|
-
isCursorNativeToolDisplayEnabled,
|
|
6
|
-
isCursorNativeToolDisplayRuntimeEnabled,
|
|
7
|
-
recordCursorNativeToolDisplay,
|
|
8
|
-
__testUtils,
|
|
9
|
-
} from "./cursor-native-tool-display-state.js";
|
|
10
|
-
export { registerCursorNativeToolDisplay } from "./cursor-native-tool-display-registration.js";
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { resolveCursorApiKey } from "./cursor-api-key.js";
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { countCursorAgentMessages } from "./cursor-agent-message-web-tools.js";
|
|
2
|
-
import type { CursorSdkEventDebugSink } from "./cursor-sdk-event-debug.js";
|
|
3
|
-
|
|
4
|
-
export async function getCursorAgentMessageOffset(
|
|
5
|
-
agentId: string,
|
|
6
|
-
cwd: string,
|
|
7
|
-
sdkEventDebug: CursorSdkEventDebugSink | undefined,
|
|
8
|
-
): Promise<number | undefined> {
|
|
9
|
-
try {
|
|
10
|
-
return await countCursorAgentMessages(agentId, cwd);
|
|
11
|
-
} catch (error) {
|
|
12
|
-
sdkEventDebug?.recordError("cursor_agent_message_count", error);
|
|
13
|
-
return undefined;
|
|
14
|
-
}
|
|
15
|
-
}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
getCursorSessionCwdFromScope,
|
|
3
|
-
registerCursorSessionScope,
|
|
4
|
-
__testUtils as cursorSessionScopeTestUtils,
|
|
5
|
-
} from "./cursor-session-scope.js";
|
|
6
|
-
import type { ExtensionHandler, SessionStartEvent } from "@earendil-works/pi-coding-agent";
|
|
7
|
-
|
|
8
|
-
interface CursorSessionCwdExtensionApi {
|
|
9
|
-
on(event: "session_start", handler: ExtensionHandler<SessionStartEvent>): void;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Pi session cwd when known; falls back to process.cwd() before session_start.
|
|
14
|
-
* Updated on session_start only until pi threads cwd into streamSimple—mid-session cwd
|
|
15
|
-
* changes without a new session_start event are not reflected here.
|
|
16
|
-
*/
|
|
17
|
-
export function getCursorSessionCwd(): string {
|
|
18
|
-
return getCursorSessionCwdFromScope();
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export function registerCursorSessionCwd(pi: CursorSessionCwdExtensionApi): void {
|
|
22
|
-
registerCursorSessionScope(pi);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export const __testUtils = {
|
|
26
|
-
set: cursorSessionScopeTestUtils.set,
|
|
27
|
-
reset: cursorSessionScopeTestUtils.reset,
|
|
28
|
-
};
|
package/src/cursor-tool-names.ts
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
export {
|
|
2
|
-
CURSOR_REPLAY_ACTIVITY_TOOL_NAME,
|
|
3
|
-
getCursorReplayActivityTitle,
|
|
4
|
-
getCursorReplayPromptLabel,
|
|
5
|
-
isCursorReplayToolName,
|
|
6
|
-
isExcludedFromCursorBridgeExposure,
|
|
7
|
-
type CursorReplayActivityToolName,
|
|
8
|
-
type CursorReplayToolName,
|
|
9
|
-
} from "./cursor-tool-presentation-registry.js";
|