pi-cursor-sdk 0.2.0 → 0.3.7

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 (177) hide show
  1. package/CHANGELOG.md +73 -0
  2. package/README.md +55 -51
  3. package/dist/bundled-context-windows.js +39 -0
  4. package/dist/context-window-cache.js +92 -0
  5. package/dist/context.js +353 -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 +127 -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 +6 -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 +409 -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 +130 -0
  41. package/dist/cursor-pi-context.js +30 -0
  42. package/dist/cursor-pi-tool-bridge-abort.js +114 -0
  43. package/dist/cursor-pi-tool-bridge-constants.js +6 -0
  44. package/dist/cursor-pi-tool-bridge-diagnostics.js +125 -0
  45. package/dist/cursor-pi-tool-bridge-env.js +18 -0
  46. package/dist/cursor-pi-tool-bridge-mcp.js +100 -0
  47. package/dist/cursor-pi-tool-bridge-run.js +355 -0
  48. package/dist/cursor-pi-tool-bridge-server.js +163 -0
  49. package/dist/cursor-pi-tool-bridge-snapshot.js +63 -0
  50. package/dist/cursor-pi-tool-bridge-types.js +1 -0
  51. package/dist/cursor-pi-tool-bridge.js +123 -0
  52. package/dist/cursor-provider-errors.js +311 -0
  53. package/dist/cursor-provider-lazy.js +39 -0
  54. package/dist/cursor-provider-live-run-drain.js +420 -0
  55. package/dist/cursor-provider-overflow.js +90 -0
  56. package/dist/cursor-provider-run-finalizer.js +176 -0
  57. package/dist/cursor-provider-run-outcome.js +76 -0
  58. package/dist/cursor-provider-turn-coordinator.js +293 -0
  59. package/dist/cursor-provider-turn-display-router.js +158 -0
  60. package/dist/cursor-provider-turn-emit.js +41 -0
  61. package/dist/cursor-provider-turn-finalize.js +154 -0
  62. package/dist/cursor-provider-turn-lifecycle-emitter.js +126 -0
  63. package/dist/cursor-provider-turn-prepare.js +379 -0
  64. package/dist/cursor-provider-turn-runner.js +146 -0
  65. package/dist/cursor-provider-turn-sdk-normalizer.js +74 -0
  66. package/dist/cursor-provider-turn-send.js +138 -0
  67. package/dist/cursor-provider-turn-shell-output.js +117 -0
  68. package/dist/cursor-provider-turn-tool-ledger.js +99 -0
  69. package/dist/cursor-provider-turn-types.js +1 -0
  70. package/dist/cursor-provider.js +72 -0
  71. package/dist/cursor-question-tool.js +194 -0
  72. package/dist/cursor-record-utils.js +61 -0
  73. package/dist/cursor-replay-activity-builders.js +114 -0
  74. package/dist/cursor-replay-source-names.js +27 -0
  75. package/dist/cursor-replay-summary-args.js +105 -0
  76. package/dist/cursor-replay-tool-details.js +167 -0
  77. package/dist/cursor-ripgrep-path.js +27 -0
  78. package/dist/cursor-run-final-text.js +63 -0
  79. package/dist/cursor-runtime-state.js +380 -0
  80. package/dist/cursor-sdk-billed-usage.js +86 -0
  81. package/dist/cursor-sdk-event-debug-constants.js +33 -0
  82. package/dist/cursor-sdk-event-debug-session.js +107 -0
  83. package/dist/cursor-sdk-event-debug.js +461 -0
  84. package/dist/cursor-sdk-output-filter.js +1 -0
  85. package/dist/cursor-sdk-process-error-guard.js +221 -0
  86. package/dist/cursor-sdk-runtime.js +3 -0
  87. package/dist/cursor-sensitive-text.js +23 -0
  88. package/dist/cursor-session-agent-cleanup.js +311 -0
  89. package/dist/cursor-session-agent-lifecycle.js +32 -0
  90. package/dist/cursor-session-agent-lineage.js +103 -0
  91. package/dist/cursor-session-agent-resume.js +417 -0
  92. package/dist/cursor-session-agent.js +507 -0
  93. package/dist/cursor-session-compaction-prep.js +21 -0
  94. package/dist/cursor-session-scope.js +113 -0
  95. package/dist/cursor-session-send-policy.js +18 -0
  96. package/dist/cursor-session-store.js +105 -0
  97. package/dist/cursor-session-turn-queue.js +50 -0
  98. package/dist/cursor-setting-sources.js +15 -0
  99. package/dist/cursor-skill-tool.js +230 -0
  100. package/dist/cursor-state.js +519 -0
  101. package/dist/cursor-task-presentation.js +59 -0
  102. package/dist/cursor-tool-lifecycle.js +94 -0
  103. package/dist/cursor-tool-manifest.js +34 -0
  104. package/dist/cursor-tool-presentation-registry.js +305 -0
  105. package/dist/cursor-tool-result-display-readers.js +158 -0
  106. package/dist/cursor-tool-transcript.js +32 -0
  107. package/dist/cursor-tool-visibility.js +28 -0
  108. package/dist/cursor-transcript-tool-formatters.js +618 -0
  109. package/dist/cursor-transcript-tool-specs.js +284 -0
  110. package/dist/cursor-transcript-utils.js +170 -0
  111. package/dist/cursor-usage-accounting.js +166 -0
  112. package/dist/cursor-web-tool-activity.js +23 -0
  113. package/dist/cursor-web-tool-args.js +12 -0
  114. package/dist/index.js +82 -0
  115. package/dist/model-discovery.js +305 -0
  116. package/dist/model-list-cache.js +156 -0
  117. package/docs/cursor-dogfood-checklist.md +2 -1
  118. package/docs/cursor-live-smoke-checklist.md +13 -12
  119. package/docs/cursor-model-ux-spec.md +27 -6
  120. package/docs/cursor-native-tool-replay.md +13 -13
  121. package/docs/cursor-native-tool-visual-audit.md +6 -3
  122. package/docs/cursor-testing-lessons.md +14 -8
  123. package/docs/cursor-tool-surfaces.md +5 -5
  124. package/docs/platform-smoke.md +3 -3
  125. package/package.json +19 -7
  126. package/platform-smoke.config.mjs +1 -1
  127. package/scripts/build.mjs +126 -0
  128. package/scripts/cloud-runtime-smoke.mjs +3 -1
  129. package/scripts/debug-provider-events.mjs +3 -1
  130. package/scripts/debug-sdk-events.mjs +1 -1
  131. package/scripts/isolated-cursor-smoke.sh +5 -4
  132. package/scripts/lib/cloud-smoke-cleanup-evidence.mjs +1 -1
  133. package/scripts/lib/cursor-visual-render.mjs +1 -1
  134. package/scripts/lib/ensure-built.mjs +16 -0
  135. package/scripts/lib/local-resume-smoke-harness.mjs +1 -1
  136. package/scripts/local-resume-smoke.mjs +3 -1
  137. package/scripts/platform-smoke/card-detect.mjs +1 -1
  138. package/scripts/platform-smoke/live-suite-runner.mjs +1 -1
  139. package/scripts/platform-smoke/render-ansi.mjs +1 -1
  140. package/scripts/platform-smoke/targets.mjs +2 -2
  141. package/scripts/prepare.mjs +102 -0
  142. package/scripts/steering-rpc-smoke.mjs +4 -2
  143. package/scripts/tmux-live-smoke.sh +11 -11
  144. package/scripts/visual-tui-smoke-self-test.mjs +4 -1
  145. package/scripts/visual-tui-smoke.mjs +51 -7
  146. package/src/bundled-context-windows.ts +4 -1
  147. package/src/context.ts +27 -4
  148. package/src/cursor-agents-context-registration.ts +2 -2
  149. package/src/cursor-agents-context.ts +24 -10
  150. package/src/cursor-api-key.ts +5 -4
  151. package/src/cursor-cloud-reporting.ts +23 -2
  152. package/src/cursor-context-tools.ts +3 -1
  153. package/src/cursor-fallback-models.generated.ts +592 -204
  154. package/src/cursor-live-run-coordinator.ts +11 -6
  155. package/src/cursor-native-tool-display-registration.ts +10 -11
  156. package/src/cursor-partial-content-emitter.ts +13 -0
  157. package/src/cursor-pi-context.ts +44 -0
  158. package/src/cursor-provider-errors.ts +1 -1
  159. package/src/cursor-provider-lazy.ts +5 -4
  160. package/src/cursor-provider-live-run-drain.ts +14 -9
  161. package/src/cursor-provider-run-finalizer.ts +1 -0
  162. package/src/cursor-provider-turn-coordinator.ts +15 -0
  163. package/src/cursor-provider-turn-emit.ts +6 -0
  164. package/src/cursor-provider-turn-finalize.ts +12 -0
  165. package/src/cursor-provider-turn-prepare.ts +10 -5
  166. package/src/cursor-provider-turn-types.ts +2 -0
  167. package/src/cursor-sdk-billed-usage.ts +107 -0
  168. package/src/cursor-sdk-process-error-guard.ts +6 -4
  169. package/src/cursor-session-agent-lifecycle.ts +8 -9
  170. package/src/cursor-session-agent-resume.ts +23 -0
  171. package/src/cursor-session-compaction-prep.ts +4 -0
  172. package/src/cursor-skill-tool.ts +1 -1
  173. package/src/cursor-tool-manifest.ts +1 -1
  174. package/src/cursor-usage-accounting.ts +70 -9
  175. package/src/index.ts +1 -1
  176. package/tsconfig.build.json +8 -0
  177. package/tsconfig.json +16 -0
