@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.
- package/README.md +92 -43
- package/dist/adapters/llm/standalone.js +1 -1
- package/dist/cli/init.js +20 -3
- package/dist/cli/parseArgs.d.ts +5 -2
- package/dist/cli/parseArgs.js +13 -0
- package/dist/cli/schedulerSync.d.ts +2 -0
- package/dist/cli/schedulerSync.js +26 -0
- package/dist/cli/status.d.ts +4 -40
- package/dist/cli/status.js +6 -230
- package/dist/cli/theme.d.ts +2 -0
- package/dist/cli/theme.js +8 -0
- package/dist/cli.js +5 -0
- package/dist/commands/status.js +2 -2
- package/dist/compact/parseMemoryExport.js +2 -1
- package/dist/compact/register.js +1 -1
- package/dist/compact/runSummary.js +1 -16
- package/dist/compact/subagentDelta.js +3 -5
- package/dist/consolidate/index.d.ts +1 -0
- package/dist/consolidate/index.js +1 -0
- package/dist/consolidate/mergeEntries.js +3 -3
- package/dist/consolidate/mergeMemoryEntries.d.ts +8 -0
- package/dist/consolidate/mergeMemoryEntries.js +23 -0
- package/dist/consolidate/mergePrompt.js +2 -2
- package/dist/consolidate/mergeWithLlm.js +2 -2
- package/dist/consolidate/runJob.d.ts +2 -2
- package/dist/consolidate/runJob.js +6 -12
- package/dist/consolidate/scheduler.d.ts +2 -2
- package/dist/consolidate/scheduler.js +1 -1
- package/dist/constants/env.d.ts +1 -0
- package/dist/constants/env.js +1 -0
- package/dist/constants/paths.d.ts +3 -1
- package/dist/constants/paths.js +6 -1
- package/dist/extension/createMemoryRuntime.d.ts +3 -0
- package/dist/extension/createMemoryRuntime.js +203 -0
- package/dist/extension/index.d.ts +2 -0
- package/dist/extension/index.js +1 -0
- package/dist/extension/lifecycle.d.ts +28 -0
- package/dist/extension/lifecycle.js +52 -0
- package/dist/extension/messageUtils.d.ts +4 -0
- package/dist/extension/messageUtils.js +18 -0
- package/dist/extension/types.d.ts +35 -0
- package/dist/extension/types.js +1 -0
- package/dist/index.d.ts +7 -9
- package/dist/index.js +7 -9
- package/dist/pi-extension.js +26 -236
- package/dist/preflight/episodic.js +13 -30
- package/dist/preflight/queryIntent.js +1 -1
- package/dist/preflight/render.js +1 -1
- package/dist/preflight/strip.d.ts +0 -1
- package/dist/preflight/strip.js +0 -24
- package/dist/redaction/index.d.ts +4 -0
- package/dist/redaction/index.js +3 -0
- package/dist/redaction/patterns/constants.d.ts +6 -0
- package/dist/redaction/patterns/constants.js +6 -0
- package/dist/redaction/patterns/crypto.d.ts +3 -0
- package/dist/redaction/patterns/crypto.js +17 -0
- package/dist/redaction/patterns/generic.d.ts +3 -0
- package/dist/redaction/patterns/generic.js +38 -0
- package/dist/redaction/patterns/index.d.ts +16 -0
- package/dist/redaction/patterns/index.js +23 -0
- package/dist/redaction/patterns/llm.d.ts +3 -0
- package/dist/redaction/patterns/llm.js +144 -0
- package/dist/redaction/patterns/platform.d.ts +3 -0
- package/dist/redaction/patterns/platform.js +87 -0
- package/dist/redaction/patterns/types.d.ts +18 -0
- package/dist/redaction/patterns/types.js +1 -0
- package/dist/redaction/redactText.d.ts +9 -0
- package/dist/redaction/redactText.js +31 -0
- package/dist/redaction/types.d.ts +19 -0
- package/dist/redaction/types.js +1 -0
- package/dist/redaction/utils.d.ts +28 -0
- package/dist/redaction/utils.js +106 -0
- package/dist/scheduler/index.d.ts +3 -0
- package/dist/scheduler/index.js +3 -0
- package/dist/scheduler/launchd.d.ts +14 -0
- package/dist/scheduler/launchd.js +69 -0
- package/dist/scheduler/launchdPlist.d.ts +14 -0
- package/dist/scheduler/launchdPlist.js +62 -0
- package/dist/scheduler/sync.d.ts +36 -0
- package/dist/scheduler/sync.js +79 -0
- package/dist/shutdown/enqueue.d.ts +1 -1
- package/dist/shutdown/enqueue.js +2 -5
- package/dist/shutdown/readQueue.js +1 -1
- package/dist/shutdown/runDrainJob.js +8 -37
- package/dist/shutdown/sessionReader.js +1 -14
- package/dist/sidecar/client.d.ts +6 -2
- package/dist/sidecar/client.js +49 -14
- package/dist/{preflight → sidecar}/queryCache.d.ts +1 -1
- package/dist/sidecar/reindexBridge.js +2 -2
- package/dist/sidecar/server/server.js +1 -1
- package/dist/sidecar/server/vec/chunkQuery.d.ts +4 -0
- package/dist/sidecar/server/vec/chunkQuery.js +46 -0
- package/dist/sidecar/server/vec/chunkReindex.d.ts +5 -0
- package/dist/sidecar/server/vec/chunkReindex.js +40 -0
- package/dist/sidecar/server/vec/embeddingCodec.d.ts +2 -0
- package/dist/sidecar/server/vec/embeddingCodec.js +6 -0
- package/dist/sidecar/server/vec/schema.d.ts +20 -0
- package/dist/sidecar/server/vec/schema.js +61 -0
- package/dist/sidecar/server/vec/store.d.ts +2 -13
- package/dist/sidecar/server/vec/store.js +12 -139
- package/dist/sidecar/sidecarManager.js +4 -58
- package/dist/sidecar/spawnLock.d.ts +2 -0
- package/dist/sidecar/spawnLock.js +57 -0
- package/dist/sidecar/syncIndex.d.ts +3 -0
- package/dist/sidecar/syncIndex.js +12 -0
- package/dist/sidecar/warmup.js +1 -1
- package/dist/status/copy.d.ts +2 -0
- package/dist/status/copy.js +2 -0
- package/dist/status/format.d.ts +7 -0
- package/dist/status/format.js +133 -0
- package/dist/status/gather.d.ts +2 -0
- package/dist/status/gather.js +88 -0
- package/dist/status/index.d.ts +4 -0
- package/dist/status/index.js +3 -0
- package/dist/status/types.d.ts +33 -0
- package/dist/status/types.js +1 -0
- package/dist/store/consolidatePort.d.ts +11 -0
- package/dist/store/consolidatePort.js +1 -0
- package/dist/store/index.d.ts +2 -0
- package/dist/store/index.js +1 -0
- package/dist/store/ingestEntries.d.ts +16 -0
- package/dist/store/ingestEntries.js +22 -0
- package/dist/{init/workspace.d.ts → store/initWorkspace.d.ts} +1 -1
- package/dist/{init/workspace.js → store/initWorkspace.js} +7 -5
- package/dist/store/listeners.d.ts +11 -0
- package/dist/store/listeners.js +27 -0
- package/dist/store/markdown/insert.d.ts +3 -0
- package/dist/store/markdown/insert.js +23 -0
- package/dist/store/memoryStore.d.ts +9 -22
- package/dist/store/memoryStore.js +71 -205
- package/dist/store/resolveEntries.d.ts +11 -0
- package/dist/store/resolveEntries.js +23 -0
- package/dist/store/types.d.ts +0 -1
- package/dist/store/writePath.d.ts +20 -0
- package/dist/store/writePath.js +123 -0
- package/dist/ui/memoryStatusWidget.d.ts +4 -8
- package/dist/ui/memoryStatusWidget.js +5 -17
- package/dist/utils/async.d.ts +11 -0
- package/dist/utils/async.js +24 -0
- package/dist/utils/index.d.ts +5 -1
- package/dist/utils/index.js +5 -1
- package/dist/{ipc/jsonlFramer.d.ts → utils/jsonl.d.ts} +1 -1
- package/dist/{ipc/jsonlFramer.js → utils/jsonl.js} +1 -1
- package/dist/utils/memory/index.d.ts +10 -0
- package/dist/utils/memory/index.js +43 -0
- package/dist/utils/paths.d.ts +4 -0
- package/dist/utils/paths.js +13 -3
- package/dist/utils/scheduler.d.ts +1 -1
- package/dist/utils/scheduler.js +6 -6
- package/dist/{preflight/session.d.ts → utils/session/index.d.ts} +1 -0
- package/dist/{preflight/session.js → utils/session/index.js} +5 -2
- package/doc/LAUNCH-KIT.md +229 -0
- package/doc/README-zh.md +445 -0
- package/doc/ROADMAP-zh.md +114 -0
- package/doc/ROADMAP.md +114 -0
- package/package.json +16 -4
- package/scripts/postinstall.mjs +11 -1
- package/templates/com.pi.memory.consolidate.plist.example +41 -0
- package/templates/consolidate.cmd.example +15 -0
- package/templates/crontab.example +14 -0
- package/templates/schtasks.example.txt +34 -0
- package/dist/consolidate/entryKey.d.ts +0 -5
- package/dist/consolidate/entryKey.js +0 -4
- /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 {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
58
|
-
return Number(raw ?? "0");
|
|
22
|
+
return getIndexGeneration(this.db);
|
|
59
23
|
}
|
|
60
24
|
getChunkCount() {
|
|
61
|
-
|
|
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
|
-
|
|
83
|
-
|
|
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
|
-
|
|
120
|
-
|
|
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、
|
|
2
|
-
import { readFileSync, unlinkSync, writeFileSync } from "node:fs";
|
|
1
|
+
// Agent 侧:connect-or-create、execa 生命周期
|
|
3
2
|
import { execa } from "execa";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
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,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
|
+
}
|
package/dist/sidecar/warmup.js
CHANGED
|
@@ -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,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,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 {};
|
package/dist/store/index.d.ts
CHANGED
|
@@ -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";
|
package/dist/store/index.js
CHANGED
|
@@ -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";
|