pi-cursor-sdk 0.5.3 → 0.5.5
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 +61 -1
- package/README.md +62 -816
- package/dist/context.js +11 -5
- package/dist/cursor-agents-context.js +6 -6
- package/dist/cursor-api-key.js +1 -1
- package/dist/cursor-bridge-contract.js +1 -13
- package/dist/cursor-cloud-local-state.js +0 -1
- package/dist/cursor-config.js +1 -17
- package/dist/cursor-fallback-models.generated.js +424 -1
- package/dist/cursor-live-run-coordinator.js +1 -0
- package/dist/cursor-local-run-terminal-error.js +57 -0
- package/dist/cursor-mcp-timeout-override.js +6 -17
- package/dist/cursor-native-tool-display-replay.js +5 -5
- package/dist/cursor-native-tools.js +11 -0
- package/dist/cursor-pi-tool-bridge-env.js +25 -2
- package/dist/cursor-pi-tool-bridge-server.js +98 -48
- package/dist/cursor-pi-tool-bridge-snapshot.js +1 -1
- package/dist/cursor-pi-tool-bridge.js +4 -12
- package/dist/cursor-provider-errors.js +0 -6
- package/dist/cursor-provider-live-run-drain.js +8 -3
- package/dist/cursor-provider-overflow.js +7 -21
- package/dist/cursor-provider-turn-finalize.js +2 -0
- package/dist/cursor-provider-turn-prepare.js +8 -1
- package/dist/cursor-provider-turn-runner.js +17 -6
- package/dist/cursor-provider-turn-send.js +3 -0
- package/dist/cursor-provider.js +1 -4
- package/dist/cursor-runtime-state.js +3 -3
- package/dist/cursor-sdk-event-debug.js +2 -2
- package/dist/cursor-sdk-output-filter.js +1 -1
- package/dist/cursor-sdk-process-error-guard.js +1 -2
- package/dist/cursor-session-agent.js +6 -8
- package/dist/cursor-session-scope.js +1 -7
- package/dist/cursor-session-store.js +3 -2
- package/dist/cursor-state.js +9 -23
- package/dist/cursor-tool-lifecycle.js +0 -3
- package/dist/cursor-tool-manifest.js +3 -1
- package/dist/cursor-tool-presentation-registry.js +2 -4
- package/dist/cursor-tool-visibility.js +0 -3
- package/dist/cursor-transcript-tool-specs.js +1 -2
- package/dist/index.js +21 -0
- package/dist/model-discovery.js +0 -1
- package/dist/model-list-cache.js +4 -9
- package/docs/cursor-live-smoke-checklist.md +4 -4
- package/docs/cursor-model-ux-spec.md +18 -8
- package/docs/cursor-native-tool-replay.md +2 -2
- package/docs/cursor-native-tool-visual-audit.md +1 -1
- package/docs/cursor-testing-lessons.md +22 -4
- package/docs/cursor-tool-surfaces.md +32 -3
- package/docs/development.md +101 -0
- package/docs/platform-smoke-implementation.md +3 -2
- package/docs/reference.md +810 -0
- package/node_modules/@modelcontextprotocol/core/node_modules/zod/package.json +1 -1
- package/node_modules/@modelcontextprotocol/core/node_modules/zod/src/v4/classic/tests/array.test.ts +27 -0
- package/node_modules/@modelcontextprotocol/core/node_modules/zod/src/v4/classic/tests/map.test.ts +7 -0
- package/node_modules/@modelcontextprotocol/core/node_modules/zod/src/v4/classic/tests/set.test.ts +6 -0
- package/node_modules/@modelcontextprotocol/core/node_modules/zod/src/v4/core/compile.ts +9 -0
- package/node_modules/@modelcontextprotocol/core/node_modules/zod/src/v4/core/schemas.ts +35 -1
- package/node_modules/@modelcontextprotocol/core/node_modules/zod/src/v4/core/util.ts +13 -0
- package/node_modules/@modelcontextprotocol/core/node_modules/zod/src/v4/core/versions.ts +1 -1
- package/node_modules/@modelcontextprotocol/core/node_modules/zod/v4/core/compile.cjs +8 -0
- package/node_modules/@modelcontextprotocol/core/node_modules/zod/v4/core/compile.js +8 -0
- package/node_modules/@modelcontextprotocol/core/node_modules/zod/v4/core/schemas.cjs +30 -0
- package/node_modules/@modelcontextprotocol/core/node_modules/zod/v4/core/schemas.d.cts +3 -1
- package/node_modules/@modelcontextprotocol/core/node_modules/zod/v4/core/schemas.d.ts +3 -1
- package/node_modules/@modelcontextprotocol/core/node_modules/zod/v4/core/schemas.js +30 -0
- package/node_modules/@modelcontextprotocol/core/node_modules/zod/v4/core/util.cjs +15 -0
- package/node_modules/@modelcontextprotocol/core/node_modules/zod/v4/core/util.d.cts +2 -0
- package/node_modules/@modelcontextprotocol/core/node_modules/zod/v4/core/util.d.ts +2 -0
- package/node_modules/@modelcontextprotocol/core/node_modules/zod/v4/core/util.js +14 -0
- package/node_modules/@modelcontextprotocol/core/node_modules/zod/v4/core/versions.cjs +1 -1
- package/node_modules/@modelcontextprotocol/core/node_modules/zod/v4/core/versions.js +1 -1
- package/node_modules/@modelcontextprotocol/server/node_modules/zod/package.json +1 -1
- package/node_modules/@modelcontextprotocol/server/node_modules/zod/src/v4/classic/tests/array.test.ts +27 -0
- package/node_modules/@modelcontextprotocol/server/node_modules/zod/src/v4/classic/tests/map.test.ts +7 -0
- package/node_modules/@modelcontextprotocol/server/node_modules/zod/src/v4/classic/tests/set.test.ts +6 -0
- package/node_modules/@modelcontextprotocol/server/node_modules/zod/src/v4/core/compile.ts +9 -0
- package/node_modules/@modelcontextprotocol/server/node_modules/zod/src/v4/core/schemas.ts +35 -1
- package/node_modules/@modelcontextprotocol/server/node_modules/zod/src/v4/core/util.ts +13 -0
- package/node_modules/@modelcontextprotocol/server/node_modules/zod/src/v4/core/versions.ts +1 -1
- package/node_modules/@modelcontextprotocol/server/node_modules/zod/v4/core/compile.cjs +8 -0
- package/node_modules/@modelcontextprotocol/server/node_modules/zod/v4/core/compile.js +8 -0
- package/node_modules/@modelcontextprotocol/server/node_modules/zod/v4/core/schemas.cjs +30 -0
- package/node_modules/@modelcontextprotocol/server/node_modules/zod/v4/core/schemas.d.cts +3 -1
- package/node_modules/@modelcontextprotocol/server/node_modules/zod/v4/core/schemas.d.ts +3 -1
- package/node_modules/@modelcontextprotocol/server/node_modules/zod/v4/core/schemas.js +30 -0
- package/node_modules/@modelcontextprotocol/server/node_modules/zod/v4/core/util.cjs +15 -0
- package/node_modules/@modelcontextprotocol/server/node_modules/zod/v4/core/util.d.cts +2 -0
- package/node_modules/@modelcontextprotocol/server/node_modules/zod/v4/core/util.d.ts +2 -0
- package/node_modules/@modelcontextprotocol/server/node_modules/zod/v4/core/util.js +14 -0
- package/node_modules/@modelcontextprotocol/server/node_modules/zod/v4/core/versions.cjs +1 -1
- package/node_modules/@modelcontextprotocol/server/node_modules/zod/v4/core/versions.js +1 -1
- package/package.json +14 -6
- package/scripts/platform-smoke/artifact-secrets.mjs +13 -4
- package/scripts/platform-smoke/local-resume-runner.mjs +0 -1
- package/scripts/platform-smoke/target-runtime.mjs +4 -2
- package/scripts/platform-smoke/targets.mjs +0 -1
- package/scripts/steering-rpc-smoke.mjs +1 -1
- package/shared/cursor-sdk-output-filter.d.mts +2 -0
- package/shared/cursor-sdk-output-filter.mjs +92 -28
- package/shared/cursor-sensitive-text.d.mts +1 -0
- package/shared/cursor-sensitive-text.mjs +7 -0
- package/src/context.ts +16 -9
- package/src/cursor-agents-context.ts +6 -6
- package/src/cursor-api-key.ts +1 -1
- package/src/cursor-bridge-contract.ts +1 -15
- package/src/cursor-cloud-local-state.ts +0 -2
- package/src/cursor-config.ts +1 -24
- package/src/cursor-fallback-models.generated.ts +424 -1
- package/src/cursor-live-run-coordinator.ts +5 -0
- package/src/cursor-local-run-terminal-error.ts +58 -0
- package/src/cursor-mcp-timeout-override.ts +6 -30
- package/src/cursor-native-tool-display-replay.ts +4 -6
- package/src/cursor-native-tools.ts +14 -0
- package/src/cursor-pi-tool-bridge-env.ts +31 -2
- package/src/cursor-pi-tool-bridge-server.ts +89 -45
- package/src/cursor-pi-tool-bridge-snapshot.ts +0 -1
- package/src/cursor-pi-tool-bridge.ts +2 -18
- package/src/cursor-provider-errors.ts +0 -8
- package/src/cursor-provider-live-run-drain.ts +8 -2
- package/src/cursor-provider-overflow.ts +6 -24
- package/src/cursor-provider-turn-finalize.ts +2 -0
- package/src/cursor-provider-turn-prepare.ts +10 -1
- package/src/cursor-provider-turn-runner.ts +17 -6
- package/src/cursor-provider-turn-send.ts +2 -0
- package/src/cursor-provider.ts +0 -5
- package/src/cursor-runtime-state.ts +3 -3
- package/src/cursor-sdk-event-debug.ts +2 -2
- package/src/cursor-sdk-output-filter.ts +2 -1
- package/src/cursor-sdk-process-error-guard.ts +1 -3
- package/src/cursor-session-agent.ts +8 -8
- package/src/cursor-session-scope.ts +1 -9
- package/src/cursor-session-store.ts +3 -2
- package/src/cursor-state.ts +9 -36
- package/src/cursor-tool-lifecycle.ts +0 -4
- package/src/cursor-tool-manifest.ts +4 -1
- package/src/cursor-tool-presentation-registry.ts +3 -5
- package/src/cursor-tool-visibility.ts +0 -4
- package/src/cursor-transcript-tool-specs.ts +0 -3
- package/src/index.ts +18 -0
- package/src/model-discovery.ts +0 -1
- package/src/model-list-cache.ts +4 -10
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { assertCursorNativeToolsRuntime, resolveCursorNativeToolsEnabled } from "./cursor-native-tools.js";
|
|
1
2
|
import { CursorLiveRunAbortError } from "./cursor-live-run-coordinator.js";
|
|
2
|
-
import { drainExistingCursorLiveRunBeforeSend } from "./cursor-provider-live-run-drain.js";
|
|
3
|
+
import { cursorLiveRuns, drainExistingCursorLiveRunBeforeSend } from "./cursor-provider-live-run-drain.js";
|
|
3
4
|
import { installCursorSdkProcessErrorGuard } from "./cursor-sdk-process-error-guard.js";
|
|
4
5
|
import type { CursorRuntime } from "./cursor-config.js";
|
|
5
6
|
import { CursorSdkEventDebugSink } from "./cursor-sdk-event-debug.js";
|
|
@@ -68,6 +69,13 @@ export class CursorProviderTurnRunner {
|
|
|
68
69
|
this.throwIfAborted();
|
|
69
70
|
const { scope } = this.params;
|
|
70
71
|
const cwd = scope.cwd;
|
|
72
|
+
|
|
73
|
+
// Resolved once here, before any drain await, so the drain decision and the
|
|
74
|
+
// prepare dispatch below always act on the same config snapshot.
|
|
75
|
+
const resolvedConfig = resolveCursorProviderTurnConfig(cwd, scope.projectTrusted, scope.scopeKey);
|
|
76
|
+
this.runtimeTarget = resolvedConfig.runtime.value;
|
|
77
|
+
const nativeToolsEnabled = resolveCursorNativeToolsEnabled();
|
|
78
|
+
assertCursorNativeToolsRuntime(this.runtimeTarget, nativeToolsEnabled);
|
|
71
79
|
this.sdkEventDebug = CursorSdkEventDebugSink.maybeCreate({
|
|
72
80
|
cwd,
|
|
73
81
|
modelId: model.id,
|
|
@@ -76,13 +84,9 @@ export class CursorProviderTurnRunner {
|
|
|
76
84
|
});
|
|
77
85
|
sdkEventDebugRef.current = this.sdkEventDebug;
|
|
78
86
|
this.sdkEventDebug?.recordContextSnapshot(context);
|
|
79
|
-
// Resolved once here, before any drain await, so the drain decision and the
|
|
80
|
-
// prepare dispatch below always act on the same config snapshot.
|
|
81
|
-
const resolvedConfig = resolveCursorProviderTurnConfig(cwd, scope.projectTrusted, scope.scopeKey);
|
|
82
|
-
this.runtimeTarget = resolvedConfig.runtime.value;
|
|
83
87
|
if (resolvedConfig.runtime.value === "local" && this.params.request.purpose === "normal") {
|
|
84
88
|
if (
|
|
85
|
-
(await drainExistingCursorLiveRunBeforeSend(stream, partial, model, context, options?.signal, this.sdkEventDebug, scope.scopeKey, this.params.request.occupancyFloor)) ===
|
|
89
|
+
(await drainExistingCursorLiveRunBeforeSend(stream, partial, model, context, options?.signal, this.sdkEventDebug, scope.scopeKey, this.params.request.occupancyFloor, nativeToolsEnabled)) ===
|
|
86
90
|
"stream_ended"
|
|
87
91
|
) {
|
|
88
92
|
return;
|
|
@@ -98,6 +102,7 @@ export class CursorProviderTurnRunner {
|
|
|
98
102
|
sdkEventDebug: this.sdkEventDebug,
|
|
99
103
|
throwIfAborted: () => this.throwIfAborted(),
|
|
100
104
|
resolvedConfig,
|
|
105
|
+
nativeToolsEnabled,
|
|
101
106
|
});
|
|
102
107
|
|
|
103
108
|
const usage = await this.params.usageRecorder.start({
|
|
@@ -173,6 +178,12 @@ export class CursorProviderTurnRunner {
|
|
|
173
178
|
displayOnlyTraceBlock: finalized.displayOnlyTraceBlock,
|
|
174
179
|
});
|
|
175
180
|
} catch (error) {
|
|
181
|
+
// This body owns the session turn lease; a queued request aborted before
|
|
182
|
+
// entry must not release another request's run in handleOuterCatch().
|
|
183
|
+
if (!prepared && (error instanceof CursorLiveRunAbortError || options?.signal?.aborted)) {
|
|
184
|
+
const pending = cursorLiveRuns.getActiveForScope(this.params.scope.scopeKey);
|
|
185
|
+
if (pending && !pending.disposed) await cursorLiveRuns.release(pending);
|
|
186
|
+
}
|
|
176
187
|
await runFinalizer.applyTerminalEvent({ kind: "error", prepared: started ?? prepared, error });
|
|
177
188
|
} finally {
|
|
178
189
|
await runFinalizer.cleanup(prepared, sendResult, liveCompletion);
|
|
@@ -4,6 +4,7 @@ import { createCursorCloudLifecyclePersistenceError } from "./cursor-cloud-lifec
|
|
|
4
4
|
import { cursorLiveRuns } from "./cursor-provider-live-run-drain.js";
|
|
5
5
|
import { consumeCursorLocalForceOverride } from "./cursor-runtime-state.js";
|
|
6
6
|
import { recordCursorSessionAgentLineage } from "./cursor-session-agent-lineage.js";
|
|
7
|
+
import { watchCursorLocalRunTerminalError } from "./cursor-local-run-terminal-error.js";
|
|
7
8
|
import type { installCursorSdkProcessErrorGuard } from "./cursor-sdk-process-error-guard.js";
|
|
8
9
|
import type {
|
|
9
10
|
CursorProviderTurnRunnerParams,
|
|
@@ -123,6 +124,7 @@ export async function sendCursorProviderTurn(sendParams: SendCursorProviderTurnP
|
|
|
123
124
|
recordCursorSessionAgentLineage(agent.agentId, params.scope.scopeKey);
|
|
124
125
|
}
|
|
125
126
|
const run = await runPromise;
|
|
127
|
+
if (prepared.runtimeTarget === "local") watchCursorLocalRunTerminalError(prepared.store, run);
|
|
126
128
|
sdkRun = run;
|
|
127
129
|
runtime.sdkRun = run;
|
|
128
130
|
try {
|
package/src/cursor-provider.ts
CHANGED
|
@@ -9,8 +9,6 @@ import {
|
|
|
9
9
|
} from "@earendil-works/pi-ai";
|
|
10
10
|
import {
|
|
11
11
|
cursorLiveRuns,
|
|
12
|
-
DEFAULT_CURSOR_NATIVE_REPLAY_IDLE_DISPOSE_MS,
|
|
13
|
-
getPendingCursorLiveRun,
|
|
14
12
|
hasTrailingUserMessagesAfterToolResults,
|
|
15
13
|
releaseAllPendingCursorLiveRunsForTests,
|
|
16
14
|
resetCursorNativeReplayIdleDisposeMs,
|
|
@@ -114,10 +112,7 @@ export function streamCursor(
|
|
|
114
112
|
}
|
|
115
113
|
|
|
116
114
|
export const __testUtils = {
|
|
117
|
-
DEFAULT_CURSOR_NATIVE_REPLAY_IDLE_DISPOSE_MS,
|
|
118
115
|
pendingCursorNativeRunCount: cursorLiveRuns.count,
|
|
119
|
-
getPendingCursorLiveRun,
|
|
120
|
-
getActiveCursorLiveRunForScope: cursorLiveRuns.getActiveForScope,
|
|
121
116
|
hasTrailingUserMessagesAfterToolResults,
|
|
122
117
|
setCursorNativeReplayIdleDisposeMs,
|
|
123
118
|
resetCursorNativeReplayIdleDisposeMs,
|
|
@@ -39,9 +39,9 @@ import { getResolvedSessionCursorHttp1Enabled } from "./cursor-http1.js";
|
|
|
39
39
|
import { getCursorSessionScopeSnapshot } from "./cursor-session-scope.js";
|
|
40
40
|
import { cursorSettingsScopeForContext, getCursorSessionSettings } from "./cursor-session-settings.js";
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
const CURSOR_RUNTIME_ENTRY_TYPE = "cursor-runtime-state";
|
|
43
43
|
|
|
44
|
-
|
|
44
|
+
const CURSOR_CLOUD_ACK_DISCLOSURE = [
|
|
45
45
|
"Cursor Cloud executes this work remotely.",
|
|
46
46
|
"Fresh context is used by default; prior Pi context is included only with explicit bootstrap opt-in.",
|
|
47
47
|
"Pi-local tools and the Pi bridge are unavailable, and Pi environment variables are not forwarded.",
|
|
@@ -145,7 +145,7 @@ export function resolveCursorStatusRuntime(ctx: CursorRuntimeContext): CursorRun
|
|
|
145
145
|
}
|
|
146
146
|
}
|
|
147
147
|
|
|
148
|
-
|
|
148
|
+
function formatResolvedCursorRuntime(runtime: CursorResolvedSetting<CursorRuntime>): string {
|
|
149
149
|
const cap = runtime.cappedBy;
|
|
150
150
|
return cap
|
|
151
151
|
? `${runtime.value} (source: ${runtime.source} safety cap over ${cap.cappedSource} ${cap.cappedValue})`
|
|
@@ -171,7 +171,7 @@ export type DiscardedIncompleteStartedToolCallReason =
|
|
|
171
171
|
| "sdk-failure"
|
|
172
172
|
| "run-drain";
|
|
173
173
|
|
|
174
|
-
|
|
174
|
+
function hashCursorSdkCallId(callId: string): string {
|
|
175
175
|
return createHash("sha256").update(callId).digest("hex").slice(0, 8);
|
|
176
176
|
}
|
|
177
177
|
|
|
@@ -182,7 +182,7 @@ export interface DiscardedIncompleteStartedToolCallRecord {
|
|
|
182
182
|
reason: DiscardedIncompleteStartedToolCallReason;
|
|
183
183
|
}
|
|
184
184
|
|
|
185
|
-
|
|
185
|
+
function serializeDiscardedIncompleteStartedToolCall(record: {
|
|
186
186
|
toolName: string;
|
|
187
187
|
callId: string;
|
|
188
188
|
reason?: DiscardedIncompleteStartedToolCallReason;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
|
-
import { classifyCursorConnectError,
|
|
2
|
+
import { classifyCursorConnectError, isCursorSdkConnectionStalledError } from "./cursor-provider-errors.js";
|
|
3
3
|
|
|
4
4
|
interface CursorSdkProcessErrorGuardToken {
|
|
5
5
|
suppressAbortErrors: boolean;
|
|
@@ -147,8 +147,6 @@ export const __testUtils = {
|
|
|
147
147
|
},
|
|
148
148
|
};
|
|
149
149
|
|
|
150
|
-
export { isCursorSdkAbortConnectError };
|
|
151
|
-
|
|
152
150
|
export function installCursorSdkProcessErrorGuard(): CursorSdkProcessErrorGuard {
|
|
153
151
|
const token: CursorSdkProcessErrorGuardToken = { suppressAbortErrors: false };
|
|
154
152
|
activeProviderTurns.add(token);
|
|
@@ -140,6 +140,8 @@ interface SessionCursorAgentCreateParams {
|
|
|
140
140
|
settingSources?: SettingSource[];
|
|
141
141
|
localSafety?: CursorLocalSafetyOptions;
|
|
142
142
|
agents?: AgentOptions["agents"];
|
|
143
|
+
/** Captured by the turn owner before asynchronous acquisition. */
|
|
144
|
+
nativeToolsEnabled?: boolean;
|
|
143
145
|
useHttp1ForAgent?: boolean;
|
|
144
146
|
onBridgeToolRequest?: (request: CursorPiBridgeToolRequest) => void;
|
|
145
147
|
debugRecorder?: CursorSdkEventDebugRecorder;
|
|
@@ -153,7 +155,7 @@ interface SessionCursorAgentCreateParams {
|
|
|
153
155
|
const sessionAgentsByScope = new Map<string, SessionCursorAgentPoolEntry>();
|
|
154
156
|
const invalidatedScopeKeys = new Set<string>();
|
|
155
157
|
const deadTransportScopeKeys = new Set<string>();
|
|
156
|
-
|
|
158
|
+
const DEAD_TRANSPORT_AGENT_DISPOSE_TIMEOUT_MS = 3000;
|
|
157
159
|
const terminalDisposedScopeGenerations = new Map<string, number>();
|
|
158
160
|
const scopeCreationGenerations = new Map<string, number>();
|
|
159
161
|
const EMPTY_POOL_STATE: SessionCursorAgentPoolState = { status: "empty" };
|
|
@@ -254,6 +256,8 @@ function buildSessionAgentPoolKey(scopeKey: string, params: SessionCursorAgentCr
|
|
|
254
256
|
buildApiKeyPoolKeyFingerprint(params.apiKey),
|
|
255
257
|
buildBridgePoolKeySuffix("bridge" in params ? params.bridge : getRegisteredCursorPiToolBridge()),
|
|
256
258
|
...buildCustomSubagentPoolKeySuffix(params.agents),
|
|
259
|
+
// Omit for the default so historical durable keys remain byte-for-byte stable.
|
|
260
|
+
...(params.nativeToolsEnabled === false ? ["tools:mcp-only"] : []),
|
|
257
261
|
].join("\0");
|
|
258
262
|
}
|
|
259
263
|
|
|
@@ -273,7 +277,7 @@ async function disposePoolEntry(entry: SessionCursorAgentPoolEntry, options?: {
|
|
|
273
277
|
await (options?.deadTransport
|
|
274
278
|
? Promise.race([
|
|
275
279
|
disposal,
|
|
276
|
-
new Promise<void>((resolve) => setTimeout(resolve,
|
|
280
|
+
new Promise<void>((resolve) => setTimeout(resolve, DEAD_TRANSPORT_AGENT_DISPOSE_TIMEOUT_MS).unref?.()),
|
|
277
281
|
])
|
|
278
282
|
: disposal);
|
|
279
283
|
} catch {
|
|
@@ -520,6 +524,7 @@ async function createSessionAgentEntry(
|
|
|
520
524
|
localSafety: params.localSafety,
|
|
521
525
|
store: sessionStore!.store,
|
|
522
526
|
}),
|
|
527
|
+
...(params.nativeToolsEnabled === false ? { tools: ["mcp"] } : {}),
|
|
523
528
|
...(bridgeRun?.mcpServers ? { mcpServers: bridgeRun.mcpServers } : {}),
|
|
524
529
|
...(params.agents && Object.keys(params.agents).length ? { agents: params.agents } : {}),
|
|
525
530
|
});
|
|
@@ -584,7 +589,7 @@ export {
|
|
|
584
589
|
} from "./cursor-session-send-policy.js";
|
|
585
590
|
|
|
586
591
|
export function invalidateSessionAgent(
|
|
587
|
-
scopeKey: string
|
|
592
|
+
scopeKey: string,
|
|
588
593
|
options?: { deadTransport?: boolean },
|
|
589
594
|
): void {
|
|
590
595
|
invalidatedScopeKeys.add(scopeKey);
|
|
@@ -725,11 +730,6 @@ export const __testUtils = {
|
|
|
725
730
|
resetSessionCursorAgent,
|
|
726
731
|
refreshSessionCursorAgentConfig,
|
|
727
732
|
disposeAllSessionCursorAgents,
|
|
728
|
-
setDeadTransportAgentDisposeTimeoutMs(ms: number): number {
|
|
729
|
-
const previous = deadTransportAgentDisposeTimeoutMs;
|
|
730
|
-
deadTransportAgentDisposeTimeoutMs = ms;
|
|
731
|
-
return previous;
|
|
732
|
-
},
|
|
733
733
|
SessionCursorAgentCreationSupersededError,
|
|
734
734
|
SessionCursorAgentScopeClosedError,
|
|
735
735
|
};
|
|
@@ -12,7 +12,7 @@ interface CursorSessionScopeExtensionApi {
|
|
|
12
12
|
|
|
13
13
|
const ANONYMOUS_SESSION_SCOPE_KEY = "__anonymous__";
|
|
14
14
|
const EPHEMERAL_SESSION_SCOPE_PREFIX = "__ephemeral__:";
|
|
15
|
-
|
|
15
|
+
const MAX_CURSOR_SESSION_NAME_LENGTH = 100;
|
|
16
16
|
|
|
17
17
|
type CursorSessionScopeChangeHandler = (previousScopeKey: string) => Promise<void> | void;
|
|
18
18
|
|
|
@@ -103,18 +103,10 @@ export function getCursorSessionScopeGeneration(scopeKey: string = getCursorSess
|
|
|
103
103
|
return scopeGenerations.get(scopeKey) ?? 0;
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
-
export function getCursorSessionCwd(): string {
|
|
107
|
-
return state.sessionCwd;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
106
|
export function getCursorSessionProjectTrusted(pi?: object): boolean {
|
|
111
107
|
return pi ? scopes.get(pi)?.projectTrusted === true : state.projectTrusted;
|
|
112
108
|
}
|
|
113
109
|
|
|
114
|
-
export function getCursorSessionName(): string | undefined {
|
|
115
|
-
return state.sessionName;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
110
|
function normalizeCursorSessionName(name: string | undefined): string | undefined {
|
|
119
111
|
if (name === undefined) return undefined;
|
|
120
112
|
return truncateCursorDisplayLine(name, MAX_CURSOR_SESSION_NAME_LENGTH) || undefined;
|
|
@@ -6,6 +6,7 @@ import { dirname, isAbsolute, join, relative, resolve, sep, toNamespacedPath } f
|
|
|
6
6
|
import type { LocalAgentStore } from "@cursor/sdk";
|
|
7
7
|
import { loadCursorSdk } from "./cursor-sdk-runtime.js";
|
|
8
8
|
import { guardCursorCustomStorePath, normalizeCursorCustomStoreRoot, prepareCursorCustomStoreRoot } from "./cursor-custom-store-path.js";
|
|
9
|
+
import { observeCursorLocalRunTerminalErrors } from "./cursor-local-run-terminal-error.js";
|
|
9
10
|
|
|
10
11
|
export interface CursorSessionStoreIdentity {
|
|
11
12
|
readonly version: 1;
|
|
@@ -55,7 +56,7 @@ const activeWorkspaceRoots = new Map<string, { root: Promise<string>; users: num
|
|
|
55
56
|
// ponytail: leases protect only extension-owned stores in this process. Other SDK
|
|
56
57
|
// users/processes must stop before upgrade; a cross-process lock belongs in the SDK.
|
|
57
58
|
|
|
58
|
-
|
|
59
|
+
function hashCursorSessionStoreScope(scopeKey: string): string {
|
|
59
60
|
return createHash("sha256")
|
|
60
61
|
.update("pi-cursor-sdk-session-store\0")
|
|
61
62
|
.update(scopeKey)
|
|
@@ -299,7 +300,7 @@ async function openOwnedCursorSessionStore(
|
|
|
299
300
|
const openedStore = store;
|
|
300
301
|
return {
|
|
301
302
|
identity: openedIdentity,
|
|
302
|
-
store: openedStore,
|
|
303
|
+
store: observeCursorLocalRunTerminalErrors(openedStore),
|
|
303
304
|
dispose: async () => {
|
|
304
305
|
try {
|
|
305
306
|
await openedStore.dispose();
|
package/src/cursor-state.ts
CHANGED
|
@@ -44,11 +44,7 @@ import {
|
|
|
44
44
|
updateCursorSdkConfig,
|
|
45
45
|
} from "./cursor-config.js";
|
|
46
46
|
import {
|
|
47
|
-
consumeCursorLocalForceOverride,
|
|
48
|
-
CURSOR_RUNTIME_ENTRY_TYPE,
|
|
49
47
|
formatCursorStatus,
|
|
50
|
-
getCursorCliConfig,
|
|
51
|
-
getCursorSessionConfig,
|
|
52
48
|
registerCursorCloudRuntimeControls,
|
|
53
49
|
resetCursorRuntimeStateForTests,
|
|
54
50
|
resolveCursorStatusRuntime,
|
|
@@ -57,19 +53,11 @@ import {
|
|
|
57
53
|
type CursorRuntimeStateExtensionApi,
|
|
58
54
|
} from "./cursor-runtime-state.js";
|
|
59
55
|
|
|
60
|
-
export {
|
|
61
|
-
consumeCursorLocalForceOverride,
|
|
62
|
-
getCursorCliConfig,
|
|
63
|
-
getCursorSessionConfig,
|
|
64
|
-
} from "./cursor-runtime-state.js";
|
|
65
|
-
|
|
66
56
|
export const CURSOR_FOOTER_ENV = "PI_CURSOR_FOOTER";
|
|
67
57
|
|
|
68
58
|
const FAST_ENTRY_TYPE = "cursor-fast-state";
|
|
69
59
|
const MODE_ENTRY_TYPE = "cursor-mode-state";
|
|
70
60
|
|
|
71
|
-
export type CursorAgentMode = AgentModeOption;
|
|
72
|
-
|
|
73
61
|
const DEFAULT_CURSOR_AGENT_MODE: AgentModeOption = "agent";
|
|
74
62
|
|
|
75
63
|
interface CursorFastEntryData {
|
|
@@ -91,11 +79,11 @@ const authoritativeGlobalFastPreferenceIds = new Set<string>();
|
|
|
91
79
|
let globalFastPreferences = new Map<string, boolean>();
|
|
92
80
|
const invalidCursorModeNotifiedSessionScopeKeys = new Set<string>();
|
|
93
81
|
|
|
94
|
-
|
|
82
|
+
function isCursorAgentMode(value: unknown): value is AgentModeOption {
|
|
95
83
|
return value === "agent" || value === "plan";
|
|
96
84
|
}
|
|
97
85
|
|
|
98
|
-
|
|
86
|
+
function parseCursorAgentMode(raw: unknown): AgentModeOption | undefined {
|
|
99
87
|
if (typeof raw !== "string") return undefined;
|
|
100
88
|
const mode = raw.trim();
|
|
101
89
|
return isCursorAgentMode(mode) ? mode : undefined;
|
|
@@ -115,17 +103,13 @@ function isCursorModeEntryData(value: unknown): value is CursorModeEntryData {
|
|
|
115
103
|
return isCursorAgentMode(asRecord(value)?.mode);
|
|
116
104
|
}
|
|
117
105
|
|
|
118
|
-
function getConfigPath(): string {
|
|
119
|
-
return getCursorSdkUserConfigPath();
|
|
120
|
-
}
|
|
121
|
-
|
|
122
106
|
function loadGlobalFastPreferences(): Map<string, boolean> {
|
|
123
107
|
return cursorFastDefaultsFromConfig(loadCursorSdkUserConfig());
|
|
124
108
|
}
|
|
125
109
|
|
|
126
110
|
function saveGlobalFastPreference(modelId: string, fast: boolean): void {
|
|
127
111
|
updateCursorSdkConfig(
|
|
128
|
-
|
|
112
|
+
getCursorSdkUserConfigPath(),
|
|
129
113
|
(current) => {
|
|
130
114
|
const fastDefaults = { ...asRecord(current.fastDefaults), [modelId]: fast };
|
|
131
115
|
return {
|
|
@@ -141,7 +125,7 @@ function saveGlobalFastPreference(modelId: string, fast: boolean): void {
|
|
|
141
125
|
|
|
142
126
|
function saveGlobalCursorHttp1Enabled(enabled: boolean): void {
|
|
143
127
|
updateCursorSdkConfig(
|
|
144
|
-
|
|
128
|
+
getCursorSdkUserConfigPath(),
|
|
145
129
|
(current) => mergeCursorSdkConfigForUpdate(current, {
|
|
146
130
|
local: { useHttp1ForAgent: enabled },
|
|
147
131
|
}),
|
|
@@ -227,15 +211,15 @@ function formatInvalidCursorMode(raw: string): string {
|
|
|
227
211
|
return `Invalid --cursor-mode "${raw}". Use "agent" or "plan".`;
|
|
228
212
|
}
|
|
229
213
|
|
|
230
|
-
|
|
214
|
+
type CursorAgentModeResolution =
|
|
231
215
|
| { kind: "valid"; mode: AgentModeOption }
|
|
232
216
|
| { kind: "invalid"; raw: string; message: string };
|
|
233
217
|
|
|
234
|
-
|
|
218
|
+
function getStoredCursorAgentMode(scopeKey?: string): AgentModeOption {
|
|
235
219
|
return getCursorSessionSettings(scopeKey).mode ?? DEFAULT_CURSOR_AGENT_MODE;
|
|
236
220
|
}
|
|
237
221
|
|
|
238
|
-
|
|
222
|
+
function resolveCursorAgentMode(scopeKey?: string): CursorAgentModeResolution {
|
|
239
223
|
const cliCursorModeState = getCursorSessionSettings(scopeKey).cli.mode;
|
|
240
224
|
switch (cliCursorModeState.kind) {
|
|
241
225
|
case "valid":
|
|
@@ -383,10 +367,10 @@ function formatEffectiveCursorSettingSourcesLabel(raw: string | undefined = proc
|
|
|
383
367
|
return `${rawLabel} (effective: ${effectiveLabel})`;
|
|
384
368
|
}
|
|
385
369
|
|
|
386
|
-
|
|
370
|
+
function formatCursorToolsDebugReport(
|
|
387
371
|
pi: Pick<ExtensionAPI, "getActiveTools" | "getAllTools">,
|
|
388
|
-
env: Record<string, string | undefined> = process.env,
|
|
389
372
|
): string {
|
|
373
|
+
const env = process.env;
|
|
390
374
|
const bridgeEnabled = resolveCursorPiToolBridgeEnabled(env);
|
|
391
375
|
const manifestEnabled = resolveCursorToolManifestEnabled(env);
|
|
392
376
|
const lines = [
|
|
@@ -689,16 +673,5 @@ function resetCursorModeStateForTests(): void {
|
|
|
689
673
|
}
|
|
690
674
|
|
|
691
675
|
export const __testUtils = {
|
|
692
|
-
FAST_ENTRY_TYPE,
|
|
693
|
-
MODE_ENTRY_TYPE,
|
|
694
|
-
CURSOR_HTTP1_ENTRY_TYPE,
|
|
695
|
-
RUNTIME_ENTRY_TYPE: CURSOR_RUNTIME_ENTRY_TYPE,
|
|
696
|
-
DEFAULT_CURSOR_AGENT_MODE,
|
|
697
|
-
getConfigPath,
|
|
698
|
-
loadGlobalFastPreferences,
|
|
699
|
-
get sessionFastPreferences() { return getCursorSessionSettings().fast; },
|
|
700
|
-
getSessionCursorAgentMode: () => getCursorSessionSettings().mode,
|
|
701
|
-
getCliCursorAgentMode: () => { const mode = getCursorSessionSettings().cli.mode; return mode.kind === "valid" ? mode.mode : undefined; },
|
|
702
|
-
getCliCursorModeState: () => getCursorSessionSettings().cli.mode,
|
|
703
676
|
resetCursorModeStateForTests,
|
|
704
677
|
};
|
|
@@ -86,10 +86,6 @@ function buildCursorToolLifecycleLabelFromVisibility(
|
|
|
86
86
|
}
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
-
export function buildCursorToolLifecycleLabel(toolCall: unknown, apiKey?: string): string | undefined {
|
|
90
|
-
return buildCursorToolLifecycleLabelFromVisibility(classifyCursorToolVisibility(toolCall), apiKey);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
89
|
export function formatCursorToolLifecycleProgressText(toolCall: unknown, apiKey?: string): string | undefined {
|
|
94
90
|
const visibility = classifyCursorToolVisibility(toolCall);
|
|
95
91
|
const label = buildCursorToolLifecycleLabelFromVisibility(visibility, apiKey);
|
|
@@ -18,6 +18,7 @@ export function resolveCursorToolManifestEnabled(
|
|
|
18
18
|
|
|
19
19
|
export function buildCursorToolManifestText(options: {
|
|
20
20
|
bridgeSnapshot?: CursorPiToolBridgeSnapshot;
|
|
21
|
+
nativeToolsEnabled?: boolean;
|
|
21
22
|
/** When false, bridge is off via PI_CURSOR_PI_TOOL_BRIDGE=0 (not merely empty). */
|
|
22
23
|
piBridgeEnabled?: boolean;
|
|
23
24
|
includePiBridgeGuidance?: boolean;
|
|
@@ -26,7 +27,9 @@ export function buildCursorToolManifestText(options: {
|
|
|
26
27
|
const includePiBridgeGuidance = options.includePiBridgeGuidance !== false;
|
|
27
28
|
const lines = [
|
|
28
29
|
"Callable tool surfaces this run:",
|
|
29
|
-
|
|
30
|
+
options.nativeToolsEnabled === false
|
|
31
|
+
? "- Cursor native tools (including task/subagents): disabled (PI_CURSOR_NATIVE_TOOLS=0). MCP remains available; configured MCP depends on Cursor settings."
|
|
32
|
+
: `- Cursor host/MCP: ${CURSOR_HOST_TOOL_MANIFEST_SUMMARY}; configured MCP depends on Cursor settings.`,
|
|
30
33
|
"- Pi tool toggles affect pi tools/bridge exposure only; they do not disable Cursor host/configured MCP tools.",
|
|
31
34
|
];
|
|
32
35
|
const bridgeTools = includePiBridgeGuidance ? options.bridgeSnapshot?.tools ?? [] : [];
|
|
@@ -40,7 +40,7 @@ import {
|
|
|
40
40
|
buildWebSearchReplaySummaryArgs,
|
|
41
41
|
type CursorReplayActivityBuildContext,
|
|
42
42
|
} from "./cursor-replay-activity-builders.js";
|
|
43
|
-
import {
|
|
43
|
+
import type { CursorReplaySourceToolName } from "./cursor-replay-source-names.js";
|
|
44
44
|
import type {
|
|
45
45
|
CursorReplayActivityDetailFields,
|
|
46
46
|
CursorReplayGenerateImageDetailFields,
|
|
@@ -112,7 +112,7 @@ const WEB_SEARCH_NAME_PATTERN =
|
|
|
112
112
|
const WEB_FETCH_NAME_PATTERN =
|
|
113
113
|
/^(?:web[-_ ]?fetch|fetch[-_ ]?web|webfetch|browser[-_ ]?fetch|fetch[-_ ]?url)$/i;
|
|
114
114
|
|
|
115
|
-
|
|
115
|
+
const CURSOR_TOOL_PRESENTATION_SPECS = [
|
|
116
116
|
{
|
|
117
117
|
normalizedName: "read",
|
|
118
118
|
nameAliases: ["read_file"],
|
|
@@ -345,9 +345,7 @@ const WEB_KIND_BY_PATTERN = CURSOR_TOOL_PRESENTATION_SPECS.flatMap((spec) => {
|
|
|
345
345
|
return webNamePatterns.map((pattern) => ({ pattern, webKind }));
|
|
346
346
|
});
|
|
347
347
|
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
export function getCursorToolPresentationSpec(
|
|
348
|
+
function getCursorToolPresentationSpec(
|
|
351
349
|
name: string,
|
|
352
350
|
): CursorToolPresentationSpec | undefined {
|
|
353
351
|
const trimmed = name.trim();
|
|
@@ -41,7 +41,3 @@ export function classifyCursorToolVisibility(toolCall: unknown): CursorToolVisib
|
|
|
41
41
|
fastLocalDiscovery: config?.fastLocalDiscovery ?? false,
|
|
42
42
|
};
|
|
43
43
|
}
|
|
44
|
-
|
|
45
|
-
export function isFastLocalDiscoveryTool(toolCall: unknown): boolean {
|
|
46
|
-
return classifyCursorToolVisibility(toolCall).fastLocalDiscovery;
|
|
47
|
-
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import {
|
|
2
|
-
CURSOR_KNOWN_NORMALIZED_TOOL_NAMES,
|
|
3
2
|
CURSOR_REPLAY_ACTIVITY_TOOL_NAME,
|
|
4
3
|
getCursorReplayCallSummary,
|
|
5
4
|
getCursorToolActivityTitle,
|
|
@@ -417,8 +416,6 @@ const TOOL_DISPLAY_IMPLEMENTATIONS: Record<CursorNormalizedToolName, ToolDisplay
|
|
|
417
416
|
},
|
|
418
417
|
};
|
|
419
418
|
|
|
420
|
-
export const CURSOR_TOOL_DISPLAY_SPEC_KEYS = CURSOR_KNOWN_NORMALIZED_TOOL_NAMES;
|
|
421
|
-
|
|
422
419
|
function getToolDisplaySpec(name: string): ToolDisplaySpec | undefined {
|
|
423
420
|
if (Object.hasOwn(TOOL_DISPLAY_IMPLEMENTATIONS, name)) return TOOL_DISPLAY_IMPLEMENTATIONS[name as CursorNormalizedToolName];
|
|
424
421
|
return undefined;
|
package/src/index.ts
CHANGED
|
@@ -15,6 +15,8 @@ import { registerCursorFallbackIssueWarning } from "./cursor-fallback-warning.js
|
|
|
15
15
|
import { registerCursorAgentsContextDedup } from "./cursor-agents-context-registration.js";
|
|
16
16
|
import { registerCursorOverflowNormalization } from "./cursor-provider-overflow.js";
|
|
17
17
|
import { registerCursorSdkSessionProcessErrorGuard } from "./cursor-sdk-process-error-guard.js";
|
|
18
|
+
import { installCursorSdkProcessOutputFilter } from "./cursor-sdk-output-filter.js";
|
|
19
|
+
import { cursorLiveRuns } from "./cursor-provider-live-run-drain.js";
|
|
18
20
|
import { prepareCursorSessionForCompaction } from "./cursor-session-compaction-prep.js";
|
|
19
21
|
import { registerCursorUsageLedger } from "./cursor-usage-ledger.js";
|
|
20
22
|
import { registerCursorUsageCommand } from "./cursor-usage-command.js";
|
|
@@ -37,10 +39,26 @@ type CursorExtensionApi =
|
|
|
37
39
|
& Parameters<typeof registerCursorUsageLedger>[0];
|
|
38
40
|
|
|
39
41
|
export default async function (pi: CursorExtensionApi) {
|
|
42
|
+
installCursorSdkProcessOutputFilter();
|
|
40
43
|
// Session cwd must register before other session_start listeners that depend on it.
|
|
41
44
|
registerCursorSessionScope(pi);
|
|
42
45
|
registerCursorUsageLedger(pi);
|
|
43
46
|
registerCursorUsageCommand(pi);
|
|
47
|
+
pi.registerCommand("cursor-abort", {
|
|
48
|
+
description: "Abort this session's pending local Cursor run without undoing completed work",
|
|
49
|
+
handler: async (_args, ctx) => {
|
|
50
|
+
const run = cursorLiveRuns.getActiveForScope(getCursorSessionScopeSnapshot(pi).scopeKey);
|
|
51
|
+
if (run && !run.disposed) {
|
|
52
|
+
if (!ctx.isIdle() && ctx.model?.provider === "cursor") ctx.abort();
|
|
53
|
+
await cursorLiveRuns.release(run);
|
|
54
|
+
}
|
|
55
|
+
const message = run
|
|
56
|
+
? "Pending local Cursor run released. Retry under the current policy; already-executed work was not undone."
|
|
57
|
+
: "No pending local Cursor run in this session.";
|
|
58
|
+
if (ctx.hasUI) ctx.ui.notify(message, "info");
|
|
59
|
+
else process.stderr.write(`${message}\n`);
|
|
60
|
+
},
|
|
61
|
+
});
|
|
44
62
|
let fallbackIssue: CursorModelFallbackIssue | undefined;
|
|
45
63
|
const registerCursorProvider = registerCursorProviderBinding(pi);
|
|
46
64
|
const setFallbackWarning = registerCursorFallbackIssueWarning(pi);
|
package/src/model-discovery.ts
CHANGED
package/src/model-list-cache.ts
CHANGED
|
@@ -29,11 +29,11 @@ function getCachePath(): string {
|
|
|
29
29
|
return join(getAgentDir(), MODEL_LIST_CACHE_FILE);
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
function isModelCacheDisabled(): boolean {
|
|
33
33
|
return parseEnvBoolean(process.env[DISABLE_ENV_VAR], false);
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
function getModelCacheTtlMs(): number {
|
|
37
37
|
const raw = process.env[TTL_ENV_VAR];
|
|
38
38
|
if (raw === undefined) return DEFAULT_TTL_MS;
|
|
39
39
|
const parsed = Number.parseInt(raw, 10);
|
|
@@ -133,7 +133,8 @@ function readCacheFile(): ModelListCacheFile | undefined {
|
|
|
133
133
|
|
|
134
134
|
// Return cached models only when caching is enabled, the key matches, and the
|
|
135
135
|
// entry is within the TTL. Used on the hot startup path to skip the network.
|
|
136
|
-
export function loadFreshCachedModels(keyFingerprint: string
|
|
136
|
+
export function loadFreshCachedModels(keyFingerprint: string): ModelListItem[] | undefined {
|
|
137
|
+
const now = Date.now();
|
|
137
138
|
if (isModelCacheDisabled()) return undefined;
|
|
138
139
|
const ttlMs = getModelCacheTtlMs();
|
|
139
140
|
if (ttlMs <= 0) return undefined;
|
|
@@ -180,10 +181,3 @@ export function clearModelListCache(): void {
|
|
|
180
181
|
// Cache cleanup is best-effort; it must not break native logout.
|
|
181
182
|
}
|
|
182
183
|
}
|
|
183
|
-
|
|
184
|
-
export const __testUtils = {
|
|
185
|
-
getCachePath,
|
|
186
|
-
DEFAULT_TTL_MS,
|
|
187
|
-
DISABLE_ENV_VAR,
|
|
188
|
-
TTL_ENV_VAR,
|
|
189
|
-
};
|