@chendpoc/pi-memory 0.2.4 → 0.3.2

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 (164) hide show
  1. package/README.md +92 -43
  2. package/dist/adapters/llm/standalone.js +1 -1
  3. package/dist/cli/init.js +20 -3
  4. package/dist/cli/parseArgs.d.ts +5 -2
  5. package/dist/cli/parseArgs.js +13 -0
  6. package/dist/cli/schedulerSync.d.ts +2 -0
  7. package/dist/cli/schedulerSync.js +26 -0
  8. package/dist/cli/status.d.ts +4 -40
  9. package/dist/cli/status.js +6 -230
  10. package/dist/cli/theme.d.ts +2 -0
  11. package/dist/cli/theme.js +8 -0
  12. package/dist/cli.js +5 -0
  13. package/dist/commands/status.js +2 -2
  14. package/dist/compact/parseMemoryExport.js +2 -1
  15. package/dist/compact/register.js +1 -1
  16. package/dist/compact/runSummary.js +1 -16
  17. package/dist/compact/subagentDelta.js +3 -5
  18. package/dist/consolidate/index.d.ts +1 -0
  19. package/dist/consolidate/index.js +1 -0
  20. package/dist/consolidate/mergeEntries.js +3 -3
  21. package/dist/consolidate/mergeMemoryEntries.d.ts +8 -0
  22. package/dist/consolidate/mergeMemoryEntries.js +23 -0
  23. package/dist/consolidate/mergePrompt.js +2 -2
  24. package/dist/consolidate/mergeWithLlm.js +2 -2
  25. package/dist/consolidate/runJob.d.ts +2 -2
  26. package/dist/consolidate/runJob.js +6 -12
  27. package/dist/consolidate/scheduler.d.ts +2 -2
  28. package/dist/consolidate/scheduler.js +1 -1
  29. package/dist/constants/env.d.ts +1 -0
  30. package/dist/constants/env.js +1 -0
  31. package/dist/constants/paths.d.ts +3 -1
  32. package/dist/constants/paths.js +6 -1
  33. package/dist/extension/createMemoryRuntime.d.ts +3 -0
  34. package/dist/extension/createMemoryRuntime.js +203 -0
  35. package/dist/extension/index.d.ts +2 -0
  36. package/dist/extension/index.js +1 -0
  37. package/dist/extension/lifecycle.d.ts +28 -0
  38. package/dist/extension/lifecycle.js +52 -0
  39. package/dist/extension/messageUtils.d.ts +4 -0
  40. package/dist/extension/messageUtils.js +18 -0
  41. package/dist/extension/types.d.ts +35 -0
  42. package/dist/extension/types.js +1 -0
  43. package/dist/index.d.ts +7 -9
  44. package/dist/index.js +7 -9
  45. package/dist/pi-extension.js +26 -236
  46. package/dist/preflight/episodic.js +13 -30
  47. package/dist/preflight/queryIntent.js +1 -1
  48. package/dist/preflight/render.js +1 -1
  49. package/dist/preflight/strip.d.ts +0 -1
  50. package/dist/preflight/strip.js +0 -24
  51. package/dist/redaction/index.d.ts +4 -0
  52. package/dist/redaction/index.js +3 -0
  53. package/dist/redaction/patterns/constants.d.ts +6 -0
  54. package/dist/redaction/patterns/constants.js +6 -0
  55. package/dist/redaction/patterns/crypto.d.ts +3 -0
  56. package/dist/redaction/patterns/crypto.js +17 -0
  57. package/dist/redaction/patterns/generic.d.ts +3 -0
  58. package/dist/redaction/patterns/generic.js +38 -0
  59. package/dist/redaction/patterns/index.d.ts +16 -0
  60. package/dist/redaction/patterns/index.js +23 -0
  61. package/dist/redaction/patterns/llm.d.ts +3 -0
  62. package/dist/redaction/patterns/llm.js +144 -0
  63. package/dist/redaction/patterns/platform.d.ts +3 -0
  64. package/dist/redaction/patterns/platform.js +87 -0
  65. package/dist/redaction/patterns/types.d.ts +18 -0
  66. package/dist/redaction/patterns/types.js +1 -0
  67. package/dist/redaction/redactText.d.ts +9 -0
  68. package/dist/redaction/redactText.js +31 -0
  69. package/dist/redaction/types.d.ts +19 -0
  70. package/dist/redaction/types.js +1 -0
  71. package/dist/redaction/utils.d.ts +28 -0
  72. package/dist/redaction/utils.js +106 -0
  73. package/dist/scheduler/index.d.ts +3 -0
  74. package/dist/scheduler/index.js +3 -0
  75. package/dist/scheduler/launchd.d.ts +14 -0
  76. package/dist/scheduler/launchd.js +69 -0
  77. package/dist/scheduler/launchdPlist.d.ts +14 -0
  78. package/dist/scheduler/launchdPlist.js +62 -0
  79. package/dist/scheduler/sync.d.ts +36 -0
  80. package/dist/scheduler/sync.js +79 -0
  81. package/dist/shutdown/enqueue.d.ts +1 -1
  82. package/dist/shutdown/enqueue.js +2 -5
  83. package/dist/shutdown/readQueue.js +1 -1
  84. package/dist/shutdown/runDrainJob.js +8 -37
  85. package/dist/shutdown/sessionReader.js +1 -14
  86. package/dist/sidecar/client.d.ts +6 -2
  87. package/dist/sidecar/client.js +49 -14
  88. package/dist/{preflight → sidecar}/queryCache.d.ts +1 -1
  89. package/dist/sidecar/reindexBridge.js +2 -2
  90. package/dist/sidecar/server/server.js +1 -1
  91. package/dist/sidecar/server/vec/chunkQuery.d.ts +4 -0
  92. package/dist/sidecar/server/vec/chunkQuery.js +46 -0
  93. package/dist/sidecar/server/vec/chunkReindex.d.ts +5 -0
  94. package/dist/sidecar/server/vec/chunkReindex.js +40 -0
  95. package/dist/sidecar/server/vec/embeddingCodec.d.ts +2 -0
  96. package/dist/sidecar/server/vec/embeddingCodec.js +6 -0
  97. package/dist/sidecar/server/vec/schema.d.ts +20 -0
  98. package/dist/sidecar/server/vec/schema.js +61 -0
  99. package/dist/sidecar/server/vec/store.d.ts +2 -13
  100. package/dist/sidecar/server/vec/store.js +12 -139
  101. package/dist/sidecar/sidecarManager.js +4 -58
  102. package/dist/sidecar/spawnLock.d.ts +2 -0
  103. package/dist/sidecar/spawnLock.js +57 -0
  104. package/dist/sidecar/syncIndex.d.ts +3 -0
  105. package/dist/sidecar/syncIndex.js +12 -0
  106. package/dist/sidecar/warmup.js +1 -1
  107. package/dist/status/copy.d.ts +2 -0
  108. package/dist/status/copy.js +2 -0
  109. package/dist/status/format.d.ts +7 -0
  110. package/dist/status/format.js +133 -0
  111. package/dist/status/gather.d.ts +2 -0
  112. package/dist/status/gather.js +88 -0
  113. package/dist/status/index.d.ts +4 -0
  114. package/dist/status/index.js +3 -0
  115. package/dist/status/types.d.ts +33 -0
  116. package/dist/status/types.js +1 -0
  117. package/dist/store/consolidatePort.d.ts +11 -0
  118. package/dist/store/consolidatePort.js +1 -0
  119. package/dist/store/index.d.ts +2 -0
  120. package/dist/store/index.js +1 -0
  121. package/dist/store/ingestEntries.d.ts +16 -0
  122. package/dist/store/ingestEntries.js +22 -0
  123. package/dist/{init/workspace.d.ts → store/initWorkspace.d.ts} +1 -1
  124. package/dist/{init/workspace.js → store/initWorkspace.js} +7 -5
  125. package/dist/store/listeners.d.ts +11 -0
  126. package/dist/store/listeners.js +27 -0
  127. package/dist/store/markdown/insert.d.ts +3 -0
  128. package/dist/store/markdown/insert.js +23 -0
  129. package/dist/store/memoryStore.d.ts +9 -22
  130. package/dist/store/memoryStore.js +71 -205
  131. package/dist/store/resolveEntries.d.ts +11 -0
  132. package/dist/store/resolveEntries.js +23 -0
  133. package/dist/store/types.d.ts +0 -1
  134. package/dist/store/writePath.d.ts +20 -0
  135. package/dist/store/writePath.js +123 -0
  136. package/dist/ui/memoryStatusWidget.d.ts +4 -8
  137. package/dist/ui/memoryStatusWidget.js +5 -17
  138. package/dist/utils/async.d.ts +11 -0
  139. package/dist/utils/async.js +24 -0
  140. package/dist/utils/index.d.ts +5 -1
  141. package/dist/utils/index.js +5 -1
  142. package/dist/{ipc/jsonlFramer.d.ts → utils/jsonl.d.ts} +1 -1
  143. package/dist/{ipc/jsonlFramer.js → utils/jsonl.js} +1 -1
  144. package/dist/utils/memory/index.d.ts +10 -0
  145. package/dist/utils/memory/index.js +43 -0
  146. package/dist/utils/paths.d.ts +4 -0
  147. package/dist/utils/paths.js +13 -3
  148. package/dist/utils/scheduler.d.ts +1 -1
  149. package/dist/utils/scheduler.js +6 -6
  150. package/dist/{preflight/session.d.ts → utils/session/index.d.ts} +1 -0
  151. package/dist/{preflight/session.js → utils/session/index.js} +5 -2
  152. package/doc/LAUNCH-KIT.md +229 -0
  153. package/doc/README-zh.md +445 -0
  154. package/doc/ROADMAP-zh.md +114 -0
  155. package/doc/ROADMAP.md +114 -0
  156. package/package.json +16 -4
  157. package/scripts/postinstall.mjs +11 -1
  158. package/templates/com.pi.memory.consolidate.plist.example +41 -0
  159. package/templates/consolidate.cmd.example +15 -0
  160. package/templates/crontab.example +14 -0
  161. package/templates/schtasks.example.txt +34 -0
  162. package/dist/consolidate/entryKey.d.ts +0 -5
  163. package/dist/consolidate/entryKey.js +0 -4
  164. /package/dist/{preflight → sidecar}/queryCache.js +0 -0
