@rol1n/memory-log 0.0.0-stage → 0.1.0

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 (37) hide show
  1. package/README.md +53 -2
  2. package/extensions/memory-log/README.md +54 -0
  3. package/extensions/memory-log/agent/consolidator/prompt.ts +47 -0
  4. package/extensions/memory-log/agent/consolidator/tools.ts +160 -0
  5. package/extensions/memory-log/agent/cost.ts +32 -0
  6. package/extensions/memory-log/agent/index.ts +42 -0
  7. package/extensions/memory-log/agent/observer/prompt.ts +73 -0
  8. package/extensions/memory-log/agent/observer/tool.ts +73 -0
  9. package/extensions/memory-log/commands/compact.ts +35 -0
  10. package/extensions/memory-log/commands/consolidate.ts +47 -0
  11. package/extensions/memory-log/commands/status.ts +50 -0
  12. package/extensions/memory-log/config.ts +253 -0
  13. package/extensions/memory-log/debug-log.ts +76 -0
  14. package/extensions/memory-log/hooks/compaction-hook.ts +161 -0
  15. package/extensions/memory-log/hooks/compaction-trigger.ts +160 -0
  16. package/extensions/memory-log/hooks/consolidator-trigger.ts +143 -0
  17. package/extensions/memory-log/hooks/observer-trigger.ts +176 -0
  18. package/extensions/memory-log/ids.ts +78 -0
  19. package/extensions/memory-log/index.ts +149 -0
  20. package/extensions/memory-log/ledger/fold.ts +77 -0
  21. package/extensions/memory-log/ledger/index.ts +7 -0
  22. package/extensions/memory-log/ledger/pool.ts +38 -0
  23. package/extensions/memory-log/ledger/progress.ts +181 -0
  24. package/extensions/memory-log/ledger/projection.ts +115 -0
  25. package/extensions/memory-log/ledger/render.ts +38 -0
  26. package/extensions/memory-log/ledger/serialize.ts +161 -0
  27. package/extensions/memory-log/ledger/types.ts +201 -0
  28. package/extensions/memory-log/memory/index-render.ts +45 -0
  29. package/extensions/memory-log/memory/paths.ts +120 -0
  30. package/extensions/memory-log/memory/session.ts +64 -0
  31. package/extensions/memory-log/runtime.ts +127 -0
  32. package/extensions/memory-log/spawn/launch.ts +133 -0
  33. package/extensions/memory-log/spawn/runs.ts +83 -0
  34. package/extensions/memory-log/tokens.ts +66 -0
  35. package/extensions/memory-log/ui/status-controller.ts +225 -0
  36. package/extensions/memory-log/ui/timeline.ts +103 -0
  37. package/package.json +41 -4
