pi-mega-compact 0.4.2 → 0.4.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -333,19 +333,22 @@ export default function (pi: ExtensionAPI) {
333
333
  const dedupStr = storageRate * 100 >= 10
334
334
  ? `${Math.round(storageRate * 100)}%`
335
335
  : `${(storageRate * 100).toFixed(1)}%`;
336
- // saved = cumulative original − stored tokens (this session). Show real
337
- // token counts; use "k" only at/above 1000 so small-but-real savings are
338
- // visible (previously Math.round(x/1000) rounded everything <1000 to 0).
339
- const savedStr = rt.tokensSaved >= 1000
340
- ? `${(rt.tokensSaved / 1000).toFixed(1)}k`
341
- : `${rt.tokensSaved}`;
336
+ // saved = tokens removed from context (cumulative original − stored).
337
+ // Show BOTH this-session (rt.tokensSaved) and repo-wide-total
338
+ // (repo.tokensSaved) so the user sees per-session progress vs the running
339
+ // repo total. "used" = stored checkpoint tokens (repo.totalTokenEstimate
340
+ // vs st.totalTokenEstimate). Use "k" only at/above 1000 so small-but-real
341
+ // numbers stay visible (previously Math.round(x/1000) zeroed <1000).
342
+ const fmt = (x: number) => (x >= 1000 ? `${(x / 1000).toFixed(1)}k` : `${x}`);
343
+ const savedStr = `${fmt(rt.tokensSaved)} sess / ${fmt(repo.tokensSaved)} repo`;
344
+ const usedStr = `${fmt(st.totalTokenEstimate)} sess / ${fmt(repo.totalTokenEstimate)} repo`;
342
345
  const agentStr = activeAgents > 0 ? ` │ 🤖 ${activeAgents} agent${activeAgents === 1 ? "" : "s"}` : "";
343
346
  const turnStr = currentTurn > 0 ? ` │ turn ${currentTurn}` : "";
344
347
  ctx.ui.setWidget(
345
348
  WIDGET_KEY,
346
349
  [
347
350
  ` ⚡ ${config.tier} │ ${tokStr}/${maxStr} tokens (${pctStr}) │ ${st.checkpointCount} chkpt${st.checkpointCount === 1 ? "" : "s"}${agentStr}${turnStr}`,
348
- ` ${triggerLabel} │ dedup: ${dedupStr} │ saved: ${savedStr} tok`,
351
+ ` ${triggerLabel} │ dedup: ${dedupStr} │ used: ${usedStr} │ saved: ${savedStr}`,
349
352
  ],
350
353
  { placement: "aboveEditor" },
351
354
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-mega-compact",
3
- "version": "0.4.2",
3
+ "version": "0.4.4",
4
4
  "description": "Layered, local, vector-backed context compressor for pi — supersede/collapse/cluster compaction with deduped inline recall.",
5
5
  "type": "module",
6
6
  "license": "BSD-2-Clause",
@@ -174,6 +174,38 @@ test("checkAllIntegrity covers every session", () => {
174
174
  assert.ok(reports.every((r: { ok: boolean }) => r.ok));
175
175
  });
176
176
 
177
+ // --- schema migration (v0.4.2: original_token_estimate) -------------------
178
+
179
+ test("Sprint 10 migration: pre-0.4.2 db gains original_token_estimate and repoStats works", () => {
180
+ const dir = join(baseTmp, `run-${counter++}`);
181
+ // Simulate a v0.4.1-era store: openStore() builds the full current schema,
182
+ // then drop the original_token_estimate column that 0.4.2 added. CREATE TABLE
183
+ // IF NOT EXISTS is a no-op on an existing table, which is exactly why the
184
+ // shipped v0.4.2 crashed at runtime on old repos ("no such column").
185
+ const db = openStore(dir);
186
+ db.exec("ALTER TABLE context_chunks DROP COLUMN original_token_estimate;");
187
+ closeStore(dir);
188
+
189
+ // Re-open through the real code path — must ALTER the column in, not crash.
190
+ const vs = new VectorStore({ dedupSim: 0.9, stateDir: dir });
191
+ vs.add({
192
+ sessionId: "sess_mig",
193
+ summary: "legacy region",
194
+ regionText: "some region text to compact",
195
+ tokenEstimate: 100,
196
+ originalTokenEstimate: 500,
197
+ timestamp: 1,
198
+ });
199
+ const repo = vs.repoStats();
200
+ // No "no such column" crash = migration succeeded; totals reflect the new col.
201
+ assert.equal(repo.checkpointCount, 1);
202
+ assert.equal(repo.originalTokens, 500, "originalTokens read from migrated column");
203
+ // Re-open a second time to prove idempotency (column already exists).
204
+ closeStore(dir);
205
+ const vs2 = new VectorStore({ dedupSim: 0.9, stateDir: dir });
206
+ assert.equal(vs2.repoStats().originalTokens, 500, "stable across re-open");
207
+ });
208
+
177
209
  // --- cleanup ---------------------------------------------------------------
178
210
 
179
211
  test("Sprint 10 cleanup", () => {
@@ -190,6 +190,12 @@ function initSchema(db: Database.Database): void {
190
190
  tokenize='trigram'
191
191
  );
192
192
  `);
193
+ // Idempotent column migrations. `CREATE TABLE IF NOT EXISTS` is a no-op on a
194
+ // pre-existing table, so new columns added to context_chunks after a store was
195
+ // first created (e.g. original_token_estimate in v0.4.2) must be ALTERed in for
196
+ // databases created by an older version — otherwise repoStats()/upsert crash
197
+ // with "no such column" and the extension fails to load. Additive only.
198
+ ensureColumn(db, "context_chunks", "original_token_estimate", "INTEGER");
193
199
  const v = db.prepare("SELECT value FROM meta WHERE key='schema_version'").get() as
194
200
  | { value: string }
195
201
  | undefined;
@@ -198,6 +204,19 @@ function initSchema(db: Database.Database): void {
198
204
  }
199
205
  }
200
206
 
207
+ /**
208
+ * Add `column` (with `decl`, e.g. "INTEGER") to `table` if it does not already
209
+ * exist. Idempotent: checks PRAGMA table_info first, so it is safe to run on
210
+ * every open. Table/column/decl are code-controlled constants (never user
211
+ * input), so the unavoidable identifier interpolation here does not violate
212
+ * PREVENT-002 (no external data reaches this SQL).
213
+ */
214
+ function ensureColumn(db: Database.Database, table: string, column: string, decl: string): void {
215
+ const cols = db.prepare(`PRAGMA table_info(${table})`).all() as Array<{ name: string }>;
216
+ if (cols.some((c) => c.name === column)) return;
217
+ db.exec(`ALTER TABLE ${table} ADD COLUMN ${column} ${decl}`);
218
+ }
219
+
201
220
  /** Read a string-valued meta key (or undefined). Used for cumulative counters. */
202
221
  export function getMeta(key: string, stateDir: string = getStateDir()): string | undefined {
203
222
  const db = openStore(stateDir);