@@ -7,6 +7,11 @@ import type {
7
7
  } from "@earendil-works/pi-coding-agent";
8
8
  import { clearCursorSdkHttp1 } from "./cursor-http1.js";
9
9
  import { onCursorSessionScopeKeyChange } from "./cursor-session-scope.js";
10
+ import {
11
+ disposeSessionCursorAgent,
12
+ invalidateSessionAgent,
13
+ resetSessionCursorAgent,
14
+ } from "./cursor-session-agent.js";
10
15
 
11
16
  export interface CursorSessionAgentLifecycleExtensionApi {
12
17
  on(event: "session_shutdown", handler: ExtensionHandler<SessionShutdownEvent>): void;
@@ -18,11 +23,9 @@ export interface CursorSessionAgentLifecycleExtensionApi {
18
23
 
19
24
  export function registerCursorSessionAgentLifecycle(pi: CursorSessionAgentLifecycleExtensionApi): void {
20
25
  onCursorSessionScopeKeyChange(async (previousScopeKey) => {
21
- const { disposeSessionCursorAgent } = await import("./cursor-session-agent.js");
22
26
  await disposeSessionCursorAgent(previousScopeKey);
23
27
  });
24
28
  pi.on("session_shutdown", async (event) => {
25
- const { disposeSessionCursorAgent, resetSessionCursorAgent } = await import("./cursor-session-agent.js");
26
29
  try {
27
30
  if (event.reason === "reload") {
28
31
  await resetSessionCursorAgent();
@@ -33,20 +36,16 @@ export function registerCursorSessionAgentLifecycle(pi: CursorSessionAgentLifecy
33
36
  clearCursorSdkHttp1();
34
37
  }
35
38
  });
36
- pi.on("session_compact", async () => {
37
- const { invalidateSessionAgent } = await import("./cursor-session-agent.js");
39
+ pi.on("session_compact", () => {
38
40
  invalidateSessionAgent();
39
41
  });
40
- pi.on("session_before_tree", async () => {
41
- const { invalidateSessionAgent } = await import("./cursor-session-agent.js");
42
+ pi.on("session_before_tree", () => {
42
43
  invalidateSessionAgent();
43
44
  });
44
45
  pi.on("session_tree", async () => {
45
- const { resetSessionCursorAgent } = await import("./cursor-session-agent.js");
46
46
  await resetSessionCursorAgent();
47
47
  });
48
- pi.on("model_select", async () => {
49
- const { invalidateSessionAgent } = await import("./cursor-session-agent.js");
48
+ pi.on("model_select", () => {
50
49
  invalidateSessionAgent();
51
50
  });
52
51
  }
@@ -81,6 +81,19 @@ const state: CursorSessionResumeState = {
81
81
  unownedUserEntryIds: new Set(),
82
82
  };
83
83
 
84
+ // Compaction summarizer sends commit a one-message resume handle. Drop it so the
85
+ // next normal turn_end cannot persist that lineage (#223).
86
+ let resumeHandlePersistSuppressed = false;
87
+
88
+ export function suppressCursorSessionAgentResumeHandlePersist(): void {
89
+ resumeHandlePersistSuppressed = true;
90
+ state.pendingHandle = undefined;
91
+ }
92
+
93
+ export function allowCursorSessionAgentResumeHandlePersist(): void {
94
+ resumeHandlePersistSuppressed = false;
95
+ }
96
+
84
97
  function hashParts(parts: readonly string[]): string {
85
98
  const hash = createHash("sha256");
86
99
  for (const part of parts) {
@@ -360,6 +373,7 @@ export function getMatchingCursorSessionAgentResumeHandle(poolKey: string): Curs
360
373
  }
361
374
 
362
375
  export function persistCursorSessionAgentResumeHandle(input: PendingCursorSessionAgentResumeHandle): void {
376
+ if (resumeHandlePersistSuppressed) return;
363
377
  if (!isCursorLocalAgentId(input.agentId)) return;
364
378
  state.pendingHandle = {
365
379
  runtime: input.runtime,
@@ -371,6 +385,11 @@ export function persistCursorSessionAgentResumeHandle(input: PendingCursorSessio
371
385
  }
372
386
 
373
387
  function flushPendingCursorSessionAgentResumeHandle(branch: readonly SessionEntry[]): void {
388
+ if (resumeHandlePersistSuppressed) {
389
+ state.pendingHandle = undefined;
390
+ restoreFromBranch(branch);
391
+ return;
392
+ }
374
393
  restoreFromBranch(branch);
375
394
  const pending = state.pendingHandle;
376
395
  state.pendingHandle = undefined;
@@ -442,6 +461,8 @@ export function registerCursorSessionAgentResume(pi: CursorSessionAgentResumeExt
442
461
  restoreFromSessionManager(ctx.sessionManager);
443
462
  });
444
463
  pi.on("session_compact", (event, ctx) => {
464
+ state.pendingHandle = undefined;
465
+ resumeHandlePersistSuppressed = false;
445
466
  const branch = ctx.sessionManager.getBranch();
446
467
  if (branch.length > 0) {
447
468
  restoreFromSessionManager(ctx.sessionManager);
@@ -471,6 +492,7 @@ function resetStateForTests(): void {
471
492
  state.lastBranchHandle = undefined;
472
493
  state.pendingHandle = undefined;
473
494
  state.unownedUserEntryIds = new Set();
495
+ resumeHandlePersistSuppressed = false;
474
496
  }
475
497
 
476
498
  export const __testUtils = {
@@ -479,4 +501,5 @@ export const __testUtils = {
479
501
  reset: resetStateForTests,
480
502
  set: setStateForTests,
481
503
  state,
504
+ isResumeHandlePersistSuppressed: () => resumeHandlePersistSuppressed,
482
505
  };
@@ -1,15 +1,19 @@
1
1
  import { cursorLiveRuns } from "./cursor-provider-live-run-drain.js";
2
2
  import { resetSessionCursorAgent } from "./cursor-session-agent.js";
3
+ import { suppressCursorSessionAgentResumeHandlePersist } from "./cursor-session-agent-resume.js";
3
4
  import { getCursorSessionScopeKey } from "./cursor-session-scope.js";
4
5
 
5
6
  /**
6
7
  * Prepare the pooled Cursor session agent for pi compaction summarization.
7
8
  * Releases any scoped live-run drain state still tied to the pooled agent, then
8
9
  * disposes the pool entry so summarization acquires a clean SDK agent.
10
+ * Suppresses local-resume handle persist so the summarizer send cannot leak
11
+ * a one-message handle into the next normal turn (#223).
9
12
  */
10
13
  export async function prepareCursorSessionForCompaction(
11
14
  scopeKey: string = getCursorSessionScopeKey(),
12
15
  ): Promise<void> {
16
+ suppressCursorSessionAgentResumeHandlePersist();
13
17
  while (true) {
14
18
  const run = cursorLiveRuns.getActiveForScope(scopeKey);
15
19
  if (!run || run.disposed) break;
@@ -18,7 +18,7 @@ import { resolveEffectiveCursorConfigForContext } from "./cursor-runtime-state.j
18
18
  export const CURSOR_ACTIVATE_SKILL_TOOL_NAME = "cursor_activate_skill";
19
19
  export const CURSOR_ACTIVATE_SKILL_MCP_NAME = "pi__cursor_activate_skill";
20
20
 
21
- const AVAILABLE_SKILLS_SECTION_PATTERN = /\n\nThe following skills provide specialized instructions for specific tasks\.[\s\S]*?<\/available_skills>/;
21
+ const AVAILABLE_SKILLS_SECTION_PATTERN = /<skills>\nThe following skills provide specialized instructions for specific tasks\.[\s\S]*?<\/available_skills>\n<\/skills>|\n\nThe following skills provide specialized instructions for specific tasks\.[\s\S]*?<\/available_skills>/;
22
22
  const MAX_SKILL_RESOURCES = 80;
23
23
  const RESOURCE_DIR_NAMES = ["scripts", "references", "assets"] as const;
24
24
 
@@ -4,7 +4,7 @@ import type { CursorPiToolBridgeSnapshot } from "./cursor-pi-tool-bridge-types.j
4
4
  export const CURSOR_TOOL_MANIFEST_ENV = "PI_CURSOR_TOOL_MANIFEST";
5
5
 
6
6
  /**
7
- * Representative @cursor/sdk@1.0.23 local-agent ToolType values; actual exposure can vary by run.
7
+ * Representative @cursor/sdk@1.0.27 local-agent ToolType values; actual exposure can vary by run.
8
8
  * See docs/cursor-native-tool-replay.md#sdk-tooltype-replay-matrix.
9
9
  */
10
10
  export const CURSOR_HOST_TOOL_MANIFEST_SUMMARY =
@@ -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);
@@ -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
+ }