pi-cursor-sdk 0.5.2 → 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.
Files changed (114) hide show
  1. package/CHANGELOG.md +66 -1
  2. package/README.md +62 -795
  3. package/dist/context.js +13 -7
  4. package/dist/cursor-agents-context.js +6 -6
  5. package/dist/cursor-api-key.js +1 -1
  6. package/dist/cursor-bridge-contract.js +1 -13
  7. package/dist/cursor-cloud-local-state.js +0 -1
  8. package/dist/cursor-config.js +1 -17
  9. package/dist/cursor-custom-subagent-definitions.js +1 -1
  10. package/dist/cursor-fallback-models.generated.js +665 -1
  11. package/dist/cursor-live-run-coordinator.js +1 -0
  12. package/dist/cursor-local-agent-ownership.js +17 -0
  13. package/dist/cursor-mcp-timeout-override.js +7 -18
  14. package/dist/cursor-native-tool-display-replay.js +5 -5
  15. package/dist/cursor-native-tools.js +11 -0
  16. package/dist/cursor-pi-tool-bridge-diagnostics.js +4 -0
  17. package/dist/cursor-pi-tool-bridge-env.js +25 -2
  18. package/dist/cursor-pi-tool-bridge-run.js +10 -3
  19. package/dist/cursor-pi-tool-bridge-server.js +98 -48
  20. package/dist/cursor-pi-tool-bridge-snapshot.js +1 -1
  21. package/dist/cursor-pi-tool-bridge.js +4 -12
  22. package/dist/cursor-provider-errors.js +2 -8
  23. package/dist/cursor-provider-live-run-drain.js +8 -3
  24. package/dist/cursor-provider-overflow.js +7 -21
  25. package/dist/cursor-provider-turn-prepare.js +8 -1
  26. package/dist/cursor-provider-turn-runner.js +17 -6
  27. package/dist/cursor-provider-turn-summary.js +1 -1
  28. package/dist/cursor-provider.js +1 -4
  29. package/dist/cursor-runtime-state.js +3 -3
  30. package/dist/cursor-sdk-event-debug.js +2 -2
  31. package/dist/cursor-sdk-output-filter.js +1 -1
  32. package/dist/cursor-sdk-process-error-guard.js +1 -2
  33. package/dist/cursor-session-agent-cleanup.js +10 -1
  34. package/dist/cursor-session-agent-lifecycle.js +2 -2
  35. package/dist/cursor-session-agent-lineage.js +11 -7
  36. package/dist/cursor-session-agent-resume.js +15 -9
  37. package/dist/cursor-session-agent.js +28 -15
  38. package/dist/cursor-session-scope.js +28 -21
  39. package/dist/cursor-session-settings.js +3 -3
  40. package/dist/cursor-session-store.js +2 -2
  41. package/dist/cursor-state.js +26 -29
  42. package/dist/cursor-tool-lifecycle.js +0 -3
  43. package/dist/cursor-tool-manifest.js +3 -1
  44. package/dist/cursor-tool-presentation-registry.js +2 -4
  45. package/dist/cursor-tool-visibility.js +0 -3
  46. package/dist/cursor-transcript-tool-specs.js +1 -2
  47. package/dist/index.js +21 -0
  48. package/dist/model-discovery.js +8 -10
  49. package/dist/model-list-cache.js +4 -9
  50. package/docs/cursor-live-smoke-checklist.md +4 -4
  51. package/docs/cursor-model-ux-spec.md +36 -19
  52. package/docs/cursor-native-tool-replay.md +8 -5
  53. package/docs/cursor-native-tool-visual-audit.md +1 -1
  54. package/docs/cursor-testing-lessons.md +39 -9
  55. package/docs/cursor-tool-surfaces.md +48 -4
  56. package/docs/development.md +93 -0
  57. package/docs/platform-smoke-implementation.md +3 -2
  58. package/docs/reference.md +806 -0
  59. package/package.json +13 -11
  60. package/scripts/platform-smoke/artifact-secrets.mjs +13 -4
  61. package/scripts/platform-smoke/local-resume-runner.mjs +0 -1
  62. package/scripts/platform-smoke/target-runtime.mjs +4 -2
  63. package/scripts/platform-smoke/targets.mjs +0 -1
  64. package/shared/cursor-sdk-output-filter.d.mts +2 -0
  65. package/shared/cursor-sdk-output-filter.mjs +92 -28
  66. package/shared/cursor-sensitive-text.d.mts +1 -0
  67. package/shared/cursor-sensitive-text.mjs +7 -0
  68. package/src/context.ts +18 -11
  69. package/src/cursor-agents-context.ts +6 -6
  70. package/src/cursor-api-key.ts +1 -1
  71. package/src/cursor-bridge-contract.ts +1 -15
  72. package/src/cursor-cloud-local-state.ts +0 -2
  73. package/src/cursor-config.ts +1 -24
  74. package/src/cursor-custom-subagent-definitions.ts +1 -1
  75. package/src/cursor-fallback-models.generated.ts +665 -1
  76. package/src/cursor-live-run-coordinator.ts +5 -0
  77. package/src/cursor-local-agent-ownership.ts +18 -0
  78. package/src/cursor-mcp-timeout-override.ts +7 -31
  79. package/src/cursor-native-tool-display-replay.ts +4 -6
  80. package/src/cursor-native-tools.ts +14 -0
  81. package/src/cursor-pi-tool-bridge-diagnostics.ts +17 -0
  82. package/src/cursor-pi-tool-bridge-env.ts +31 -2
  83. package/src/cursor-pi-tool-bridge-run.ts +10 -3
  84. package/src/cursor-pi-tool-bridge-server.ts +89 -45
  85. package/src/cursor-pi-tool-bridge-snapshot.ts +0 -1
  86. package/src/cursor-pi-tool-bridge.ts +2 -18
  87. package/src/cursor-provider-errors.ts +2 -10
  88. package/src/cursor-provider-live-run-drain.ts +8 -2
  89. package/src/cursor-provider-overflow.ts +6 -24
  90. package/src/cursor-provider-turn-prepare.ts +10 -1
  91. package/src/cursor-provider-turn-runner.ts +17 -6
  92. package/src/cursor-provider-turn-summary.ts +1 -1
  93. package/src/cursor-provider.ts +0 -5
  94. package/src/cursor-runtime-state.ts +3 -3
  95. package/src/cursor-sdk-event-debug.ts +2 -2
  96. package/src/cursor-sdk-output-filter.ts +2 -1
  97. package/src/cursor-sdk-process-error-guard.ts +1 -3
  98. package/src/cursor-session-agent-cleanup.ts +6 -1
  99. package/src/cursor-session-agent-lifecycle.ts +2 -2
  100. package/src/cursor-session-agent-lineage.ts +10 -5
  101. package/src/cursor-session-agent-resume.ts +14 -7
  102. package/src/cursor-session-agent.ts +31 -15
  103. package/src/cursor-session-scope.ts +34 -20
  104. package/src/cursor-session-settings.ts +3 -3
  105. package/src/cursor-session-store.ts +2 -2
  106. package/src/cursor-state.ts +34 -41
  107. package/src/cursor-tool-lifecycle.ts +0 -4
  108. package/src/cursor-tool-manifest.ts +4 -1
  109. package/src/cursor-tool-presentation-registry.ts +3 -5
  110. package/src/cursor-tool-visibility.ts +0 -4
  111. package/src/cursor-transcript-tool-specs.ts +0 -3
  112. package/src/index.ts +18 -0
  113. package/src/model-discovery.ts +7 -9
  114. package/src/model-list-cache.ts +4 -10
