pi-cursor-sdk 0.5.3 → 0.5.4
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 +40 -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 +264 -1
- package/dist/cursor-live-run-coordinator.js +1 -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-prepare.js +8 -1
- package/dist/cursor-provider-turn-runner.js +17 -6
- 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 +1 -1
- 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 +3 -3
- package/docs/cursor-model-ux-spec.md +16 -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 +15 -3
- package/docs/cursor-tool-surfaces.md +32 -3
- package/docs/development.md +93 -0
- package/docs/platform-smoke-implementation.md +3 -2
- package/docs/reference.md +806 -0
- package/package.json +6 -4
- 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/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 +264 -1
- package/src/cursor-live-run-coordinator.ts +5 -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-prepare.ts +10 -1
- package/src/cursor-provider-turn-runner.ts +17 -6
- 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 +1 -1
- 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,18 +1,12 @@
|
|
|
1
1
|
import { spawnSync } from "node:child_process";
|
|
2
2
|
import {
|
|
3
|
-
CURSOR_PI_TOOL_BRIDGE_DEBUG_ENV,
|
|
4
3
|
CURSOR_PI_TOOL_BRIDGE_DIAGNOSTIC_PREFIX,
|
|
5
4
|
type CursorPiToolBridgeDiagnosticEvent,
|
|
6
5
|
serializeCursorPiToolBridgeDiagnostic,
|
|
7
6
|
} from "./cursor-pi-tool-bridge-diagnostics.js";
|
|
8
|
-
import {
|
|
9
|
-
CURSOR_PI_TOOL_BRIDGE_BUILTINS_ENV,
|
|
10
|
-
CURSOR_PI_TOOL_BRIDGE_CALL_TIMEOUT_MS_ENV,
|
|
11
|
-
CURSOR_PI_TOOL_BRIDGE_ENV,
|
|
12
|
-
} from "./cursor-pi-tool-bridge-env.js";
|
|
13
7
|
import { bridgeToolExecutionAbortTracker } from "./cursor-pi-tool-bridge-abort.js";
|
|
14
|
-
import { isCursorPiBridgeToolCallId
|
|
15
|
-
import {
|
|
8
|
+
import { isCursorPiBridgeToolCallId } from "./cursor-pi-tool-bridge-constants.js";
|
|
9
|
+
import { CursorPiToolBridgeRegistry } from "./cursor-pi-tool-bridge-server.js";
|
|
16
10
|
import type {
|
|
17
11
|
CursorPiToolBridge,
|
|
18
12
|
CursorPiToolBridgeExtensionApi,
|
|
@@ -34,9 +28,6 @@ export type {
|
|
|
34
28
|
export type { CursorPiToolBridgeDiagnosticEvent } from "./cursor-pi-tool-bridge-diagnostics.js";
|
|
35
29
|
export { resolveCursorPiToolBridgeDebugEnabled } from "./cursor-pi-tool-bridge-diagnostics.js";
|
|
36
30
|
export {
|
|
37
|
-
CURSOR_PI_TOOL_BRIDGE_BUILTINS_ENV,
|
|
38
|
-
CURSOR_PI_TOOL_BRIDGE_CALL_TIMEOUT_MS_ENV,
|
|
39
|
-
CURSOR_PI_TOOL_BRIDGE_ENV,
|
|
40
31
|
resolveCursorPiToolBridgeBuiltinsEnabled,
|
|
41
32
|
resolveCursorPiToolBridgeCallTimeoutMs,
|
|
42
33
|
resolveCursorPiToolBridgeEnabled,
|
|
@@ -145,13 +136,7 @@ export function getRegisteredCursorPiToolBridge(pi?: object): CursorPiToolBridge
|
|
|
145
136
|
}
|
|
146
137
|
|
|
147
138
|
export const __testUtils = {
|
|
148
|
-
CURSOR_PI_TOOL_BRIDGE_ENV,
|
|
149
|
-
CURSOR_PI_TOOL_BRIDGE_BUILTINS_ENV,
|
|
150
|
-
CURSOR_PI_TOOL_BRIDGE_CALL_TIMEOUT_MS_ENV,
|
|
151
|
-
CURSOR_PI_TOOL_BRIDGE_DEBUG_ENV,
|
|
152
139
|
CURSOR_PI_TOOL_BRIDGE_DIAGNOSTIC_PREFIX,
|
|
153
|
-
LOOPBACK_HOST,
|
|
154
|
-
MCP_SERVER_NAME,
|
|
155
140
|
createRegistry(
|
|
156
141
|
pi: CursorPiToolBridgeSnapshotApi,
|
|
157
142
|
env: Record<string, string | undefined> = process.env,
|
|
@@ -168,7 +153,6 @@ export const __testUtils = {
|
|
|
168
153
|
return bridgeToolExecutionAbortTracker.getActiveCount();
|
|
169
154
|
},
|
|
170
155
|
buildWindowsBridgeBashAbortCommandForTests: buildWindowsBridgeBashAbortCommand,
|
|
171
|
-
installWindowsBridgeBashAbortMarkerForTests: installWindowsBridgeBashAbortMarker,
|
|
172
156
|
emitBridgeToolExecutionProcessAbortSignalForTests(signal: NodeJS.Signals) {
|
|
173
157
|
bridgeToolExecutionAbortTracker.emitProcessAbortSignalForTests(signal);
|
|
174
158
|
},
|
|
@@ -258,14 +258,6 @@ export function classifyCursorConnectError(error: unknown): CursorConnectErrorCl
|
|
|
258
258
|
return undefined;
|
|
259
259
|
}
|
|
260
260
|
|
|
261
|
-
export function isCursorSdkAbortConnectError(error: unknown): boolean {
|
|
262
|
-
return classifyCursorConnectError(error)?.kind === "abort";
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
export function isUnauthenticatedConnectError(error: unknown): boolean {
|
|
266
|
-
return classifyCursorConnectError(error)?.kind === "unauthenticated";
|
|
267
|
-
}
|
|
268
|
-
|
|
269
261
|
function isLikelyNetworkTimeout(message: string): boolean {
|
|
270
262
|
return (
|
|
271
263
|
/\b(ETIMEDOUT|ECONNRESET|ECONNREFUSED|ENETUNREACH|EAI_AGAIN|NGHTTP2_ENHANCE_YOUR_CALM|ERR_HTTP2_STREAM_ERROR|ERR_HTTP2_SESSION_ERROR)\b/i.test(
|
|
@@ -29,7 +29,7 @@ import { formatInactiveCursorReplayTrace } from "./cursor-native-replay-trace.js
|
|
|
29
29
|
import { partitionNativeToolsByActiveContext } from "./cursor-native-replay-routing.js";
|
|
30
30
|
import type { CursorSdkEventDebugRecorder } from "./cursor-sdk-event-debug.js";
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
const DEFAULT_CURSOR_NATIVE_REPLAY_IDLE_DISPOSE_MS = 5 * 60 * 1000;
|
|
33
33
|
function parseCursorNativeReplayIdleDisposeMs(raw: string | undefined): number {
|
|
34
34
|
if (!raw || /[^0-9]/.test(raw)) return DEFAULT_CURSOR_NATIVE_REPLAY_IDLE_DISPOSE_MS;
|
|
35
35
|
const value = Number(raw);
|
|
@@ -69,7 +69,7 @@ function getCursorNativeReplayIdFromToolCallId(toolCallId: string): string | und
|
|
|
69
69
|
return CURSOR_NATIVE_REPLAY_TOOL_ID_PATTERN.exec(toolCallId)?.[1];
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
-
|
|
72
|
+
function getPendingCursorLiveRun(context: Context, scopeKey: string): CursorLiveRun | undefined {
|
|
73
73
|
return cursorLiveRuns.getPendingFromContext(context, getCursorNativeReplayIdFromToolCallId, scopeKey);
|
|
74
74
|
}
|
|
75
75
|
|
|
@@ -449,6 +449,7 @@ export async function drainExistingCursorLiveRunBeforeSend(
|
|
|
449
449
|
turnDebugRecorder: CursorSdkEventDebugRecorder | undefined,
|
|
450
450
|
scopeKey: string,
|
|
451
451
|
occupancyFloor?: number,
|
|
452
|
+
nativeToolsEnabled = true,
|
|
452
453
|
): Promise<LiveRunPreSendOutcome> {
|
|
453
454
|
turnDebugRecorder?.recordDrainEvent("pre_send_start", {});
|
|
454
455
|
while (true) {
|
|
@@ -461,6 +462,11 @@ export async function drainExistingCursorLiveRunBeforeSend(
|
|
|
461
462
|
try {
|
|
462
463
|
const outcome = await cursorLiveRuns.withRunLease(run, signal, async () => {
|
|
463
464
|
if (run.disposed) return "continue_send" as const;
|
|
465
|
+
// Check under the drain lease, before consuming/delivering Pi results:
|
|
466
|
+
// resolving a bridge waiter can resume the SDK's old tool surface.
|
|
467
|
+
if ((run.nativeToolsEnabled !== false) !== nativeToolsEnabled) {
|
|
468
|
+
throw new Error("Cursor native tool policy changed while a local run is pending; restore the prior policy to finish it or run /cursor-abort before retrying. Already-executed work is not undone.");
|
|
469
|
+
}
|
|
464
470
|
const consumed = cursorLiveRuns.consumeToolResults(run, context, getCursorNativeReplayIdFromToolCallId);
|
|
465
471
|
await run.bridgeRun?.resolveToolResults(consumed.toolResults);
|
|
466
472
|
const shouldChainUserInput = run.chainUserInputAfterCompletion || hasTrailingUserMessagesAfterToolResults(context);
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
|
-
import type { AssistantMessage } from "@earendil-works/pi-ai";
|
|
3
2
|
import { CURSOR_PROVIDER } from "./cursor-model.js";
|
|
4
3
|
|
|
5
4
|
/**
|
|
@@ -15,7 +14,7 @@ import { CURSOR_PROVIDER } from "./cursor-model.js";
|
|
|
15
14
|
*
|
|
16
15
|
* See pi `docs/custom-provider.md` -> "Context Overflow Errors".
|
|
17
16
|
*/
|
|
18
|
-
|
|
17
|
+
const CURSOR_OVERFLOW_MARKER = "context_length_exceeded";
|
|
19
18
|
|
|
20
19
|
/**
|
|
21
20
|
* Context-overflow phrases. These overlap pi's own `OVERFLOW_PATTERNS` so the
|
|
@@ -43,9 +42,9 @@ const CURSOR_THROTTLE_PATTERN = /too many requests|rate.?limit|throttl|retry.?af
|
|
|
43
42
|
* Map a finalized Cursor assistant `errorMessage` to pi's overflow form.
|
|
44
43
|
* Returns `undefined` when the message is not a Cursor context-overflow failure.
|
|
45
44
|
*
|
|
46
|
-
*
|
|
45
|
+
* Idempotent so repeated message delivery does not add another prefix.
|
|
47
46
|
*/
|
|
48
|
-
|
|
47
|
+
function normalizeCursorOverflowErrorMessage(errorMessage: string | undefined): string | undefined {
|
|
49
48
|
const message = errorMessage?.trim();
|
|
50
49
|
if (!message) return undefined;
|
|
51
50
|
// Idempotent: never double-prefix an already-normalized message.
|
|
@@ -57,24 +56,6 @@ export function normalizeCursorOverflowErrorMessage(errorMessage: string | undef
|
|
|
57
56
|
return undefined;
|
|
58
57
|
}
|
|
59
58
|
|
|
60
|
-
/**
|
|
61
|
-
* Rewrite a finalized assistant message into pi's overflow form when it is a
|
|
62
|
-
* Cursor context-overflow failure. Returns the replacement message (for the
|
|
63
|
-
* `message_end` result) or `undefined` to leave it unchanged.
|
|
64
|
-
*
|
|
65
|
-
* `isCursorProvider` carries the provider scoping decision out of the handler so
|
|
66
|
-
* the full logic is unit-testable without a pi event harness.
|
|
67
|
-
*/
|
|
68
|
-
export function rewriteCursorOverflowAssistantMessage(
|
|
69
|
-
message: AssistantMessage,
|
|
70
|
-
isCursorProvider: boolean,
|
|
71
|
-
): AssistantMessage | undefined {
|
|
72
|
-
if (!isCursorProvider || message.stopReason !== "error") return undefined;
|
|
73
|
-
const rewritten = normalizeCursorOverflowErrorMessage(message.errorMessage);
|
|
74
|
-
if (!rewritten) return undefined;
|
|
75
|
-
return { ...message, errorMessage: rewritten };
|
|
76
|
-
}
|
|
77
|
-
|
|
78
59
|
export type CursorOverflowExtensionApi = Pick<ExtensionAPI, "on">;
|
|
79
60
|
|
|
80
61
|
/**
|
|
@@ -89,8 +70,9 @@ export function registerCursorOverflowNormalization(pi: CursorOverflowExtensionA
|
|
|
89
70
|
const message = event.message;
|
|
90
71
|
if (message.role !== "assistant") return undefined;
|
|
91
72
|
const isCursorProvider = message.provider === CURSOR_PROVIDER || ctx.model?.provider === CURSOR_PROVIDER;
|
|
92
|
-
|
|
73
|
+
if (!isCursorProvider || message.stopReason !== "error") return undefined;
|
|
74
|
+
const rewritten = normalizeCursorOverflowErrorMessage(message.errorMessage);
|
|
93
75
|
if (!rewritten) return undefined;
|
|
94
|
-
return { message: rewritten };
|
|
76
|
+
return { message: { ...message, errorMessage: rewritten } };
|
|
95
77
|
});
|
|
96
78
|
}
|
|
@@ -59,6 +59,7 @@ import type {
|
|
|
59
59
|
} from "./cursor-provider-turn-types.js";
|
|
60
60
|
import type { CursorSdkEventDebugSink } from "./cursor-sdk-event-debug.js";
|
|
61
61
|
import type { SessionCursorAgentLease } from "./cursor-session-agent.js";
|
|
62
|
+
import { assertCursorNativeToolsRuntime } from "./cursor-native-tools.js";
|
|
62
63
|
import { prepareCursorSummaryProviderTurn } from "./cursor-provider-turn-summary.js";
|
|
63
64
|
|
|
64
65
|
export interface PrepareCursorProviderTurnParams {
|
|
@@ -69,6 +70,8 @@ export interface PrepareCursorProviderTurnParams {
|
|
|
69
70
|
throwIfAborted: () => void;
|
|
70
71
|
/** Snapshot resolved once by the runner before draining; reused unchanged through prepare. */
|
|
71
72
|
resolvedConfig: CursorResolvedSdkConfig;
|
|
73
|
+
/** Snapshotted before drain/acquisition, never re-read during creation/resume. */
|
|
74
|
+
nativeToolsEnabled: boolean;
|
|
72
75
|
}
|
|
73
76
|
|
|
74
77
|
interface PrepareCursorProviderTurnContext extends PrepareCursorProviderTurnParams {
|
|
@@ -252,7 +255,7 @@ async function prepareCursorCloudProviderTurn(
|
|
|
252
255
|
async function prepareCursorLocalProviderTurn(
|
|
253
256
|
prepareParams: PrepareCursorProviderTurnContext,
|
|
254
257
|
): Promise<LocalCursorProviderTurnPrepareResult> {
|
|
255
|
-
const { params, cwd, resolvedApiKey, sdkEventDebug, throwIfAborted, resolvedConfig, agentMode, selection, fastEnabled, agents, customSubagentNames } = prepareParams;
|
|
258
|
+
const { params, cwd, resolvedApiKey, sdkEventDebug, throwIfAborted, resolvedConfig, agentMode, selection, fastEnabled, agents, customSubagentNames, nativeToolsEnabled } = prepareParams;
|
|
256
259
|
const { model, context, options } = params;
|
|
257
260
|
|
|
258
261
|
let restoreCursorSdkOutputFilter: (() => void) | undefined;
|
|
@@ -289,6 +292,7 @@ async function prepareCursorLocalProviderTurn(
|
|
|
289
292
|
settingSources,
|
|
290
293
|
localSafety,
|
|
291
294
|
agents,
|
|
295
|
+
nativeToolsEnabled,
|
|
292
296
|
localResume: resolvedConfig.local.resume.value,
|
|
293
297
|
storeRoot: resolvedConfig.local.storeRoot.value,
|
|
294
298
|
useHttp1ForAgent,
|
|
@@ -314,6 +318,7 @@ async function prepareCursorLocalProviderTurn(
|
|
|
314
318
|
...getCursorPromptOptions(model),
|
|
315
319
|
nativeSourceRoles: params.request.nativeSourceRoles,
|
|
316
320
|
agentMode,
|
|
321
|
+
nativeToolsEnabled,
|
|
317
322
|
includePiBridgeGuidance,
|
|
318
323
|
includePiAskQuestionGuidance: false,
|
|
319
324
|
};
|
|
@@ -323,6 +328,7 @@ async function prepareCursorLocalProviderTurn(
|
|
|
323
328
|
return {
|
|
324
329
|
...promptOptions,
|
|
325
330
|
toolManifest: buildCursorToolManifestText({
|
|
331
|
+
nativeToolsEnabled,
|
|
326
332
|
bridgeSnapshot: sessionAgentLease.bridgeRun?.snapshot,
|
|
327
333
|
piBridgeEnabled: resolveCursorPiToolBridgeEnabled(),
|
|
328
334
|
includePiBridgeGuidance,
|
|
@@ -373,6 +379,7 @@ async function prepareCursorLocalProviderTurn(
|
|
|
373
379
|
agentMode,
|
|
374
380
|
localForce: resolvedConfig.local.force.value,
|
|
375
381
|
localResume: resolvedConfig.local.resume.value,
|
|
382
|
+
...(nativeToolsEnabled ? {} : { nativeToolsEnabled: false, tools: ["mcp"] }),
|
|
376
383
|
resumedAgent: sessionAgentLease.resumed,
|
|
377
384
|
activeToolNames: activeToolNames ? [...activeToolNames] : [],
|
|
378
385
|
sessionAgentScopeKey,
|
|
@@ -386,6 +393,7 @@ async function prepareCursorLocalProviderTurn(
|
|
|
386
393
|
? cursorLiveRuns.start({
|
|
387
394
|
id: useNativeToolReplay ? nativeReplayId : bridgeRun?.id ?? nativeReplayId,
|
|
388
395
|
agent,
|
|
396
|
+
nativeToolsEnabled,
|
|
389
397
|
bridgeRun,
|
|
390
398
|
sessionBridgeRun,
|
|
391
399
|
sessionAgentScopeKey,
|
|
@@ -467,6 +475,7 @@ export async function prepareCursorProviderTurn(
|
|
|
467
475
|
): Promise<CursorProviderTurnPrepareResult> {
|
|
468
476
|
const { params, resolvedConfig } = prepareParams;
|
|
469
477
|
const { model, options } = params;
|
|
478
|
+
assertCursorNativeToolsRuntime(resolvedConfig.runtime.value, prepareParams.nativeToolsEnabled);
|
|
470
479
|
|
|
471
480
|
const fastEnabled = resolvedConfig.runtime.value === "cloud" ? undefined : getEffectiveFastForModelId(model.id, params.scope.scopeKey);
|
|
472
481
|
const selection = buildCursorModelSelection(model.id, options?.reasoning ?? "off", fastEnabled);
|
|
@@ -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);
|
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;
|
|
@@ -55,7 +55,7 @@ const activeWorkspaceRoots = new Map<string, { root: Promise<string>; users: num
|
|
|
55
55
|
// ponytail: leases protect only extension-owned stores in this process. Other SDK
|
|
56
56
|
// users/processes must stop before upgrade; a cross-process lock belongs in the SDK.
|
|
57
57
|
|
|
58
|
-
|
|
58
|
+
function hashCursorSessionStoreScope(scopeKey: string): string {
|
|
59
59
|
return createHash("sha256")
|
|
60
60
|
.update("pi-cursor-sdk-session-store\0")
|
|
61
61
|
.update(scopeKey)
|
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 ?? [] : [];
|