@@ -0,0 +1,103 @@
1
+ import type { Config } from "../config.ts";
2
+ import {
3
+ entryIndexById,
4
+ foldLedger,
5
+ isObservationsRecordedEntry,
6
+ rawTokensAfterIndex,
7
+ rawTokensSinceObservationCoverage,
8
+ type Entry,
9
+ } from "../ledger/index.ts";
10
+
11
+ /** Glyphs for the timeline strip. */
12
+ const GLYPH = {
13
+ consolidated: "▓", // observations promoted to .memory (long-term)
14
+ partial: "▚", // chunk straddling the pool-target boundary (some obs promoted)
15
+ pool: "▒", // observed, still in the short-term buffer
16
+ raw: "░", // raw history not yet distilled into observations
17
+ cut: "┊", // compaction cutoff (verbatim tail began here)
18
+ tip: "▶", // live branch tip
19
+ } as const;
20
+
21
+ /** A committed observation chunk, in branch order. */
22
+ type Chunk = { coversUpToIndex: number; timestamps: string[] };
23
+
24
+ function fmtK(tokens: number): string {
25
+ if (tokens < 1000) return `${tokens}`;
26
+ return `${(tokens / 1000).toFixed(1)}k`;
27
+ }
28
+
29
+ function collectChunks(branch: Entry[]): Chunk[] {
30
+ const indexById = entryIndexById(branch);
31
+ const chunks: Chunk[] = [];
32
+ for (const entry of branch) {
33
+ if (!isObservationsRecordedEntry(entry)) continue;
34
+ const coversUpToIndex = indexById.get(entry.data.coversUpToId);
35
+ if (coversUpToIndex === undefined) continue;
36
+ chunks.push({ coversUpToIndex, timestamps: entry.data.observations.map((o) => o.timestamp) });
37
+ }
38
+ return chunks;
39
+ }
40
+
41
+ /** Branch indices of every compaction entry's `firstKeptEntryId` (the verbatim-tail cutoffs). */
42
+ function compactionCutIndices(branch: Entry[]): number[] {
43
+ const indexById = entryIndexById(branch);
44
+ const cuts: number[] = [];
45
+ for (const entry of branch) {
46
+ if (entry.type !== "compaction") continue;
47
+ const idx = entry.firstKeptEntryId ? indexById.get(entry.firstKeptEntryId) : undefined;
48
+ if (idx !== undefined) cuts.push(idx);
49
+ }
50
+ return cuts;
51
+ }
52
+
53
+ function chunkGlyph(chunk: Chunk, dropped: Set<string>): string {
54
+ let droppedCount = 0;
55
+ for (const ts of chunk.timestamps) if (dropped.has(ts)) droppedCount++;
56
+ if (droppedCount === 0) return GLYPH.pool;
57
+ if (droppedCount === chunk.timestamps.length) return GLYPH.consolidated;
58
+ return GLYPH.partial;
59
+ }
60
+
61
+ function wrap(cells: string[], width: number): string {
62
+ if (cells.length === 0) return "";
63
+ const rows: string[] = [];
64
+ for (let i = 0; i < cells.length; i += width) rows.push(cells.slice(i, i + width).join(""));
65
+ return rows.join("\n");
66
+ }
67
+
68
+ /**
69
+ * Render the full session as a horizontal strip.
70
+ */
71
+ export function renderTimeline(branch: Entry[], config: Config, width = 60): string {
72
+ const folded = foldLedger(branch);
73
+ const dropped = folded.droppedObservationTimestamps;
74
+ const chunks = collectChunks(branch);
75
+
76
+ const cells: string[] = chunks.map((chunk) => chunkGlyph(chunk, dropped));
77
+ const tailTokens = rawTokensSinceObservationCoverage(branch);
78
+ const tailCells = tailTokens > 0 ? Math.ceil(tailTokens / config.chunkTokens) : 0;
79
+ for (let i = 0; i < tailCells; i++) cells.push(GLYPH.raw);
80
+
81
+ const cutPositions = compactionCutIndices(branch)
82
+ .map((cutIdx) => {
83
+ let pos = 0;
84
+ for (const chunk of chunks) if (chunk.coversUpToIndex < cutIdx) pos++;
85
+ return Math.min(pos, cells.length);
86
+ })
87
+ .sort((a, b) => b - a);
88
+ for (const pos of cutPositions) cells.splice(pos, 0, GLYPH.cut);
89
+
90
+ const consolidatedChunks = chunks.filter((c) => chunkGlyph(c, dropped) === GLYPH.consolidated).length;
91
+ const poolChunks = chunks.filter((c) => chunkGlyph(c, dropped) === GLYPH.pool).length;
92
+ const rawTotal = rawTokensAfterIndex(branch, -1);
93
+ const compactions = cutPositions.length;
94
+
95
+ const strip = cells.length > 0 ? `${wrap(cells, width)}${GLYPH.tip}` : "(timeline empty)";
96
+
97
+ return [
98
+ `memory-log timeline · 1 cell ≈ ${fmtK(config.chunkTokens)} tok · cumulative ${fmtK(rawTotal)} raw · ${compactions} compaction${compactions === 1 ? "" : "s"}`,
99
+ strip,
100
+ ``,
101
+ ` ${GLYPH.consolidated} .memory (${consolidatedChunks}) ${GLYPH.pool} pool (${poolChunks}) ${GLYPH.raw} raw ${GLYPH.cut} compaction cut ${GLYPH.tip} tip`,
102
+ ].join("\n");
103
+ }
package/package.json CHANGED
@@ -1,6 +1,43 @@
1
1
  {
2
2
  "name": "@rol1n/memory-log",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "description": "memory-log extension from pi-kit",
6
+ "keywords": [
7
+ "pi-package"
8
+ ],
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+ssh://git@github.com/RolinShmily/pi-kit.git",
12
+ "directory": "extensions/memory-log"
13
+ },
14
+ "homepage": "https://github.com/RolinShmily/pi-kit#readme",
15
+ "bugs": {
16
+ "url": "https://github.com/RolinShmily/pi-kit/issues"
17
+ },
18
+ "engines": {
19
+ "node": ">=22.19.0"
20
+ },
21
+ "publishConfig": {
22
+ "access": "public"
23
+ },
24
+ "files": [
25
+ "extensions/memory-log",
26
+ "extensions/memory-log/README.md"
27
+ ],
28
+ "peerDependencies": {
29
+ "@earendil-works/pi-ai": "*",
30
+ "@earendil-works/pi-agent-core": "*",
31
+ "@earendil-works/pi-coding-agent": "*",
32
+ "@earendil-works/pi-tui": "*",
33
+ "typebox": "*"
34
+ },
35
+ "pi": {
36
+ "extensions": [
37
+ "./extensions/memory-log/index.ts"
38
+ ],
39
+ "skills": [],
40
+ "prompts": [],
41
+ "themes": []
42
+ }
43
+ }