pi-mega-compact 0.4.19 → 0.4.21
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/dist/extensions/conflict-scan.js +201 -0
- package/dist/extensions/dashboard-server.js +47 -2
- package/dist/extensions/mega-compact.js +2 -0
- package/dist/extensions/mega-compact.test.js +9 -4
- package/dist/extensions/mega-conflict-cmds.js +121 -0
- package/dist/extensions/mega-dashboard-cmds.js +89 -7
- package/dist/extensions/mega-pipeline.js +3 -5
- package/dist/extensions/mega-runtime.js +14 -20
- package/dist/src/store/sqlite.js +59 -0
- package/extensions/conflict-scan.ts +209 -0
- package/extensions/dashboard-server.ts +42 -2
- package/extensions/mega-compact.test.ts +9 -4
- package/extensions/mega-compact.ts +2 -0
- package/extensions/mega-conflict-cmds.ts +129 -0
- package/extensions/mega-dashboard-cmds.ts +81 -7
- package/extensions/mega-pipeline.ts +3 -5
- package/extensions/mega-runtime.ts +14 -18
- package/package.json +1 -1
- package/src/store/sqlite.ts +84 -0
|
@@ -90,10 +90,7 @@ export class MegaRuntime {
|
|
|
90
90
|
// on model_select + session_start; persisted to SQL so cost + the dashboard
|
|
91
91
|
// can read it without a live ctx.
|
|
92
92
|
currentModel: ModelSnapshot | undefined;
|
|
93
|
-
// Live "what it's doing right now"
|
|
94
|
-
// compaction; shown in teal while recent, then kept as the last-seen action so
|
|
95
|
-
// the widget is never blank. Cleared on session reset.
|
|
96
|
-
currentActivity: string | undefined;
|
|
93
|
+
// Live "what it's doing right now" timestamp, used for the fresh-window.
|
|
97
94
|
lastActivityAt = 0;
|
|
98
95
|
// Live per-tier dedup trace (Phase 1): e.g. "L0 ✓ → L1 ✓ → L2 0.91 → stored".
|
|
99
96
|
// Built from the store's sync onTier callback during a compaction so the user
|
|
@@ -269,25 +266,25 @@ export class MegaRuntime {
|
|
|
269
266
|
const bar = "▓".repeat(filled) + "░".repeat(10 - filled);
|
|
270
267
|
lines.push(` ${C.green}saved ${fmt(this.rt.tokensSaved)} ${bar}${C.reset} ${pct}% of ${fmt(goal)}`);
|
|
271
268
|
}
|
|
272
|
-
// Live "now processing" line
|
|
273
|
-
//
|
|
269
|
+
// Live "now processing" line + why + recent deduped/compacted events,
|
|
270
|
+
// collapsed to ONE rotating line (fresh only). The ticker ring buffer
|
|
271
|
+
// (≤5 most-recent events) is cycled one-per-repaint so the line scrolls
|
|
272
|
+
// through recent files in real time while activity fires. We rotate on a
|
|
273
|
+
// 250ms step (same cadence as the pulse), using an event counter as the
|
|
274
|
+
// deterministic phase so consecutive repaints advance the visible entry.
|
|
274
275
|
const fresh = Date.now() - this.lastActivityAt < 4000;
|
|
275
276
|
if (this.tierTrace && fresh) {
|
|
276
277
|
lines.push(` ${pulse}${this.tierTrace}`);
|
|
277
|
-
} else if (this.
|
|
278
|
-
|
|
278
|
+
} else if (this.ticker.length > 0) {
|
|
279
|
+
const step = Math.floor(Date.now() / 250);
|
|
280
|
+
const idx = this.ticker.length - 1 - (step % this.ticker.length);
|
|
281
|
+
const head = this.ticker[idx].text;
|
|
282
|
+
const why = this.lastWhy ? ` ${C.gray}· ${this.lastWhy}${C.reset}` : "";
|
|
283
|
+
const more = this.ticker.length > 1 ? ` ${C.dim}(+${this.ticker.length - 1} more)${C.reset}` : "";
|
|
284
|
+
lines.push(` ${fresh ? C.teal : C.dim}${head}${why}${more}${C.reset}`);
|
|
279
285
|
} else if (this.pulsing) {
|
|
280
286
|
lines.push(` ${pulse}${C.teal}compacting…${C.reset}`);
|
|
281
287
|
}
|
|
282
|
-
// Phase 3 — explain-why line (fresh only).
|
|
283
|
-
if (this.lastWhy && fresh) lines.push(` ${C.gray}${this.lastWhy}${C.reset}`);
|
|
284
|
-
// Phase 3 — recall/activity ticker (most-recent first), fresh only.
|
|
285
|
-
if (fresh) {
|
|
286
|
-
for (let i = this.ticker.length - 1; i >= 0; i--) {
|
|
287
|
-
if (lines.length >= 9) break; // leave room for the hint line (MAX 10)
|
|
288
|
-
lines.push(` ${i === this.ticker.length - 1 ? "" : C.dim}${this.ticker[i].text}${C.reset}`);
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
288
|
// Plain-language hint so first-time users understand the widget. Always
|
|
292
289
|
// last, dimmed. "/mega-help explains these terms."
|
|
293
290
|
if (lines.length < 10) {
|
|
@@ -318,7 +315,6 @@ export class MegaRuntime {
|
|
|
318
315
|
this.statusKey = undefined;
|
|
319
316
|
this.activeAgents = 0;
|
|
320
317
|
this.currentTurn = 0;
|
|
321
|
-
this.currentActivity = undefined;
|
|
322
318
|
this.lastActivityAt = 0;
|
|
323
319
|
this.tierTrace = undefined;
|
|
324
320
|
this.ticker.length = 0;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-mega-compact",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.21",
|
|
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",
|
package/src/store/sqlite.ts
CHANGED
|
@@ -415,6 +415,20 @@ function initSchema(db: Database.Database): void {
|
|
|
415
415
|
ts INTEGER
|
|
416
416
|
);
|
|
417
417
|
|
|
418
|
+
-- Durable "save to memory" store (taken over from memory extensions).
|
|
419
|
+
-- One row per saved memory; scoped by repo so memory travels with the
|
|
420
|
+
-- clone. All params are parameterized (PREVENT-002).
|
|
421
|
+
CREATE TABLE IF NOT EXISTS memories (
|
|
422
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
423
|
+
repo TEXT,
|
|
424
|
+
kind TEXT DEFAULT 'note', -- note | fact | decision | preference
|
|
425
|
+
content TEXT NOT NULL,
|
|
426
|
+
tags TEXT, -- JSON array of strings
|
|
427
|
+
created_at INTEGER,
|
|
428
|
+
last_recalled_at INTEGER
|
|
429
|
+
);
|
|
430
|
+
CREATE INDEX IF NOT EXISTS idx_memories_repo ON memories(repo);
|
|
431
|
+
|
|
418
432
|
-- FTS5 trigram virtual table (Sprint 9+ pg_trgm-equivalent verification).
|
|
419
433
|
CREATE VIRTUAL TABLE IF NOT EXISTS context_chunks_trgm USING fts5(
|
|
420
434
|
id UNINDEXED,
|
|
@@ -582,6 +596,76 @@ export function addLesson(
|
|
|
582
596
|
).run(normalizeSessionId(sessionId), repo ?? null, lesson, now);
|
|
583
597
|
}
|
|
584
598
|
|
|
599
|
+
// --- Durable memory (save-to-memory takeover) ---------------------------------
|
|
600
|
+
// One SQLite store for user-saved memories, scoped by repo. Mirrors the
|
|
601
|
+
// lessons/sessions pattern: all state lives in SQLite from day one.
|
|
602
|
+
|
|
603
|
+
export interface MemoryRecord {
|
|
604
|
+
id: number;
|
|
605
|
+
repo: string | null;
|
|
606
|
+
kind: string;
|
|
607
|
+
content: string;
|
|
608
|
+
tags: string[];
|
|
609
|
+
createdAt: number;
|
|
610
|
+
lastRecalledAt: number | null;
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
/** Save a memory to the current repo's store. Returns the new row id. */
|
|
614
|
+
export function addMemory(
|
|
615
|
+
memory: { kind?: string; content: string; tags?: string[] },
|
|
616
|
+
repo: string | null,
|
|
617
|
+
stateDir: string = getStateDir(),
|
|
618
|
+
): number {
|
|
619
|
+
const db = openStore(stateDir);
|
|
620
|
+
const now = Math.floor(Date.now() / 1000);
|
|
621
|
+
const res = db
|
|
622
|
+
.prepare(
|
|
623
|
+
`INSERT INTO memories(repo, kind, content, tags, created_at, last_recalled_at)
|
|
624
|
+
VALUES(?, ?, ?, ?, ?, NULL)`,
|
|
625
|
+
)
|
|
626
|
+
.run(repo ?? null, memory.kind ?? "note", memory.content, JSON.stringify(memory.tags ?? []), now);
|
|
627
|
+
return Number(res.lastInsertRowid);
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
/** List recent memories for a repo (or all repos when repo is null). */
|
|
631
|
+
export function listMemories(repo: string | null, limit = 50, stateDir: string = getStateDir()): MemoryRecord[] {
|
|
632
|
+
const db = openStore(stateDir);
|
|
633
|
+
const rows = repo
|
|
634
|
+
? db.prepare("SELECT * FROM memories WHERE repo = ? ORDER BY created_at DESC LIMIT ?").all(repo, limit)
|
|
635
|
+
: db.prepare("SELECT * FROM memories ORDER BY created_at DESC LIMIT ?").all(limit);
|
|
636
|
+
return (rows as any[]).map(mapMemoryRow);
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
/** Substring search across content + tags. */
|
|
640
|
+
export function searchMemories(query: string, repo: string | null = null, limit = 50, stateDir: string = getStateDir()): MemoryRecord[] {
|
|
641
|
+
const db = openStore(stateDir);
|
|
642
|
+
const like = `%${query}%`;
|
|
643
|
+
const rows = repo
|
|
644
|
+
? db.prepare("SELECT * FROM memories WHERE repo = ? AND (content LIKE ? OR tags LIKE ?) ORDER BY created_at DESC LIMIT ?").all(repo, like, like, limit)
|
|
645
|
+
: db.prepare("SELECT * FROM memories WHERE content LIKE ? OR tags LIKE ? ORDER BY created_at DESC LIMIT ?").all(like, like, limit);
|
|
646
|
+
return (rows as any[]).map(mapMemoryRow);
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
/** Mark a memory as recalled (updates last_recalled_at). Returns true if found. */
|
|
650
|
+
export function recallMemory(id: number, stateDir: string = getStateDir()): boolean {
|
|
651
|
+
const db = openStore(stateDir);
|
|
652
|
+
const now = Math.floor(Date.now() / 1000);
|
|
653
|
+
const res = db.prepare("UPDATE memories SET last_recalled_at = ? WHERE id = ?").run(now, id);
|
|
654
|
+
return res.changes > 0;
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
function mapMemoryRow(row: any): MemoryRecord {
|
|
658
|
+
return {
|
|
659
|
+
id: row.id,
|
|
660
|
+
repo: row.repo ?? null,
|
|
661
|
+
kind: row.kind ?? "note",
|
|
662
|
+
content: row.content ?? "",
|
|
663
|
+
tags: row.tags ? JSON.parse(row.tags) : [],
|
|
664
|
+
createdAt: row.created_at ?? 0,
|
|
665
|
+
lastRecalledAt: row.last_recalled_at ?? null,
|
|
666
|
+
};
|
|
667
|
+
}
|
|
668
|
+
|
|
585
669
|
/** Map a DB row to the public StoredCheckpoint shape. */
|
|
586
670
|
function rowToCheckpoint(row: any): StoredCheckpoint {
|
|
587
671
|
return {
|