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
|
@@ -60,10 +60,10 @@ export function resolveCursorSdkEventDebugEnabled(env = process.env) {
|
|
|
60
60
|
return parseEnvBoolean(env[CURSOR_SDK_EVENT_DEBUG_ENV], false);
|
|
61
61
|
}
|
|
62
62
|
export const DISCARDED_INCOMPLETE_TOOL_CALL_REASON = "no-completion-at-run-end";
|
|
63
|
-
|
|
63
|
+
function hashCursorSdkCallId(callId) {
|
|
64
64
|
return createHash("sha256").update(callId).digest("hex").slice(0, 8);
|
|
65
65
|
}
|
|
66
|
-
|
|
66
|
+
function serializeDiscardedIncompleteStartedToolCall(record) {
|
|
67
67
|
return {
|
|
68
68
|
event: "discarded-incomplete-started-tool-call",
|
|
69
69
|
toolName: record.toolName,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { __testUtils, installCursorSdkProcessOutputFilter, withCursorSdkOutputNoticeHandler, installCursorSdkOutputFilter, isCursorSdkOutputSuppressed, isCursorSdkStartupNoise, suppressCursorSdkOutput, } from "../shared/cursor-sdk-output-filter.mjs";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { classifyCursorConnectError,
|
|
1
|
+
import { classifyCursorConnectError, isCursorSdkConnectionStalledError } from "./cursor-provider-errors.js";
|
|
2
2
|
// Cursor SDK controlled-exec tasks can reject after their originating provider turn
|
|
3
3
|
// has ended. The exact closed-writable failure is therefore session-scoped; existing
|
|
4
4
|
// ConnectRPC suppression remains scoped to active provider turns.
|
|
@@ -128,7 +128,6 @@ export const __testUtils = {
|
|
|
128
128
|
lifecycleSessionGuards.clear();
|
|
129
129
|
},
|
|
130
130
|
};
|
|
131
|
-
export { isCursorSdkAbortConnectError };
|
|
132
131
|
export function installCursorSdkProcessErrorGuard() {
|
|
133
132
|
const token = { suppressAbortErrors: false };
|
|
134
133
|
activeProviderTurns.add(token);
|
|
@@ -42,7 +42,7 @@ function rethrowSupersededWhenReplacedByDifferentPoolIdentity(scopeKey, poolKey,
|
|
|
42
42
|
const sessionAgentsByScope = new Map();
|
|
43
43
|
const invalidatedScopeKeys = new Set();
|
|
44
44
|
const deadTransportScopeKeys = new Set();
|
|
45
|
-
|
|
45
|
+
const DEAD_TRANSPORT_AGENT_DISPOSE_TIMEOUT_MS = 3000;
|
|
46
46
|
const terminalDisposedScopeGenerations = new Map();
|
|
47
47
|
const scopeCreationGenerations = new Map();
|
|
48
48
|
const EMPTY_POOL_STATE = { status: "empty" };
|
|
@@ -121,6 +121,8 @@ function buildSessionAgentPoolKey(scopeKey, params) {
|
|
|
121
121
|
buildApiKeyPoolKeyFingerprint(params.apiKey),
|
|
122
122
|
buildBridgePoolKeySuffix("bridge" in params ? params.bridge : getRegisteredCursorPiToolBridge()),
|
|
123
123
|
...buildCustomSubagentPoolKeySuffix(params.agents),
|
|
124
|
+
// Omit for the default so historical durable keys remain byte-for-byte stable.
|
|
125
|
+
...(params.nativeToolsEnabled === false ? ["tools:mcp-only"] : []),
|
|
124
126
|
].join("\0");
|
|
125
127
|
}
|
|
126
128
|
async function disposePoolEntry(entry, options) {
|
|
@@ -141,7 +143,7 @@ async function disposePoolEntry(entry, options) {
|
|
|
141
143
|
await (options?.deadTransport
|
|
142
144
|
? Promise.race([
|
|
143
145
|
disposal,
|
|
144
|
-
new Promise((resolve) => setTimeout(resolve,
|
|
146
|
+
new Promise((resolve) => setTimeout(resolve, DEAD_TRANSPORT_AGENT_DISPOSE_TIMEOUT_MS).unref?.()),
|
|
145
147
|
])
|
|
146
148
|
: disposal);
|
|
147
149
|
}
|
|
@@ -343,6 +345,7 @@ async function createSessionAgentEntry(scopeKey, persistentStore, instanceId, se
|
|
|
343
345
|
localSafety: params.localSafety,
|
|
344
346
|
store: sessionStore.store,
|
|
345
347
|
}),
|
|
348
|
+
...(params.nativeToolsEnabled === false ? { tools: ["mcp"] } : {}),
|
|
346
349
|
...(bridgeRun?.mcpServers ? { mcpServers: bridgeRun.mcpServers } : {}),
|
|
347
350
|
...(params.agents && Object.keys(params.agents).length ? { agents: params.agents } : {}),
|
|
348
351
|
});
|
|
@@ -404,7 +407,7 @@ async function createSessionAgentEntry(scopeKey, persistentStore, instanceId, se
|
|
|
404
407
|
}
|
|
405
408
|
}
|
|
406
409
|
export { buildCursorSessionSendPrompt, MAX_COMPLETED_INCREMENTAL_SENDS_BEFORE_REBOOTSTRAP, planCursorSessionSend, } from "./cursor-session-send-policy.js";
|
|
407
|
-
export function invalidateSessionAgent(scopeKey
|
|
410
|
+
export function invalidateSessionAgent(scopeKey, options) {
|
|
408
411
|
invalidatedScopeKeys.add(scopeKey);
|
|
409
412
|
if (options?.deadTransport)
|
|
410
413
|
deadTransportScopeKeys.add(scopeKey);
|
|
@@ -534,11 +537,6 @@ export const __testUtils = {
|
|
|
534
537
|
resetSessionCursorAgent,
|
|
535
538
|
refreshSessionCursorAgentConfig,
|
|
536
539
|
disposeAllSessionCursorAgents,
|
|
537
|
-
setDeadTransportAgentDisposeTimeoutMs(ms) {
|
|
538
|
-
const previous = deadTransportAgentDisposeTimeoutMs;
|
|
539
|
-
deadTransportAgentDisposeTimeoutMs = ms;
|
|
540
|
-
return previous;
|
|
541
|
-
},
|
|
542
540
|
SessionCursorAgentCreationSupersededError,
|
|
543
541
|
SessionCursorAgentScopeClosedError,
|
|
544
542
|
};
|
|
@@ -3,7 +3,7 @@ import { parseArgs } from "@earendil-works/pi-coding-agent";
|
|
|
3
3
|
import { truncateCursorDisplayLine } from "./cursor-display-text.js";
|
|
4
4
|
const ANONYMOUS_SESSION_SCOPE_KEY = "__anonymous__";
|
|
5
5
|
const EPHEMERAL_SESSION_SCOPE_PREFIX = "__ephemeral__:";
|
|
6
|
-
|
|
6
|
+
const MAX_CURSOR_SESSION_NAME_LENGTH = 100;
|
|
7
7
|
const state = {
|
|
8
8
|
sessionCwd: process.cwd(),
|
|
9
9
|
sessionFile: undefined,
|
|
@@ -65,15 +65,9 @@ export function getCursorSessionScopeKey() {
|
|
|
65
65
|
export function getCursorSessionScopeGeneration(scopeKey = getCursorSessionScopeKey()) {
|
|
66
66
|
return scopeGenerations.get(scopeKey) ?? 0;
|
|
67
67
|
}
|
|
68
|
-
export function getCursorSessionCwd() {
|
|
69
|
-
return state.sessionCwd;
|
|
70
|
-
}
|
|
71
68
|
export function getCursorSessionProjectTrusted(pi) {
|
|
72
69
|
return pi ? scopes.get(pi)?.projectTrusted === true : state.projectTrusted;
|
|
73
70
|
}
|
|
74
|
-
export function getCursorSessionName() {
|
|
75
|
-
return state.sessionName;
|
|
76
|
-
}
|
|
77
71
|
function normalizeCursorSessionName(name) {
|
|
78
72
|
if (name === undefined)
|
|
79
73
|
return undefined;
|
|
@@ -5,11 +5,12 @@ import { homedir, tmpdir } from "node:os";
|
|
|
5
5
|
import { dirname, isAbsolute, join, relative, resolve, sep, toNamespacedPath } from "node:path";
|
|
6
6
|
import { loadCursorSdk } from "./cursor-sdk-runtime.js";
|
|
7
7
|
import { guardCursorCustomStorePath, normalizeCursorCustomStoreRoot, prepareCursorCustomStoreRoot } from "./cursor-custom-store-path.js";
|
|
8
|
+
import { observeCursorLocalRunTerminalErrors } from "./cursor-local-run-terminal-error.js";
|
|
8
9
|
let sdkOperationsForTests;
|
|
9
10
|
const activeWorkspaceRoots = new Map();
|
|
10
11
|
// ponytail: leases protect only extension-owned stores in this process. Other SDK
|
|
11
12
|
// users/processes must stop before upgrade; a cross-process lock belongs in the SDK.
|
|
12
|
-
|
|
13
|
+
function hashCursorSessionStoreScope(scopeKey) {
|
|
13
14
|
return createHash("sha256")
|
|
14
15
|
.update("pi-cursor-sdk-session-store\0")
|
|
15
16
|
.update(scopeKey)
|
|
@@ -244,7 +245,7 @@ async function openOwnedCursorSessionStore(cwd, identity, options) {
|
|
|
244
245
|
const openedStore = store;
|
|
245
246
|
return {
|
|
246
247
|
identity: openedIdentity,
|
|
247
|
-
store: openedStore,
|
|
248
|
+
store: observeCursorLocalRunTerminalErrors(openedStore),
|
|
248
249
|
dispose: async () => {
|
|
249
250
|
try {
|
|
250
251
|
await openedStore.dispose();
|
package/dist/cursor-state.js
CHANGED
|
@@ -13,8 +13,7 @@ import { cursorSettingsScopeForContext, getCursorSessionSettings, registerCursor
|
|
|
13
13
|
import { refreshSessionCursorAgentConfig } from "./cursor-session-agent.js";
|
|
14
14
|
import { getCursorModelMetadata } from "./model-discovery.js";
|
|
15
15
|
import { cursorFastDefaultsFromConfig, getCursorSdkUserConfigPath, loadCursorSdkUserConfig, mergeCursorSdkConfigForUpdate, resolveCursorFastDefault, resolveCursorLocalStoreRoot, updateCursorSdkConfig, } from "./cursor-config.js";
|
|
16
|
-
import {
|
|
17
|
-
export { consumeCursorLocalForceOverride, getCursorCliConfig, getCursorSessionConfig, } from "./cursor-runtime-state.js";
|
|
16
|
+
import { formatCursorStatus, registerCursorCloudRuntimeControls, resetCursorRuntimeStateForTests, resolveCursorStatusRuntime, restoreCursorCliState, restoreSessionCursorRuntimeState, } from "./cursor-runtime-state.js";
|
|
18
17
|
export const CURSOR_FOOTER_ENV = "PI_CURSOR_FOOTER";
|
|
19
18
|
const FAST_ENTRY_TYPE = "cursor-fast-state";
|
|
20
19
|
const MODE_ENTRY_TYPE = "cursor-mode-state";
|
|
@@ -22,10 +21,10 @@ const DEFAULT_CURSOR_AGENT_MODE = "agent";
|
|
|
22
21
|
const authoritativeGlobalFastPreferenceIds = new Set();
|
|
23
22
|
let globalFastPreferences = new Map();
|
|
24
23
|
const invalidCursorModeNotifiedSessionScopeKeys = new Set();
|
|
25
|
-
|
|
24
|
+
function isCursorAgentMode(value) {
|
|
26
25
|
return value === "agent" || value === "plan";
|
|
27
26
|
}
|
|
28
|
-
|
|
27
|
+
function parseCursorAgentMode(raw) {
|
|
29
28
|
if (typeof raw !== "string")
|
|
30
29
|
return undefined;
|
|
31
30
|
const mode = raw.trim();
|
|
@@ -43,14 +42,11 @@ function getCursorFastEntryModelId(data) {
|
|
|
43
42
|
function isCursorModeEntryData(value) {
|
|
44
43
|
return isCursorAgentMode(asRecord(value)?.mode);
|
|
45
44
|
}
|
|
46
|
-
function getConfigPath() {
|
|
47
|
-
return getCursorSdkUserConfigPath();
|
|
48
|
-
}
|
|
49
45
|
function loadGlobalFastPreferences() {
|
|
50
46
|
return cursorFastDefaultsFromConfig(loadCursorSdkUserConfig());
|
|
51
47
|
}
|
|
52
48
|
function saveGlobalFastPreference(modelId, fast) {
|
|
53
|
-
updateCursorSdkConfig(
|
|
49
|
+
updateCursorSdkConfig(getCursorSdkUserConfigPath(), (current) => {
|
|
54
50
|
const fastDefaults = { ...asRecord(current.fastDefaults), [modelId]: fast };
|
|
55
51
|
return {
|
|
56
52
|
...current,
|
|
@@ -59,7 +55,7 @@ function saveGlobalFastPreference(modelId, fast) {
|
|
|
59
55
|
}, { newFileMode: 0o600 });
|
|
60
56
|
}
|
|
61
57
|
function saveGlobalCursorHttp1Enabled(enabled) {
|
|
62
|
-
updateCursorSdkConfig(
|
|
58
|
+
updateCursorSdkConfig(getCursorSdkUserConfigPath(), (current) => mergeCursorSdkConfigForUpdate(current, {
|
|
63
59
|
local: { useHttp1ForAgent: enabled },
|
|
64
60
|
}), { newFileMode: 0o600 });
|
|
65
61
|
}
|
|
@@ -134,10 +130,10 @@ function getEffectiveFast(modelId, scopeKey) {
|
|
|
134
130
|
function formatInvalidCursorMode(raw) {
|
|
135
131
|
return `Invalid --cursor-mode "${raw}". Use "agent" or "plan".`;
|
|
136
132
|
}
|
|
137
|
-
|
|
133
|
+
function getStoredCursorAgentMode(scopeKey) {
|
|
138
134
|
return getCursorSessionSettings(scopeKey).mode ?? DEFAULT_CURSOR_AGENT_MODE;
|
|
139
135
|
}
|
|
140
|
-
|
|
136
|
+
function resolveCursorAgentMode(scopeKey) {
|
|
141
137
|
const cliCursorModeState = getCursorSessionSettings(scopeKey).cli.mode;
|
|
142
138
|
switch (cliCursorModeState.kind) {
|
|
143
139
|
case "valid":
|
|
@@ -271,7 +267,8 @@ function formatEffectiveCursorSettingSourcesLabel(raw = process.env[CURSOR_SETTI
|
|
|
271
267
|
const rawLabel = raw?.trim() ? raw.trim() : `(unset → ${DEFAULT_CURSOR_SETTING_SOURCES.join(",")})`;
|
|
272
268
|
return `${rawLabel} (effective: ${effectiveLabel})`;
|
|
273
269
|
}
|
|
274
|
-
|
|
270
|
+
function formatCursorToolsDebugReport(pi) {
|
|
271
|
+
const env = process.env;
|
|
275
272
|
const bridgeEnabled = resolveCursorPiToolBridgeEnabled(env);
|
|
276
273
|
const manifestEnabled = resolveCursorToolManifestEnabled(env);
|
|
277
274
|
const lines = [
|
|
@@ -528,16 +525,5 @@ function resetCursorModeStateForTests() {
|
|
|
528
525
|
authoritativeGlobalFastPreferenceIds.clear();
|
|
529
526
|
}
|
|
530
527
|
export const __testUtils = {
|
|
531
|
-
FAST_ENTRY_TYPE,
|
|
532
|
-
MODE_ENTRY_TYPE,
|
|
533
|
-
CURSOR_HTTP1_ENTRY_TYPE,
|
|
534
|
-
RUNTIME_ENTRY_TYPE: CURSOR_RUNTIME_ENTRY_TYPE,
|
|
535
|
-
DEFAULT_CURSOR_AGENT_MODE,
|
|
536
|
-
getConfigPath,
|
|
537
|
-
loadGlobalFastPreferences,
|
|
538
|
-
get sessionFastPreferences() { return getCursorSessionSettings().fast; },
|
|
539
|
-
getSessionCursorAgentMode: () => getCursorSessionSettings().mode,
|
|
540
|
-
getCliCursorAgentMode: () => { const mode = getCursorSessionSettings().cli.mode; return mode.kind === "valid" ? mode.mode : undefined; },
|
|
541
|
-
getCliCursorModeState: () => getCursorSessionSettings().cli.mode,
|
|
542
528
|
resetCursorModeStateForTests,
|
|
543
529
|
};
|
|
@@ -82,9 +82,6 @@ function buildCursorToolLifecycleLabelFromVisibility(visibility, apiKey) {
|
|
|
82
82
|
return undefined;
|
|
83
83
|
}
|
|
84
84
|
}
|
|
85
|
-
export function buildCursorToolLifecycleLabel(toolCall, apiKey) {
|
|
86
|
-
return buildCursorToolLifecycleLabelFromVisibility(classifyCursorToolVisibility(toolCall), apiKey);
|
|
87
|
-
}
|
|
88
85
|
export function formatCursorToolLifecycleProgressText(toolCall, apiKey) {
|
|
89
86
|
const visibility = classifyCursorToolVisibility(toolCall);
|
|
90
87
|
const label = buildCursorToolLifecycleLabelFromVisibility(visibility, apiKey);
|
|
@@ -13,7 +13,9 @@ export function buildCursorToolManifestText(options = {}) {
|
|
|
13
13
|
const includePiBridgeGuidance = options.includePiBridgeGuidance !== false;
|
|
14
14
|
const lines = [
|
|
15
15
|
"Callable tool surfaces this run:",
|
|
16
|
-
|
|
16
|
+
options.nativeToolsEnabled === false
|
|
17
|
+
? "- Cursor native tools (including task/subagents): disabled (PI_CURSOR_NATIVE_TOOLS=0). MCP remains available; configured MCP depends on Cursor settings."
|
|
18
|
+
: `- Cursor host/MCP: ${CURSOR_HOST_TOOL_MANIFEST_SUMMARY}; configured MCP depends on Cursor settings.`,
|
|
17
19
|
"- Pi tool toggles affect pi tools/bridge exposure only; they do not disable Cursor host/configured MCP tools.",
|
|
18
20
|
];
|
|
19
21
|
const bridgeTools = includePiBridgeGuidance ? options.bridgeSnapshot?.tools ?? [] : [];
|
|
@@ -7,13 +7,12 @@ import { formatReplaySemSearchQuery, readCursorReplaySummaryString, summarizeRep
|
|
|
7
7
|
import { truncateCursorDisplayLine } from "./cursor-display-text.js";
|
|
8
8
|
import { getCursorTaskActivityTitle } from "./cursor-task-presentation.js";
|
|
9
9
|
import { buildCreatePlanReplaySummaryArgs, buildDeleteReplayDetailFields, buildDeleteReplaySummaryArgs, buildGenerateImageReplayDetailFields, buildGenerateImageReplaySummaryArgs, buildMcpReplaySummaryArgs, buildReadLintsReplaySummaryArgs, buildRecordScreenReplaySummaryArgs, buildSemSearchReplaySummaryArgs, buildTaskReplaySummaryArgs, buildTodoReplaySummaryArgs, buildWebFetchReplaySummaryArgs, buildWebSearchReplaySummaryArgs, } from "./cursor-replay-activity-builders.js";
|
|
10
|
-
import { CURSOR_REPLAY_SOURCE_TOOL_NAMES } from "./cursor-replay-source-names.js";
|
|
11
10
|
export const CURSOR_REPLAY_ACTIVITY_TOOL_NAME = "cursor";
|
|
12
11
|
const EMPTY_REPLAY_DETAIL_FIELDS = () => ({});
|
|
13
12
|
const COLLAPSED_REPLAY_DETAIL_FIELDS = () => ({ collapseDetailsByDefault: true });
|
|
14
13
|
const WEB_SEARCH_NAME_PATTERN = /^(?:web[-_ ]?search|search[-_ ]?web|websearch|browser[-_ ]?search)$/i;
|
|
15
14
|
const WEB_FETCH_NAME_PATTERN = /^(?:web[-_ ]?fetch|fetch[-_ ]?web|webfetch|browser[-_ ]?fetch|fetch[-_ ]?url)$/i;
|
|
16
|
-
|
|
15
|
+
const CURSOR_TOOL_PRESENTATION_SPECS = [
|
|
17
16
|
{
|
|
18
17
|
normalizedName: "read",
|
|
19
18
|
nameAliases: ["read_file"],
|
|
@@ -218,8 +217,7 @@ const WEB_KIND_BY_PATTERN = CURSOR_TOOL_PRESENTATION_SPECS.flatMap((spec) => {
|
|
|
218
217
|
return [];
|
|
219
218
|
return webNamePatterns.map((pattern) => ({ pattern, webKind }));
|
|
220
219
|
});
|
|
221
|
-
|
|
222
|
-
export function getCursorToolPresentationSpec(name) {
|
|
220
|
+
function getCursorToolPresentationSpec(name) {
|
|
223
221
|
const trimmed = name.trim();
|
|
224
222
|
if (!trimmed)
|
|
225
223
|
return undefined;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { CURSOR_REPLAY_ACTIVITY_TOOL_NAME, getCursorReplayCallSummary, getCursorToolActivityTitle, getCursorToolActivityReplaySpec, getCursorToolGenerateImageReplaySpec, } from "./cursor-tool-presentation-registry.js";
|
|
2
2
|
import { resolveCursorEditDiff } from "./cursor-edit-diff.js";
|
|
3
3
|
import { assembleCursorReplayActivityDetails, assembleCursorReplayGenerateImageDetails, CURSOR_REPLAY_UNREGISTERED_ACTIVITY_TOOL_NAME, } from "./cursor-replay-tool-details.js";
|
|
4
4
|
import { asRecord, getNumber, getString } from "./cursor-record-utils.js";
|
|
@@ -264,7 +264,6 @@ const TOOL_DISPLAY_IMPLEMENTATIONS = {
|
|
|
264
264
|
buildPiToolDisplay: (context) => buildActivityReplayDisplay("webFetch", context),
|
|
265
265
|
},
|
|
266
266
|
};
|
|
267
|
-
export const CURSOR_TOOL_DISPLAY_SPEC_KEYS = CURSOR_KNOWN_NORMALIZED_TOOL_NAMES;
|
|
268
267
|
function getToolDisplaySpec(name) {
|
|
269
268
|
if (Object.hasOwn(TOOL_DISPLAY_IMPLEMENTATIONS, name))
|
|
270
269
|
return TOOL_DISPLAY_IMPLEMENTATIONS[name];
|
package/dist/index.js
CHANGED
|
@@ -14,14 +14,35 @@ import { registerCursorFallbackIssueWarning } from "./cursor-fallback-warning.js
|
|
|
14
14
|
import { registerCursorAgentsContextDedup } from "./cursor-agents-context-registration.js";
|
|
15
15
|
import { registerCursorOverflowNormalization } from "./cursor-provider-overflow.js";
|
|
16
16
|
import { registerCursorSdkSessionProcessErrorGuard } from "./cursor-sdk-process-error-guard.js";
|
|
17
|
+
import { installCursorSdkProcessOutputFilter } from "./cursor-sdk-output-filter.js";
|
|
18
|
+
import { cursorLiveRuns } from "./cursor-provider-live-run-drain.js";
|
|
17
19
|
import { prepareCursorSessionForCompaction } from "./cursor-session-compaction-prep.js";
|
|
18
20
|
import { registerCursorUsageLedger } from "./cursor-usage-ledger.js";
|
|
19
21
|
import { registerCursorUsageCommand } from "./cursor-usage-command.js";
|
|
20
22
|
export default async function (pi) {
|
|
23
|
+
installCursorSdkProcessOutputFilter();
|
|
21
24
|
// Session cwd must register before other session_start listeners that depend on it.
|
|
22
25
|
registerCursorSessionScope(pi);
|
|
23
26
|
registerCursorUsageLedger(pi);
|
|
24
27
|
registerCursorUsageCommand(pi);
|
|
28
|
+
pi.registerCommand("cursor-abort", {
|
|
29
|
+
description: "Abort this session's pending local Cursor run without undoing completed work",
|
|
30
|
+
handler: async (_args, ctx) => {
|
|
31
|
+
const run = cursorLiveRuns.getActiveForScope(getCursorSessionScopeSnapshot(pi).scopeKey);
|
|
32
|
+
if (run && !run.disposed) {
|
|
33
|
+
if (!ctx.isIdle() && ctx.model?.provider === "cursor")
|
|
34
|
+
ctx.abort();
|
|
35
|
+
await cursorLiveRuns.release(run);
|
|
36
|
+
}
|
|
37
|
+
const message = run
|
|
38
|
+
? "Pending local Cursor run released. Retry under the current policy; already-executed work was not undone."
|
|
39
|
+
: "No pending local Cursor run in this session.";
|
|
40
|
+
if (ctx.hasUI)
|
|
41
|
+
ctx.ui.notify(message, "info");
|
|
42
|
+
else
|
|
43
|
+
process.stderr.write(`${message}\n`);
|
|
44
|
+
},
|
|
45
|
+
});
|
|
25
46
|
let fallbackIssue;
|
|
26
47
|
const registerCursorProvider = registerCursorProviderBinding(pi);
|
|
27
48
|
const setFallbackWarning = registerCursorFallbackIssueWarning(pi);
|
package/dist/model-discovery.js
CHANGED
package/dist/model-list-cache.js
CHANGED
|
@@ -13,10 +13,10 @@ const TTL_ENV_VAR = "PI_CURSOR_SDK_MODEL_CACHE_TTL_MS";
|
|
|
13
13
|
function getCachePath() {
|
|
14
14
|
return join(getAgentDir(), MODEL_LIST_CACHE_FILE);
|
|
15
15
|
}
|
|
16
|
-
|
|
16
|
+
function isModelCacheDisabled() {
|
|
17
17
|
return parseEnvBoolean(process.env[DISABLE_ENV_VAR], false);
|
|
18
18
|
}
|
|
19
|
-
|
|
19
|
+
function getModelCacheTtlMs() {
|
|
20
20
|
const raw = process.env[TTL_ENV_VAR];
|
|
21
21
|
if (raw === undefined)
|
|
22
22
|
return DEFAULT_TTL_MS;
|
|
@@ -105,7 +105,8 @@ function readCacheFile() {
|
|
|
105
105
|
}
|
|
106
106
|
// Return cached models only when caching is enabled, the key matches, and the
|
|
107
107
|
// entry is within the TTL. Used on the hot startup path to skip the network.
|
|
108
|
-
export function loadFreshCachedModels(keyFingerprint
|
|
108
|
+
export function loadFreshCachedModels(keyFingerprint) {
|
|
109
|
+
const now = Date.now();
|
|
109
110
|
if (isModelCacheDisabled())
|
|
110
111
|
return undefined;
|
|
111
112
|
const ttlMs = getModelCacheTtlMs();
|
|
@@ -159,9 +160,3 @@ export function clearModelListCache() {
|
|
|
159
160
|
// Cache cleanup is best-effort; it must not break native logout.
|
|
160
161
|
}
|
|
161
162
|
}
|
|
162
|
-
export const __testUtils = {
|
|
163
|
-
getCachePath,
|
|
164
|
-
DEFAULT_TTL_MS,
|
|
165
|
-
DISABLE_ENV_VAR,
|
|
166
|
-
TTL_ENV_VAR,
|
|
167
|
-
};
|
|
@@ -71,7 +71,7 @@ Pass criteria:
|
|
|
71
71
|
|
|
72
72
|
- `node --version` reports Node 24+.
|
|
73
73
|
- `pi --version` reports official Pi 0.87.1 or the selected official-latest/current-`fitchmultz/pi` compatibility target.
|
|
74
|
-
- `npm ls` shows exact `@cursor/sdk@1.0.
|
|
74
|
+
- `npm ls` shows exact `@cursor/sdk@1.0.40`; development `@earendil-works/pi-ai`, `@earendil-works/pi-coding-agent`, and `@earendil-works/pi-tui` 1.1.0 with matching lockfile-resolved Pi cohort, nested host TypeBox 1.3.27 and root validation TypeBox 1.3.36; and the bundled bridge runtime closure `@modelcontextprotocol/server@2.3.1`, `@modelcontextprotocol/hono@2.0.2`, `hono@4.13.13`, and `@hono/node-server@2.1.3`. These locked development snapshots are not qualification targets; resolve latest stable official Pi and current `fitchmultz/pi` main once per workflow run.
|
|
75
75
|
- `cursor/grok-4.6` appears in the model list.
|
|
76
76
|
- No Cursor key or auth token is printed.
|
|
77
77
|
- If neither `~/.pi/agent/auth.json` cursor auth nor `CURSOR_API_KEY` is available, stop and report the live smoke as blocked.
|
|
@@ -136,7 +136,7 @@ Pass criteria:
|
|
|
136
136
|
|
|
137
137
|
## 4. Focused visual card/color rendering check
|
|
138
138
|
|
|
139
|
-
This is a visual debug menu for changed card/color behavior requiring new live evidence. Select only the relevant category below, or reuse a batched existing platform suite; do not execute the entire list by default. It requires offscreen TUI visual inspection, not only JSONL or code review. Use Node 24+, official Pi 0.87.1 or another current compatibility target, exact `@cursor/sdk@1.0.
|
|
139
|
+
This is a visual debug menu for changed card/color behavior requiring new live evidence. Select only the relevant category below, or reuse a batched existing platform suite; do not execute the entire list by default. It requires offscreen TUI visual inspection, not only JSONL or code review. Use Node 24+, official Pi 0.87.1 or another current compatibility target, exact `@cursor/sdk@1.0.40`, a fresh temporary session dir, Cursor SDK `plan` mode, native replay enabled, and the checked-in visual runner. The runner resolves `pi` by directly walking the parent `PATH`, uses `process.execPath` for Node, and prepends that Node directory for both prereq checks and tmux launches so `#!/usr/bin/env node` shims use the validated Node. The default matrix is native replay only: native replay registration is forced on, settings sources are `none`, the pi bridge is off, overlapping built-in pi tools are not exposed, and inherited Cursor SDK event-debug artifact env is cleared. With `--event-debug`, debug capture writes to a deterministic directory under `VISUAL_DIR`.
|
|
140
140
|
|
|
141
141
|
```bash
|
|
142
142
|
VISUAL_DIR="$(mktemp -d /tmp/pi-cursor-sdk-1016-visual.XXXXXX)"
|
|
@@ -354,7 +354,7 @@ Script-enforced pass criteria:
|
|
|
354
354
|
Additional manual usage checks for provider/accounting changes:
|
|
355
355
|
|
|
356
356
|
- Each assistant message's disjoint input/cache/output components should sum to `totalTokens` and describe one coherent current context, not cumulative billed spend.
|
|
357
|
-
- In split runs, earlier tool results can remain in later current-context estimates. Fresh, attributable LOCAL SDK usage takes precedence; late raw and billed facts remain in the separate ledger without rewriting emitted messages. See [context, compaction, and Cursor usage](
|
|
357
|
+
- In split runs, earlier tool results can remain in later current-context estimates. Fresh, attributable LOCAL SDK usage takes precedence; late raw and billed facts remain in the separate ledger without rewriting emitted messages. See [context, compaction, and Cursor usage](reference.md#context-compaction-and-cursor-usage).
|
|
358
358
|
|
|
359
359
|
## 11. Standard local gates
|
|
360
360
|
|
|
@@ -391,7 +391,7 @@ Use this list to choose meaningful changed-behavior coverage, not to trigger a f
|
|
|
391
391
|
- Native replay cards beyond read, especially shell/edit/write cards, when those renderers change.
|
|
392
392
|
- Bridge question UI when `cursor_ask_question` changes.
|
|
393
393
|
- MCP timeout override behavior (3600s `callTool` default, 10s initialize/listTools default, and SDK-default unknown protocol stacks) when timeout code changes.
|
|
394
|
-
- SDK `semSearch` / `recordScreen` activity replay when those formatters change. There is no reliable local prompt that forces Cursor to call these built-in SDK tools on demand; regression is covered by `test/cursor-tool-transcript.test.ts`. Opportunistically confirm neutral `Cursor semantic search` / `Cursor screen recording` cards if a live run surfaces them.
|
|
394
|
+
- SDK `semSearch` / `recordScreen` activity replay when those formatters change. There is no reliable local prompt that forces Cursor to call these built-in SDK tools on demand; regression is covered by `test/cursor-tool-transcript-mcp-web.test.ts`. Opportunistically confirm neutral `Cursor semantic search` / `Cursor screen recording` cards if a live run surfaces them.
|
|
395
395
|
- Ambient Cursor setting-source behavior when startup filtering or local Cursor settings handling changes.
|
|
396
396
|
- Model discovery aliases/context variants when model-discovery code or Cursor SDK versions change.
|
|
397
397
|
|
|
@@ -22,20 +22,25 @@ Current implementation notes:
|
|
|
22
22
|
- Native request receipts retain canonical source-role/order scalars from public `SessionManager.buildSessionProjection()` and `convertToLlm()`, not prompt text. Only exact equivalence with the actual provider request enables native input selection. Hidden `display:false` notices remain model-visible background context, never genuine user submissions. Pending native input travels together in bootstrap/incremental prompts and remains required under transcript budgeting. Custom-only continuation has no new user request or attachment; historical bootstrap requests are explicitly history. Cloud fresh prompts select the same pending input group without previous requests; bootstrap handoff preserves history with the same native-origin selection. Origin means the persisted native source, not a role-only rewrite yielding identical LLM content. If request-local transforms change payload/order (including blocked-image conversion), origin is unavailable: preserve conservative last-converted-user/reset behavior rather than guessing. This is not universal final-preconversion provenance.
|
|
23
23
|
- Image payload forwarding for request-equivalent native inputs sends images only from the latest pending native user message, even with trailing custom notices. Other/unmatched contexts retain last-converted-user selection. If the latest user turn is plain text after an earlier image turn, the transcript keeps an `[image omitted from transcript]` placeholder but no image bytes are sent to Cursor. The prompt explicitly tells Cursor that prior image bytes are unavailable and to ask the user to reattach or describe a prior image when needed. Carrying images forward across turns remains a future product decision because it affects token cost, privacy, stale visual context, and expected multimodal follow-up behavior.
|
|
24
24
|
- Shared SDK initialization resolves the native platform package relative to `@cursor/sdk` before import and sets `CURSOR_TREE_SITTER_VENDOR_DIR` only when unset and both vendored parser entrypoints exist. Explicit overrides remain SDK-owned. This repairs sibling-install native lookup. Static SDK and bridge imports additionally repair the compiled-Bun extension graph; the packed extension was qualified in official compiled Pi 1.0.3 on macOS arm64 with isolated empty credentials and a fake key. Embedded Windows missing-export behavior remains unqualified.
|
|
25
|
-
- The selected vendor path is process-wide and inherited by shell commands and child Pi processes. A child using another install/worktree preserves the inherited value as an explicit override, even when stale. Unset it in the child's environment to rediscover that install's parser, or provide its absolute vendor path; see [native parser troubleshooting](
|
|
25
|
+
- The selected vendor path is process-wide and inherited by shell commands and child Pi processes. A child using another install/worktree preserves the inherited value as an explicit override, even when stale. Unset it in the child's environment to rediscover that install's parser, or provide its absolute vendor path; see [native parser troubleshooting](reference.md#native-shell-parsing-or-module-loading-fails).
|
|
26
26
|
- Provider and discovery errors retain bounded, scrubbed name/code/message/cause fields without serializing arbitrary error objects. Module-loading and ambiguous session-authentication failures do not imply a rejected API key. Explicit key rejection retains actionable setup guidance; no automatic reload or authentication recovery is performed.
|
|
27
|
-
- Exact `@cursor/sdk@1.0.
|
|
27
|
+
- Exact `@cursor/sdk@1.0.40` is an unbundled package dependency; users do not need a global SDK install. This package requires Node 24+ and official Pi 0.87.1+, while optional published Pi and TypeBox peer dependencies use `"*"` ranges per Pi guidance. Direct development Pi dependencies and their lockfile-resolved Pi cohort are 1.1.0 reproducible snapshots, not qualification targets or user runtime upgrades; nested host TypeBox remains 1.3.27; the extension's root TypeBox validation baseline is 1.3.36.
|
|
28
28
|
- Native usage contains coherent current-context components only. The origin-owned ledger records every raw turn, original wait/handle snapshots, terminal success/error/abort/abandon, and bounded public whole-agent `getUsage()` observations separately. Cumulative bills never enter native usage. Request floors use actual native source chronology and projection equivalence, not text or clocks; see [Context and accounting boundaries](#context-and-accounting-boundaries).
|
|
29
29
|
- Cursor owns one public native Provider with native API-key login and a small resolver for stored/provider-env, runtime, composed config, and ambient credentials. Startup and auth mutations are cache-only, retaining known cached/fallback identities. `PI_CURSOR_HIDE_MODELS_WHEN_LOGGED_OUT=1` hides only unauthenticated picker/list availability, not CLI selection or login. Discovery receives explicit effective native auth and never guesses a default auth store. `/cursor-refresh-models` uses native registry refresh/publication. Provider turns keep Pi's resolved `options.apiKey`; non-secret Cursor-only settings remain separate.
|
|
30
30
|
- Cursor Cloud repository overrides accept only HTTPS repository URLs without userinfo, query parameters, or fragments. Invalid values fail during preflight before `Agent.create()`, messages never echo the supplied URL, and shared provider/maintainer scrubbing removes URL/SCP-style userinfo defensively.
|
|
31
31
|
- Cursor Cloud requires a persisted pi session. Immediately after remote `Agent.create()` returns, before debug work or abort checks, the provider appends a branch-local pi lifecycle entry, fsyncs the existing Pi session JSONL anchor through a read-write descriptor, and then fsyncs a newline-framed sidecar keyed by the stable pi session ID (POSIX mode `0600`; Windows inherits the user session directory ACL) in the session directory. Journal creation is exclusive, and existing append/read opens reject symlinks and require matching regular-file descriptor/path identity before using the descriptor. Existing session files use the exact lifecycle entry ID as anchor; fileless first turns use an orphan marker that a same-session-ID restart durably claims onto exactly one matching or replacement branch, surviving the timestamped path change and later JSONL creation without granting sibling access; returned run IDs are fsynced before abort/wait handling, readers skip malformed/truncated lines independently, and branch-only history events are reduced after deduplicated sidecar history, and tombstones are tracked before records exist. A durable sidecar result remains authoritative if its optional Pi mirror append fails; if the sidecar result itself fails, the prior durable intent remains pending and blocks retry rather than claiming success. `--no-session` and durable-ledger failures fail closed before send, while post-send ledger failure requests bounded cancellation. `/cursor-cloud` always validates the embedded session ID, accepts only null anchors while truly fileless, and reconciles each orphan to one branch before listing or mutation. Successive record events merge run/branch metadata monotonically even when mirrored sources arrive out of order. Archive/delete require resolved Cursor auth, fsync a durable intent before the SDK call, and fsync a durable success result afterward; unresolved intent blocks repeat mutation and requires dashboard inspection.
|
|
32
|
-
- Local agents pass `settingSources: ["all"]` by default so Cursor MCP servers, plugin tools, project/user settings, and related Cursor-native capabilities are available. Users can narrow loading with a comma-separated list such as `PI_CURSOR_SETTING_SOURCES=project,user,plugins`, or disable ambient setting sources with `PI_CURSOR_SETTING_SOURCES=none`. `/cursor-refresh-config` calls the current pooled SDK agent's `agent.reload()` to refresh filesystem Cursor config without recreating the agent.
|
|
32
|
+
- Local agents pass `settingSources: ["all"]` by default so Cursor MCP servers, plugin tools, project/user settings, and related Cursor-native capabilities are available. Users can narrow loading with a comma-separated list such as `PI_CURSOR_SETTING_SOURCES=project,user,plugins`, or disable ambient setting sources with `PI_CURSOR_SETTING_SOURCES=none`. `/cursor-refresh-config` calls the current pooled SDK agent's `agent.reload()` to refresh filesystem Cursor config without recreating the agent.
|
|
33
|
+
- The shared process sink filters complete, verified timestamp/INFO inventory and completion frames plus exact hook-compatibility notices. Unsupported and unknown Claude Code events match only the SDK's two complete warning templates, with a variable quoted event name; new event names do not require an allowlist update, and unrelated `[hooks]` warnings remain visible. Extension initialization installs one process-lifetime lease before session/turn work. It remains active across idle time, detached rules/skills loads, filesystem watchers, config refresh, teardown, the asynchronous interval during `/reload`, and failed reloads. This is deliberate: SDK service disposal does not await every pending load, and Pi's old shutdown/new startup events cannot safely hand off a lease when reload fails or removes Cursor. The narrowly classified sink therefore remains installed until Pi exits, even if Cursor is removed from the active extension set. Reloaded module copies share the sink, refcounts and both async attribution scopes through one versioned process singleton; repeated initialization and concurrent sessions do not stack permanent leases. Existing provider/live-run/script leases remain temporary and nested, so direct provider/script use without extension initialization still restores on its own cleanup. The sink also catches pre-bound SDK console methods.
|
|
34
|
+
- A new write invocation starts a frame after unrelated partial output has forwarded, so an unterminated native Pi footer/prompt does not admit the following SDK diagnostic. stdout/stderr framing is independent; outside the retained creation-only mute around `Agent.create()`, unrecognized output retains its original bytes, callbacks and delegated backpressure. The creation mute expires when its operation returns/settles, so detached async work cannot inherit an indefinite mute for unrelated warnings. Only possible diagnostic prefixes buffer (up to 16 KiB); undecided tails flush on final restoration (process exit for an initialized extension) and oversized candidates pass through. Final restoration only removes stream methods still owned by this filter; later third-party wrappers remain installed and retained filter methods become transparent delegates.
|
|
35
|
+
- Outside-root local-plugin symlink rejection and the exact native-parser-unavailable warning escape the creation mute and route through the request's async owner to a bounded native notification (stderr in non-UI modes) and a `pi-cursor-sdk:capability-notice` non-model session entry, at most once per kind per request. Notices contain fixed recovery guidance, not raw paths or arbitrary SDK metadata. Unowned maintainer scripts retain these meaningful diagnostics. Outside the creation mute, real plugin/hook/config/parser/ripgrep failures stay visible; the plugin confinement guard, settings sources and native parser setup are unchanged. Text alone cannot distinguish an unrelated emitter printing an identical complete SDK frame; strict attribution requires a future public SDK logger/sink API.
|
|
33
36
|
- On `cursor/*` models, pi-cursor-sdk removes only pi-generated `<project_instructions>` blocks that overlap the effective Cursor `settingSources`: `user` for `~/.pi/agent/AGENTS.md`; `project` for discovered repo/parent `AGENTS.md` and `CLAUDE.md` (verified Cursor behavior: local agents load project `AGENTS.md` and `CLAUDE.md`). `~/.pi/agent/CLAUDE.md` is not removed (Cursor user layer uses `~/.claude/CLAUDE.md`). Blocks are removed by exact pi serialization match from structured `contextFiles` via the `before_agent_start` hook, not in `buildCursorPrompt` sanitization. Suppression is skipped with `-nc`, `PI_CURSOR_SETTING_SOURCES=none`, narrowed sources such as `plugins` that omit the matching layer, or `PI_CURSOR_PRESERVE_PI_AGENTS_MD=1`. Switching away from a Cursor model restores pi's full context block on the next user message.
|
|
34
37
|
- Cursor SDK models are treated as thinking-capable even when pi reports `thinking=no`; that pi column only means the SDK did not expose a pi-controllable thinking parameter for that model.
|
|
35
38
|
- Cursor-side thinking remains visible through pi's native thinking rendering when the Cursor SDK emits thinking or summary deltas.
|
|
36
|
-
- Local Cursor agents get two tool surfaces. First, Cursor keeps the Cursor SDK local-agent tool surface plus configured Cursor settings, plugins, and Cursor MCP servers. Second, pi-cursor-sdk exposes active pi tools through a default-on, tokenized loopback stable MCP v2 bridge when bridgeable tools exist. The bundled bridge runtime closure is `@modelcontextprotocol/server@2.3.1`, `@modelcontextprotocol/hono@2.0.2`, `hono@4.13.13`, and `@hono/node-server@2.1.3`; it binds loopback and validates Hono `Host` and `Origin` headers.
|
|
39
|
+
- Local Cursor agents get two tool surfaces. First, Cursor keeps the Cursor SDK local-agent tool surface plus configured Cursor settings, plugins, and Cursor MCP servers. Second, pi-cursor-sdk exposes active pi tools through a default-on, tokenized loopback stable MCP v2 bridge when bridgeable tools exist. The bundled bridge runtime closure is `@modelcontextprotocol/server@2.3.1`, `@modelcontextprotocol/hono@2.0.2`, `hono@4.13.13`, and `@hono/node-server@2.1.3`; it binds loopback and validates Hono `Host` and `Origin` headers. The default owns IPv4 only; opt-in `localhost` owns both explicit loopbacks on one port before advertising a URL, fails closed without IPv6, and freezes the advertised host/listener policy for that shared server lifetime. Failed run startup releases its run state and partial listeners without changing sibling routes.
|
|
40
|
+
- Ordinary LOCAL turns preserve the SDK default tools unless `PI_CURSOR_NATIVE_TOOLS=0` (canonical boolean parsing) opts into public `AgentOptions.tools: ["mcp"]`. This excludes all other main tools, especially native task/subagents whose toolsets are independently curated. Capture before drain/acquisition; forward identically to create/resume and append a compatibility suffix only for opt-out, preserving default durable bytes. Cloud fails before SDK/lifecycle side effects with explicit native disable; isolated LOCAL summaries retain `tools: []`. Bootstrap/plan/tail guidance and manifest must not advertise unavailable host tools under opt-out. Pi builtins still require explicit `PI_CURSOR_EXPOSE_BUILTIN_TOOLS=1`. MCP-only retains ambient configured Cursor MCP unless setting sources are disabled; it is not Pi-only, a sandbox, or an OS security boundary. Permitted Pi/MCP tools can execute arbitrary work.
|
|
41
|
+
- Pi `tool_call` hooks govern actual bridged Pi execution, not native Cursor execution already completed before replay. Hiding replay/registration, disabling the bridge, or narrowing Pi tools is not native enforcement; do not fabricate pre-execution callbacks. Before any drain consumption/result delivery, compare the pending run's captured native policy under its drain lease; reject opposite-policy continuation without implicit cancellation and with restore-or-`/cursor-abort` guidance. `/cursor-abort` is a registered explicit local command using the current bound runtime scope and existing live-run release owner; it sends no model turn and never releases a sibling scope or Cloud agent. Only when that owned run exists and the current Cursor Pi turn is active does the command also invoke public `ctx.abort()` before release; no-owned-run and non-Cursor Pi turns are not aborted. Idle native Pi abort has no active signal/event after a settled mismatch, so it cannot be advertised as recovery for the old Cursor waiter. On explicit active provider abort before preparation, turn-owned cleanup releases only the pending live run from that request's captured runtime scope; ordinary errors and requests aborted before acquiring the turn lease do not release it. Already-executed work is not undone; no retroactive promise is made.
|
|
37
42
|
- `buildCursorPiToolBridgeSnapshot()` is the runtime capability source for pi bridge tools. It snapshots `pi.getActiveTools()` and `pi.getAllTools()`, carries pi 0.77+ per-tool `promptGuidelines` into bridge MCP descriptions, filters internal replay names, hides overlapping built-in pi tools (`read`, `bash`, `write`, `edit`, `grep`, `find`, `ls`) unless `PI_CURSOR_EXPOSE_BUILTIN_TOOLS=1`, and creates collision-safe MCP names such as `pi__sem_reindex`. Cursor discovers the current run's exposed bridge tools through MCP `listTools`. Bootstrap prompts include a compact callable-surface manifest from `buildCursorToolManifestText()` by default (`PI_CURSOR_TOOL_MANIFEST=1`); disable with `PI_CURSOR_TOOL_MANIFEST=0`. There is no per-turn visible tool list, status manifest, or footer manifest. User-facing summary: [Cursor tool surfaces in pi](./cursor-tool-surfaces.md).
|
|
38
|
-
- Prompt text is the primary provider/bridge contract. Bootstrap prompts carry a short boundary block plus the callable-surface manifest by default (`PI_CURSOR_TOOL_MANIFEST=1`). MCP `listTools` descriptions use a one-line pointer to the bootstrap prompt instead of repeating the full contract (`buildCursorPiBridgeMcpToolDescription()`). Cursor must call the exposed `pi__*` MCP name, not the real pi tool name shown in pi history or transcripts.
|
|
43
|
+
- Prompt text is the primary provider/bridge contract. Bootstrap prompts carry a short boundary block plus the callable-surface manifest by default (`PI_CURSOR_TOOL_MANIFEST=1`). MCP `listTools` descriptions use a one-line pointer to the bootstrap prompt instead of repeating the full contract (`buildCursorPiBridgeMcpToolDescription()`). Cursor must call the exposed `pi__*` MCP name, not the real pi tool name shown in pi history or transcripts. Exposed `pi__*` tools take preference for their described capabilities, including delegation or MCP work, using the current schemas and prompt guidelines rather than historical tool aliases; Cursor-configured MCP and Cursor-native subagents remain fallbacks when no matching exposed Pi capability is available. Pi emits and executes the real pi tool name. Maintainer debug: `/cursor-tools` prints bridge/manifest enablement, effective `PI_CURSOR_SETTING_SOURCES`, and current-registry exposure eligibility with builtin filtering, not a live-run surface or health check. The shared prompt tail retains GitHub issue/PR comment guidance on both bootstrap and incremental sends; this is model guidance, not an execution permission guard.
|
|
39
44
|
- The provider also registers `cursor_ask_question` for Cursor models when the bridge is on and `PI_CURSOR_ASK_QUESTION=1`. While the tool awaits pi UI input it emits package event `pi-cursor-sdk:ask-question:blocked` with `{ active: true }` and clears `{ active: false }` in `finally`; the tool runs with `executionMode: "sequential"` so parallel sibling calls cannot overlap dialogs. Cursor sees it as `pi__cursor_ask_question`, and pi executes it through the normal tool path. The control is off by default so Cursor states an assumption instead of prompting. In non-UI modes it reports that UI is unavailable so Cursor can state a default assumption instead. When pi has visible Agent Skills loaded, the provider rewrites the skill catalog for Cursor and registers `cursor_activate_skill` as `pi__cursor_activate_skill`; pi executes it through the normal tool path so Cursor can load the full `SKILL.md` and skill resource list for the current pi-loaded skill source of truth. `PI_CURSOR_PI_TOOL_BRIDGE=0` disables the local bridge, including question and skill activation bridging. Cloud Cursor agents remain out of scope for the bridge.
|
|
40
45
|
- The bridge queues MCP calls, emits provider `toolcall_*` events, waits for matching pi `toolResult` messages by `toolCallId`, resolves the result back into the same live Cursor SDK run without creating a new `Agent`, and never calls tool `execute()` handlers directly. The same-run resume invariant holds unless the run was disposed, aborted, or cancelled.
|
|
41
46
|
- Pending pi bridge calls keep their owning local Cursor live run active, including queued/emitted calls and pi execution awaiting human input. The last matching settlement restarts the full idle interval (five minutes by default) after the provider drain lease ends, including when cleanup was armed before the lease. Lease/queue/pending state is rechecked at expiry, and the per-run observer is detached on release/replacement. A pending call in another session does not delay cleanup. Explicit abort/shutdown/reset/compaction and the fail-closed MCP/bridge deadline (one hour by default) remain effective; display-only native replay abandonment is unchanged.
|
|
@@ -98,7 +103,7 @@ Cursor SDK is the source of truth for Cursor model IDs and Cursor-supported para
|
|
|
98
103
|
|
|
99
104
|
Startup registers a known cached/fallback baseline without catalog/network calls. The SDK remains in the static extension graph for compiled-Bun loading; cache-only does not promise zero SDK module evaluation. Native cache-only refresh follows real login/logout/rotation/runtime-key mutations; the genuine extension context supplies composed config auth once bound. There is no polling, replayed auth lifecycle event, or independent CLI parser.
|
|
100
105
|
|
|
101
|
-
Known host limitation: official Pi 1.0.4
|
|
106
|
+
Known host limitation: official Pi 1.0.4 and 1.1.0 have a demonstrated startup refresh-admission/readiness race that can temporarily leave fallback models and an incorrect missing-key warning despite a matching authenticated cache. The shared-owner repair is merged in `fitchmultz/pi` via [#190](https://github.com/fitchmultz/pi/pull/190); this is not an official-host fix. See [#337](https://github.com/fitchmultz/pi-cursor-sdk/pull/337) and the [native auth/catalog fixture boundary](./cursor-testing-lessons.md#native-authcatalog-fixture-boundary). It is separate from Cursor service authentication failures and the compiled-loader repair.
|
|
102
107
|
|
|
103
108
|
Effective auth follows native precedence: runtime override, stored API-key credential (key then provider-scoped `env.CURSOR_API_KEY` then ambient), composed `models.json` key when no stored credential exists, then ambient `CURSOR_API_KEY`. Placeholder normalization does not shadow composed config. An unsupported stored credential type remains unconfigured. Native logout removes both stored credentials and the runtime override; surviving env/config auth prevents false orphan-cache deletion. Same-process environment/config changes are picked up by native registry refresh, not a watcher.
|
|
104
109
|
|
|
@@ -154,7 +159,11 @@ CURSOR_API_KEY="your-key" npm run refresh:cursor-snapshots -- --write \
|
|
|
154
159
|
|
|
155
160
|
Both modes call `Cursor.models.list({ apiKey })` and use the same sanitizer and stable sort. `--check` byte-compares `src/cursor-fallback-models.generated.ts` without writing; `--write` refreshes it and updates `src/bundled-context-windows.ts` only when `--context-windows` is provided. Context-window inputs are limited to current selectable model IDs; redundant default `:fast`/`:slow` aliases collapse to one key, conflicting equivalent selections fail generation, and stale or ambiguous aliases are omitted. Generated provenance and command output record the installed `@cursor/sdk` version and model count. The script prints model IDs/counts only and scrubs known auth material from SDK errors; it must not print or store API keys. Review generated diffs before committing because Cursor can change aliases, defaults, and parameter meanings.
|
|
156
161
|
|
|
157
|
-
The authenticated SDK 1.0.37 catalog-only refresh on 2026-10-07 returned 46 models, adding Claude Haiku 5.5 (`claude-haiku-5-5`) while preserving all 45 model records from the SDK 1.0.36 capture on 2026-10-06.
|
|
162
|
+
The authenticated SDK 1.0.37 catalog-only refresh on 2026-10-07 returned 46 models, adding Claude Haiku 5.5 (`claude-haiku-5-5`) while preserving all 45 model records from the SDK 1.0.36 capture on 2026-10-06. That refresh's generated header recorded SDK 1.0.37 and the 46-model count. Haiku 5.5 exposes 20 variants across `thinking=false|true`, `context=300k|1m`, and `reasoning_effort=low|medium|high|xhigh|max`; its default is `thinking=true`, `context=1m`, `reasoning_effort=high`. The 33 bundled checkpoint-derived entries were unchanged; this refresh started no Agent/model turn or Cloud run and does not qualify effective checkpoint limits.
|
|
163
|
+
|
|
164
|
+
The authenticated SDK 1.0.37 catalog-only refresh on 2026-10-22 returned 48 models, adding Gemini 4 Argon (`gemini-4-argon`, low/medium/high reasoning with medium default) and Muse Wonder (`muse-wonder-1.0`, 300k/1m contexts and minimal through max reasoning, default 1m/xhigh) while preserving the prior 46 records. Existing generic context/thinking selection handles both; neither adds fast controls or aliases.
|
|
165
|
+
|
|
166
|
+
The current snapshot is the authenticated SDK 1.0.40 catalog-only refresh on 2026-10-31: 49 models, adding DeepSeek V4.1 Flash 1017 (`deepseek-v41-flash-1017`, reasoning effort none/low/medium/high/max with max default) while retaining the prior 48 model IDs. All 20 Claude Opus 5.5 variants now include `cyber=false`. Preserve SDK variant labels verbatim. The 33 checkpoint-derived entries remain unchanged; this refresh started no Agent/model turn or Cloud run and establishes no new checkpoint limit.
|
|
158
167
|
|
|
159
168
|
Dated evidence for Cursor's assistant-visible, model-specific system text and reconstructed tool guidance lives in [Cursor System Prompts and Tool Guidance — 2026-08-02](https://github.com/fitchmultz/pi-cursor-sdk/blob/main/docs/evidence/cursor-system-prompts-2026-08-02/README.md). Keep that evidence separate from pi-cursor-sdk's own bootstrap prompt: Cursor persists its base system message in the local SDK checkpoint, while this extension sends Pi context and bridge instructions as user content.
|
|
160
169
|
|
|
@@ -287,6 +296,7 @@ Cursor extension controls:
|
|
|
287
296
|
| Set SDK mode | `/cursor-mode agent\|plan` | Cursor model selected |
|
|
288
297
|
| Set local HTTP transport | `/cursor-http on\|off\|toggle` | local Cursor runtime |
|
|
289
298
|
| Refresh filesystem Cursor config | `/cursor-refresh-config` | Cursor model selected and an SDK agent may exist |
|
|
299
|
+
| Explicitly abort pending local Cursor work | `/cursor-abort` | current session owns a pending local Cursor run |
|
|
290
300
|
| Show tool surfaces (maintainer) | `/cursor-tools` | Cursor model selected |
|
|
291
301
|
| View, refresh, or export accounting | `/cursor-usage [refresh\|export <new-file.json>\|help]` | current branch has recorded Cursor facts |
|
|
292
302
|
|
|
@@ -424,7 +434,7 @@ The full resolved selection is submitted for both runtimes. **SDK 1.0.37 LOCAL c
|
|
|
424
434
|
|
|
425
435
|
Nonempty definitions add a SHA256 pool-key suffix over name-sorted fixed-shape content with sorted model params. Definition/property/param order does not affect identity; text/model/param/add/remove changes do. No-feature keys stay byte-identical. Each turn captures one definition set, including forced rebootstrap; next-turn config edits repool and exact persisted-key matching rejects stale resumes. Existing persistence stores the key digest only, not definition prompts.
|
|
426
436
|
|
|
427
|
-
LOCAL compact/tree summary constructors remain fresh, tool-free, definition-free and ephemeral, without conversation resume/lineage. Bug-report streams have no public summary-purpose hook and retain ordinary capabilities. Bridge snapshots/manifests/routing are unchanged
|
|
437
|
+
LOCAL compact/tree summary constructors remain fresh, tool-free, definition-free and ephemeral, without conversation resume/lineage. Bug-report streams have no public summary-purpose hook and retain ordinary capabilities. Bridge snapshots/manifests/routing are unchanged; an exposed Pi delegation capability stays preferred under its actual current name, schema and prompt guidelines. User configuration and limits: [SDK-native custom agents](reference.md#sdk-native-custom-agents).
|
|
428
438
|
|
|
429
439
|
## Fast Behavior
|
|
430
440
|
|
|
@@ -7,7 +7,7 @@ pi-cursor-sdk has two separate pi-facing paths plus Cursor's own local-agent too
|
|
|
7
7
|
1. **Local pi MCP bridge:** default-on for local Cursor agents. It exposes the current pi session's bridgeable active tools to Cursor through a tokenized `127.0.0.1` MCP endpoint, excluding internal Cursor replay activity names and, by default, overlapping built-in pi tools (`read`, `bash`, `write`, `edit`, `grep`, `find`, `ls`). When Cursor calls one of those MCP tools, pi executes the real pi tool through the normal pi tool path.
|
|
8
8
|
2. **Cursor native tool replay:** display-only. It renders completed Cursor SDK tool activity as pi-native-looking cards using recorded Cursor results.
|
|
9
9
|
|
|
10
|
-
This document is about replay. Replay is not execution and is not the local pi bridge.
|
|
10
|
+
This document is about replay. Replay is not execution and is not the local pi bridge. Pi `tool_call` hooks cannot govern native work that already ran before replay; blocking or rewriting its card cannot prevent or transform the original command. Hiding replay or disabling the bridge is not enforcement. For the opt-in public SDK MCP-only local surface, its ambient-MCP limits, and why it is not a sandbox, see [MCP-only local turns](./cursor-tool-surfaces.md#opt-in-mcp-only-local-turns).
|
|
11
11
|
|
|
12
12
|
## Live bridge vs replay
|
|
13
13
|
|
|
@@ -197,7 +197,7 @@ Lifecycle rules:
|
|
|
197
197
|
|
|
198
198
|
As Cursor SDK tool completions arrive, the extension mirrors native Codex ordering by ending a tool-use turn, letting pi render the recorded tool results, then continuing with live post-tool Cursor thinking/text, later Cursor tool batches, or Cursor's final answer as the next assistant turn. For plan-mode runs, neutral Cursor plan/todo cards can therefore appear before the final Cursor plan text.
|
|
199
199
|
|
|
200
|
-
Bridged pi tool calls follow the same visible pi `toolUse` turn shape, but they are real pi tool executions rather than replayed Cursor results. Local usage accounting uses per-turn raw Cursor SDK `turn-ended.usage` when available before the corresponding pi turn is emitted, including cache read/write fields; observed local full-prompt/partition semantics map into disjoint pi input/cache components and set `usage.totalTokens = inputTokens + outputTokens` for occupancy (distinct from published SDK `toTokenUsage` additive totals). If the local SDK reports no safely attributable usage in time, the provider estimates the current context; its disjoint input/cache/output components sum to `usage.totalTokens`. Later raw usage is retained by the separate ledger, not applied retroactively to emitted pi messages. Public billed facts never enter native occupancy. Cloud raw telemetry is retained without assuming LOCAL normalization, while native Cloud messages use approximate context accounting. See [context, compaction, and Cursor usage](
|
|
200
|
+
Bridged pi tool calls follow the same visible pi `toolUse` turn shape, but they are real pi tool executions rather than replayed Cursor results. Local usage accounting uses per-turn raw Cursor SDK `turn-ended.usage` when available before the corresponding pi turn is emitted, including cache read/write fields; observed local full-prompt/partition semantics map into disjoint pi input/cache components and set `usage.totalTokens = inputTokens + outputTokens` for occupancy (distinct from published SDK `toTokenUsage` additive totals). If the local SDK reports no safely attributable usage in time, the provider estimates the current context; its disjoint input/cache/output components sum to `usage.totalTokens`. Later raw usage is retained by the separate ledger, not applied retroactively to emitted pi messages. Public billed facts never enter native occupancy. Cloud raw telemetry is retained without assuming LOCAL normalization, while native Cloud messages use approximate context accounting. See [context, compaction, and Cursor usage](reference.md#context-compaction-and-cursor-usage).
|
|
201
201
|
|
|
202
202
|
For shell replay, completed `stdout` / `stderr` remain the primary source. While exactly one shell call is active, the provider also emits a bounded scrubbed preview of the first few `shell-output-delta` stdout/stderr chunks so long-running commands show visible progress before completion. If a successful completed shell result is empty, the replay card uses unambiguous buffered delta data as display-only fallback data. Overlapping shell calls make delta attribution ambiguous, so those fallback/progress deltas are dropped rather than guessed. `(no output)` is kept only when no completed output or safe delta fallback is available.
|
|
203
203
|
|
|
@@ -6,7 +6,7 @@ This workflow is the canonical repo path for verifying Cursor SDK tool replay th
|
|
|
6
6
|
|
|
7
7
|
Use it when changed replay-card behavior needs new visual proof not covered by offline rendering contracts or valid retained evidence. Text logs and JSONL are not enough for visual-parity claims: keep PNGs for the exact prompt, and before/after PNGs for a rendering comparison. Reuse a valid retained baseline instead of paying to recreate it.
|
|
8
8
|
|
|
9
|
-
Current validation baseline: Node 24+, exact `@cursor/sdk@1.0.
|
|
9
|
+
Current validation baseline: Node 24+, exact `@cursor/sdk@1.0.40`, official Pi 0.87.1/latest and current `fitchmultz/pi` main. Development Pi packages `@earendil-works/pi-ai`, `@earendil-works/pi-coding-agent`, and `@earendil-works/pi-tui` are exact locked 1.1.0 snapshots, with host TypeBox 1.3.27. These locked development snapshots are not qualification targets; resolve latest stable official Pi and current `fitchmultz/pi` main once per workflow run. Optional published Pi and TypeBox peer dependencies use `"*"` ranges per Pi guidance.
|
|
10
10
|
|
|
11
11
|
## Cursor SDK 1.0.17 / pi 0.79.0 cutover visual record
|
|
12
12
|
|