@chendpoc/pi-memory 0.1.0 → 0.1.12
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 +156 -111
- package/dist/adapters/ollamaClient.d.ts +11 -0
- package/dist/adapters/ollamaClient.d.ts.map +1 -0
- package/dist/adapters/ollamaClient.js +122 -0
- package/dist/adapters/ollamaClient.js.map +1 -0
- package/dist/adapters/openaiCompatClient.d.ts +11 -0
- package/dist/adapters/openaiCompatClient.d.ts.map +1 -0
- package/dist/adapters/openaiCompatClient.js +118 -0
- package/dist/adapters/openaiCompatClient.js.map +1 -0
- package/dist/cli.js +2 -2
- package/dist/cli.js.map +1 -1
- package/dist/fallback/sessionIndex.d.ts.map +1 -1
- package/dist/fallback/sessionIndex.js +90 -25
- package/dist/fallback/sessionIndex.js.map +1 -1
- package/dist/fallback/sessionSearch.d.ts +1 -1
- package/dist/fallback/sessionSearch.d.ts.map +1 -1
- package/dist/fallback/sessionSearch.js +101 -28
- package/dist/fallback/sessionSearch.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/local/graphQuery.d.ts +21 -0
- package/dist/local/graphQuery.d.ts.map +1 -0
- package/dist/local/graphQuery.js +170 -0
- package/dist/local/graphQuery.js.map +1 -0
- package/dist/paths.js +1 -1
- package/dist/paths.js.map +1 -1
- package/dist/pi-extension.d.ts.map +1 -1
- package/dist/pi-extension.js +57 -17
- package/dist/pi-extension.js.map +1 -1
- package/dist/service.d.ts +10 -10
- package/dist/service.d.ts.map +1 -1
- package/dist/service.js +72 -30
- package/dist/service.js.map +1 -1
- package/dist/settings.d.ts +38 -0
- package/dist/settings.d.ts.map +1 -0
- package/dist/settings.js +68 -0
- package/dist/settings.js.map +1 -0
- package/dist/sidecar/process.d.ts.map +1 -1
- package/dist/sidecar/process.js +16 -4
- package/dist/sidecar/process.js.map +1 -1
- package/dist/trainer/sessionLoader.d.ts +2 -2
- package/dist/trainer/sessionLoader.d.ts.map +1 -1
- package/dist/trainer/sessionLoader.js +115 -39
- package/dist/trainer/sessionLoader.js.map +1 -1
- package/package.json +8 -4
- package/src/adapters/ollamaClient.ts +179 -0
- package/src/adapters/openaiCompatClient.ts +155 -0
- package/src/cache/memoryCaches.ts +72 -0
- package/src/cli.ts +4 -3
- package/src/fallback/llmRerank.ts +8 -1
- package/src/fallback/sessionIndex.ts +78 -40
- package/src/fallback/sessionSearch.ts +107 -27
- package/src/index.ts +28 -0
- package/src/local/graphQuery.ts +252 -0
- package/src/paths.ts +1 -1
- package/src/pi-extension.ts +164 -36
- package/src/preflight/detectIntents.ts +6 -0
- package/src/preflight/hook.ts +68 -5
- package/src/preflight/render.ts +28 -3
- package/src/service.ts +133 -29
- package/src/settings.ts +126 -0
- package/src/sidecar/process.ts +19 -4
- package/src/tools/memoryRecall.ts +33 -9
- package/src/trainer/scheduler.ts +3 -0
- package/src/trainer/sessionLoader.ts +128 -42
|
@@ -29,6 +29,21 @@ interface PiSessionFile {
|
|
|
29
29
|
messages?: PiSessionMessage[];
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
interface JsonlSessionHeader {
|
|
33
|
+
type: "session";
|
|
34
|
+
id?: string;
|
|
35
|
+
timestamp?: string;
|
|
36
|
+
title?: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
interface JsonlMessageLine {
|
|
40
|
+
type: "message";
|
|
41
|
+
message?: {
|
|
42
|
+
role?: string;
|
|
43
|
+
content?: unknown;
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
32
47
|
function messageText(content: unknown): string {
|
|
33
48
|
if (typeof content === "string") return content;
|
|
34
49
|
if (!Array.isArray(content)) return "";
|
|
@@ -53,72 +68,143 @@ export interface SessionLoaderOptions {
|
|
|
53
68
|
}
|
|
54
69
|
|
|
55
70
|
/**
|
|
56
|
-
*
|
|
57
|
-
*
|
|
71
|
+
* Collect all session files recursively (supports project subdirectories).
|
|
72
|
+
* Returns both .json and .jsonl files.
|
|
58
73
|
*/
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
): Promise<LoadedSession[]> {
|
|
62
|
-
const { sessionsDir, modifiedAfter } = opts;
|
|
63
|
-
if (!sessionsDir.trim()) return [];
|
|
64
|
-
|
|
65
|
-
let entries: string[];
|
|
74
|
+
async function collectSessionFiles(dir: string): Promise<string[]> {
|
|
75
|
+
let names: string[];
|
|
66
76
|
try {
|
|
67
|
-
|
|
77
|
+
names = await fs.readdir(dir);
|
|
68
78
|
} catch {
|
|
69
79
|
return [];
|
|
70
80
|
}
|
|
71
81
|
|
|
72
|
-
const
|
|
82
|
+
const files: string[] = [];
|
|
83
|
+
for (const name of names) {
|
|
84
|
+
const full = path.join(dir, name);
|
|
85
|
+
let st;
|
|
86
|
+
try { st = await fs.stat(full); } catch { continue; }
|
|
87
|
+
if (st.isDirectory()) {
|
|
88
|
+
const sub = await collectSessionFiles(full);
|
|
89
|
+
files.push(...sub);
|
|
90
|
+
} else if (st.isFile() && (name.endsWith(".json") || name.endsWith(".jsonl"))) {
|
|
91
|
+
files.push(full);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return files;
|
|
95
|
+
}
|
|
73
96
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
97
|
+
function parseJsonlSession(raw: string, filePath: string): LoadedSession | null {
|
|
98
|
+
const lines = raw.split("\n").filter((l) => l.trim());
|
|
99
|
+
if (lines.length === 0) return null;
|
|
77
100
|
|
|
78
|
-
|
|
101
|
+
let header: JsonlSessionHeader | null = null;
|
|
102
|
+
const turns: SessionTurn[] = [];
|
|
103
|
+
let turnIndex = 0;
|
|
104
|
+
|
|
105
|
+
for (const line of lines) {
|
|
106
|
+
let obj: Record<string, unknown>;
|
|
79
107
|
try {
|
|
80
|
-
|
|
108
|
+
obj = JSON.parse(line) as Record<string, unknown>;
|
|
81
109
|
} catch {
|
|
82
110
|
continue;
|
|
83
111
|
}
|
|
84
|
-
if (!st.isFile()) continue;
|
|
85
112
|
|
|
86
|
-
if (
|
|
113
|
+
if (obj.type === "session" && !header) {
|
|
114
|
+
header = obj as unknown as JsonlSessionHeader;
|
|
87
115
|
continue;
|
|
88
116
|
}
|
|
89
117
|
|
|
90
|
-
|
|
118
|
+
if (obj.type === "message") {
|
|
119
|
+
const msg = (obj as unknown as JsonlMessageLine).message;
|
|
120
|
+
if (!msg?.role || !msg.content) continue;
|
|
121
|
+
if (msg.role !== "user" && msg.role !== "assistant") continue;
|
|
122
|
+
const text = messageText(msg.content);
|
|
123
|
+
if (!text.trim()) continue;
|
|
124
|
+
turns.push({ role: msg.role, content: text, turnIndex: turnIndex++ });
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (turns.length === 0) return null;
|
|
129
|
+
|
|
130
|
+
return {
|
|
131
|
+
id: header?.id ?? path.basename(filePath, path.extname(filePath)),
|
|
132
|
+
title: header?.title ?? "",
|
|
133
|
+
createdAt: header?.timestamp ?? "",
|
|
134
|
+
filePath,
|
|
135
|
+
modifiedAt: new Date(),
|
|
136
|
+
turns,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function parseJsonSession(raw: string, filePath: string): LoadedSession | null {
|
|
141
|
+
let session: PiSessionFile;
|
|
142
|
+
try {
|
|
143
|
+
session = JSON.parse(raw) as PiSessionFile;
|
|
144
|
+
} catch {
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (!session.messages || session.messages.length === 0) return null;
|
|
149
|
+
|
|
150
|
+
const turns: SessionTurn[] = [];
|
|
151
|
+
for (let i = 0; i < session.messages.length; i++) {
|
|
152
|
+
const msg = session.messages[i]!;
|
|
153
|
+
const text = messageText(msg.content);
|
|
154
|
+
if (!text.trim()) continue;
|
|
155
|
+
turns.push({ role: msg.role ?? "unknown", content: text, turnIndex: i });
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (turns.length === 0) return null;
|
|
159
|
+
|
|
160
|
+
return {
|
|
161
|
+
id: session.id ?? path.basename(filePath, ".json"),
|
|
162
|
+
title: session.title ?? "",
|
|
163
|
+
createdAt: session.created_at ?? "",
|
|
164
|
+
filePath,
|
|
165
|
+
modifiedAt: new Date(),
|
|
166
|
+
turns,
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Scan session files (JSON + JSONL, recursive subdirectories), parse Pi session
|
|
172
|
+
* format, optionally filter by modified-after timestamp for incremental training.
|
|
173
|
+
*/
|
|
174
|
+
export async function loadSessions(
|
|
175
|
+
opts: SessionLoaderOptions,
|
|
176
|
+
): Promise<LoadedSession[]> {
|
|
177
|
+
const { sessionsDir, modifiedAfter } = opts;
|
|
178
|
+
if (!sessionsDir.trim()) return [];
|
|
179
|
+
|
|
180
|
+
const filePaths = await collectSessionFiles(sessionsDir);
|
|
181
|
+
const sessions: LoadedSession[] = [];
|
|
182
|
+
|
|
183
|
+
for (const filePath of filePaths) {
|
|
184
|
+
let st: Awaited<ReturnType<typeof fs.stat>>;
|
|
91
185
|
try {
|
|
92
|
-
|
|
93
|
-
session = JSON.parse(raw) as PiSessionFile;
|
|
186
|
+
st = await fs.stat(filePath);
|
|
94
187
|
} catch {
|
|
95
188
|
continue;
|
|
96
189
|
}
|
|
190
|
+
if (!st.isFile()) continue;
|
|
191
|
+
if (modifiedAfter && st.mtime <= modifiedAfter) continue;
|
|
97
192
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
const text = messageText(msg.content);
|
|
104
|
-
if (!text.trim()) continue;
|
|
105
|
-
turns.push({
|
|
106
|
-
role: msg.role ?? "unknown",
|
|
107
|
-
content: text,
|
|
108
|
-
turnIndex: i,
|
|
109
|
-
});
|
|
193
|
+
let raw: string;
|
|
194
|
+
try {
|
|
195
|
+
raw = await fs.readFile(filePath, "utf8");
|
|
196
|
+
} catch {
|
|
197
|
+
continue;
|
|
110
198
|
}
|
|
111
199
|
|
|
112
|
-
|
|
200
|
+
const isJsonl = filePath.endsWith(".jsonl");
|
|
201
|
+
const parsed = isJsonl
|
|
202
|
+
? parseJsonlSession(raw, filePath)
|
|
203
|
+
: parseJsonSession(raw, filePath);
|
|
113
204
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
createdAt: session.created_at ?? "",
|
|
118
|
-
filePath,
|
|
119
|
-
modifiedAt: st.mtime,
|
|
120
|
-
turns,
|
|
121
|
-
});
|
|
205
|
+
if (!parsed) continue;
|
|
206
|
+
parsed.modifiedAt = st.mtime;
|
|
207
|
+
sessions.push(parsed);
|
|
122
208
|
}
|
|
123
209
|
|
|
124
210
|
sessions.sort((a, b) => a.modifiedAt.getTime() - b.modifiedAt.getTime());
|