pi-cursor-sdk 0.1.62 → 0.3.6

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 (279) hide show
  1. package/CHANGELOG.md +85 -0
  2. package/README.md +57 -53
  3. package/dist/bundled-context-windows.js +39 -0
  4. package/dist/context-window-cache.js +92 -0
  5. package/dist/context.js +338 -0
  6. package/dist/cursor-active-tools.js +3 -0
  7. package/dist/cursor-agent-message-web-tools.js +165 -0
  8. package/dist/cursor-agents-context-registration.js +17 -0
  9. package/dist/cursor-agents-context.js +113 -0
  10. package/dist/cursor-api-key.js +34 -0
  11. package/dist/cursor-bridge-contract.js +27 -0
  12. package/dist/cursor-cloud-lifecycle.js +650 -0
  13. package/dist/cursor-cloud-local-state.js +460 -0
  14. package/dist/cursor-cloud-options.js +145 -0
  15. package/dist/cursor-cloud-reporting.js +222 -0
  16. package/dist/cursor-compact-tool-summary.js +75 -0
  17. package/dist/cursor-config.js +613 -0
  18. package/dist/cursor-context-tools.js +4 -0
  19. package/dist/cursor-display-only-trace.js +11 -0
  20. package/dist/cursor-display-text.js +18 -0
  21. package/dist/cursor-durable-fs.js +53 -0
  22. package/dist/cursor-edit-diff.js +13 -0
  23. package/dist/cursor-env-boolean.js +19 -0
  24. package/dist/cursor-fallback-models.generated.js +7760 -0
  25. package/dist/cursor-fallback-warning.js +15 -0
  26. package/dist/cursor-http1.js +45 -0
  27. package/dist/cursor-incomplete-tool-visibility.js +83 -0
  28. package/dist/cursor-live-run-accounting.js +55 -0
  29. package/dist/cursor-live-run-coordinator.js +406 -0
  30. package/dist/cursor-mcp-timeout-override.js +116 -0
  31. package/dist/cursor-model-lifecycle.js +32 -0
  32. package/dist/cursor-model.js +5 -0
  33. package/dist/cursor-native-replay-routing.js +35 -0
  34. package/dist/cursor-native-replay-trace.js +28 -0
  35. package/dist/cursor-native-tool-display-registration.js +102 -0
  36. package/dist/cursor-native-tool-display-replay.js +428 -0
  37. package/dist/cursor-native-tool-display-state.js +70 -0
  38. package/dist/cursor-native-tool-display-tools.js +123 -0
  39. package/dist/cursor-native-tool-names.js +11 -0
  40. package/dist/cursor-partial-content-emitter.js +118 -0
  41. package/dist/cursor-pi-tool-bridge-abort.js +114 -0
  42. package/dist/cursor-pi-tool-bridge-constants.js +6 -0
  43. package/dist/cursor-pi-tool-bridge-diagnostics.js +125 -0
  44. package/dist/cursor-pi-tool-bridge-env.js +18 -0
  45. package/dist/cursor-pi-tool-bridge-mcp.js +100 -0
  46. package/dist/cursor-pi-tool-bridge-run.js +355 -0
  47. package/dist/cursor-pi-tool-bridge-server.js +163 -0
  48. package/dist/cursor-pi-tool-bridge-snapshot.js +63 -0
  49. package/dist/cursor-pi-tool-bridge-types.js +1 -0
  50. package/dist/cursor-pi-tool-bridge.js +123 -0
  51. package/dist/cursor-provider-errors.js +311 -0
  52. package/dist/cursor-provider-lazy.js +39 -0
  53. package/dist/cursor-provider-live-run-drain.js +412 -0
  54. package/dist/cursor-provider-overflow.js +90 -0
  55. package/dist/cursor-provider-run-finalizer.js +176 -0
  56. package/dist/cursor-provider-run-outcome.js +76 -0
  57. package/dist/cursor-provider-turn-coordinator.js +275 -0
  58. package/dist/cursor-provider-turn-display-router.js +158 -0
  59. package/dist/cursor-provider-turn-emit.js +35 -0
  60. package/dist/cursor-provider-turn-finalize.js +154 -0
  61. package/dist/cursor-provider-turn-lifecycle-emitter.js +126 -0
  62. package/dist/cursor-provider-turn-prepare.js +374 -0
  63. package/dist/cursor-provider-turn-runner.js +146 -0
  64. package/dist/cursor-provider-turn-sdk-normalizer.js +74 -0
  65. package/dist/cursor-provider-turn-send.js +138 -0
  66. package/dist/cursor-provider-turn-shell-output.js +117 -0
  67. package/dist/cursor-provider-turn-tool-ledger.js +99 -0
  68. package/dist/cursor-provider-turn-types.js +1 -0
  69. package/dist/cursor-provider.js +72 -0
  70. package/dist/cursor-question-tool.js +194 -0
  71. package/dist/cursor-record-utils.js +61 -0
  72. package/dist/cursor-replay-activity-builders.js +114 -0
  73. package/dist/cursor-replay-source-names.js +27 -0
  74. package/dist/cursor-replay-summary-args.js +105 -0
  75. package/dist/cursor-replay-tool-details.js +167 -0
  76. package/dist/cursor-ripgrep-path.js +27 -0
  77. package/dist/cursor-run-final-text.js +63 -0
  78. package/dist/cursor-runtime-state.js +380 -0
  79. package/dist/cursor-sdk-billed-usage.js +86 -0
  80. package/dist/cursor-sdk-event-debug-constants.js +33 -0
  81. package/dist/cursor-sdk-event-debug-session.js +107 -0
  82. package/dist/cursor-sdk-event-debug.js +461 -0
  83. package/dist/cursor-sdk-output-filter.js +1 -0
  84. package/dist/cursor-sdk-process-error-guard.js +221 -0
  85. package/dist/cursor-sdk-runtime.js +3 -0
  86. package/dist/cursor-sensitive-text.js +23 -0
  87. package/dist/cursor-session-agent-cleanup.js +311 -0
  88. package/dist/cursor-session-agent-lifecycle.js +32 -0
  89. package/dist/cursor-session-agent-lineage.js +103 -0
  90. package/dist/cursor-session-agent-resume.js +417 -0
  91. package/dist/cursor-session-agent.js +507 -0
  92. package/dist/cursor-session-compaction-prep.js +21 -0
  93. package/dist/cursor-session-scope.js +113 -0
  94. package/dist/cursor-session-send-policy.js +18 -0
  95. package/dist/cursor-session-store.js +105 -0
  96. package/dist/cursor-session-turn-queue.js +50 -0
  97. package/dist/cursor-setting-sources.js +15 -0
  98. package/dist/cursor-skill-tool.js +230 -0
  99. package/dist/cursor-state.js +519 -0
  100. package/dist/cursor-task-presentation.js +59 -0
  101. package/dist/cursor-tool-lifecycle.js +94 -0
  102. package/dist/cursor-tool-manifest.js +34 -0
  103. package/dist/cursor-tool-presentation-registry.js +305 -0
  104. package/dist/cursor-tool-result-display-readers.js +158 -0
  105. package/dist/cursor-tool-transcript.js +32 -0
  106. package/dist/cursor-tool-visibility.js +28 -0
  107. package/dist/cursor-transcript-tool-formatters.js +618 -0
  108. package/dist/cursor-transcript-tool-specs.js +284 -0
  109. package/dist/cursor-transcript-utils.js +170 -0
  110. package/dist/cursor-usage-accounting.js +166 -0
  111. package/dist/cursor-web-tool-activity.js +23 -0
  112. package/dist/cursor-web-tool-args.js +12 -0
  113. package/dist/index.js +82 -0
  114. package/dist/model-discovery.js +305 -0
  115. package/dist/model-list-cache.js +156 -0
  116. package/docs/cursor-dogfood-checklist.md +2 -1
  117. package/docs/cursor-live-smoke-checklist.md +15 -14
  118. package/docs/cursor-model-ux-spec.md +29 -8
  119. package/docs/cursor-native-tool-replay.md +13 -13
  120. package/docs/cursor-native-tool-visual-audit.md +6 -3
  121. package/docs/cursor-testing-lessons.md +7 -7
  122. package/docs/cursor-tool-surfaces.md +5 -5
  123. package/docs/platform-smoke.md +6 -6
  124. package/node_modules/fast-uri/index.js +37 -1
  125. package/node_modules/fast-uri/package.json +1 -1
  126. package/node_modules/fast-uri/test/security.test.js +136 -0
  127. package/node_modules/hono/dist/cjs/context.js +28 -13
  128. package/node_modules/hono/dist/cjs/helper/proxy/index.js +4 -0
  129. package/node_modules/hono/dist/cjs/helper/streaming/sse.js +1 -1
  130. package/node_modules/hono/dist/cjs/hono-base.js +3 -2
  131. package/node_modules/hono/dist/cjs/jsx/base.js +27 -23
  132. package/node_modules/hono/dist/cjs/jsx/hooks/index.js +16 -13
  133. package/node_modules/hono/dist/cjs/middleware/cache/index.js +103 -8
  134. package/node_modules/hono/dist/cjs/middleware/compress/index.js +5 -0
  135. package/node_modules/hono/dist/cjs/middleware/cors/index.js +2 -2
  136. package/node_modules/hono/dist/cjs/middleware/etag/index.js +1 -1
  137. package/node_modules/hono/dist/cjs/middleware/jwk/jwk.js +9 -4
  138. package/node_modules/hono/dist/cjs/middleware/jwt/jwt.js +9 -4
  139. package/node_modules/hono/dist/cjs/middleware/language/language.js +10 -6
  140. package/node_modules/hono/dist/cjs/middleware/method-not-allowed/index.js +90 -0
  141. package/node_modules/hono/dist/cjs/middleware/secure-headers/secure-headers.js +10 -4
  142. package/node_modules/hono/dist/cjs/request.js +7 -9
  143. package/node_modules/hono/dist/cjs/router/reg-exp-router/node.js +55 -56
  144. package/node_modules/hono/dist/cjs/router/reg-exp-router/router.js +64 -85
  145. package/node_modules/hono/dist/cjs/router/reg-exp-router/trie.js +13 -5
  146. package/node_modules/hono/dist/cjs/router.js +1 -1
  147. package/node_modules/hono/dist/cjs/utils/accept.js +1 -1
  148. package/node_modules/hono/dist/cjs/utils/cookie.js +3 -2
  149. package/node_modules/hono/dist/cjs/utils/url.js +8 -8
  150. package/node_modules/hono/dist/context.js +28 -13
  151. package/node_modules/hono/dist/helper/proxy/index.js +4 -0
  152. package/node_modules/hono/dist/helper/streaming/sse.js +1 -1
  153. package/node_modules/hono/dist/hono-base.js +3 -2
  154. package/node_modules/hono/dist/jsx/base.js +27 -23
  155. package/node_modules/hono/dist/jsx/hooks/index.js +16 -13
  156. package/node_modules/hono/dist/middleware/cache/index.js +103 -8
  157. package/node_modules/hono/dist/middleware/compress/index.js +5 -0
  158. package/node_modules/hono/dist/middleware/cors/index.js +2 -2
  159. package/node_modules/hono/dist/middleware/etag/index.js +1 -1
  160. package/node_modules/hono/dist/middleware/jwk/jwk.js +9 -4
  161. package/node_modules/hono/dist/middleware/jwt/jwt.js +9 -4
  162. package/node_modules/hono/dist/middleware/language/language.js +10 -6
  163. package/node_modules/hono/dist/middleware/method-not-allowed/index.js +68 -0
  164. package/node_modules/hono/dist/middleware/secure-headers/secure-headers.js +10 -4
  165. package/node_modules/hono/dist/request.js +8 -10
  166. package/node_modules/hono/dist/router/reg-exp-router/node.js +55 -56
  167. package/node_modules/hono/dist/router/reg-exp-router/router.js +64 -85
  168. package/node_modules/hono/dist/router/reg-exp-router/trie.js +13 -5
  169. package/node_modules/hono/dist/router.js +1 -1
  170. package/node_modules/hono/dist/types/adapter/aws-lambda/types.d.ts +9 -0
  171. package/node_modules/hono/dist/types/client/types.d.ts +1 -1
  172. package/node_modules/hono/dist/types/hono-base.d.ts +4 -3
  173. package/node_modules/hono/dist/types/jsx/base.d.ts +4 -2
  174. package/node_modules/hono/dist/types/jsx/dom/index.d.ts +5 -5
  175. package/node_modules/hono/dist/types/jsx/dom/intrinsic-element/components.d.ts +2 -2
  176. package/node_modules/hono/dist/types/jsx/dom/server.d.ts +5 -5
  177. package/node_modules/hono/dist/types/jsx/hooks/index.d.ts +8 -6
  178. package/node_modules/hono/dist/types/jsx/index.d.ts +5 -5
  179. package/node_modules/hono/dist/types/middleware/cache/index.d.ts +6 -4
  180. package/node_modules/hono/dist/types/middleware/cors/index.d.ts +1 -1
  181. package/node_modules/hono/dist/types/middleware/jsx-renderer/index.d.ts +2 -2
  182. package/node_modules/hono/dist/types/middleware/jwk/jwk.d.ts +2 -0
  183. package/node_modules/hono/dist/types/middleware/jwt/jwt.d.ts +2 -0
  184. package/node_modules/hono/dist/types/middleware/method-not-allowed/index.d.ts +49 -0
  185. package/node_modules/hono/dist/types/router/reg-exp-router/node.d.ts +1 -1
  186. package/node_modules/hono/dist/types/router/reg-exp-router/trie.d.ts +2 -1
  187. package/node_modules/hono/dist/types/router.d.ts +1 -1
  188. package/node_modules/hono/dist/types/utils/headers.d.ts +2 -2
  189. package/node_modules/hono/dist/types/utils/url.d.ts +1 -0
  190. package/node_modules/hono/dist/utils/accept.js +1 -1
  191. package/node_modules/hono/dist/utils/cookie.js +4 -3
  192. package/node_modules/hono/dist/utils/url.js +6 -7
  193. package/node_modules/hono/package.json +10 -3
  194. package/node_modules/ip-address/README.md +146 -126
  195. package/node_modules/ip-address/dist/common.d.ts +29 -0
  196. package/node_modules/ip-address/dist/common.js +44 -5
  197. package/node_modules/ip-address/dist/common.js.map +1 -1
  198. package/node_modules/ip-address/dist/ipv4.d.ts +8 -1
  199. package/node_modules/ip-address/dist/ipv4.js +24 -20
  200. package/node_modules/ip-address/dist/ipv4.js.map +1 -1
  201. package/node_modules/ip-address/dist/ipv6.d.ts +68 -3
  202. package/node_modules/ip-address/dist/ipv6.js +180 -53
  203. package/node_modules/ip-address/dist/ipv6.js.map +1 -1
  204. package/node_modules/ip-address/dist/v4/constants.js +5 -1
  205. package/node_modules/ip-address/dist/v4/constants.js.map +1 -1
  206. package/node_modules/ip-address/dist/v6/constants.js +3 -2
  207. package/node_modules/ip-address/dist/v6/constants.js.map +1 -1
  208. package/node_modules/ip-address/package.json +6 -4
  209. package/package.json +23 -11
  210. package/platform-smoke.config.mjs +1 -1
  211. package/scripts/build.mjs +126 -0
  212. package/scripts/cloud-runtime-smoke.mjs +3 -1
  213. package/scripts/debug-provider-events.mjs +3 -1
  214. package/scripts/debug-sdk-events.mjs +1 -1
  215. package/scripts/isolated-cursor-smoke.sh +5 -4
  216. package/scripts/lib/cloud-smoke-cleanup-evidence.mjs +1 -1
  217. package/scripts/lib/cursor-visual-render.mjs +1 -1
  218. package/scripts/lib/ensure-built.mjs +16 -0
  219. package/scripts/lib/local-resume-smoke-harness.mjs +1 -1
  220. package/scripts/local-resume-smoke.mjs +3 -1
  221. package/scripts/platform-smoke/card-detect.mjs +1 -1
  222. package/scripts/platform-smoke/live-suite-runner.mjs +16 -40
  223. package/scripts/platform-smoke/platform-build-windows.ps1 +1 -1
  224. package/scripts/platform-smoke/render-ansi.mjs +1 -1
  225. package/scripts/platform-smoke/scenarios.mjs +1 -1
  226. package/scripts/platform-smoke/targets.mjs +2 -2
  227. package/scripts/platform-smoke.mjs +5 -5
  228. package/scripts/prepare.mjs +102 -0
  229. package/scripts/refresh-cursor-model-snapshots.mjs +16 -7
  230. package/scripts/steering-rpc-smoke.mjs +4 -2
  231. package/scripts/tmux-live-smoke.sh +11 -11
  232. package/scripts/visual-tui-smoke-self-test.mjs +4 -1
  233. package/scripts/visual-tui-smoke.mjs +51 -7
  234. package/shared/cursor-model-selection-identities.d.mts +21 -0
  235. package/shared/cursor-model-selection-identities.mjs +95 -0
  236. package/src/bundled-context-windows.ts +21 -12
  237. package/src/context.ts +1 -1
  238. package/src/cursor-agents-context-registration.ts +2 -2
  239. package/src/cursor-agents-context.ts +0 -2
  240. package/src/cursor-api-key.ts +5 -4
  241. package/src/cursor-cloud-reporting.ts +23 -2
  242. package/src/cursor-context-tools.ts +1 -1
  243. package/src/cursor-display-only-trace.ts +1 -1
  244. package/src/cursor-fallback-models.generated.ts +1726 -824
  245. package/src/cursor-live-run-accounting.ts +1 -1
  246. package/src/cursor-live-run-coordinator.ts +2 -1
  247. package/src/cursor-native-replay-routing.ts +1 -1
  248. package/src/cursor-native-tool-display-registration.ts +10 -11
  249. package/src/cursor-partial-content-emitter.ts +1 -1
  250. package/src/cursor-pi-tool-bridge-mcp.ts +1 -1
  251. package/src/cursor-pi-tool-bridge-run.ts +1 -1
  252. package/src/cursor-pi-tool-bridge-types.ts +1 -1
  253. package/src/cursor-provider-errors.ts +1 -1
  254. package/src/cursor-provider-lazy.ts +6 -5
  255. package/src/cursor-provider-live-run-drain.ts +2 -1
  256. package/src/cursor-provider-overflow.ts +1 -1
  257. package/src/cursor-provider-run-finalizer.ts +2 -1
  258. package/src/cursor-provider-turn-coordinator.ts +1 -1
  259. package/src/cursor-provider-turn-finalize.ts +12 -0
  260. package/src/cursor-provider-turn-lifecycle-emitter.ts +1 -1
  261. package/src/cursor-provider-turn-prepare.ts +1 -1
  262. package/src/cursor-provider-turn-types.ts +3 -1
  263. package/src/cursor-provider.ts +1 -1
  264. package/src/cursor-run-final-text.ts +1 -1
  265. package/src/cursor-sdk-billed-usage.ts +107 -0
  266. package/src/cursor-sdk-event-debug.ts +1 -1
  267. package/src/cursor-sdk-process-error-guard.ts +6 -4
  268. package/src/cursor-session-agent-lifecycle.ts +8 -9
  269. package/src/cursor-session-agent-resume.ts +23 -0
  270. package/src/cursor-session-agent.ts +1 -1
  271. package/src/cursor-session-compaction-prep.ts +4 -0
  272. package/src/cursor-session-send-policy.ts +1 -1
  273. package/src/cursor-tool-manifest.ts +1 -1
  274. package/src/cursor-usage-accounting.ts +71 -10
  275. package/src/index.ts +1 -1
  276. package/src/model-discovery.ts +32 -89
  277. package/tsconfig.build.json +8 -0
  278. package/tsconfig.json +16 -0
  279. package/node_modules/cross-spawn/node_modules/which/CHANGELOG.md +0 -166