@@ -1,161 +1,34 @@
1
1
  import { createRequire } from "node:module";
2
2
  import { ensureDirSync, pathDirname } from "../../../utils/fs.js";
3
- import { readRetrievalConfig } from "../../../config/retrieval.js";
4
- import { getEmbedder } from "./embedder.js";
5
- import { cosineSimilarity, distanceToRelevance, mmrSelect } from "./mmr.js";
3
+ import { queryChunks } from "./chunkQuery.js";
4
+ import { reindexChunks } from "./chunkReindex.js";
5
+ import { embeddingMetaMatches, getChunkCount, getIndexGeneration, getStoredEmbeddingMeta, initVecSchema, } from "./schema.js";
6
6
  const require = createRequire(import.meta.url);
7
- function embeddingToBlob(embedding) {
8
- return Buffer.from(embedding.buffer, embedding.byteOffset, embedding.byteLength);
9
- }
10
- function blobToEmbedding(blob) {
11
- return new Float32Array(blob.buffer, blob.byteOffset, blob.byteLength / 4);
12
- }
13
7
  export class VecStore {
14
8
  db;
15
9
  constructor(dbPath) {
16
10
  ensureDirSync(pathDirname(dbPath));
17
11
  const Database = require("better-sqlite3");
18
12
  this.db = new Database(dbPath);
19
- this.initSchema();
20
- }
21
- initSchema() {
22
- this.db.exec(`
23
- CREATE TABLE IF NOT EXISTS meta (
24
- key TEXT PRIMARY KEY,
25
- value TEXT NOT NULL
26
- );
27
- CREATE TABLE IF NOT EXISTS memory_chunks (
28
- chunk_id TEXT PRIMARY KEY,
29
- content TEXT NOT NULL,
30
- source TEXT NOT NULL,
31
- timestamp TEXT NOT NULL,
32
- embedding BLOB NOT NULL
33
- );
34
- `);
13
+ initVecSchema(this.db);
35
14
  }
36
15
  getStoredEmbeddingMeta() {
37
- const read = (key) => this.db.prepare("SELECT value FROM meta WHERE key = ?").pluck().get(key);
38
- const model = read("embedding_model");
39
- const provider = read("embedding_provider");
40
- const dimRaw = read("embedding_dim");
41
- if (!model || !provider || !dimRaw)
42
- return null;
43
- const dim = Number.parseInt(dimRaw, 10);
44
- if (!Number.isFinite(dim))
45
- return null;
46
- return { model, provider, dim };
16
+ return getStoredEmbeddingMeta(this.db);
47
17
  }
48
18
  embeddingMetaMatches(embedder) {
49
- const stored = this.getStoredEmbeddingMeta();
50
- if (!stored)
51
- return true;
52
- return (stored.model === embedder.model &&
53
- stored.provider === embedder.provider &&
54
- stored.dim === embedder.dim);
19
+ return embeddingMetaMatches(this.db, embedder);
55
20
  }
56
21
  getIndexGeneration() {
57
- const raw = this.db.prepare("SELECT value FROM meta WHERE key = 'index_generation'").pluck().get();
58
- return Number(raw ?? "0");
22
+ return getIndexGeneration(this.db);
59
23
  }
60
24
  getChunkCount() {
61
- const row = this.db.prepare("SELECT COUNT(*) AS count FROM memory_chunks").get();
62
- return row.count;
63
- }
64
- clearChunksIfEmbeddingMismatch(embedder) {
65
- if (this.embeddingMetaMatches(embedder))
66
- return;
67
- this.db.prepare("DELETE FROM memory_chunks").run();
68
- }
69
- bumpIndexGeneration() {
70
- const generation = this.getIndexGeneration() + 1;
71
- this.db
72
- .prepare("INSERT INTO meta(key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value")
73
- .run("index_generation", String(generation));
74
- return generation;
75
- }
76
- writeEmbeddingMeta(embedder) {
77
- const upsert = this.db.prepare("INSERT INTO meta(key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value");
78
- upsert.run("embedding_model", embedder.model);
79
- upsert.run("embedding_provider", embedder.provider);
80
- upsert.run("embedding_dim", String(embedder.dim));
25
+ return getChunkCount(this.db);
81
26
  }
82
- async reindex(documents) {
83
- const embedder = getEmbedder();
84
- this.clearChunksIfEmbeddingMismatch(embedder);
85
- if (documents.length === 0) {
86
- const indexGeneration = this.getIndexGeneration();
87
- this.writeEmbeddingMeta(embedder);
88
- return { indexed: 0, indexGeneration };
89
- }
90
- const embeddings = await embedder.embedBatch(documents.map((doc) => doc.content));
91
- const sync = this.db.transaction((docs, vectors) => {
92
- const incomingIds = new Set(docs.map((doc) => doc.id));
93
- const existing = this.db.prepare("SELECT chunk_id FROM memory_chunks").all();
94
- const deleteChunk = this.db.prepare("DELETE FROM memory_chunks WHERE chunk_id = ?");
95
- for (const row of existing) {
96
- if (!incomingIds.has(row.chunk_id)) {
97
- deleteChunk.run(row.chunk_id);
98
- }
99
- }
100
- const upsert = this.db.prepare(`
101
- INSERT INTO memory_chunks(chunk_id, content, source, timestamp, embedding)
102
- VALUES (?, ?, ?, ?, ?)
103
- ON CONFLICT(chunk_id) DO UPDATE SET
104
- content = excluded.content,
105
- source = excluded.source,
106
- timestamp = excluded.timestamp,
107
- embedding = excluded.embedding
108
- `);
109
- for (let i = 0; i < docs.length; i++) {
110
- const doc = docs[i];
111
- upsert.run(doc.id, doc.content, doc.source, doc.timestamp, embeddingToBlob(vectors[i]));
112
- }
113
- const indexGeneration = this.bumpIndexGeneration();
114
- this.writeEmbeddingMeta(embedder);
115
- return { indexed: docs.length, indexGeneration };
116
- });
117
- return sync(documents, embeddings);
27
+ reindex(documents) {
28
+ return reindexChunks(this.db, documents);
118
29
  }
119
- async query(queryText, topK) {
120
- const retrieval = readRetrievalConfig();
121
- const limit = topK ?? retrieval.topK;
122
- const embedder = getEmbedder();
123
- this.clearChunksIfEmbeddingMismatch(embedder);
124
- const queryEmbedding = await embedder.embed(queryText);
125
- const rows = this.db
126
- .prepare("SELECT chunk_id, content, source, timestamp, embedding FROM memory_chunks")
127
- .all();
128
- if (rows.length === 0)
129
- return [];
130
- const candidates = [];
131
- for (const row of rows) {
132
- const embedding = blobToEmbedding(row.embedding);
133
- if (embedding.length !== queryEmbedding.length)
134
- continue;
135
- const similarity = cosineSimilarity(queryEmbedding, embedding);
136
- if (similarity < retrieval.minRelevance)
137
- continue;
138
- candidates.push({
139
- chunkId: row.chunk_id,
140
- content: row.content,
141
- source: row.source,
142
- timestamp: row.timestamp,
143
- distance: 1 - similarity,
144
- embedding,
145
- });
146
- }
147
- if (candidates.length === 0)
148
- return [];
149
- candidates.sort((a, b) => a.distance - b.distance);
150
- const poolSize = Math.min(limit * retrieval.candidatePoolMultiplier, candidates.length);
151
- const pool = candidates.slice(0, poolSize);
152
- const selected = mmrSelect(queryEmbedding, pool, limit, retrieval.mmrLambda);
153
- return selected.map((item) => ({
154
- content: item.content,
155
- source: item.source,
156
- timestamp: item.timestamp,
157
- relevance: distanceToRelevance(item.distance),
158
- }));
30
+ query(queryText, topK) {
31
+ return queryChunks(this.db, queryText, topK);
159
32
  }
160
33
  close() {
161
34
  this.db.close();
@@ -1,12 +1,10 @@
1
- // Agent 侧:connect-or-create、spawn lock、execa 生命周期
2
- import { readFileSync, unlinkSync, writeFileSync } from "node:fs";
1
+ // Agent 侧:connect-or-create、execa 生命周期
3
2
  import { execa } from "execa";
4
- import { SIDECAR_SPAWN_LOCK_FILE } from "../constants/paths.js";
5
- import { SIDECAR_FORCE_KILL_DELAY_MS, SIDECAR_SPAWN_LOCK_STALE_MS, SIDECAR_START_TIMEOUT_MS, } from "../constants/timing.js";
6
- import { ensureDirSync, joinPath, pathDirname, pathExists } from "../utils/fs.js";
7
- import { nowMs } from "../utils/time.js";
3
+ import { SIDECAR_FORCE_KILL_DELAY_MS, SIDECAR_START_TIMEOUT_MS } from "../constants/timing.js";
4
+ import { ensureDirSync, pathDirname } from "../utils/fs.js";
8
5
  import { ping } from "./client.js";
9
6
  import { resolveSidecarEntry } from "./paths.js";
7
+ import { acquireSpawnLock, releaseSpawnLock } from "./spawnLock.js";
10
8
  import { canConnect, waitUntilReady } from "./utils.js";
11
9
  export { resolveSidecarEntry } from "./paths.js";
12
10
  const START_TIMEOUT_MS = SIDECAR_START_TIMEOUT_MS;
@@ -56,55 +54,3 @@ class SidecarManager {
56
54
  this.child = undefined;
57
55
  }
58
56
  }
59
- function spawnLockPath(socketPath) {
60
- return joinPath(pathDirname(socketPath), SIDECAR_SPAWN_LOCK_FILE);
61
- }
62
- function acquireSpawnLock(socketPath) {
63
- const lockPath = spawnLockPath(socketPath);
64
- for (let i = 0; i < 5; i++) {
65
- try {
66
- writeFileSync(lockPath, `${process.pid}\n${nowMs()}\n`, { flag: "wx" });
67
- return true;
68
- }
69
- catch (error) {
70
- if (error.code !== "EEXIST")
71
- throw error;
72
- if (isLockStale(lockPath)) {
73
- try {
74
- unlinkSync(lockPath);
75
- }
76
- catch { }
77
- continue;
78
- }
79
- return false;
80
- }
81
- }
82
- return false;
83
- }
84
- function isLockStale(lockPath) {
85
- if (!pathExists(lockPath))
86
- return false;
87
- try {
88
- const [pidLine = "", tsLine = "0"] = readFileSync(lockPath, "utf8").trim().split("\n");
89
- const pid = Number.parseInt(pidLine, 10);
90
- const ts = Number.parseInt(tsLine, 10);
91
- if (Number.isFinite(pid)) {
92
- try {
93
- process.kill(pid, 0);
94
- }
95
- catch {
96
- return true;
97
- }
98
- }
99
- return !Number.isFinite(ts) || nowMs() - ts > SIDECAR_SPAWN_LOCK_STALE_MS;
100
- }
101
- catch {
102
- return true;
103
- }
104
- }
105
- function releaseSpawnLock(socketPath) {
106
- try {
107
- unlinkSync(spawnLockPath(socketPath));
108
- }
109
- catch { }
110
- }
@@ -0,0 +1,2 @@
1
+ export declare function acquireSpawnLock(socketPath: string): boolean;
2
+ export declare function releaseSpawnLock(socketPath: string): void;
@@ -0,0 +1,57 @@
1
+ import { readFileSync, unlinkSync, writeFileSync } from "node:fs";
2
+ import { SIDECAR_SPAWN_LOCK_FILE } from "../constants/paths.js";
3
+ import { SIDECAR_SPAWN_LOCK_STALE_MS } from "../constants/timing.js";
4
+ import { joinPath, pathDirname, pathExists } from "../utils/fs.js";
5
+ import { nowMs } from "../utils/time.js";
6
+ function spawnLockPath(socketPath) {
7
+ return joinPath(pathDirname(socketPath), SIDECAR_SPAWN_LOCK_FILE);
8
+ }
9
+ export function acquireSpawnLock(socketPath) {
10
+ const lockPath = spawnLockPath(socketPath);
11
+ for (let i = 0; i < 5; i++) {
12
+ try {
13
+ writeFileSync(lockPath, `${process.pid}\n${nowMs()}\n`, { flag: "wx" });
14
+ return true;
15
+ }
16
+ catch (error) {
17
+ if (error.code !== "EEXIST")
18
+ throw error;
19
+ if (isLockStale(lockPath)) {
20
+ try {
21
+ unlinkSync(lockPath);
22
+ }
23
+ catch { }
24
+ continue;
25
+ }
26
+ return false;
27
+ }
28
+ }
29
+ return false;
30
+ }
31
+ function isLockStale(lockPath) {
32
+ if (!pathExists(lockPath))
33
+ return false;
34
+ try {
35
+ const [pidLine = "", tsLine = "0"] = readFileSync(lockPath, "utf8").trim().split("\n");
36
+ const pid = Number.parseInt(pidLine, 10);
37
+ const ts = Number.parseInt(tsLine, 10);
38
+ if (Number.isFinite(pid)) {
39
+ try {
40
+ process.kill(pid, 0);
41
+ }
42
+ catch {
43
+ return true;
44
+ }
45
+ }
46
+ return !Number.isFinite(ts) || nowMs() - ts > SIDECAR_SPAWN_LOCK_STALE_MS;
47
+ }
48
+ catch {
49
+ return true;
50
+ }
51
+ }
52
+ export function releaseSpawnLock(socketPath) {
53
+ try {
54
+ unlinkSync(spawnLockPath(socketPath));
55
+ }
56
+ catch { }
57
+ }
@@ -0,0 +1,3 @@
1
+ import type { ConsolidateStoreAccess } from "../store/consolidatePort.js";
2
+ /** Ensure sidecar is up, reindex from store export, and invalidate query cache. */
3
+ export declare function syncSidecarIndex(agentDir: string, store: Pick<ConsolidateStoreAccess, "exportForIndex">): Promise<number>;
@@ -0,0 +1,12 @@
1
+ import { reindex } from "./client.js";
2
+ import { sidecarQueryCache } from "./queryCache.js";
3
+ import { resolveSidecarPaths } from "./paths.js";
4
+ import { ensureSidecarRunning } from "./sidecarManager.js";
5
+ /** Ensure sidecar is up, reindex from store export, and invalidate query cache. */
6
+ export async function syncSidecarIndex(agentDir, store) {
7
+ const sidecar = resolveSidecarPaths(agentDir);
8
+ await ensureSidecarRunning(sidecar);
9
+ const result = await reindex(sidecar.socketPath, await store.exportForIndex());
10
+ sidecarQueryCache.onReindexComplete(agentDir, result.index_generation);
11
+ return result.index_generation;
12
+ }
@@ -21,7 +21,7 @@ export async function warmSidecar(socketPath, options = {}) {
21
21
  const queryStartedAt = nowMs();
22
22
  const queryTimeoutMs = options.queryTimeoutMs ?? SIDECAR_WARMUP_QUERY_TIMEOUT_MS;
23
23
  try {
24
- await query(socketPath, ".", queryTimeoutMs);
24
+ await query(socketPath, ".", { timeoutMs: queryTimeoutMs });
25
25
  }
26
26
  catch {
27
27
  // warm query is best-effort
@@ -0,0 +1,2 @@
1
+ export declare const MEMORY_STATUS_EXPAND_HINT = " (/memory-status to expand)";
2
+ export declare const MEMORY_STATUS_COLLAPSE_HINT = " /memory-status to collapse \u00B7 /memory-status hide to dismiss";
@@ -0,0 +1,2 @@
1
+ export const MEMORY_STATUS_EXPAND_HINT = " (/memory-status to expand)";
2
+ export const MEMORY_STATUS_COLLAPSE_HINT = " /memory-status to collapse · /memory-status hide to dismiss";
@@ -0,0 +1,7 @@
1
+ import type { Theme } from "@earendil-works/pi-coding-agent";
2
+ import type { MemoryStatusReport, StatusPalette } from "./types.js";
3
+ export declare function piStatusPalette(theme: Theme): StatusPalette;
4
+ export declare function formatMemoryStatusSummary(report: MemoryStatusReport, palette: StatusPalette, accent: (text: string) => string): string;
5
+ export declare function formatMemoryStatusLines(report: MemoryStatusReport, palette?: StatusPalette): string[];
6
+ export declare function formatMemoryStatusTuiLines(report: MemoryStatusReport, palette: StatusPalette, theme: Theme): string[];
7
+ export declare function printMemoryStatusRows(report: MemoryStatusReport, palette: StatusPalette, logLine: (label: string, value: string) => void): void;
@@ -0,0 +1,133 @@
1
+ const plainPalette = {
2
+ dim: (text) => text,
3
+ ok: (text) => text,
4
+ bad: (text) => text,
5
+ warn: (text) => text,
6
+ };
7
+ export function piStatusPalette(theme) {
8
+ return {
9
+ dim: (text) => theme.fg("dim", text),
10
+ ok: (text) => theme.fg("success", text),
11
+ bad: (text) => theme.fg("error", text),
12
+ warn: (text) => theme.fg("warning", text),
13
+ };
14
+ }
15
+ function embedderMatchesIndex(report) {
16
+ const { embeddingProvider, embeddingModel, embeddingDim } = report.vectorIndex;
17
+ if (!embeddingProvider || !embeddingModel || embeddingDim === undefined)
18
+ return true;
19
+ return (embeddingProvider === report.embedder.provider &&
20
+ embeddingModel === report.embedder.model &&
21
+ embeddingDim === report.embedder.dim);
22
+ }
23
+ function formatVectorIndexLine(report) {
24
+ const { generation, chunkCount, readError } = report.vectorIndex;
25
+ if (readError) {
26
+ return `(unreadable: ${readError})`;
27
+ }
28
+ if (generation === undefined || chunkCount === undefined) {
29
+ return "(unknown — start sidecar or run pi-memory status again)";
30
+ }
31
+ return `gen=${generation} chunks=${chunkCount}`;
32
+ }
33
+ function formatIndexEmbedderLine(report, palette) {
34
+ const { embeddingProvider, embeddingModel, embeddingDim, chunkCount, readError } = report.vectorIndex;
35
+ if (readError) {
36
+ return palette.dim("(unavailable)");
37
+ }
38
+ if (!embeddingProvider || !embeddingModel || embeddingDim === undefined) {
39
+ if (chunkCount === 0) {
40
+ return palette.dim("(empty — reindex pending)");
41
+ }
42
+ return palette.dim("(no embedding meta — run reindex)");
43
+ }
44
+ const label = `${embeddingProvider}/${embeddingModel} (${embeddingDim}d)`;
45
+ if (embedderMatchesIndex(report)) {
46
+ return label;
47
+ }
48
+ return palette.warn(`${label} ≠ configured`);
49
+ }
50
+ function memoryStatusRows(report, palette = plainPalette) {
51
+ const lastConsolidated = report.memory.lastConsolidatedAt ?? "(never)";
52
+ const rows = [
53
+ { label: "agent dir", value: () => report.agentDir },
54
+ { label: "MEMORY lines", value: () => String(report.memory.lineCount) },
55
+ { label: "entries", value: () => String(report.memory.entryCount) },
56
+ { label: "overflow files", value: () => String(report.memory.overflowFileCount) },
57
+ {
58
+ label: "last consolidate",
59
+ value: () => !report.memory.lastConsolidatedAt ? palette.dim(lastConsolidated) : lastConsolidated,
60
+ },
61
+ {
62
+ label: "sidecar",
63
+ value: () => {
64
+ const sidecarState = report.sidecar.running ? "running" : "not reachable";
65
+ const state = report.sidecar.running ? palette.ok(sidecarState) : palette.bad(sidecarState);
66
+ return `${state} ${palette.dim(`(${report.sidecar.socketPath})`)}`;
67
+ },
68
+ },
69
+ ];
70
+ if (!report.vectorIndex.exists) {
71
+ rows.push({
72
+ label: "vector index",
73
+ value: () => palette.dim("(missing — write MEMORY or start session)"),
74
+ });
75
+ }
76
+ else {
77
+ rows.push({
78
+ label: "vector index",
79
+ value: () => {
80
+ const line = formatVectorIndexLine(report);
81
+ if (report.vectorIndex.readError)
82
+ return palette.bad(line);
83
+ if (report.vectorIndex.generation === undefined || report.vectorIndex.chunkCount === undefined) {
84
+ return palette.dim(line);
85
+ }
86
+ return line;
87
+ },
88
+ });
89
+ rows.push({
90
+ label: "index embedder",
91
+ value: () => formatIndexEmbedderLine(report, palette),
92
+ });
93
+ }
94
+ rows.push({
95
+ label: "configured embedder",
96
+ value: () => `${report.embedder.provider}/${report.embedder.model} (${report.embedder.dim}d)`,
97
+ });
98
+ return rows;
99
+ }
100
+ export function formatMemoryStatusSummary(report, palette, accent) {
101
+ const parts = [
102
+ accent("pi-memory"),
103
+ palette.dim(`entries=${report.memory.entryCount}`),
104
+ report.sidecar.running ? palette.ok("sidecar up") : palette.bad("sidecar down"),
105
+ ];
106
+ if (!report.vectorIndex.exists) {
107
+ parts.push(palette.dim("no index"));
108
+ }
109
+ else {
110
+ const vec = formatVectorIndexLine(report);
111
+ if (report.vectorIndex.readError) {
112
+ parts.push(palette.bad(vec));
113
+ }
114
+ else if (report.vectorIndex.generation === undefined || report.vectorIndex.chunkCount === undefined) {
115
+ parts.push(palette.dim(vec));
116
+ }
117
+ else {
118
+ parts.push(vec);
119
+ }
120
+ }
121
+ return parts.join(palette.dim(" · "));
122
+ }
123
+ export function formatMemoryStatusLines(report, palette) {
124
+ return memoryStatusRows(report, palette).map(({ label, value }) => `${label.padEnd(16)} ${value()}`);
125
+ }
126
+ export function formatMemoryStatusTuiLines(report, palette, theme) {
127
+ return memoryStatusRows(report, palette).map(({ label, value }) => `${theme.fg("muted", label.padEnd(16))} ${value()}`);
128
+ }
129
+ export function printMemoryStatusRows(report, palette, logLine) {
130
+ for (const { label, value } of memoryStatusRows(report, palette)) {
131
+ logLine(label, value());
132
+ }
133
+ }
@@ -0,0 +1,2 @@
1
+ import type { MemoryStatusReport } from "./types.js";
2
+ export declare function gatherMemoryStatus(agentDir: string): Promise<MemoryStatusReport>;
@@ -0,0 +1,88 @@
1
+ import { readPiMemoryEnv, resolveEmbedDim } from "../config/env.js";
2
+ import { DEFAULT_HASH_EMBED_DIM } from "../constants/env.js";
3
+ import { createEmbedder } from "../adapters/embed/factory.js";
4
+ import { fetchIndexStats, ping } from "../sidecar/client.js";
5
+ import { resolveSidecarPaths } from "../sidecar/paths.js";
6
+ import { getVecStore } from "../sidecar/server/vec/store.js";
7
+ import { createMemoryStore } from "../store/index.js";
8
+ import { pathExists } from "../utils/fs.js";
9
+ function applyLocalVecStats(report, dbPath) {
10
+ const vec = getVecStore(dbPath);
11
+ report.vectorIndex.generation = vec.getIndexGeneration();
12
+ report.vectorIndex.chunkCount = vec.getChunkCount();
13
+ const meta = vec.getStoredEmbeddingMeta();
14
+ if (meta) {
15
+ report.vectorIndex.embeddingProvider = meta.provider;
16
+ report.vectorIndex.embeddingModel = meta.model;
17
+ report.vectorIndex.embeddingDim = meta.dim;
18
+ }
19
+ }
20
+ function applySidecarVecStats(report, stats) {
21
+ report.vectorIndex.fromSidecar = true;
22
+ report.vectorIndex.generation = stats.index_generation;
23
+ report.vectorIndex.chunkCount = stats.chunk_count;
24
+ if (stats.embedding_provider && stats.embedding_model && stats.embedding_dim !== undefined) {
25
+ report.vectorIndex.embeddingProvider = stats.embedding_provider;
26
+ report.vectorIndex.embeddingModel = stats.embedding_model;
27
+ report.vectorIndex.embeddingDim = stats.embedding_dim;
28
+ }
29
+ }
30
+ function resolveConfiguredEmbedder(env) {
31
+ try {
32
+ const embedder = createEmbedder(env);
33
+ return { provider: embedder.provider, model: embedder.model, dim: embedder.dim };
34
+ }
35
+ catch {
36
+ const embedModel = env.embedder === "openai"
37
+ ? env.openaiEmbedModel
38
+ : env.embedder === "ollama"
39
+ ? env.ollamaEmbedModel
40
+ : "hash/dev";
41
+ const dim = env.embedder === "hash"
42
+ ? (env.embedDimOverride ?? DEFAULT_HASH_EMBED_DIM)
43
+ : resolveEmbedDim(embedModel, env.embedDimOverride);
44
+ return { provider: env.embedder, model: embedModel, dim };
45
+ }
46
+ }
47
+ export async function gatherMemoryStatus(agentDir) {
48
+ const store = createMemoryStore({ agentDir });
49
+ await store.ensureInitialized();
50
+ const sidecar = resolveSidecarPaths(agentDir);
51
+ const env = readPiMemoryEnv();
52
+ const sidecarRunning = await ping(sidecar.socketPath);
53
+ const report = {
54
+ agentDir,
55
+ memory: await store.getStats(),
56
+ sidecar: {
57
+ socketPath: sidecar.socketPath,
58
+ running: sidecarRunning,
59
+ },
60
+ vectorIndex: {
61
+ dbPath: sidecar.dbPath,
62
+ exists: pathExists(sidecar.dbPath),
63
+ },
64
+ embedder: resolveConfiguredEmbedder(env),
65
+ };
66
+ if (!report.vectorIndex.exists)
67
+ return report;
68
+ if (sidecarRunning) {
69
+ const result = await fetchIndexStats(sidecar.socketPath);
70
+ if ("stats" in result) {
71
+ applySidecarVecStats(report, result.stats);
72
+ return report;
73
+ }
74
+ const hint = result.error.includes("unknown frame type")
75
+ ? "restart sidecar (reload Pi session or pi-memory)"
76
+ : result.error;
77
+ report.vectorIndex.readError = hint;
78
+ return report;
79
+ }
80
+ try {
81
+ applyLocalVecStats(report, sidecar.dbPath);
82
+ }
83
+ catch (error) {
84
+ report.vectorIndex.readError =
85
+ error instanceof Error ? error.message : "unable to open vector index (start sidecar)";
86
+ }
87
+ return report;
88
+ }
@@ -0,0 +1,4 @@
1
+ export type { MemoryStatusReport, StatusPalette } from "./types.js";
2
+ export { gatherMemoryStatus } from "./gather.js";
3
+ export { formatMemoryStatusLines, formatMemoryStatusSummary, formatMemoryStatusTuiLines, piStatusPalette, printMemoryStatusRows, } from "./format.js";
4
+ export { MEMORY_STATUS_COLLAPSE_HINT, MEMORY_STATUS_EXPAND_HINT } from "./copy.js";
@@ -0,0 +1,3 @@
1
+ export { gatherMemoryStatus } from "./gather.js";
2
+ export { formatMemoryStatusLines, formatMemoryStatusSummary, formatMemoryStatusTuiLines, piStatusPalette, printMemoryStatusRows, } from "./format.js";
3
+ export { MEMORY_STATUS_COLLAPSE_HINT, MEMORY_STATUS_EXPAND_HINT } from "./copy.js";
@@ -0,0 +1,33 @@
1
+ import type { MemoryStats } from "../store/types.js";
2
+ export type StatusPalette = {
3
+ dim: (text: string) => string;
4
+ ok: (text: string) => string;
5
+ bad: (text: string) => string;
6
+ warn: (text: string) => string;
7
+ };
8
+ export type MemoryStatusReport = {
9
+ agentDir: string;
10
+ memory: MemoryStats;
11
+ sidecar: {
12
+ socketPath: string;
13
+ running: boolean;
14
+ };
15
+ vectorIndex: {
16
+ dbPath: string;
17
+ exists: boolean;
18
+ generation?: number;
19
+ chunkCount?: number;
20
+ embeddingProvider?: string;
21
+ embeddingModel?: string;
22
+ embeddingDim?: number;
23
+ /** Set when the index file exists but could not be read locally. */
24
+ readError?: string;
25
+ /** Stats came from sidecar RPC rather than opening sqlite in-process. */
26
+ fromSidecar?: boolean;
27
+ };
28
+ embedder: {
29
+ provider: string;
30
+ model: string;
31
+ dim: number;
32
+ };
33
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,11 @@
1
+ import type { IndexDocument } from "../sidecar/protocol.js";
2
+ import type { TimeInput } from "../utils/time.js";
3
+ import type { MemoryStats, ParsedEntry } from "./types.js";
4
+ /** Store capabilities used by consolidate jobs (no merge algorithms). */
5
+ export type ConsolidateStoreAccess = {
6
+ shouldConsolidate(at?: TimeInput, cronFired?: boolean): Promise<boolean>;
7
+ getStats(): Promise<MemoryStats>;
8
+ exportForIndex(): Promise<IndexDocument[]>;
9
+ isConsolidating(): boolean;
10
+ rewriteMemoryUnderLock(updateEntries: (entries: ParsedEntry[]) => Promise<ParsedEntry[]>): Promise<void>;
11
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -1,4 +1,6 @@
1
+ export { initializeMemoryWorkspace, readMemoryTemplateExample, type InitMemoryWorkspaceResult } from "./initWorkspace.js";
1
2
  export { MemoryStore, createMemoryStore } from "./memoryStore.js";
3
+ export type { ConsolidateStoreAccess } from "./consolidatePort.js";
2
4
  export { MarkdownMemoryBackend } from "./backend.js";
3
5
  export { getAgentPaths, resolveAgentDir } from "./paths.js";
4
6
  export * from "./types.js";
@@ -1,3 +1,4 @@
1
+ export { initializeMemoryWorkspace, readMemoryTemplateExample } from "./initWorkspace.js";
1
2
  export { MemoryStore, createMemoryStore } from "./memoryStore.js";
2
3
  export { MarkdownMemoryBackend } from "./backend.js";
3
4
  export { getAgentPaths, resolveAgentDir } from "./paths.js";