@emmaneugene/pi-cursor-sdk 0.4.0 → 0.4.2
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 +12 -0
- package/README.md +2 -0
- package/dist/cursor-extension-factory-guard.js +31 -0
- package/dist/cursor-pi-tool-bridge-server.js +3 -0
- package/dist/cursor-pi-tool-bridge.js +28 -7
- package/dist/cursor-provider-lazy.js +2 -2
- package/dist/cursor-provider-live-run-drain.js +4 -4
- package/dist/cursor-provider-run-finalizer.js +1 -0
- package/dist/cursor-provider-runtime-context.js +1 -0
- package/dist/cursor-provider-turn-prepare.js +17 -9
- package/dist/cursor-provider-turn-runner.js +5 -4
- package/dist/cursor-provider.js +3 -2
- package/dist/cursor-session-agent.js +9 -7
- package/dist/index.js +108 -53
- package/docs/cursor-live-smoke-checklist.md +1 -1
- package/docs/cursor-model-ux-spec.md +1 -1
- package/docs/cursor-native-tool-visual-audit.md +3 -3
- package/docs/cursor-testing-lessons.md +24 -0
- package/docs/platform-smoke.md +1 -1
- package/node_modules/cross-spawn/node_modules/which/CHANGELOG.md +166 -0
- package/package.json +1 -1
- package/scripts/lib/cursor-visual-render.mjs +12 -2
- package/scripts/visual-tui-smoke-self-test.mjs +9 -33
- package/scripts/visual-tui-smoke.mjs +54 -11
- package/src/cursor-extension-factory-guard.ts +57 -0
- package/src/cursor-pi-tool-bridge-server.ts +4 -0
- package/src/cursor-pi-tool-bridge.ts +33 -7
- package/src/cursor-provider-lazy.ts +3 -1
- package/src/cursor-provider-live-run-drain.ts +4 -3
- package/src/cursor-provider-run-finalizer.ts +1 -0
- package/src/cursor-provider-runtime-context.ts +13 -0
- package/src/cursor-provider-turn-prepare.ts +20 -9
- package/src/cursor-provider-turn-runner.ts +13 -4
- package/src/cursor-provider-turn-types.ts +2 -0
- package/src/cursor-provider.ts +4 -1
- package/src/cursor-session-agent.ts +11 -7
- package/src/index.ts +124 -52
|
@@ -20,7 +20,6 @@ import {
|
|
|
20
20
|
abandonSessionCursorAgent,
|
|
21
21
|
createCursorNativeReplayId,
|
|
22
22
|
cursorLiveRuns,
|
|
23
|
-
getActiveCursorLiveRunForCurrentScope,
|
|
24
23
|
getPendingCursorLiveRun,
|
|
25
24
|
} from "./cursor-provider-live-run-drain.js";
|
|
26
25
|
import {
|
|
@@ -66,19 +65,22 @@ interface PrepareCursorProviderTurnContext extends PrepareCursorProviderTurnPara
|
|
|
66
65
|
fastEnabled: boolean | undefined;
|
|
67
66
|
}
|
|
68
67
|
|
|
69
|
-
export function resolveCursorProviderTurnConfig(cwd: string) {
|
|
70
|
-
return resolveEffectiveCursorConfig({ cwd, projectTrusted
|
|
68
|
+
export function resolveCursorProviderTurnConfig(cwd: string, projectTrusted = getCursorSessionProjectTrusted()) {
|
|
69
|
+
return resolveEffectiveCursorConfig({ cwd, projectTrusted });
|
|
71
70
|
}
|
|
72
71
|
|
|
73
72
|
function buildLocalCursorProviderTurnLifecycle(
|
|
74
73
|
lease: SessionCursorAgentLease,
|
|
75
74
|
scopeKey: string,
|
|
75
|
+
disposeAgentAfterTurn: boolean,
|
|
76
76
|
): CursorProviderTurnLifecycle {
|
|
77
77
|
return {
|
|
78
78
|
trackRunCompletion: (completion) => lease.trackRunCompletion(completion),
|
|
79
79
|
commitSend: (context, bootstrapped) => lease.commitSend(context, bootstrapped),
|
|
80
80
|
abandon: () => abandonSessionCursorAgent(scopeKey),
|
|
81
|
-
dispose: async () => {
|
|
81
|
+
dispose: async () => {
|
|
82
|
+
if (disposeAgentAfterTurn) await resetSessionCursorAgent(scopeKey);
|
|
83
|
+
},
|
|
82
84
|
};
|
|
83
85
|
}
|
|
84
86
|
|
|
@@ -86,7 +88,7 @@ async function prepareCursorLocalProviderTurn(
|
|
|
86
88
|
prepareParams: PrepareCursorProviderTurnContext,
|
|
87
89
|
): Promise<CursorProviderTurnPrepareResult> {
|
|
88
90
|
const { params, cwd, resolvedApiKey, sdkEventDebug, throwIfAborted, resolvedConfig, agentMode, selection, fastEnabled } = prepareParams;
|
|
89
|
-
const { model, context, options } = params;
|
|
91
|
+
const { model, context, options, runtimeContext } = params;
|
|
90
92
|
|
|
91
93
|
let restoreCursorSdkOutputFilter: (() => void) | undefined;
|
|
92
94
|
let sessionAgentScopeKey: string | undefined;
|
|
@@ -113,6 +115,7 @@ async function prepareCursorLocalProviderTurn(
|
|
|
113
115
|
const queuedBridgeRequestsBeforeLiveRun: CursorPiBridgeToolRequest[] = [];
|
|
114
116
|
let liveRunForBridgeQueue: CursorLiveRun | undefined;
|
|
115
117
|
const bridgeExcludeToolNames = buildCursorBridgeExcludeToolNames(resolvedConfig);
|
|
118
|
+
const localResumeEnabled = runtimeContext?.localResume ?? resolvedConfig.local.resume.value;
|
|
116
119
|
|
|
117
120
|
const sessionAgentAcquireParams = {
|
|
118
121
|
apiKey: resolvedApiKey,
|
|
@@ -121,8 +124,12 @@ async function prepareCursorLocalProviderTurn(
|
|
|
121
124
|
modelSelection: selection,
|
|
122
125
|
settingSources,
|
|
123
126
|
localSafety,
|
|
124
|
-
localResume:
|
|
127
|
+
localResume: localResumeEnabled,
|
|
125
128
|
useHttp1ForAgent,
|
|
129
|
+
runtimeScope: runtimeContext
|
|
130
|
+
? { scopeKey: runtimeContext.scopeKey, sessionFile: runtimeContext.sessionFile }
|
|
131
|
+
: undefined,
|
|
132
|
+
bridge: runtimeContext?.bridge,
|
|
126
133
|
bridgeExcludeToolNames,
|
|
127
134
|
debugRecorder: sdkEventDebug,
|
|
128
135
|
onBridgeToolRequest: (request: CursorPiBridgeToolRequest) => {
|
|
@@ -185,7 +192,7 @@ async function prepareCursorLocalProviderTurn(
|
|
|
185
192
|
};
|
|
186
193
|
const sessionBridgeRun = bridgeRun;
|
|
187
194
|
const promptInputTokens = estimateCursorPromptTokens(prompt, promptOptions);
|
|
188
|
-
const useNativeToolReplay = isCursorNativeToolDisplayRuntimeEnabled();
|
|
195
|
+
const useNativeToolReplay = runtimeContext?.nativeToolReplay ?? isCursorNativeToolDisplayRuntimeEnabled();
|
|
189
196
|
const activeToolNames = getActiveContextToolNames(context);
|
|
190
197
|
sdkEventDebug?.recordProviderMeta({
|
|
191
198
|
model: {
|
|
@@ -203,7 +210,7 @@ async function prepareCursorLocalProviderTurn(
|
|
|
203
210
|
toolManifestEnabled: resolveCursorToolManifestEnabled(),
|
|
204
211
|
agentMode,
|
|
205
212
|
localForce: resolvedConfig.local.force.value,
|
|
206
|
-
localResume:
|
|
213
|
+
localResume: localResumeEnabled,
|
|
207
214
|
resumedAgent: sessionAgentLease.resumed,
|
|
208
215
|
activeToolNames: activeToolNames ? [...activeToolNames] : [],
|
|
209
216
|
sessionAgentScopeKey,
|
|
@@ -266,7 +273,11 @@ async function prepareCursorLocalProviderTurn(
|
|
|
266
273
|
sessionAgentLease,
|
|
267
274
|
localForce: resolvedConfig.local.force,
|
|
268
275
|
restoreCursorSdkOutputFilter,
|
|
269
|
-
lifecycle: buildLocalCursorProviderTurnLifecycle(
|
|
276
|
+
lifecycle: buildLocalCursorProviderTurnLifecycle(
|
|
277
|
+
sessionAgentLease,
|
|
278
|
+
sessionAgentScopeKey,
|
|
279
|
+
runtimeContext?.disposeAgentAfterTurn === true,
|
|
280
|
+
),
|
|
270
281
|
runtime: liveRun
|
|
271
282
|
? { kind: "live", liveRun, turnCoordinator }
|
|
272
283
|
: { kind: "direct", turnCoordinator },
|
|
@@ -62,7 +62,8 @@ export class CursorProviderTurnRunner {
|
|
|
62
62
|
|
|
63
63
|
try {
|
|
64
64
|
this.throwIfAborted();
|
|
65
|
-
const
|
|
65
|
+
const runtimeContext = this.params.runtimeContext;
|
|
66
|
+
const cwd = runtimeContext?.cwd ?? getCursorSessionCwd();
|
|
66
67
|
this.sdkEventDebug = CursorSdkEventDebugSink.maybeCreate({
|
|
67
68
|
cwd,
|
|
68
69
|
modelId: model.id,
|
|
@@ -70,13 +71,21 @@ export class CursorProviderTurnRunner {
|
|
|
70
71
|
});
|
|
71
72
|
sdkEventDebugRef.current = this.sdkEventDebug;
|
|
72
73
|
this.sdkEventDebug?.recordContextSnapshot(context);
|
|
73
|
-
const resolvedConfig = resolveCursorProviderTurnConfig(cwd);
|
|
74
|
-
const localScopeKey = getCursorSessionScopeKey();
|
|
74
|
+
const resolvedConfig = resolveCursorProviderTurnConfig(cwd, runtimeContext?.projectTrusted);
|
|
75
|
+
const localScopeKey = runtimeContext?.scopeKey ?? getCursorSessionScopeKey();
|
|
75
76
|
sdkProcessErrorGuard.containLocalTransportClosedPipe(() =>
|
|
76
77
|
invalidateSessionAgent(localScopeKey, { deadTransport: true }),
|
|
77
78
|
);
|
|
78
79
|
if (
|
|
79
|
-
(await drainExistingCursorLiveRunBeforeSend(
|
|
80
|
+
(await drainExistingCursorLiveRunBeforeSend(
|
|
81
|
+
stream,
|
|
82
|
+
partial,
|
|
83
|
+
model,
|
|
84
|
+
context,
|
|
85
|
+
options?.signal,
|
|
86
|
+
this.sdkEventDebug,
|
|
87
|
+
localScopeKey,
|
|
88
|
+
)) ===
|
|
80
89
|
"stream_ended"
|
|
81
90
|
) {
|
|
82
91
|
return;
|
|
@@ -15,6 +15,7 @@ import type { CursorSdkTurnCoordinator } from "./cursor-provider-turn-coordinato
|
|
|
15
15
|
import type { CursorPrompt } from "./context.js";
|
|
16
16
|
import type { CursorResolvedSetting } from "./cursor-config.js";
|
|
17
17
|
import type { CursorSdkTurnUsage } from "./cursor-usage-accounting.js";
|
|
18
|
+
import type { CursorProviderRuntimeContext } from "./cursor-provider-runtime-context.js";
|
|
18
19
|
|
|
19
20
|
export interface CursorProviderTurnRunnerParams {
|
|
20
21
|
model: Model<Api>;
|
|
@@ -22,6 +23,7 @@ export interface CursorProviderTurnRunnerParams {
|
|
|
22
23
|
stream: AssistantMessageEventStream;
|
|
23
24
|
partial: AssistantMessage;
|
|
24
25
|
options?: SimpleStreamOptions;
|
|
26
|
+
runtimeContext?: CursorProviderRuntimeContext;
|
|
25
27
|
sdkEventDebugRef: { current?: CursorSdkEventDebugSink };
|
|
26
28
|
}
|
|
27
29
|
|
package/src/cursor-provider.ts
CHANGED
|
@@ -23,6 +23,7 @@ import { resolveCursorApiKey } from "./cursor-api-key.js";
|
|
|
23
23
|
import { CursorProviderTurnRunner } from "./cursor-provider-turn-runner.js";
|
|
24
24
|
import { getCursorSessionScopeKey } from "./cursor-session-scope.js";
|
|
25
25
|
import { runExclusiveCursorSessionTurn, __testUtils as cursorSessionTurnQueueTestUtils } from "./cursor-session-turn-queue.js";
|
|
26
|
+
import type { CursorProviderRuntimeContext } from "./cursor-provider-runtime-context.js";
|
|
26
27
|
|
|
27
28
|
function makeInitialMessage(model: Model<Api>): AssistantMessage {
|
|
28
29
|
return {
|
|
@@ -48,6 +49,7 @@ export function streamCursor(
|
|
|
48
49
|
model: Model<Api>,
|
|
49
50
|
context: Context,
|
|
50
51
|
options?: SimpleStreamOptions,
|
|
52
|
+
runtimeContext?: CursorProviderRuntimeContext,
|
|
51
53
|
): AssistantMessageEventStream {
|
|
52
54
|
const stream = createAssistantMessageEventStream();
|
|
53
55
|
const sdkEventDebugRef: { current?: CursorSdkEventDebugSink } = {};
|
|
@@ -62,13 +64,14 @@ export function streamCursor(
|
|
|
62
64
|
stream,
|
|
63
65
|
partial,
|
|
64
66
|
options,
|
|
67
|
+
runtimeContext,
|
|
65
68
|
sdkEventDebugRef,
|
|
66
69
|
});
|
|
67
70
|
|
|
68
71
|
try {
|
|
69
72
|
stream.push({ type: "start", partial });
|
|
70
73
|
await runExclusiveCursorSessionTurn(
|
|
71
|
-
getCursorSessionScopeKey(),
|
|
74
|
+
runtimeContext?.scopeKey ?? getCursorSessionScopeKey(),
|
|
72
75
|
() => runner.run(installCursorSdkProcessErrorGuard()),
|
|
73
76
|
options?.signal,
|
|
74
77
|
);
|
|
@@ -4,6 +4,7 @@ import type { Context } from "@earendil-works/pi-ai";
|
|
|
4
4
|
import {
|
|
5
5
|
getRegisteredCursorPiToolBridge,
|
|
6
6
|
type CursorPiBridgeToolRequest,
|
|
7
|
+
type CursorPiToolBridge,
|
|
7
8
|
type CursorPiToolBridgeRun,
|
|
8
9
|
} from "./cursor-pi-tool-bridge.js";
|
|
9
10
|
import { computeCursorContextFingerprint } from "./context.js";
|
|
@@ -136,6 +137,8 @@ interface SessionCursorAgentCreateParams {
|
|
|
136
137
|
bridgeExcludeToolNames?: ReadonlySet<string>;
|
|
137
138
|
onBridgeToolRequest?: (request: CursorPiBridgeToolRequest) => void;
|
|
138
139
|
debugRecorder?: CursorSdkEventDebugRecorder;
|
|
140
|
+
runtimeScope?: { scopeKey: string; sessionFile: string | undefined };
|
|
141
|
+
bridge?: CursorPiToolBridge;
|
|
139
142
|
localResume?: boolean;
|
|
140
143
|
forceCreate?: boolean;
|
|
141
144
|
createAgent?: CursorSdkModule["Agent"]["create"];
|
|
@@ -453,16 +456,17 @@ async function createSessionAgentEntry(
|
|
|
453
456
|
let bridgeRun: CursorPiToolBridgeRun | undefined;
|
|
454
457
|
let sessionStore: OpenCursorSessionStore | undefined;
|
|
455
458
|
try {
|
|
456
|
-
const registeredBridge = getRegisteredCursorPiToolBridge();
|
|
459
|
+
const registeredBridge = params.bridge ?? getRegisteredCursorPiToolBridge();
|
|
457
460
|
if (registeredBridge) {
|
|
458
|
-
|
|
461
|
+
const createdBridgeRun = await registeredBridge.createRun({
|
|
459
462
|
onToolRequest: params.onBridgeToolRequest,
|
|
460
463
|
debugRecorder: params.debugRecorder,
|
|
461
464
|
excludeToolNames: params.bridgeExcludeToolNames,
|
|
462
465
|
});
|
|
463
|
-
if (!
|
|
464
|
-
await
|
|
465
|
-
|
|
466
|
+
if (!createdBridgeRun.enabled || !createdBridgeRun.mcpServers) {
|
|
467
|
+
await createdBridgeRun.dispose();
|
|
468
|
+
} else {
|
|
469
|
+
bridgeRun = createdBridgeRun;
|
|
466
470
|
}
|
|
467
471
|
}
|
|
468
472
|
|
|
@@ -559,8 +563,8 @@ export function invalidateSessionAgent(
|
|
|
559
563
|
}
|
|
560
564
|
|
|
561
565
|
export async function acquireSessionCursorAgent(params: SessionCursorAgentCreateParams): Promise<SessionCursorAgentLease> {
|
|
562
|
-
const scopeKey = getCursorSessionScopeKey();
|
|
563
|
-
const persistentStore = getCursorSessionFile() !== undefined;
|
|
566
|
+
const scopeKey = params.runtimeScope?.scopeKey ?? getCursorSessionScopeKey();
|
|
567
|
+
const persistentStore = params.runtimeScope ? params.runtimeScope.sessionFile !== undefined : getCursorSessionFile() !== undefined;
|
|
564
568
|
let forceCreate = params.forceCreate === true;
|
|
565
569
|
|
|
566
570
|
while (true) {
|
package/src/index.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
1
2
|
import type { ExtensionAPI, ProviderConfig, ProviderModelConfig } from "@earendil-works/pi-coding-agent";
|
|
2
3
|
import { discoverModels, type CursorModelFallbackIssue } from "./model-discovery.js";
|
|
3
4
|
import { registerCursorRuntimeControls } from "./cursor-state.js";
|
|
4
5
|
import { registerCursorNativeToolDisplay } from "./cursor-native-tool-display-registration.js";
|
|
5
|
-
import { registerCursorPiToolBridge } from "./cursor-pi-tool-bridge.js";
|
|
6
|
+
import { registerCursorPiToolBridge, registerNestedCursorPiToolBridge } from "./cursor-pi-tool-bridge.js";
|
|
6
7
|
import { registerCursorQuestionTool } from "./cursor-question-tool.js";
|
|
7
8
|
import { registerCursorSkillTool } from "./cursor-skill-tool.js";
|
|
8
9
|
import { registerCursorSessionScope } from "./cursor-session-scope.js";
|
|
@@ -16,6 +17,14 @@ import { registerCursorAgentsContextDedup } from "./cursor-agents-context-regist
|
|
|
16
17
|
import { registerCursorOverflowNormalization } from "./cursor-provider-overflow.js";
|
|
17
18
|
import { registerCursorSdkSessionProcessErrorGuard } from "./cursor-sdk-process-error-guard.js";
|
|
18
19
|
import { prepareCursorSessionForCompaction } from "./cursor-session-compaction-prep.js";
|
|
20
|
+
import { disposeSessionCursorAgent } from "./cursor-session-agent.js";
|
|
21
|
+
import { getCursorSessionCwd, getCursorSessionProjectTrusted } from "./cursor-session-scope.js";
|
|
22
|
+
import type { CursorProviderRuntimeContext } from "./cursor-provider-runtime-context.js";
|
|
23
|
+
import {
|
|
24
|
+
claimCursorExtensionFactory,
|
|
25
|
+
registerCursorExtensionFactoryRelease,
|
|
26
|
+
releaseCursorExtensionFactory,
|
|
27
|
+
} from "./cursor-extension-factory-guard.js";
|
|
19
28
|
|
|
20
29
|
type CursorExtensionApi =
|
|
21
30
|
& Pick<ExtensionAPI, "registerProvider" | "registerCommand" | "on">
|
|
@@ -31,73 +40,136 @@ type CursorExtensionApi =
|
|
|
31
40
|
& Parameters<typeof registerCursorFallbackIssueWarning>[0]
|
|
32
41
|
& Parameters<typeof registerCursorAgentsContextDedup>[0]
|
|
33
42
|
& Parameters<typeof registerCursorOverflowNormalization>[0]
|
|
34
|
-
& Parameters<typeof registerCursorSdkSessionProcessErrorGuard>[0]
|
|
43
|
+
& Parameters<typeof registerCursorSdkSessionProcessErrorGuard>[0]
|
|
44
|
+
& Parameters<typeof registerCursorExtensionFactoryRelease>[0];
|
|
35
45
|
|
|
36
|
-
|
|
46
|
+
let activeCursorProviderModels: ProviderModelConfig[] | undefined;
|
|
47
|
+
|
|
48
|
+
function createCursorProviderConfig(
|
|
49
|
+
models: ProviderModelConfig[],
|
|
50
|
+
streamSimple: NonNullable<ProviderConfig["streamSimple"]> = streamCursorLazy,
|
|
51
|
+
): ProviderConfig {
|
|
37
52
|
return {
|
|
38
53
|
name: "Cursor",
|
|
39
54
|
baseUrl: "https://cursor.com",
|
|
40
55
|
apiKey: CURSOR_API_KEY_CONFIG_VALUE,
|
|
41
56
|
api: "cursor-sdk",
|
|
42
57
|
models,
|
|
43
|
-
streamSimple
|
|
58
|
+
streamSimple,
|
|
44
59
|
};
|
|
45
60
|
}
|
|
46
61
|
|
|
47
|
-
function registerCursorProvider(
|
|
48
|
-
pi
|
|
62
|
+
function registerCursorProvider(
|
|
63
|
+
pi: Pick<ExtensionAPI, "registerProvider">,
|
|
64
|
+
models: ProviderModelConfig[],
|
|
65
|
+
streamSimple?: NonNullable<ProviderConfig["streamSimple"]>,
|
|
66
|
+
): void {
|
|
67
|
+
pi.registerProvider("cursor", createCursorProviderConfig(models, streamSimple));
|
|
49
68
|
}
|
|
50
69
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
70
|
+
function registerNestedCursorProvider(pi: CursorExtensionApi, models: ProviderModelConfig[]): void {
|
|
71
|
+
const bridge = registerNestedCursorPiToolBridge(pi);
|
|
72
|
+
const nestedRuntimeId = randomUUID();
|
|
73
|
+
let runtimeContext: CursorProviderRuntimeContext = {
|
|
74
|
+
scopeKey: `__nested_cursor__:${nestedRuntimeId}`,
|
|
75
|
+
cwd: getCursorSessionCwd(),
|
|
76
|
+
sessionFile: undefined,
|
|
77
|
+
projectTrusted: getCursorSessionProjectTrusted(),
|
|
78
|
+
bridge,
|
|
79
|
+
localResume: false,
|
|
80
|
+
nativeToolReplay: false,
|
|
81
|
+
disposeAgentAfterTurn: true,
|
|
82
|
+
};
|
|
83
|
+
pi.on("session_start", (_event, ctx) => {
|
|
84
|
+
const sessionFile = ctx.sessionManager?.getSessionFile?.() ?? undefined;
|
|
85
|
+
const sessionId = ctx.sessionManager?.getSessionId?.() ?? nestedRuntimeId;
|
|
86
|
+
runtimeContext = {
|
|
87
|
+
...runtimeContext,
|
|
88
|
+
scopeKey: sessionFile ?? `__nested_cursor__:${sessionId}`,
|
|
89
|
+
cwd: ctx.cwd,
|
|
90
|
+
sessionFile,
|
|
91
|
+
projectTrusted: ctx.isProjectTrusted?.() === true || runtimeContext.projectTrusted,
|
|
92
|
+
};
|
|
59
93
|
});
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
registerCursorQuestionTool(pi);
|
|
63
|
-
registerCursorSkillTool(pi);
|
|
64
|
-
registerCursorPiToolBridge(pi);
|
|
65
|
-
registerCursorAgentsContextDedup(pi);
|
|
66
|
-
registerCursorOverflowNormalization(pi);
|
|
67
|
-
let fallbackIssue: CursorModelFallbackIssue | undefined;
|
|
68
|
-
const models = await discoverModels({
|
|
69
|
-
onFallback: (issue) => {
|
|
70
|
-
fallbackIssue = issue;
|
|
71
|
-
},
|
|
94
|
+
pi.on("session_shutdown", async () => {
|
|
95
|
+
await disposeSessionCursorAgent(runtimeContext.scopeKey);
|
|
72
96
|
});
|
|
97
|
+
registerCursorProvider(pi, models, (model, context, options) =>
|
|
98
|
+
streamCursorLazy(model, context, options, runtimeContext));
|
|
99
|
+
}
|
|
73
100
|
|
|
74
|
-
|
|
75
|
-
|
|
101
|
+
export default async function (pi: CursorExtensionApi) {
|
|
102
|
+
const factoryClaim = claimCursorExtensionFactory();
|
|
103
|
+
if (factoryClaim.kind === "nested") {
|
|
104
|
+
if (!activeCursorProviderModels) {
|
|
105
|
+
throw new Error("Nested Cursor provider loaded before the owner model catalog was ready");
|
|
106
|
+
}
|
|
107
|
+
registerNestedCursorProvider(pi, activeCursorProviderModels);
|
|
108
|
+
return;
|
|
76
109
|
}
|
|
77
110
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
111
|
+
try {
|
|
112
|
+
// Discover first. A discovery failure must not leave process-global
|
|
113
|
+
// registrars from a discarded extension load.
|
|
114
|
+
let fallbackIssue: CursorModelFallbackIssue | undefined;
|
|
115
|
+
const models = await discoverModels({
|
|
116
|
+
onFallback: (issue) => {
|
|
117
|
+
fallbackIssue = issue;
|
|
118
|
+
},
|
|
119
|
+
});
|
|
120
|
+
activeCursorProviderModels = models;
|
|
121
|
+
|
|
122
|
+
// Session cwd must register before other session_start listeners that depend on it.
|
|
123
|
+
registerCursorSessionScope(pi);
|
|
124
|
+
registerCursorSessionAgentLineage(pi);
|
|
125
|
+
registerCursorSessionAgentLifecycle(pi);
|
|
126
|
+
registerCursorSessionAgentResume(pi);
|
|
127
|
+
pi.on("session_before_compact", async () => {
|
|
128
|
+
await prepareCursorSessionForCompaction();
|
|
129
|
+
});
|
|
130
|
+
registerCursorRuntimeControls(pi);
|
|
131
|
+
registerCursorNativeToolDisplay(pi);
|
|
132
|
+
registerCursorQuestionTool(pi);
|
|
133
|
+
registerCursorSkillTool(pi);
|
|
134
|
+
registerCursorPiToolBridge(pi);
|
|
135
|
+
registerCursorAgentsContextDedup(pi);
|
|
136
|
+
registerCursorOverflowNormalization(pi);
|
|
137
|
+
|
|
138
|
+
if (fallbackIssue) {
|
|
139
|
+
registerCursorFallbackIssueWarning(pi, fallbackIssue);
|
|
140
|
+
}
|
|
99
141
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
142
|
+
pi.registerCommand("cursor-refresh-models", {
|
|
143
|
+
description: "Refresh the live Cursor model catalog without restarting pi",
|
|
144
|
+
handler: async (_args, ctx) => {
|
|
145
|
+
let refreshFallbackIssue: CursorModelFallbackIssue | undefined;
|
|
146
|
+
const apiKey = resolveCursorApiKey(await ctx.modelRegistry.getApiKeyForProvider("cursor"));
|
|
147
|
+
const refreshedModels = await discoverModels({
|
|
148
|
+
apiKey,
|
|
149
|
+
forceRefresh: true,
|
|
150
|
+
onFallback: (issue) => {
|
|
151
|
+
refreshFallbackIssue = issue;
|
|
152
|
+
},
|
|
153
|
+
});
|
|
154
|
+
registerCursorProvider(pi, refreshedModels);
|
|
155
|
+
if (!ctx.hasUI) return;
|
|
156
|
+
if (refreshFallbackIssue) {
|
|
157
|
+
ctx.ui.notify(`Cursor model catalog refresh did not use a live catalog: ${refreshFallbackIssue.message}`, "warning");
|
|
158
|
+
} else {
|
|
159
|
+
ctx.ui.notify(`Cursor model catalog refreshed with ${refreshedModels.length} model${refreshedModels.length === 1 ? "" : "s"}.`, "info");
|
|
160
|
+
}
|
|
161
|
+
},
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
registerCursorProvider(pi, models);
|
|
165
|
+
// Keep the process error guard near the end so earlier Cursor cleanup
|
|
166
|
+
// remains protected during session shutdown.
|
|
167
|
+
registerCursorSdkSessionProcessErrorGuard(pi);
|
|
168
|
+
// Register last so ownership remains protected until all other Cursor
|
|
169
|
+
// session_shutdown handlers finish.
|
|
170
|
+
registerCursorExtensionFactoryRelease(pi, factoryClaim);
|
|
171
|
+
} catch (error) {
|
|
172
|
+
releaseCursorExtensionFactory(factoryClaim.token);
|
|
173
|
+
throw error;
|
|
174
|
+
}
|
|
103
175
|
}
|