@@ -4,6 +4,7 @@ import { Cursor } from "@cursor/sdk";
4
4
  import { defaultApiKeyFromEnv, parseArgv } from "./lib/cursor-cli-args.mjs";
5
5
  import { scrubSensitiveText } from "../shared/cursor-sensitive-text.mjs";
6
6
  import { createScriptFail } from "./lib/cursor-script-fail.mjs";
7
+ import { normalizeCursorContextWindowEntries } from "../shared/cursor-model-selection-identities.mjs";
7
8
 
8
9
  const FALLBACK_MODELS_PATH = "src/cursor-fallback-models.generated.ts";
9
10
  const CONTEXT_WINDOWS_PATH = "src/bundled-context-windows.ts";
@@ -40,7 +41,9 @@ Notes:
40
41
  - This script prints model IDs/counts only; it never prints API keys.
41
42
  - Cursor.models.list() is the source of truth for fallback catalog metadata.
42
43
  - Checkpoint-derived context windows are optional input because collecting them
43
- requires successful local SDK runs; this script does not start agents.`);
44
+ requires successful local SDK runs; this script does not start agents.
45
+ - Context-window keys are limited to current selectable model IDs. Redundant
46
+ :fast/:slow aliases that send the default parameters collapse to one key.`);
44
47
  }