@@ -1,7 +1,8 @@
1
- import { buildCursorToolManifestText, CURSOR_TOOL_MANIFEST_ENV, resolveCursorToolManifestEnabled, } from "./cursor-tool-manifest.js";
1
+ import { CURSOR_HOST_TOOL_MANIFEST_SUMMARY, CURSOR_TOOL_MANIFEST_ENV, resolveCursorToolManifestEnabled, } from "./cursor-tool-manifest.js";
2
2
  import { runCursorSessionAgentCleanupCommand } from "./cursor-session-agent-cleanup.js";
3
3
  import { CURSOR_HTTP1_ENTRY_TYPE, getStoredCursorHttp1Enabled, isCursorHttp1EntryData, setCursorHttp1GlobalPreferenceAuthoritative, setStoredCursorHttp1Enabled, } from "./cursor-http1.js";
4
- import { buildCursorPiToolBridgeSnapshot, CURSOR_PI_TOOL_BRIDGE_ENV, resolveCursorPiToolBridgeEnabled, } from "./cursor-pi-tool-bridge-snapshot.js";
4
+ import { buildCursorPiToolBridgeSnapshot, CURSOR_PI_TOOL_BRIDGE_ENV, resolveCursorPiToolBridgeBuiltinsEnabled, resolveCursorPiToolBridgeEnabled, } from "./cursor-pi-tool-bridge-snapshot.js";
5
+ import { CURSOR_PI_TOOL_BRIDGE_DEBUG_ENV } from "./cursor-pi-tool-bridge-diagnostics.js";
5
6
  import { CURSOR_SETTING_SOURCES_ENV, DEFAULT_CURSOR_SETTING_SOURCES, resolveCursorSettingSources, } from "./cursor-setting-sources.js";
