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
@@ -1,4 +1,4 @@
1
- import type { Api, AssistantMessage, Context, Model } from "@earendil-works/pi-ai/compat";
1
+ import type { Api, AssistantMessage, Context, Model } from "@earendil-works/pi-ai";
2
2
  import {
3
3
  CURSOR_APPROX_CHARS_PER_TOKEN,
4
4
  CURSOR_IMAGE_TOKEN_ESTIMATE,
@@ -91,18 +91,30 @@ function getCursorSdkUncachedInputTokens(turnUsage: CursorSdkTurnUsage): number
91
91
  return turnUsage.inputTokens - turnUsage.cacheReadTokens - turnUsage.cacheWriteTokens;
92
92
  }
93
93
 
94
- export function isCursorSdkUsageSafeForPiMessage(turnUsage: CursorSdkTurnUsage, model: Model<Api>): boolean {
94
+ export function isCursorSdkUsagePartitionSafe(turnUsage: CursorSdkTurnUsage, model: Model<Api>): boolean {
95
95
  const counts = [turnUsage.inputTokens, turnUsage.outputTokens, turnUsage.cacheReadTokens, turnUsage.cacheWriteTokens];
96
96
  const uncachedInput = getCursorSdkUncachedInputTokens(turnUsage);
97
97
  return (
98
98
  counts.every((count) => Number.isFinite(count) && count >= 0) &&
99
99
  Number.isFinite(uncachedInput) &&
100
100
  uncachedInput >= 0 &&
101
- turnUsage.outputTokens <= model.maxTokens &&
101
+ turnUsage.outputTokens <= model.maxTokens
102
+ );
103
+ }
104
+
105
+ export function isCursorSdkUsageSafeForPiMessage(turnUsage: CursorSdkTurnUsage, model: Model<Api>): boolean {
106
+ return (
107
+ isCursorSdkUsagePartitionSafe(turnUsage, model) &&
102
108
  turnUsage.inputTokens + turnUsage.outputTokens <= model.contextWindow
103
109
  );
104
110
  }
105
111
 
112
+ export interface CursorSdkUsageApplyOptions {
113
+ runtime: CursorRuntime;
114
+ turn?: CursorSdkTurnUsage;
115
+ billed?: CursorSdkTurnUsage;
116
+ }
117
+
106
118
  export function applyCursorSdkUsage(partial: AssistantMessage, turnUsage: CursorSdkTurnUsage): void {
107
119
  // Pi treats input/cacheRead/cacheWrite as disjoint additive prompt components.
108
120
  partial.usage.input = getCursorSdkUncachedInputTokens(turnUsage);
@@ -117,8 +129,23 @@ function isCompatibleCursorAssistantMeasurement(assistant: AssistantMessage, mod
117
129
  return assistant.api === model.api && assistant.provider === model.provider && assistant.model === model.id;
118
130
  }
119
131
 
132
+ function getLatestCompactionBoundary(context: Context): { index: number; tokensBefore?: number } | undefined {
133
+ for (let index = context.messages.length - 1; index >= 0; index -= 1) {
134
+ const message = context.messages[index] as { role?: string; tokensBefore?: number };
135
+ if (message.role !== "compactionSummary") continue;
136
+ const tokensBefore = message.tokensBefore;
137
+ return {
138
+ index,
139
+ tokensBefore: Number.isFinite(tokensBefore) && tokensBefore !== undefined && tokensBefore > 0 ? Math.floor(tokensBefore) : undefined,
140
+ };
141
+ }
142
+ return undefined;
143
+ }
144
+
120
145
  function getLastAcceptedContextOccupancy(context: Context, model: Model<Api>): number {
146
+ const boundary = getLatestCompactionBoundary(context);
121
147
  for (let index = context.messages.length - 1; index >= 0; index -= 1) {
148
+ if (boundary && index < boundary.index) break;
122
149
  const message = context.messages[index];
123
150
  if (message.role !== "assistant" || !("usage" in message)) continue;
124
151
  const assistant = message as AssistantMessage;
@@ -127,7 +154,9 @@ function getLastAcceptedContextOccupancy(context: Context, model: Model<Api>): n
127
154
  const { usage } = assistant;
128
155
  const total =
129
156
  usage.totalTokens || usage.input + usage.output + usage.cacheRead + usage.cacheWrite;
130
- if (Number.isFinite(total) && total > 0 && total <= model.contextWindow) return total;
157
+ if (!Number.isFinite(total) || total <= 0 || total > model.contextWindow) continue;
158
+ if (boundary?.tokensBefore !== undefined && total >= boundary.tokensBefore) continue;
159
+ return total;
131
160
  }
132
161
  return 0;
133
162
  }
@@ -146,18 +175,50 @@ export function applyCursorApproximateUsage(partial: AssistantMessage, model: Mo
146
175
  );
147
176
  }
148
177
 
178
+ function applyCursorOccupancyEstimate(partial: AssistantMessage, model: Model<Api>, context: Context): void {
179
+ partial.usage.totalTokens = Math.max(
180
+ estimateCursorContextTotalTokens(partial, model, context),
181
+ getLastAcceptedContextOccupancy(context, model),
182
+ );
183
+ }
184
+
185
+ function isCurrentLocalOccupancy(turn: CursorSdkTurnUsage, model: Model<Api>, context: Context): boolean {
186
+ if (!isCursorSdkUsageSafeForPiMessage(turn, model)) return false;
187
+ const tokensBefore = getLatestCompactionBoundary(context)?.tokensBefore;
188
+ return tokensBefore === undefined || turn.inputTokens + turn.outputTokens < tokensBefore;
189
+ }
190
+
191
+ function applyResolvedCursorOccupancy(
192
+ partial: AssistantMessage,
193
+ model: Model<Api>,
194
+ context: Context,
195
+ localTurn: CursorSdkTurnUsage | undefined,
196
+ ): void {
197
+ if (localTurn && isCurrentLocalOccupancy(localTurn, model, context)) {
198
+ partial.usage.totalTokens = localTurn.inputTokens + localTurn.outputTokens;
199
+ return;
200
+ }
201
+ applyCursorOccupancyEstimate(partial, model, context);
202
+ }
203
+
149
204
  export function applyCursorUsage(
150
205
  partial: AssistantMessage,
151
206
  model: Model<Api>,
152
207
  context: Context,
153
208
  sessionInputTokens: number,
154
- sdkUsage?: { runtime: CursorRuntime; turn?: CursorSdkTurnUsage },
209
+ sdkUsage?: CursorSdkUsageApplyOptions,
155
210
  ): void {
156
- // Only local raw turn-ended usage has a captured full-prompt/cache-partition contract.
157
- // Cloud raw usage remains display-only until its field semantics are independently observed.
158
- const usage = sdkUsage?.runtime === "local" ? sdkUsage.turn : undefined;
159
- if (usage && isCursorSdkUsageSafeForPiMessage(usage, model)) {
160
- applyCursorSdkUsage(partial, usage);
211
+ const billed = sdkUsage?.billed;
212
+ const localTurn = sdkUsage?.runtime === "local" ? sdkUsage.turn : undefined;
213
+ if (billed && isCursorSdkUsagePartitionSafe(billed, model)) {
214
+ applyCursorSdkUsage(partial, billed);
215
+ applyResolvedCursorOccupancy(partial, model, context, localTurn);
216
+ return;
217
+ }
218
+ // Only local raw turn-ended usage has a captured full-prompt/cache-partition occupancy contract.
219
+ if (localTurn && isCursorSdkUsageSafeForPiMessage(localTurn, model)) {
220
+ applyCursorSdkUsage(partial, localTurn);
221
+ applyResolvedCursorOccupancy(partial, model, context, localTurn);
161
222
  return;
162
223
  }
163
224
  applyCursorApproximateUsage(partial, model, context, sessionInputTokens);
package/src/index.ts CHANGED
@@ -15,6 +15,7 @@ import { registerCursorFallbackIssueWarning } from "./cursor-fallback-warning.js
15
15
  import { registerCursorAgentsContextDedup } from "./cursor-agents-context-registration.js";
16
16
  import { registerCursorOverflowNormalization } from "./cursor-provider-overflow.js";
17
17
  import { registerCursorSdkSessionProcessErrorGuard } from "./cursor-sdk-process-error-guard.js";
18
+ import { prepareCursorSessionForCompaction } from "./cursor-session-compaction-prep.js";
18
19
 
19
20
  type CursorExtensionApi =
20
21
  & Pick<ExtensionAPI, "registerProvider" | "registerCommand" | "on">
@@ -54,7 +55,6 @@ export default async function (pi: CursorExtensionApi) {
54
55
  registerCursorSessionAgentLifecycle(pi);
55
56
  registerCursorSessionAgentResume(pi);
56
57
  pi.on("session_before_compact", async () => {
57
- const { prepareCursorSessionForCompaction } = await import("./cursor-session-compaction-prep.js");
58
58
  await prepareCursorSessionForCompaction();
59
59
  });
60
60
  registerCursorRuntimeControls(pi);
@@ -5,7 +5,8 @@ import type {
5
5
  ModelSelection,
6
6
  } from "@cursor/sdk";
7
7
  import type { ProviderModelConfig } from "@earendil-works/pi-coding-agent";
8
- import type { ModelThinkingLevel, ThinkingLevelMap } from "@earendil-works/pi-ai/compat";
8
+ import type { ModelThinkingLevel, ThinkingLevelMap } from "@earendil-works/pi-ai";
9
+ import { getCursorModelSelectionIdentities } from "../shared/cursor-model-selection-identities.mjs";
9
10
  import { loadContextWindowCache } from "./context-window-cache.js";
10
11
  import { loadCursorSdk } from "./cursor-sdk-runtime.js";
11
12
  import { resolveCursorApiKey, resolveCursorRuntimeApiKey } from "./cursor-api-key.js";
@@ -176,13 +177,6 @@ function getParamValue(params: ModelParameterValue[], id: string): string | unde
176
177
  return params.find((param) => param.id === id)?.value;
177
178
  }
178
179
 
179
- function encodePiModelId(modelId: string, context?: string, fastOverride?: boolean): string {
180
- const contextQualified = context ? `${modelId}@${context}` : modelId;
181
- if (fastOverride === true) return `${contextQualified}:fast`;
182
- if (fastOverride === false) return `${contextQualified}:slow`;
183
- return contextQualified;
184
- }
185
-
186
180
  function getModelName(item: ModelListItem, context?: string, alias?: string, fastOverride?: boolean): string {
187
181
  const displayName = item.displayName || item.id;
188
182
  const qualifiers: string[] = [];
@@ -193,18 +187,17 @@ function getModelName(item: ModelListItem, context?: string, alias?: string, fas
193
187
  return context ? `${baseName} @ ${context}` : baseName;
194
188
  }
195
189
 
196
- function getFastOverrideBasePiModelId(piModelId: string): string {
197
- return piModelId.replace(/:(?:fast|slow)$/, "");
198
- }
199
-
200
- function getContextWindow(contextWindowCache: Map<string, number>, piModelId: string, context?: string, baseModelId?: string): number {
201
- const fastOverrideBasePiModelId = getFastOverrideBasePiModelId(piModelId);
202
- const contextWindowOverride =
203
- contextWindowCache.get(piModelId) ??
204
- (fastOverrideBasePiModelId !== piModelId ? contextWindowCache.get(fastOverrideBasePiModelId) : undefined);
205
-
190
+ function getContextWindow(
191
+ contextWindowCache: Map<string, number>,
192
+ selectionKeys: readonly string[],
193
+ context?: string,
194
+ baseModelId?: string,
195
+ ): number {
196
+ for (const key of new Set(selectionKeys)) {
197
+ const contextWindow = contextWindowCache.get(key);
198
+ if (contextWindow !== undefined) return contextWindow;
199
+ }
206
200
  return (
207
- contextWindowOverride ??
208
201
  (context ? parseContextWindow(context) : undefined) ??
209
202
  (baseModelId ? contextWindowCache.get(baseModelId) : undefined) ??
210
203
  contextWindowCache.get("default") ??
@@ -219,6 +212,7 @@ function toMetadata(
219
212
  defaultParams: ModelParameterValue[],
220
213
  context: string | undefined,
221
214
  contextWindowCache: Map<string, number>,
215
+ contextWindowKeys: readonly string[],
222
216
  fastOverride?: boolean,
223
217
  ): CursorModelMetadata {
224
218
  const thinkingLevelMap = getThinkingLevelMap(item);
@@ -230,7 +224,7 @@ function toMetadata(
230
224
  displayName: item.displayName || item.id,
231
225
  defaultParams: cloneParams(defaultParams),
232
226
  ...(context ? { context } : {}),
233
- contextWindow: getContextWindow(contextWindowCache, piModelId, context, item.id),
227
+ contextWindow: getContextWindow(contextWindowCache, contextWindowKeys, context, item.id),
234
228
  supportsFast: getParameter(item, "fast") !== undefined,
235
229
  defaultFast: fastValue === "true",
236
230
  ...(fastOverride !== undefined ? { fastOverride } : {}),
@@ -259,78 +253,27 @@ function toModelConfig(metadata: CursorModelMetadata, name: string): ProviderMod
259
253
  };
260
254
  }
261
255
 
262
- function getContextValues(item: ModelListItem): string[] {
263
- return getParameter(item, "context")?.values.map((value) => value.value) ?? [];
264
- }
265
-
266
- function getAmbiguousAliases(items: ModelListItem[]): Set<string> {
267
- const aliasOwners = new Map<string, Set<string>>();
268
- for (const item of items) {
269
- for (const rawAlias of item.aliases ?? []) {
270
- const alias = rawAlias.trim();
271
- if (!alias || alias === item.id) continue;
272
- const owners = aliasOwners.get(alias) ?? new Set<string>();
273
- owners.add(item.id);
274
- aliasOwners.set(alias, owners);
275
- }
276
- }
277
- return new Set([...aliasOwners.entries()].filter(([, owners]) => owners.size > 1).map(([alias]) => alias));
278
- }
279
-
280
- function getModelIds(item: ModelListItem, reservedBaseModelIds: Set<string>, ambiguousAliases: Set<string>): string[] {
281
- const ids = [item.id];
282
- for (const rawAlias of item.aliases ?? []) {
283
- const alias = rawAlias.trim();
284
- if (!alias || alias === item.id || ids.includes(alias) || reservedBaseModelIds.has(alias) || ambiguousAliases.has(alias)) continue;
285
- ids.push(alias);
286
- }
287
- return ids;
288
- }
289
-
290
- function toModelConfigs(
291
- item: ModelListItem,
292
- usedPiModelIds: Set<string>,
293
- reservedBaseModelIds: Set<string>,
294
- ambiguousAliases: Set<string>,
295
- contextWindowCache: Map<string, number>,
296
- ): ProviderModelConfig[] {
297
- const defaultParams = getDefaultParams(item);
298
- const contextValues = getContextValues(item);
299
- const contexts = contextValues.length > 0 ? contextValues : [undefined];
300
- const configs: ProviderModelConfig[] = [];
301
-
302
- const fastOverrides = getParameter(item, "fast") === undefined ? [undefined] : [undefined, true, false];
303
-
304
- for (const selectionModelId of getModelIds(item, reservedBaseModelIds, ambiguousAliases)) {
305
- const alias = selectionModelId === item.id ? undefined : selectionModelId;
306
- for (const context of contexts) {
307
- const contextParams = context ? replaceParam(defaultParams, "context", context) : defaultParams;
308
- for (const fastOverride of fastOverrides) {
309
- const params = fastOverride === undefined ? contextParams : replaceParam(contextParams, "fast", fastOverride ? "true" : "false");
310
- const piModelId = encodePiModelId(selectionModelId, context, fastOverride);
311
- if (usedPiModelIds.has(piModelId)) continue;
312
- usedPiModelIds.add(piModelId);
313
- const metadata = toMetadata(item, piModelId, selectionModelId, params, context, contextWindowCache, fastOverride);
314
- metadataByPiModelId.set(piModelId, metadata);
315
- configs.push(toModelConfig(metadata, getModelName(item, context, alias, fastOverride)));
316
- }
317
- }
318
- }
319
-
320
- return configs;
321
- }
322
-
323
- function sortModelsByBaseId(items: ModelListItem[]): ModelListItem[] {
324
- return [...items].sort((a, b) => a.id.localeCompare(b.id));
325
- }
326
-
327
256
  function registerModelItems(items: ModelListItem[]): ProviderModelConfig[] {
328
257
  metadataByPiModelId.clear();
329
- const usedPiModelIds = new Set<string>();
330
- const reservedBaseModelIds = new Set(items.map((item) => item.id));
331
- const ambiguousAliases = getAmbiguousAliases(items);
332
258
  const contextWindowCache = loadContextWindowCache();
333
- return sortModelsByBaseId(items).flatMap((item) => toModelConfigs(item, usedPiModelIds, reservedBaseModelIds, ambiguousAliases, contextWindowCache));
259
+ return getCursorModelSelectionIdentities(items).map(({ model: item, selectionModelId, context, fastOverride, piModelId, contextWindowKey, baseContextWindowKey }) => {
260
+ const defaultParams = getDefaultParams(item);
261
+ const contextParams = context ? replaceParam(defaultParams, "context", context) : defaultParams;
262
+ const params = fastOverride === undefined ? contextParams : replaceParam(contextParams, "fast", fastOverride ? "true" : "false");
263
+ const metadata = toMetadata(
264
+ item,
265
+ piModelId,
266
+ selectionModelId,
267
+ params,
268
+ context,
269
+ contextWindowCache,
270
+ [piModelId, contextWindowKey, baseContextWindowKey],
271
+ fastOverride,
272
+ );
273
+ metadataByPiModelId.set(piModelId, metadata);
274
+ const alias = selectionModelId === item.id ? undefined : selectionModelId;
275
+ return toModelConfig(metadata, getModelName(item, context, alias, fastOverride));
276
+ });
334
277
  }
335
278
 
336
279
  export function getCursorModelMetadata(modelId: string): CursorModelMetadata | undefined {
@@ -0,0 +1,8 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "compilerOptions": {
4
+ "noEmit": false,
5
+ "declaration": false,
6
+ "sourceMap": false
7
+ }
8
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,16 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "NodeNext",
5
+ "moduleResolution": "NodeNext",
6
+ "strict": true,
7
+ "esModuleInterop": true,
8
+ "skipLibCheck": true,
9
+ "outDir": "dist",
10
+ "rootDir": "src",
11
+ "declaration": true,
12
+ "noEmit": true
13
+ },
14
+ "include": ["src"],
15
+ "exclude": ["node_modules", "dist"]
16
+ }
@@ -1,166 +0,0 @@
1
- # Changes
2
-
3
-
4
- ## 2.0.2
5
-
6
- * Rename bin to `node-which`
7
-
8
- ## 2.0.1
9
-
10
- * generate changelog and publish on version bump
11
- * enforce 100% test coverage
12
- * Promise interface
13
-
14
- ## 2.0.0
15
-
16
- * Parallel tests, modern JavaScript, and drop support for node < 8
17
-
18
- ## 1.3.1
19
-
20
- * update deps
21
- * update travis
22
-
23
- ## v1.3.0
24
-
25
- * Add nothrow option to which.sync
26
- * update tap
27
-
28
- ## v1.2.14
29
-
30
- * appveyor: drop node 5 and 0.x
31
- * travis-ci: add node 6, drop 0.x
32
-
33
- ## v1.2.13
34
-
35
- * test: Pass missing option to pass on windows
36
- * update tap
37
- * update isexe to 2.0.0
38
- * neveragain.tech pledge request
39
-
40
- ## v1.2.12
41
-
42
- * Removed unused require
43
-
44
- ## v1.2.11
45
-
46
- * Prevent changelog script from being included in package
47
-
48
- ## v1.2.10
49
-
50
- * Use env.PATH only, not env.Path
51
-
52
- ## v1.2.9
53
-
54
- * fix for paths starting with ../
55
- * Remove unused `is-absolute` module
56
-
57
- ## v1.2.8
58
-
59
- * bullet items in changelog that contain (but don't start with) #
60
-
61
- ## v1.2.7
62
-
63
- * strip 'update changelog' changelog entries out of changelog
64
-
65
- ## v1.2.6
66
-
67
- * make the changelog bulleted
68
-
69
- ## v1.2.5
70
-
71
- * make a changelog, and keep it up to date
72
- * don't include tests in package
73
- * Properly handle relative-path executables
74
- * appveyor
75
- * Attach error code to Not Found error
76
- * Make tests pass on Windows
77
-
78
- ## v1.2.4
79
-
80
- * Fix typo
81
-
82
- ## v1.2.3
83
-
84
- * update isexe, fix regression in pathExt handling
85
-
86
- ## v1.2.2
87
-
88
- * update deps, use isexe module, test windows
89
-
90
- ## v1.2.1
91
-
92
- * Sometimes windows PATH entries are quoted
93
- * Fixed a bug in the check for group and user mode bits. This bug was introduced during refactoring for supporting strict mode.
94
- * doc cli
95
-
96
- ## v1.2.0
97
-
98
- * Add support for opt.all and -as cli flags
99
- * test the bin
100
- * update travis
101
- * Allow checking for multiple programs in bin/which
102
- * tap 2
103
-
104
- ## v1.1.2
105
-
106
- * travis
107
- * Refactored and fixed undefined error on Windows
108
- * Support strict mode
109
-
110
- ## v1.1.1
111
-
112
- * test +g exes against secondary groups, if available
113
- * Use windows exe semantics on cygwin & msys
114
- * cwd should be first in path on win32, not last
115
- * Handle lower-case 'env.Path' on Windows
116
- * Update docs
117
- * use single-quotes
118
-
119
- ## v1.1.0
120
-
121
- * Add tests, depend on is-absolute
122
-
123
- ## v1.0.9
124
-
125
- * which.js: root is allowed to execute files owned by anyone
126
-
127
- ## v1.0.8
128
-
129
- * don't use graceful-fs
130
-
131
- ## v1.0.7
132
-
133
- * add license to package.json
134
-
135
- ## v1.0.6
136
-
137
- * isc license
138
-
139
- ## 1.0.5
140
-
141
- * Awful typo
142
-
143
- ## 1.0.4
144
-
145
- * Test for path absoluteness properly
146
- * win: Allow '' as a pathext if cmd has a . in it
147
-
148
- ## 1.0.3
149
-
150
- * Remove references to execPath
151
- * Make `which.sync()` work on Windows by honoring the PATHEXT variable.
152
- * Make `isExe()` always return true on Windows.
153
- * MIT
154
-
155
- ## 1.0.2
156
-
157
- * Only files can be exes
158
-
159
- ## 1.0.1
160
-
161
- * Respect the PATHEXT env for win32 support
162
- * should 0755 the bin
163
- * binary
164
- * guts
165
- * package
166
- * 1st