45
48
 
46
49
  const fail = createScriptFail("refresh-cursor-snapshots");
@@ -156,9 +159,10 @@ function hasContextParameter(model) {
156
159
  }
157
160
 
158
161
  function formatContextWindows(models, checkpointWindows, fallbackContextWindow) {
159
- const existing = parseExistingContextWindows();
160
- const merged = new Map(existing);
161
- for (const [modelId, contextWindow] of checkpointWindows) merged.set(modelId, contextWindow);
162
+ const merged = normalizeCursorContextWindowEntries(models, parseExistingContextWindows(), "existing bundled context windows");
163
+ for (const [modelId, contextWindow] of normalizeCursorContextWindowEntries(models, checkpointWindows, "checkpoint context windows")) {
164
+ merged.set(modelId, contextWindow);
165
+ }
162
166
  for (const model of models) {
163
167
  if (!hasContextParameter(model) && !merged.has(model.id)) merged.set(model.id, fallbackContextWindow);
164
168
  }
@@ -167,7 +171,7 @@ function formatContextWindows(models, checkpointWindows, fallbackContextWindow)
167
171
  const date = new Date().toISOString().slice(0, 10);
168
172
  const sorted = [...merged.entries()].sort(([a], [b]) => (a === "default" ? -1 : b === "default" ? 1 : a.localeCompare(b)));
169
173
  const lines = sorted.map(([modelId, contextWindow]) => `\t${JSON.stringify(modelId)}: ${contextWindow},`);
170
- return `// Generated from Cursor SDK checkpoint tokenDetails.maxTokens on ${date}.\n// Refresh with: npm run refresh:cursor-snapshots -- --write --context-windows ~/.pi/agent/cursor-sdk-context-windows.json\n// These are default/non-Max-mode SDK context windows for Cursor models that do not\n// expose a catalog \`context\` parameter. Do not replace them with Max Mode values\n// unless the Cursor SDK exposes an exact Max Mode model selection and the extension\n// uses that selection for matching pi model IDs.\nexport const BUNDLED_CONTEXT_WINDOWS = {\n${lines.join("\n")}\n} as const satisfies Record<string, number>;\n`;
174
+ return `// Generated from Cursor SDK checkpoint tokenDetails.maxTokens on ${date}.\n// Refresh with: npm run refresh:cursor-snapshots -- --write --context-windows ~/.pi/agent/cursor-sdk-context-windows.json\n// Keys are current selectable model IDs. Equivalent default :fast/:slow selections\n// collapse to one key; stale and ambiguous aliases are omitted. Values are observed\n// or conservative default/non-Max-mode limits and may override a catalog context\n// label when the completed SDK checkpoint reports a different effective limit.\nexport const BUNDLED_CONTEXT_WINDOWS = {\n${lines.join("\n")}\n} as const satisfies Record<string, number>;\n`;
171
175
  }