6
7
  import { parseEnvBoolean } from "./cursor-env-boolean.js";
7
8
  import { isCursorModel } from "./cursor-model.js";
@@ -12,8 +13,7 @@ import { cursorSettingsScopeForContext, getCursorSessionSettings, registerCursor
12
13
  import { refreshSessionCursorAgentConfig } from "./cursor-session-agent.js";
13
14
  import { getCursorModelMetadata } from "./model-discovery.js";
14
15
  import { cursorFastDefaultsFromConfig, getCursorSdkUserConfigPath, loadCursorSdkUserConfig, mergeCursorSdkConfigForUpdate, resolveCursorFastDefault, resolveCursorLocalStoreRoot, updateCursorSdkConfig, } from "./cursor-config.js";
15
- import { CURSOR_RUNTIME_ENTRY_TYPE, formatCursorStatus, registerCursorCloudRuntimeControls, resetCursorRuntimeStateForTests, resolveCursorStatusRuntime, restoreCursorCliState, restoreSessionCursorRuntimeState, } from "./cursor-runtime-state.js";
16
- export { consumeCursorLocalForceOverride, getCursorCliConfig, getCursorSessionConfig, } from "./cursor-runtime-state.js";
16
+ import { formatCursorStatus, registerCursorCloudRuntimeControls, resetCursorRuntimeStateForTests, resolveCursorStatusRuntime, restoreCursorCliState, restoreSessionCursorRuntimeState, } from "./cursor-runtime-state.js";
17
17
  export const CURSOR_FOOTER_ENV = "PI_CURSOR_FOOTER";
18
18
  const FAST_ENTRY_TYPE = "cursor-fast-state";
19
19
  const MODE_ENTRY_TYPE = "cursor-mode-state";
@@ -21,10 +21,10 @@ const DEFAULT_CURSOR_AGENT_MODE = "agent";
21
21
  const authoritativeGlobalFastPreferenceIds = new Set();
22
22
  let globalFastPreferences = new Map();
23
23
  const invalidCursorModeNotifiedSessionScopeKeys = new Set();
24
- export function isCursorAgentMode(value) {
24
+ function isCursorAgentMode(value) {
25
25
  return value === "agent" || value === "plan";
26
26
  }
27
- export function parseCursorAgentMode(raw) {
27
+ function parseCursorAgentMode(raw) {
28
28
  if (typeof raw !== "string")
29
29
  return undefined;
30
30
  const mode = raw.trim();
@@ -42,14 +42,11 @@ function getCursorFastEntryModelId(data) {
42
42
  function isCursorModeEntryData(value) {
43
43
  return isCursorAgentMode(asRecord(value)?.mode);
44
44
  }
45
- function getConfigPath() {
46
- return getCursorSdkUserConfigPath();
47
- }
48
45
  function loadGlobalFastPreferences() {
49
46
  return cursorFastDefaultsFromConfig(loadCursorSdkUserConfig());
50
47
  }
51
48
  function saveGlobalFastPreference(modelId, fast) {
52
- updateCursorSdkConfig(getConfigPath(), (current) => {
49
+ updateCursorSdkConfig(getCursorSdkUserConfigPath(), (current) => {
53
50
  const fastDefaults = { ...asRecord(current.fastDefaults), [modelId]: fast };
54
51
  return {
55
52
  ...current,
@@ -58,7 +55,7 @@ function saveGlobalFastPreference(modelId, fast) {
58
55
  }, { newFileMode: 0o600 });
59
56
  }
60
57
  function saveGlobalCursorHttp1Enabled(enabled) {
61
- updateCursorSdkConfig(getConfigPath(), (current) => mergeCursorSdkConfigForUpdate(current, {
58
+ updateCursorSdkConfig(getCursorSdkUserConfigPath(), (current) => mergeCursorSdkConfigForUpdate(current, {
62
59
  local: { useHttp1ForAgent: enabled },
63
60
  }), { newFileMode: 0o600 });
64
61
  }
@@ -133,10 +130,10 @@ function getEffectiveFast(modelId, scopeKey) {
133
130
  function formatInvalidCursorMode(raw) {
134
131
  return `Invalid --cursor-mode "${raw}". Use "agent" or "plan".`;
135
132
  }
136
- export function getStoredCursorAgentMode(scopeKey) {
133
+ function getStoredCursorAgentMode(scopeKey) {
137
134
  return getCursorSessionSettings(scopeKey).mode ?? DEFAULT_CURSOR_AGENT_MODE;
138
135
  }
139
- export function resolveCursorAgentMode(scopeKey) {
136
+ function resolveCursorAgentMode(scopeKey) {
140
137
  const cliCursorModeState = getCursorSessionSettings(scopeKey).cli.mode;
141
138
  switch (cliCursorModeState.kind) {
142
139
  case "valid":
@@ -270,11 +267,12 @@ function formatEffectiveCursorSettingSourcesLabel(raw = process.env[CURSOR_SETTI
270
267
  const rawLabel = raw?.trim() ? raw.trim() : `(unset → ${DEFAULT_CURSOR_SETTING_SOURCES.join(",")})`;
271
268
  return `${rawLabel} (effective: ${effectiveLabel})`;
272
269
  }
273
- export function formatCursorToolsDebugReport(pi, env = process.env) {
270
+ function formatCursorToolsDebugReport(pi) {
271
+ const env = process.env;
274
272
  const bridgeEnabled = resolveCursorPiToolBridgeEnabled(env);
275
273
  const manifestEnabled = resolveCursorToolManifestEnabled(env);
276
274
  const lines = [
277
- "Cursor tool surfaces (current session):",
275
+ "Cursor tool surfaces (current registry snapshot; not a live run):",
278
276
  `${CURSOR_PI_TOOL_BRIDGE_ENV}: ${bridgeEnabled ? "enabled" : "disabled"}`,
279
277
  `${CURSOR_TOOL_MANIFEST_ENV}: ${manifestEnabled ? "enabled" : "disabled"}`,
280
278
  `${CURSOR_SETTING_SOURCES_ENV}: ${formatEffectiveCursorSettingSourcesLabel(env[CURSOR_SETTING_SOURCES_ENV])}`,
@@ -282,13 +280,23 @@ export function formatCursorToolsDebugReport(pi, env = process.env) {
282
280
  let bridgeSnapshot;
283
281
  if (bridgeEnabled) {
284
282
  try {
285
- bridgeSnapshot = buildCursorPiToolBridgeSnapshot(pi);
283
+ bridgeSnapshot = buildCursorPiToolBridgeSnapshot(pi, {
284
+ exposeOverlappingBuiltins: resolveCursorPiToolBridgeBuiltinsEnabled(env),
285
+ });
286
286
  }
287
287
  catch {
288
288
  lines.push("Pi bridge snapshot: unavailable (extension tool APIs required).");
289
289
  }
290
290
  }
291
- lines.push(buildCursorToolManifestText({ bridgeSnapshot, piBridgeEnabled: bridgeEnabled }));
291
+ lines.push(`Cursor host/MCP: ${CURSOR_HOST_TOOL_MANIFEST_SUMMARY}; configured MCP depends on Cursor settings.`, "Pi tool toggles affect pi tools/bridge exposure only; they do not disable Cursor host/configured MCP tools.");
292
+ if (!bridgeEnabled) {
293
+ lines.push("Pi bridge: disabled (PI_CURSOR_PI_TOOL_BRIDGE=0).");
294
+ }
295
+ else if (bridgeSnapshot) {
296
+ const names = bridgeSnapshot.tools.map((tool) => tool.mcpToolName).sort();
297
+ lines.push(`Pi bridge exposure eligibility: ${names.length ? names.join(", ") : "no eligible pi__* tools"}.`);
298
+ }
299
+ lines.push("This snapshot does not prove a run's endpoint attachment, MCP initialization, catalog fetch, or tool calls.", `For per-run observations, enable ${CURSOR_PI_TOOL_BRIDGE_DEBUG_ENV}=1 before the turn.`, "run_created/tools_exposed record exposure only; mcp_initialized records a validated notifications/initialized receipt; mcp_tools_list records catalog-handler output (toolCount), not client/model consumption or ongoing connection health.", "request_queued/resolved/rejected record actual bridge call handling; absent receipts do not diagnose why a client/model made no request.", "Not callable: cursor-replay-* IDs, pi history names, transcript labels.");
292
300
  return lines.join("\n");
293
301
  }
294
302
  function emitCursorToolsDebugReport(pi, ctx) {
@@ -364,7 +372,7 @@ export function registerCursorRuntimeControls(pi) {
364
372
  },
365
373
  });
366
374
  pi.registerCommand("cursor-tools", {
367
- description: "Show live Cursor tool surfaces for this session (maintainer debug)",
375
+ description: "Show current-registry Cursor tool exposure eligibility (maintainer debug)",
368
376
  handler: async (_args, ctx) => {
369
377
  emitCursorToolsDebugReport(pi, ctx);
370
378
  },
@@ -517,16 +525,5 @@ function resetCursorModeStateForTests() {
517
525
  authoritativeGlobalFastPreferenceIds.clear();
518
526
  }
519
527
  export const __testUtils = {
520
- FAST_ENTRY_TYPE,
521
- MODE_ENTRY_TYPE,
522
- CURSOR_HTTP1_ENTRY_TYPE,
523
- RUNTIME_ENTRY_TYPE: CURSOR_RUNTIME_ENTRY_TYPE,
524
- DEFAULT_CURSOR_AGENT_MODE,
525
- getConfigPath,
526
- loadGlobalFastPreferences,
527
- get sessionFastPreferences() { return getCursorSessionSettings().fast; },
528
- getSessionCursorAgentMode: () => getCursorSessionSettings().mode,
529
- getCliCursorAgentMode: () => { const mode = getCursorSessionSettings().cli.mode; return mode.kind === "valid" ? mode.mode : undefined; },
530
- getCliCursorModeState: () => getCursorSessionSettings().cli.mode,
531
528
  resetCursorModeStateForTests,
532
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
- `- Cursor host/MCP: ${CURSOR_HOST_TOOL_MANIFEST_SUMMARY}; configured MCP depends on Cursor settings.`,
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
- export const CURSOR_TOOL_PRESENTATION_SPECS = [
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
- export const CURSOR_KNOWN_NORMALIZED_TOOL_NAMES = CURSOR_REPLAY_SOURCE_TOOL_NAMES;
222
- export function getCursorToolPresentationSpec(name) {
220
+ function getCursorToolPresentationSpec(name) {
223
221
  const trimmed = name.trim();
224
222
  if (!trimmed)
225
223
  return undefined;
@@ -23,6 +23,3 @@ export function classifyCursorToolVisibility(toolCall) {
23
23
  fastLocalDiscovery: config?.fastLocalDiscovery ?? false,
24
24
  };
25
25
  }
26
- export function isFastLocalDiscoveryTool(toolCall) {
27
- return classifyCursorToolVisibility(toolCall).fastLocalDiscovery;
28
- }
@@ -1,4 +1,4 @@
1
- import { CURSOR_KNOWN_NORMALIZED_TOOL_NAMES, CURSOR_REPLAY_ACTIVITY_TOOL_NAME, getCursorReplayCallSummary, getCursorToolActivityTitle, getCursorToolActivityReplaySpec, getCursorToolGenerateImageReplaySpec, } from "./cursor-tool-presentation-registry.js";
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);
@@ -41,9 +41,9 @@ function mapComparableLevel(parameter, level) {
41
41
  }
42
42
  function getThinkingLevelMap(item) {
43
43
  const reasoningParameter = getParameter(item, "reasoning");
44
- const effortParameter = getParameter(item, "effort");
44
+ const effortParameter = getParameter(item, "effort") ?? getParameter(item, "reasoning_effort");
45
45
  const thinkingParameter = getParameter(item, "thinking");
46
- const valueParameter = effortParameter ?? reasoningParameter ?? thinkingParameter ?? getParameter(item, "reasoning_effort");
46
+ const valueParameter = effortParameter ?? reasoningParameter ?? thinkingParameter;
47
47
  if (!valueParameter)
48
48
  return undefined;
49
49
  if (valueParameter.id === "thinking" && hasBooleanValues(valueParameter)) {
@@ -196,10 +196,12 @@ function applyThinkingLevel(metadata, params, level) {
196
196
  const mapped = metadata.thinkingLevelMap?.[level];
197
197
  if (mapped === undefined || mapped === null)
198
198
  return;
199
+ const effortId = metadata.parameterIds.effort ? "effort" : metadata.parameterIds.reasoning_effort ? "reasoning_effort" : undefined;
199
200
  if (level === "off") {
200
201
  if (metadata.parameterIds.thinking && mapped === "false") {
201
202
  setParam(params, "thinking", mapped);
202
- deleteParam(params, "effort");
203
+ if (effortId)
204
+ deleteParam(params, effortId);
203
205
  return;
204
206
  }
205
207
  if (metadata.parameterIds.reasoning) {
@@ -207,10 +209,10 @@ function applyThinkingLevel(metadata, params, level) {
207
209
  }
208
210
  return;
209
211
  }
210
- if (metadata.parameterIds.effort) {
212
+ if (effortId) {
211
213
  if (metadata.parameterIds.thinking)
212
214
  setParam(params, "thinking", "true");
213
- setParam(params, "effort", mapped);
215
+ setParam(params, effortId, mapped);
214
216
  return;
215
217
  }
216
218
  if (metadata.parameterIds.reasoning) {
@@ -220,9 +222,6 @@ function applyThinkingLevel(metadata, params, level) {
220
222
  if (metadata.parameterIds.thinking) {
221
223
  setParam(params, "thinking", mapped);
222
224
  }
223
- else if (metadata.parameterIds.reasoning_effort) {
224
- setParam(params, "reasoning_effort", mapped);
225
- }
226
225
  }
227
226
  export function buildCursorModelSelection(modelId, thinkingLevel, fastEnabled) {
228
227
  const metadata = getCursorModelMetadata(modelId);
@@ -291,7 +290,7 @@ export async function discoverModels(options = {}) {
291
290
  try {
292
291
  const { Cursor } = await loadCursorSdk();
293
292
  options.signal?.throwIfAborted();
294
- // SDK 1.0.36 CursorRequestOptions has no caller signal. A late response
293
+ // SDK 1.0.37 CursorRequestOptions has no caller signal. A late response
295
294
  // still cannot publish outside the native generation that requested it.
296
295
  const models = await Cursor.models.list({ apiKey });
297
296
  options.signal?.throwIfAborted();
@@ -325,6 +324,5 @@ export async function discoverModels(options = {}) {
325
324
  }
326
325
  }
327
326
  export const __testUtils = {
328
- parseContextWindow,
329
327
  registerModelItems,
330
328
  };
@@ -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
- export function isModelCacheDisabled() {
16
+ function isModelCacheDisabled() {
17
17
  return parseEnvBoolean(process.env[DISABLE_ENV_VAR], false);
18
18
  }
19
- export function getModelCacheTtlMs() {
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, now = Date.now()) {
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.36`; development `@earendil-works/pi-ai`, `@earendil-works/pi-coding-agent`, and `@earendil-works/pi-tui` 1.0.4 with matching lockfile-resolved transitives, 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.
74
+ - `npm ls` shows exact `@cursor/sdk@1.0.37`; 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.36`, 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`.
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.37`, 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](../README.md#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