@saccolabs/pi-claude-cli 0.4.9 → 0.4.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@saccolabs/pi-claude-cli",
3
- "version": "0.4.9",
3
+ "version": "0.4.10",
4
4
  "description": "Pi coding agent extension that routes LLM calls through the Claude Code CLI",
5
5
  "main": "index.ts",
6
6
  "keywords": [
@@ -131,9 +131,35 @@ export function createEventBridge(
131
131
  cache_creation_input_tokens: 0,
132
132
  };
133
133
  let cycleUsage: ClaudeUsage = {};
134
+ /**
135
+ * Context size of the most recent cycle — what the model actually saw on
136
+ * its last call, which is NOT the episode's summed usage.
137
+ *
138
+ * pi reads `usage.totalTokens` as the conversation's context size
139
+ * (`calculateContextTokens` in pi's compaction module short-circuits on it)
140
+ * and uses it for both the context gauge and the auto-compaction trigger.
141
+ * Every cycle re-sends the same cached prefix, so summing cycles counts
142
+ * that prefix once per cycle: a captured 3-cycle episode sums to 82,174
143
+ * while the model's real context never exceeded 28,243. The host then
144
+ * showed 41% of a 200k window instead of 14%, and a long enough turn
145
+ * crossed pi's compaction threshold at a fraction of true occupancy.
146
+ *
147
+ * So the four component figures stay cumulative — they are what gets
148
+ * billed — and `totalTokens` carries the last cycle's context instead.
149
+ */
150
+ let lastCycleContext = 0;
134
151
  /** Tool ids already surfaced as markers (envelope arrives once per block). */
135
152
  const markedToolIds = new Set<string>();
136
153
 
154
+ /** Prompt size of one cycle: everything the model read, excluding output. */
155
+ function contextOf(usage: ClaudeUsage): number {
156
+ return (
157
+ (usage.input_tokens ?? 0) +
158
+ (usage.cache_read_input_tokens ?? 0) +
159
+ (usage.cache_creation_input_tokens ?? 0)
160
+ );
161
+ }
162
+
137
163
  function recomputeUsage(): void {
138
164
  output.usage.input =
139
165
  cumulativeUsage.input_tokens + (cycleUsage.input_tokens ?? 0);
@@ -145,11 +171,18 @@ export function createEventBridge(
145
171
  output.usage.cacheWrite =
146
172
  cumulativeUsage.cache_creation_input_tokens +
147
173
  (cycleUsage.cache_creation_input_tokens ?? 0);
174
+ // Latch the newest cycle we have numbers for. `applyResult` clears
175
+ // cycleUsage, so a zero here means "no fresher figure", never "empty
176
+ // context" — the last latched value must survive.
177
+ const cycleContext = contextOf(cycleUsage);
178
+ if (cycleContext > 0) lastCycleContext = cycleContext;
148
179
  output.usage.totalTokens =
149
- output.usage.input +
150
- output.usage.output +
151
- output.usage.cacheRead +
152
- output.usage.cacheWrite;
180
+ lastCycleContext > 0
181
+ ? lastCycleContext
182
+ : output.usage.input +
183
+ output.usage.output +
184
+ output.usage.cacheRead +
185
+ output.usage.cacheWrite;
153
186
  calculateCost(model, output.usage);
154
187
  }
155
188