172
176
 
173
177
  const args = parseRefreshArgs(process.argv.slice(2));
@@ -184,11 +188,16 @@ if (!Array.isArray(rawModels) || rawModels.length === 0) fail("Cursor.models.lis
184
188
  const models = rawModels.map(sanitizeModelItem).sort((a, b) => a.id.localeCompare(b.id));
185
189
  const checkpointWindows = parseContextWindowsFile(args.contextWindowsPath);
186
190
  const fallbackSource = formatFallbackModels(models, sdkVersion);
187
- const contextWindowSource = args.contextWindowsPath ? formatContextWindows(models, checkpointWindows, args.fallbackContextWindow) : undefined;
191
+ let contextWindowSource;
192
+ try {
193
+ contextWindowSource = args.contextWindowsPath ? formatContextWindows(models, checkpointWindows, args.fallbackContextWindow) : undefined;
194
+ } catch (error) {
195
+ fail(error instanceof Error ? error.message : String(error));
196
+ }
188
197
  const existingContextWindowCount = parseExistingContextWindows().size;
189
198
 
190
199
  console.log(`Fetched ${models.length} Cursor models with @cursor/sdk@${sdkVersion}.`);
191
- console.log(`Context windows: ${checkpointWindows.size} checkpoint override(s), ${existingContextWindowCount} existing bundled entr${existingContextWindowCount === 1 ? "y" : "ies"}.`);
200
+ console.log(`Context windows: ${checkpointWindows.size} checkpoint input(s), ${existingContextWindowCount} existing bundled entr${existingContextWindowCount === 1 ? "y" : "ies"}.`);
192
201
  console.log(`First models: ${models.slice(0, 8).map((model) => model.id).join(", ")}${models.length > 8 ? ", ..." : ""}`);
193
202
 
194
203
  if (args.check) {
@@ -16,6 +16,7 @@ import {
16
16
  } from "./lib/cursor-child-process.mjs";
17
17
  import { apiKeySecretsFromProcess } from "./lib/cursor-cli-args.mjs";
18
18
  import { buildCursorSmokeEnv, CURSOR_SDK_EVENT_DEBUG_ENV_NAMES } from "./lib/cursor-smoke-env.mjs";
19
+ import { ensureBuilt } from "./lib/ensure-built.mjs";
19
20
  import { scrubSensitiveText } from "../shared/cursor-sensitive-text.mjs";
20
21
 
21
22
  const root = fileURLToPath(new URL("..", import.meta.url));
@@ -170,7 +171,7 @@ function buildPiRpcEnv(baseEnv = process.env, nodePath = process.execPath) {
170
171
  }
171
172
 
172
173
  async function runPiRpcSmoke(sessionDir, piBin) {
173
- const args = ["--approve", "-e", root, "--cursor-no-fast", "--model", "cursor/composer-2-5", "--mode", "rpc", "--session-dir", sessionDir];
174
+ const args = ["--approve", "-e", root, "--cursor-no-fast", "--model", "cursor/grok-4.6", "--mode", "rpc", "--session-dir", sessionDir];
174
175
  const env = buildPiRpcEnv();
175
176
 
176
177
  const child = spawn(piBin, args, {
@@ -263,7 +264,7 @@ async function runSelfTest() {
263
264
  const envCapture = join(tempDir, "fake-pi.env");
264
265
  writeFileSync(
265
266
  fakePi,
266
- `#!/usr/bin/env node\nconst { writeFileSync } = require("node:fs");\nwriteFileSync(${JSON.stringify(envCapture)}, Object.entries(process.env).map(([key, value]) => key + "=" + (value ?? "")).join("\\n") + "\\n", "utf8");\n`,
267
+ `#!/usr/bin/env node\nimport { writeFileSync } from "node:fs";\nwriteFileSync(${JSON.stringify(envCapture)}, Object.entries(process.env).map(([key, value]) => key + "=" + (value ?? "")).join("\\n") + "\\n", "utf8");\n`,
267
268
  "utf8",
268
269
  );
269
270
  writeFileSync(fakeNode, `#!/bin/sh\necho fake-node-used > ${JSON.stringify(fakeNodeMarker)}\nexit 99\n`, "utf8");
@@ -332,6 +333,7 @@ async function main() {
332
333
  printHelp();
333
334
  return;
334
335
  }
336
+ ensureBuilt();
335
337
  if (process.argv.includes("--self-test")) {
336
338
  await runSelfTest();
337
339
  return;
@@ -243,7 +243,7 @@ exec %s
243
243
  "$TMUX_BIN" capture-pane -pt "$session" >"$capture" 2>/dev/null || true
244
244
  missing=""
245
245
  "$RG_BIN" -q "SUM=42" "$capture" || missing="${missing} SUM=42"
246
- "$RG_BIN" -q "\\(cursor\\) composer-2[-.]5" "$capture" || missing="${missing} footer (cursor) composer-2-5"
246
+ "$RG_BIN" -q "\\(cursor\\) grok-4\\.6" "$capture" || missing="${missing} footer (cursor) grok-4.6"
247
247
  if [[ -z "$missing" ]]; then
248
248
  "$TMUX_BIN" kill-session -t "$session" 2>/dev/null || true
249
249
  log "$name PASS"
@@ -311,14 +311,14 @@ printf '\''%%s\n'\'' "$code" > %q
311
311
  model_listed() {
312
312
  local file="$1"
313
313
  if [[ -n "${RG_BIN:-}" ]]; then
314
- "$RG_BIN" -q "composer-2\.5" "$file"
314
+ "$RG_BIN" -q "grok-4\.6" "$file"
315
315
  else
316
- grep -q "composer-2\.5" "$file"
316
+ grep -q "grok-4\.6" "$file"
317
317
  fi
318
318
  }
319
319
 
320
320
  # Capture full catalog then search. Never pipe list-models into rg -q under pipefail.
321
- capture_and_require_composer_model() {
321
+ capture_and_require_default_model() {
322
322
  local list_cmd=("$@")
323
323
  local models_out="$SMOKE_DIR/prereq.models.txt"
324
324
  local models_err="$SMOKE_DIR/prereq.stderr.txt"
@@ -326,7 +326,7 @@ capture_and_require_composer_model() {
326
326
  fail "pi --list-models cursor failed"
327
327
  fi
328
328
  if ! model_listed "$models_out" && ! model_listed "$models_err"; then
329
- fail "cursor/composer-2-5 not listed"
329
+ fail "cursor/grok-4.6 not listed"
330
330
  fi
331
331
  }
332
332
 
@@ -343,7 +343,7 @@ run_self_test() {
343
343
  env_capture="$temp_dir/fake-pi.env"
344
344
  cat >"$fake_pi" <<EOF_SELFTEST_PI
345
345
  #!/usr/bin/env node
346
- const { writeFileSync } = require("node:fs");
346
+ import { writeFileSync } from "node:fs";
347
347
  writeFileSync("$env_capture", Object.entries(process.env).map(([key, value]) => key + "=" + (value ?? "")).join("\\n") + "\\n", "utf8");
348
348
  EOF_SELFTEST_PI
349
349
  cat >"$fake_node" <<EOF_SELFTEST_NODE
@@ -383,7 +383,7 @@ EOF_SELFTEST_NODE
383
383
  if grep -q '^PI_CURSOR_SETTING_SOURCES=' "$env_capture"; then
384
384
  fail "self-test failed: default-settings env did not unset PI_CURSOR_SETTING_SOURCES"
385
385
  fi
386
- # Large-catalog prereq: exercise the same capture_and_require_composer_model helper.
386
+ # Large-catalog prereq: exercise the same capture_and_require_default_model helper.
387
387
  fake_list_pi="$bin_dir/pi-list-models"
388
388
  cat >"$fake_list_pi" <<'EOF_FAKE_LIST'
389
389
  #!/usr/bin/env bash
@@ -392,11 +392,11 @@ while [[ $i -lt 20000 ]]; do
392
392
  printf 'cursor/model-%s\n' "$i"
393
393
  i=$((i + 1))
394
394
  done
395
- printf 'cursor/composer-2.5\n'
395
+ printf 'cursor/grok-4.6\n'
396
396
  exit 0
397
397
  EOF_FAKE_LIST
398
398
  chmod +x "$fake_list_pi"
399
- SMOKE_DIR="$temp_dir" capture_and_require_composer_model "$fake_list_pi"
399
+ SMOKE_DIR="$temp_dir" capture_and_require_default_model "$fake_list_pi"
400
400
  if [[ "$(wc -l <"$temp_dir/prereq.models.txt" | tr -d ' ')" -lt 20000 ]]; then
401
401
  fail "self-test failed: large catalog was truncated before search"
402
402
  fi
@@ -428,7 +428,7 @@ fi
428
428
  PI_BASE=(
429
429
  "$PI_BIN" --approve -e "$ROOT"
430
430
  --cursor-no-fast
431
- --model cursor/composer-2-5
431
+ --model cursor/grok-4.6
432
432
  )
433
433
 
434
434
  if [[ -z "${CURSOR_API_KEY:-}" ]]; then
@@ -448,7 +448,7 @@ log "partial live smoke: prereq, basic, default-settings, noninteractive-math, t
448
448
  "${BASE_ENV[@]}" "$PI_BIN" --version | tee "$SMOKE_DIR/prereq.pi-version.txt"
449
449
  "${BASE_ENV[@]}" "$NPM_BIN" --prefix "$ROOT" ls @cursor/sdk @earendil-works/pi-coding-agent @earendil-works/pi-ai @earendil-works/pi-tui | tee "$SMOKE_DIR/prereq.npm-ls.txt"
450
450
 
451
- capture_and_require_composer_model "${NONE_ENV[@]}" "${PI_BASE[@]}" --list-models cursor
451
+ capture_and_require_default_model "${NONE_ENV[@]}" "${PI_BASE[@]}" --list-models cursor
452
452
  log "prereq PASS"
453
453
 
454
454
  run_direct basic 600 retry-empty-output "PI_CURSOR_SMOKE_OK" "PI_CURSOR_SMOKE_OK" \
@@ -35,7 +35,7 @@ export function runVisualSmokeSelfTest(deps) {
35
35
  const envCapture = join(tempDir, "fake-pi.env");
36
36
  writeFileSync(
37
37
  fakePi,
38
- `#!/usr/bin/env node\nconst { writeFileSync } = require("node:fs");\nwriteFileSync(${JSON.stringify(envCapture)}, Object.entries(process.env).map(([key, value]) => key + "=" + (value ?? "")).join("\\n") + "\\n", "utf8");\n`,
38
+ `#!/usr/bin/env node\nimport { writeFileSync } from "node:fs";\nwriteFileSync(${JSON.stringify(envCapture)}, Object.entries(process.env).map(([key, value]) => key + "=" + (value ?? "")).join("\\n") + "\\n", "utf8");\n`,
39
39
  "utf8",
40
40
  );
41
41
  writeFileSync(fakeNode, `#!/bin/sh\necho fake-node-used > ${shellQuote(fakeNodeMarker)}\nexit 99\n`, "utf8");
@@ -89,6 +89,9 @@ export function runVisualSmokeSelfTest(deps) {
89
89
  assertSelfTest(defaults.get("PI_CURSOR_SETTING_SOURCES") === "none", "setting sources must default to none");
90
90
  assertSelfTest(defaults.get("PI_CURSOR_PI_TOOL_BRIDGE") === "0", "bridge must default off");
91
91
  assertSelfTest(defaults.get("PI_CURSOR_EXPOSE_BUILTIN_TOOLS") === "0", "built-in exposure must default off");
92
+ assertSelfTest(defaults.get("PI_CODING_AGENT_DIR") === join(tempDir, "pi-agent"), "agent dir must be isolated under out-dir");
93
+ assertSelfTest(defaults.get("PI_OFFLINE") === "1", "startup network operations must be off");
94
+ assertSelfTest(defaults.get("PI_SKIP_VERSION_CHECK") === "1", "pi.dev version check must be off");
92
95
  for (const name of DEBUG_ENV_NAMES) {
93
96
  assertSelfTest(plan.clearEnvNames.includes(name), `${name} must be cleared by default`);
94
97
  }
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { spawnSync } from "node:child_process";
3
- import { accessSync, constants, mkdirSync, readdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
3
+ import { accessSync, chmodSync, constants, copyFileSync, mkdirSync, readdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
4
+ import { homedir } from "node:os";
4
5
  import { delimiter, dirname, join, resolve } from "node:path";
5
6
  import { fileURLToPath } from "node:url";
6
7
  import { commonBooleanFlag, commonRepeatStringFlag, parseArgv } from "./lib/cursor-cli-args.mjs";
@@ -15,7 +16,7 @@ const DEFAULT_HEIGHT = 45;
15
16
  const DEFAULT_WAIT_MS = 60_000;
16
17
  const DEFAULT_STARTUP_MS = 5_000;
17
18
  const DEFAULT_HISTORY_LINES = 3_000;
18
- const DEFAULT_MODEL = "cursor/composer-2-5";
19
+ const DEFAULT_MODEL = "cursor/grok-4.6";
19
20
  const DEFAULT_MODE = "plan";
20
21
  const DEFAULT_SETTING_SOURCES = "none";
21
22
  const DEBUG_ENV_NAMES = CURSOR_SDK_EVENT_DEBUG_ENV_NAMES;
@@ -44,7 +45,7 @@ Common options:
44
45
  --model MODEL Cursor model. Default: ${DEFAULT_MODEL}.
45
46
  --mode agent|plan Cursor SDK mode. Default: ${DEFAULT_MODE}.
46
47
  --session-dir PATH pi session directory. Default: <out-dir>/<label>.session.
47
- --session-id ID pi session id. Default: visual-<label>-<timestamp>.
48
+ --session-id ID pi session id; pass to resume a prior capture session. Default: pi-assigned, so fresh captures avoid the new-session warning line.
48
49
  --width N PTY columns. Default: ${DEFAULT_WIDTH}.
49
50
  --height N PTY rows. Default: ${DEFAULT_HEIGHT}.
50
51
  --history-lines N tmux capture history lines. Default: ${DEFAULT_HISTORY_LINES}.
@@ -63,7 +64,11 @@ Native replay isolation defaults:
63
64
  PI_CURSOR_SETTING_SOURCES=none
64
65
  PI_CURSOR_PI_TOOL_BRIDGE=0
65
66
  PI_CURSOR_EXPOSE_BUILTIN_TOOLS=0
67
+ PI_CODING_AGENT_DIR=<out-dir>/pi-agent (seeded auth.json + quietStartup; host extensions stay out)
68
+ PI_OFFLINE=1
69
+ PI_SKIP_VERSION_CHECK=1
66
70
  TERM=xterm-256color
71
+ tmux starts in --cwd with a non-login shell so a stale tmux-server cwd cannot print getcwd errors
67
72
  Debug artifact env is cleared before each run; --event-debug sets a deterministic debug dir.
68
73
 
69
74
  Artifacts written:
@@ -194,10 +199,26 @@ function parseArgs(argv) {
194
199
  options.safeLabel = sanitizeLabel(options.label);
195
200
  options.outDir ??= resolve(`/tmp/pi-cursor-sdk-visual-smoke-${timestamp()}`);
196
201
  options.sessionDir ??= resolve(options.outDir, `${options.safeLabel}.session`);
197
- options.sessionId ??= `visual-${options.safeLabel}-${Date.now()}`;
202
+ options.agentDir ??= resolve(options.outDir, "pi-agent");
198
203
  return options;
199
204
  }
200
205
 
206
+ function seedVisualAgentDir(agentDir) {
207
+ mkdirSync(agentDir, { recursive: true });
208
+ const srcAuth = join(homedir(), ".pi", "agent", "auth.json");
209
+ const destAuth = join(agentDir, "auth.json");
210
+ try {
211
+ copyFileSync(srcAuth, destAuth);
212
+ chmodSync(destAuth, 0o600);
213
+ } catch {
214
+ // CURSOR_API_KEY remains the fallback when host auth.json is absent.
215
+ }
216
+ writeFileSync(
217
+ join(agentDir, "settings.json"),
218
+ `${JSON.stringify({ quietStartup: true, enableInstallTelemetry: false })}\n`,
219
+ );
220
+ }
221
+
201
222
  function sanitizeLabel(label) {
202
223
  const safe = label.trim().replace(/[^A-Za-z0-9_.-]+/g, "-").replace(/^-+|-+$/g, "");
203
224
  return safe || "visual-smoke";
@@ -359,7 +380,13 @@ function buildLaunchPlan(options, commands, shell) {
359
380
  eventDebugDir: options.eventDebug ? resolve(options.outDir, `${options.safeLabel ?? "visual-smoke"}.cursor-sdk-events`) : undefined,
360
381
  });
361
382
  const sealedPath = commands.sealedPath ?? smokeEnvPlan.sealedPath;
362
- const envAssignments = smokeEnvPlan.envEntries;
383
+ const agentDir = options.agentDir ?? resolve(options.outDir, "pi-agent");
384
+ const envAssignments = [
385
+ ...smokeEnvPlan.envEntries,
386
+ ["PI_CODING_AGENT_DIR", agentDir],
387
+ ["PI_OFFLINE", "1"],
388
+ ["PI_SKIP_VERSION_CHECK", "1"],
389
+ ];
363
390
  const clearEnvNames = smokeEnvPlan.clearEnvNames;
364
391
  const command = [
365
392
  ...envAssignments.map(([name, value]) => `${name}=${shellQuote(value)}`),
@@ -370,7 +397,7 @@ function buildLaunchPlan(options, commands, shell) {
370
397
  "--cursor-no-fast",
371
398
  "--cursor-mode", shellQuote(options.mode),
372
399
  "--session-dir", shellQuote(options.sessionDir),
373
- "--session-id", shellQuote(options.sessionId),
400
+ ...(options.sessionId ? ["--session-id", shellQuote(options.sessionId)] : []),
374
401
  "--model", shellQuote(options.model),
375
402
  ].join(" ");
376
403
  const clearLines = clearEnvNames.map((name) => `unset ${name}`).join("\n");
@@ -397,6 +424,8 @@ function runVisualSmoke(options) {
397
424
 
398
425
  mkdirSync(options.outDir, { recursive: true });
399
426
  mkdirSync(options.sessionDir, { recursive: true });
427
+ options.agentDir ??= resolve(options.outDir, "pi-agent");
428
+ seedVisualAgentDir(options.agentDir);
400
429
 
401
430
  const sessionName = `pi-visual-${options.safeLabel}-${process.pid}`;
402
431
  const bufferName = `pi-visual-prompt-${process.pid}`;
@@ -418,7 +447,22 @@ function runVisualSmoke(options) {
418
447
  const jsonlMtimesBeforeRun = snapshotJsonlMtimes(options.sessionDir);
419
448
  const runStartedAtMs = Date.now();
420
449
  try {
421
- const start = run(commands.tmux, ["new-session", "-d", "-s", sessionName, "-x", String(options.width), "-y", String(options.height), "--", shell, "-lc", script]);
450
+ const start = run(commands.tmux, [
451
+ "new-session",
452
+ "-d",
453
+ "-s",
454
+ sessionName,
455
+ "-c",
456
+ options.cwd,
457
+ "-x",
458
+ String(options.width),
459
+ "-y",
460
+ String(options.height),
461
+ "--",
462
+ shell,
463
+ "-c",
464
+ script,
465
+ ]);
422
466
  if (start.status !== 0) throw new Error(`tmux new-session failed: ${start.stderr?.toString().trim() || start.status}`);
423
467
  sessionStarted = true;
424
468
 
@@ -0,0 +1,21 @@
1
+ import type { ModelListItem } from "@cursor/sdk";
2
+
3
+ export interface CursorModelSelectionIdentity {
4
+ model: ModelListItem;
5
+ selectionModelId: string;
6
+ context?: string;
7
+ fastOverride?: boolean;
8
+ piModelId: string;
9
+ contextWindowKey: string;
10
+ baseContextWindowKey: string;
11
+ }
12
+
13
+ export declare function getCursorModelSelectionIdentities(
14
+ models: readonly ModelListItem[],
15
+ ): CursorModelSelectionIdentity[];
16
+
17
+ export declare function normalizeCursorContextWindowEntries(
18
+ models: readonly ModelListItem[],
19
+ entries: ReadonlyMap<string, number>,
20
+ source?: string,
21
+ ): Map<string, number>;
@@ -0,0 +1,95 @@
1
+ function getParameter(model, id) {
2
+ return (model.parameters ?? []).find((parameter) => parameter.id === id);
3
+ }
4
+
5
+ function getDefaultParam(model, id) {
6
+ const variant = (model.variants ?? []).find((candidate) => candidate.isDefault) ?? model.variants?.[0];
7
+ return variant?.params?.find((param) => param.id === id)?.value.toLowerCase();
8
+ }
9
+
10
+ function getAmbiguousAliases(models) {
11
+ const ownersByAlias = new Map();
12
+ for (const model of models) {
13
+ for (const rawAlias of model.aliases ?? []) {
14
+ const alias = rawAlias.trim();
15
+ if (!alias || alias === model.id) continue;
16
+ const owners = ownersByAlias.get(alias) ?? new Set();
17
+ owners.add(model.id);
18
+ ownersByAlias.set(alias, owners);
19
+ }
20
+ }
21
+ return new Set([...ownersByAlias].filter(([, owners]) => owners.size > 1).map(([alias]) => alias));
22
+ }
23
+
24
+ function getSelectableIds(model, reservedIds, ambiguousAliases) {
25
+ const ids = [model.id];
26
+ for (const rawAlias of model.aliases ?? []) {
27
+ const alias = rawAlias.trim();
28
+ if (!alias || alias === model.id || ids.includes(alias) || reservedIds.has(alias) || ambiguousAliases.has(alias)) continue;
29
+ ids.push(alias);
30
+ }
31
+ return ids;
32
+ }
33
+
34
+ function encodePiModelId(modelId, context, fastOverride) {
35
+ const contextQualified = context ? `${modelId}@${context}` : modelId;
36
+ if (fastOverride === true) return `${contextQualified}:fast`;
37
+ if (fastOverride === false) return `${contextQualified}:slow`;
38
+ return contextQualified;
39
+ }
40
+
41
+ export function getCursorModelSelectionIdentities(models) {
42
+ const sortedModels = [...models].sort((a, b) => a.id.localeCompare(b.id));
43
+ const reservedIds = new Set(models.map((model) => model.id));
44
+ const ambiguousAliases = getAmbiguousAliases(models);
45
+ const usedPiModelIds = new Set();
46
+ const identities = [];
47
+
48
+ for (const model of sortedModels) {
49
+ const contextValues = getParameter(model, "context")?.values?.map(({ value }) => value) ?? [];
50
+ const contexts = contextValues.length > 0 ? contextValues : [undefined];
51
+ const hasFast = getParameter(model, "fast") !== undefined;
52
+ const fastOverrides = hasFast ? [undefined, true, false] : [undefined];
53
+ const defaultFast = getDefaultParam(model, "fast");
54
+
55
+ for (const selectionModelId of getSelectableIds(model, reservedIds, ambiguousAliases)) {
56
+ for (const context of contexts) {
57
+ for (const fastOverride of fastOverrides) {
58
+ const piModelId = encodePiModelId(selectionModelId, context, fastOverride);
59
+ if (usedPiModelIds.has(piModelId)) continue;
60
+ usedPiModelIds.add(piModelId);
61
+ const defaultEquivalent =
62
+ (fastOverride === true && defaultFast === "true") || (fastOverride === false && defaultFast === "false");
63
+ identities.push({
64
+ model,
65
+ selectionModelId,
66
+ ...(context ? { context } : {}),
67
+ ...(fastOverride !== undefined ? { fastOverride } : {}),
68
+ piModelId,
69
+ contextWindowKey: defaultEquivalent ? encodePiModelId(selectionModelId, context) : piModelId,
70
+ baseContextWindowKey: encodePiModelId(model.id, context, defaultEquivalent ? undefined : fastOverride),
71
+ });
72
+ }
73
+ }
74
+ }
75
+ }
76
+
77
+ return identities;
78
+ }
79
+
80
+ export function normalizeCursorContextWindowEntries(models, entries, source = "context windows") {
81
+ const canonicalBySelectableId = new Map(
82
+ getCursorModelSelectionIdentities(models).map(({ piModelId, contextWindowKey }) => [piModelId, contextWindowKey]),
83
+ );
84
+ const normalized = new Map();
85
+ for (const [modelId, contextWindow] of entries) {
86
+ const canonicalId = modelId === "default" ? modelId : canonicalBySelectableId.get(modelId);
87
+ if (!canonicalId) continue;
88
+ const existing = normalized.get(canonicalId);
89
+ if (existing !== undefined && existing !== contextWindow) {
90
+ throw new Error(`${source} assigns conflicting windows to equivalent selection ${canonicalId}: ${existing} and ${contextWindow}`);
91
+ }
92
+ normalized.set(canonicalId, contextWindow);
93
+ }
94
+ return normalized;
95
+ }
@@ -1,30 +1,39 @@
1
- // Generated from Cursor SDK checkpoint tokenDetails.maxTokens on 2026-05-18.
1
+ // Generated from Cursor SDK checkpoint tokenDetails.maxTokens on 2026-08-18.
2
2
  // Refresh with: npm run refresh:cursor-snapshots -- --write --context-windows ~/.pi/agent/cursor-sdk-context-windows.json
3
- // These are default/non-Max-mode SDK context windows for Cursor models that do not
4
- // expose a catalog `context` parameter. Do not replace them with Max Mode values
5
- // unless the Cursor SDK exposes an exact Max Mode model selection and the extension
6
- // uses that selection for matching pi model IDs.
3
+ // Keys are current selectable model IDs. Equivalent default :fast/:slow selections
4
+ // collapse to one key; stale and ambiguous aliases are omitted. Values are observed
5
+ // or conservative default/non-Max-mode limits and may override a catalog context
6
+ // label when the completed SDK checkpoint reports a different effective limit.
7
7
  export const BUNDLED_CONTEXT_WINDOWS = {
8
8
  "default": 200000,
9
+ "auto-smart": 200000,
10
+ "claude-fable-5@300k": 300000,
9
11
  "claude-haiku-4-5": 200000,
10
12
  "claude-opus-4-5": 200000,
11
- "composer-1.5": 200000,
13
+ "claude-opus-4-8@1m": 300000,
12
14
  "composer-2": 200000,
13
15
  "composer-2.5": 200000,
14
16
  "gemini-2.5-flash": 200000,
15
17
  "gemini-3-flash": 200000,
16
18
  "gemini-3.1-pro": 200000,
19
+ "gemini-3.5-flash": 200000,
20
+ "gemini-3.6-flash": 200000,
21
+ "gemini-3.7-flash": 200000,
22
+ "glm-5.2": 200000,
23
+ "gpt-5-5@272k": 272000,
17
24
  "gpt-5-mini": 272000,
18
25
  "gpt-5.1": 272000,
19
- "gpt-5.1-codex-max": 272000,
20
- "gpt-5.1-codex-mini": 272000,
21
26
  "gpt-5.2": 272000,
22
- "gpt-5.2-codex": 272000,
23
27
  "gpt-5.3-codex": 272000,
24
- "gpt-5.3-codex-spark": 128000,
25
28
  "gpt-5.4-mini": 272000,
26
29
  "gpt-5.4-nano": 272000,
27
30
  "gpt-5.5@272k": 272000,
28
- "grok-4-20": 200000,
29
- "kimi-k2.5": 262000,
31
+ "gpt-5.6@1m": 272000,
32
+ "grok-4.5": 256000,
33
+ "grok-4.5:slow": 256000,
34
+ "grok-4.6": 256000,
35
+ "kimi-k2.7-code": 200000,
36
+ "kimi-k3": 200000,
37
+ "opus-4.8@1m": 300000,
38
+ "opus-4.8@300k": 300000,
30
39
  } as const satisfies Record<string, number>;
package/src/context.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { createHash } from "node:crypto";
2
- import type { Context, Message, ToolCall } from "@earendil-works/pi-ai/compat";
2
+ import type { Context, Message, ToolCall } from "@earendil-works/pi-ai";
3
3
  import { convertToLlm } from "@earendil-works/pi-coding-agent";
4
4
  import type { AgentModeOption, SDKImage } from "@cursor/sdk";
5
5
  import { CURSOR_PI_BRIDGE_PREFERENCE_TEXT } from "./cursor-bridge-contract.js";
@@ -1,14 +1,14 @@
1
1
  import { isCursorModel } from "./cursor-model.js";
2
2
  import { registerCursorModelLifecycle, type CursorModelLifecycleExtensionApi } from "./cursor-model-lifecycle.js";
3
3
  import { resolveEffectiveCursorConfigForContext } from "./cursor-runtime-state.js";
4
+ import { resolveCursorFacingSystemPrompt } from "./cursor-agents-context.js";
4
5
 
5
6
  export type CursorAgentsContextExtensionApi = CursorModelLifecycleExtensionApi;
6
7
 
7
8
  export function registerCursorAgentsContextDedup(pi: CursorAgentsContextExtensionApi): void {
8
9
  registerCursorModelLifecycle(pi, {
9
- beforeAgentStart: async (event, ctx) => {
10
+ beforeAgentStart: (event, ctx) => {
10
11
  if (!isCursorModel(ctx.model)) return undefined;
11
- const { resolveCursorFacingSystemPrompt } = await import("./cursor-agents-context.js");
12
12
  const runtime = resolveEffectiveCursorConfigForContext(ctx).runtime.value;
13
13
  const resolved = resolveCursorFacingSystemPrompt(
14
14
  event.systemPrompt,
@@ -12,8 +12,6 @@ import {
12
12
  } from "./cursor-setting-sources.js";
13
13
  import type { SettingSource } from "@cursor/sdk";
14
14
  import type { CursorRuntime } from "./cursor-config.js";
15
- export { registerCursorAgentsContextDedup, type CursorAgentsContextExtensionApi } from "./cursor-agents-context-registration.js";
16
-
17
15
  export const CURSOR_PRESERVE_PI_AGENTS_MD_ENV = "PI_CURSOR_PRESERVE_PI_AGENTS_MD";
18
16
 
19
17
  /** Opening tag prefix pi `buildSystemPrompt()` uses for each context file (path attribute only). */
@@ -1,3 +1,5 @@
1
+ import { readStoredCredential } from "@earendil-works/pi-coding-agent";
2
+
1
3
  export const CURSOR_API_KEY_ENV_VAR = "CURSOR_API_KEY";
2
4
  const CURSOR_PROVIDER_ID = "cursor";
3
5
 
@@ -21,9 +23,8 @@ export function resolveCursorApiKey(apiKey?: string): string | undefined {
21
23
  return trimmed;
22
24
  }
23
25
 
24
- async function getStoredCursorApiKey(): Promise<string | undefined> {
26
+ function getStoredCursorApiKey(): string | undefined {
25
27
  try {
26
- const { readStoredCredential } = await import("@earendil-works/pi-coding-agent");
27
28
  const credential = readStoredCredential(CURSOR_PROVIDER_ID);
28
29
  return resolveCursorApiKey(credential?.type === "api_key" ? credential.key : undefined);
29
30
  } catch {
@@ -31,6 +32,6 @@ async function getStoredCursorApiKey(): Promise<string | undefined> {
31
32
  }
32
33
  }
33
34
 
34
- export async function resolveCursorRuntimeApiKey(): Promise<string | undefined> {
35
- return (await getStoredCursorApiKey()) ?? resolveCursorApiKey(process.env.CURSOR_API_KEY);
35
+ export function resolveCursorRuntimeApiKey(): Promise<string | undefined> {
36
+ return Promise.resolve(getStoredCursorApiKey() ?? resolveCursorApiKey(process.env.CURSOR_API_KEY));
36
37
  }
@@ -170,17 +170,38 @@ export async function fetchCursorCloudRawUsage(
170
170
  }
171
171
  }
172
172
 
173
+ function mapCursorSdkAgentUsageToCloudReport(value: unknown, runId?: string): CursorCloudUsageReport | undefined {
174
+ const record = asRecord(value);
175
+ const runs = (getArray(record, "runs") ?? []).map(asRecord);
176
+ const matchingRun = runId ? runs.find((run) => getString(run, "runId") === runId) : undefined;
177
+ return normalizeUsage({
178
+ totalUsage: getRecord(record, "usage"),
179
+ runUsage: getRecord(matchingRun, "usage"),
180
+ });
181
+ }
182
+
173
183
  export async function collectCursorCloudRunReport(options: {
174
184
  agent: SDKAgent;
175
185
  run: Run;
176
186
  waitResult: RunResult;
177
187
  apiKey: string | undefined;
188
+ agentUsage?: unknown;
178
189
  }): Promise<CursorCloudRunReport> {
179
190
  const listArtifacts = options.agent.listArtifacts;
180
- const [artifactResult, usage] = await Promise.all([
191
+ const sdkUsage =
192
+ options.agentUsage !== undefined
193
+ ? mapCursorSdkAgentUsageToCloudReport(options.agentUsage, options.run.id)
194
+ : typeof options.agent.getUsage === "function"
195
+ ? mapCursorSdkAgentUsageToCloudReport(
196
+ await withTimeout(Promise.resolve(options.agent.getUsage({ runId: options.run.id }))),
197
+ options.run.id,
198
+ )
199
+ : undefined;
200
+ const [artifactResult, restUsage] = await Promise.all([
181
201
  typeof listArtifacts === "function" ? withTimeout(listArtifacts.call(options.agent)) : undefined,
182
- fetchCursorCloudRawUsage({ agentId: options.run.agentId, runId: options.run.id, apiKey: options.apiKey }),
202
+ sdkUsage ? undefined : fetchCursorCloudRawUsage({ agentId: options.run.agentId, runId: options.run.id, apiKey: options.apiKey }),
183
203
  ]);
204
+ const usage = sdkUsage ?? restUsage;
184
205
  const waitBranches = getArray(getRecord(asRecord(options.waitResult), "git"), "branches");
185
206
  const runBranches = getArray(getRecord(asRecord(options.run), "git"), "branches");
186
207
  return {
@@ -1,4 +1,4 @@
1
- import type { Context } from "@earendil-works/pi-ai/compat";
1
+ import type { Context } from "@earendil-works/pi-ai";
2
2
 
3
3
  /** Tool names from the provider context snapshot at stream start (not live pi.getActiveTools()). */
4
4
  export function getActiveContextToolNames(context: Context): ReadonlySet<string> | undefined {
@@ -1,4 +1,4 @@
1
- import type { AssistantMessage, AssistantMessageEventStream } from "@earendil-works/pi-ai/compat";
1
+ import type { AssistantMessage, AssistantMessageEventStream } from "@earendil-works/pi-ai";
2
2
 
3
3
  function withDisplayOnlyThinking(partial: AssistantMessage, thinking: string): AssistantMessage {
4
4
  return { ...partial, content: [...partial.content, { type: "thinking", thinking }] };