@chendpoc/pi-memory 0.1.11 → 0.1.13

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 (177) hide show
  1. package/README.md +88 -16
  2. package/dist/cache/memoryCaches.d.ts +32 -0
  3. package/dist/cache/memoryCaches.d.ts.map +1 -0
  4. package/dist/cache/memoryCaches.js +59 -0
  5. package/dist/cache/memoryCaches.js.map +1 -0
  6. package/dist/cli.js +123 -0
  7. package/dist/cli.js.map +1 -1
  8. package/dist/config.d.ts +28 -0
  9. package/dist/config.d.ts.map +1 -1
  10. package/dist/config.js +37 -1
  11. package/dist/config.js.map +1 -1
  12. package/dist/consolidation/enqueue.d.ts +33 -0
  13. package/dist/consolidation/enqueue.d.ts.map +1 -0
  14. package/dist/consolidation/enqueue.js +145 -0
  15. package/dist/consolidation/enqueue.js.map +1 -0
  16. package/dist/consolidation/index.d.ts +19 -0
  17. package/dist/consolidation/index.d.ts.map +1 -0
  18. package/dist/consolidation/index.js +15 -0
  19. package/dist/consolidation/index.js.map +1 -0
  20. package/dist/consolidation/lock.d.ts +5 -0
  21. package/dist/consolidation/lock.d.ts.map +1 -0
  22. package/dist/consolidation/lock.js +33 -0
  23. package/dist/consolidation/lock.js.map +1 -0
  24. package/dist/consolidation/log.d.ts +8 -0
  25. package/dist/consolidation/log.d.ts.map +1 -0
  26. package/dist/consolidation/log.js +32 -0
  27. package/dist/consolidation/log.js.map +1 -0
  28. package/dist/consolidation/memoryIndex.d.ts +18 -0
  29. package/dist/consolidation/memoryIndex.d.ts.map +1 -0
  30. package/dist/consolidation/memoryIndex.js +100 -0
  31. package/dist/consolidation/memoryIndex.js.map +1 -0
  32. package/dist/consolidation/phase2/runPhase2.d.ts +23 -0
  33. package/dist/consolidation/phase2/runPhase2.d.ts.map +1 -0
  34. package/dist/consolidation/phase2/runPhase2.js +196 -0
  35. package/dist/consolidation/phase2/runPhase2.js.map +1 -0
  36. package/dist/consolidation/scheduler/launchd.d.ts +5 -0
  37. package/dist/consolidation/scheduler/launchd.d.ts.map +1 -0
  38. package/dist/consolidation/scheduler/launchd.js +54 -0
  39. package/dist/consolidation/scheduler/launchd.js.map +1 -0
  40. package/dist/consolidation/scheduler/runConsolidate.d.ts +26 -0
  41. package/dist/consolidation/scheduler/runConsolidate.d.ts.map +1 -0
  42. package/dist/consolidation/scheduler/runConsolidate.js +83 -0
  43. package/dist/consolidation/scheduler/runConsolidate.js.map +1 -0
  44. package/dist/consolidation/scheduler/setupSchedule.d.ts +3 -0
  45. package/dist/consolidation/scheduler/setupSchedule.d.ts.map +1 -0
  46. package/dist/consolidation/scheduler/setupSchedule.js +79 -0
  47. package/dist/consolidation/scheduler/setupSchedule.js.map +1 -0
  48. package/dist/consolidation/scheduler/systemd.d.ts +9 -0
  49. package/dist/consolidation/scheduler/systemd.d.ts.map +1 -0
  50. package/dist/consolidation/scheduler/systemd.js +57 -0
  51. package/dist/consolidation/scheduler/systemd.js.map +1 -0
  52. package/dist/consolidation/scheduler/types.d.ts +24 -0
  53. package/dist/consolidation/scheduler/types.d.ts.map +1 -0
  54. package/dist/consolidation/scheduler/types.js +2 -0
  55. package/dist/consolidation/scheduler/types.js.map +1 -0
  56. package/dist/consolidation/scope.d.ts +12 -0
  57. package/dist/consolidation/scope.d.ts.map +1 -0
  58. package/dist/consolidation/scope.js +51 -0
  59. package/dist/consolidation/scope.js.map +1 -0
  60. package/dist/consolidation/stage1/deltaExtract.d.ts +3 -0
  61. package/dist/consolidation/stage1/deltaExtract.d.ts.map +1 -0
  62. package/dist/consolidation/stage1/deltaExtract.js +19 -0
  63. package/dist/consolidation/stage1/deltaExtract.js.map +1 -0
  64. package/dist/consolidation/stage1/drainQueue.d.ts +21 -0
  65. package/dist/consolidation/stage1/drainQueue.d.ts.map +1 -0
  66. package/dist/consolidation/stage1/drainQueue.js +113 -0
  67. package/dist/consolidation/stage1/drainQueue.js.map +1 -0
  68. package/dist/consolidation/stage1/extractSession.d.ts +6 -0
  69. package/dist/consolidation/stage1/extractSession.d.ts.map +1 -0
  70. package/dist/consolidation/stage1/extractSession.js +47 -0
  71. package/dist/consolidation/stage1/extractSession.js.map +1 -0
  72. package/dist/consolidation/stage1/store.d.ts +45 -0
  73. package/dist/consolidation/stage1/store.d.ts.map +1 -0
  74. package/dist/consolidation/stage1/store.js +297 -0
  75. package/dist/consolidation/stage1/store.js.map +1 -0
  76. package/dist/consolidation/types.d.ts +49 -0
  77. package/dist/consolidation/types.d.ts.map +1 -0
  78. package/dist/consolidation/types.js +2 -0
  79. package/dist/consolidation/types.js.map +1 -0
  80. package/dist/fallback/llmRerank.d.ts.map +1 -1
  81. package/dist/fallback/llmRerank.js +9 -1
  82. package/dist/fallback/llmRerank.js.map +1 -1
  83. package/dist/fallback/sessionIndex.d.ts.map +1 -1
  84. package/dist/fallback/sessionIndex.js +15 -63
  85. package/dist/fallback/sessionIndex.js.map +1 -1
  86. package/dist/index.d.ts +4 -3
  87. package/dist/index.d.ts.map +1 -1
  88. package/dist/index.js +4 -3
  89. package/dist/index.js.map +1 -1
  90. package/dist/local/graphQuery.d.ts +10 -0
  91. package/dist/local/graphQuery.d.ts.map +1 -1
  92. package/dist/local/graphQuery.js +25 -0
  93. package/dist/local/graphQuery.js.map +1 -1
  94. package/dist/pi-extension.d.ts.map +1 -1
  95. package/dist/pi-extension.js +356 -22
  96. package/dist/pi-extension.js.map +1 -1
  97. package/dist/preflight/detectIntents.d.ts.map +1 -1
  98. package/dist/preflight/detectIntents.js +7 -0
  99. package/dist/preflight/detectIntents.js.map +1 -1
  100. package/dist/preflight/hook.d.ts +18 -0
  101. package/dist/preflight/hook.d.ts.map +1 -1
  102. package/dist/preflight/hook.js +52 -7
  103. package/dist/preflight/hook.js.map +1 -1
  104. package/dist/preflight/render.d.ts +16 -2
  105. package/dist/preflight/render.d.ts.map +1 -1
  106. package/dist/preflight/render.js +24 -4
  107. package/dist/preflight/render.js.map +1 -1
  108. package/dist/service.d.ts +16 -0
  109. package/dist/service.d.ts.map +1 -1
  110. package/dist/service.js +57 -0
  111. package/dist/service.js.map +1 -1
  112. package/dist/session/activeBranch.d.ts +17 -0
  113. package/dist/session/activeBranch.d.ts.map +1 -0
  114. package/dist/session/activeBranch.js +251 -0
  115. package/dist/session/activeBranch.js.map +1 -0
  116. package/dist/settings.d.ts +5 -2
  117. package/dist/settings.d.ts.map +1 -1
  118. package/dist/settings.js +18 -1
  119. package/dist/settings.js.map +1 -1
  120. package/dist/tools/memoryAppend.d.ts +14 -4
  121. package/dist/tools/memoryAppend.d.ts.map +1 -1
  122. package/dist/tools/memoryAppend.js +46 -7
  123. package/dist/tools/memoryAppend.js.map +1 -1
  124. package/dist/tools/memoryRecall.d.ts +2 -2
  125. package/dist/tools/memoryRecall.d.ts.map +1 -1
  126. package/dist/tools/memoryRecall.js +35 -9
  127. package/dist/tools/memoryRecall.js.map +1 -1
  128. package/dist/trainer/extractFacts.d.ts +1 -1
  129. package/dist/trainer/index.d.ts +1 -1
  130. package/dist/trainer/index.d.ts.map +1 -1
  131. package/dist/trainer/index.js +1 -1
  132. package/dist/trainer/index.js.map +1 -1
  133. package/dist/trainer/scheduler.d.ts +2 -0
  134. package/dist/trainer/scheduler.d.ts.map +1 -1
  135. package/dist/trainer/scheduler.js +1 -0
  136. package/dist/trainer/scheduler.js.map +1 -1
  137. package/dist/trainer/sessionLoader.d.ts +3 -0
  138. package/dist/trainer/sessionLoader.d.ts.map +1 -1
  139. package/dist/trainer/sessionLoader.js +37 -74
  140. package/dist/trainer/sessionLoader.js.map +1 -1
  141. package/package.json +17 -4
  142. package/src/cache/memoryCaches.ts +72 -0
  143. package/src/cli.ts +144 -0
  144. package/src/config.ts +89 -1
  145. package/src/consolidation/enqueue.ts +205 -0
  146. package/src/consolidation/index.ts +90 -0
  147. package/src/consolidation/lock.ts +41 -0
  148. package/src/consolidation/log.ts +43 -0
  149. package/src/consolidation/memoryIndex.ts +127 -0
  150. package/src/consolidation/phase2/runPhase2.ts +255 -0
  151. package/src/consolidation/scheduler/launchd.ts +62 -0
  152. package/src/consolidation/scheduler/runConsolidate.ts +115 -0
  153. package/src/consolidation/scheduler/setupSchedule.ts +106 -0
  154. package/src/consolidation/scheduler/systemd.ts +78 -0
  155. package/src/consolidation/scheduler/types.ts +27 -0
  156. package/src/consolidation/scope.ts +58 -0
  157. package/src/consolidation/stage1/deltaExtract.ts +29 -0
  158. package/src/consolidation/stage1/drainQueue.ts +143 -0
  159. package/src/consolidation/stage1/extractSession.ts +58 -0
  160. package/src/consolidation/stage1/store.ts +411 -0
  161. package/src/consolidation/types.ts +52 -0
  162. package/src/fallback/llmRerank.ts +8 -1
  163. package/src/fallback/sessionIndex.ts +14 -51
  164. package/src/index.ts +8 -0
  165. package/src/local/graphQuery.ts +24 -0
  166. package/src/pi-extension.ts +393 -28
  167. package/src/preflight/detectIntents.ts +6 -0
  168. package/src/preflight/hook.ts +84 -7
  169. package/src/preflight/render.ts +35 -3
  170. package/src/service.ts +57 -0
  171. package/src/session/activeBranch.ts +294 -0
  172. package/src/settings.ts +37 -2
  173. package/src/tools/memoryAppend.ts +60 -7
  174. package/src/tools/memoryRecall.ts +33 -9
  175. package/src/trainer/index.ts +1 -1
  176. package/src/trainer/scheduler.ts +3 -0
  177. package/src/trainer/sessionLoader.ts +38 -87
@@ -55,7 +55,7 @@ export class MemoryRecallTool {
55
55
  };
56
56
  }
57
57
 
58
- async run(argsJson: string, signal?: AbortSignal): Promise<ToolResult> {
58
+ async run(argsJson: string, signal?: AbortSignal, onProgress?: (msg: string) => void): Promise<ToolResult> {
59
59
  const args = parseArgs(argsJson);
60
60
  if ("error" in args) {
61
61
  return { content: args.error, isError: true };
@@ -67,29 +67,53 @@ export class MemoryRecallTool {
67
67
  return { content: validation, isError: true };
68
68
  }
69
69
 
70
- return this.runIntent(intent, signal);
70
+ return this.runIntent(intent, signal, onProgress);
71
71
  }
72
72
 
73
- async runIntent(intent: QueryIntent, signal?: AbortSignal): Promise<ToolResult> {
73
+ async runIntent(intent: QueryIntent, signal?: AbortSignal, onProgress?: (msg: string) => void): Promise<ToolResult> {
74
74
  if (this.service.status() !== "ready") {
75
75
  return this.fallbackResult(intent, "service_unavailable", "fallback");
76
76
  }
77
77
 
78
- let result = await this.service.query(intent, signal);
78
+ // Emit a progress message if the sidecar takes longer than 500 ms.
79
+ let progressTimer: ReturnType<typeof setTimeout> | null = null;
80
+ if (onProgress) {
81
+ progressTimer = setTimeout(() => {
82
+ onProgress("Querying episodic memory…");
83
+ }, 500);
84
+ }
85
+
86
+ let result;
87
+ try {
88
+ result = await this.service.query(intent, signal);
89
+ } finally {
90
+ if (progressTimer != null) clearTimeout(progressTimer);
91
+ }
92
+
79
93
  if (result.transportError || result.errorClass === "unavailable") {
80
94
  return this.fallbackResult(intent, "service_unavailable", "fallback");
81
95
  }
82
96
 
83
97
  if (result.errorClass === "retryable") {
84
- await sleep(500, signal);
85
- result = await this.service.query(intent, signal);
98
+ let retryTimer: ReturnType<typeof setTimeout> | null = null;
99
+ if (onProgress) {
100
+ retryTimer = setTimeout(() => onProgress("Retrying memory query…"), 500);
101
+ }
102
+ let retryResult;
103
+ try {
104
+ await sleep(500, signal);
105
+ retryResult = await this.service.query(intent, signal);
106
+ } finally {
107
+ if (retryTimer != null) clearTimeout(retryTimer);
108
+ }
86
109
  if (
87
- result.transportError ||
88
- result.errorClass === "unavailable" ||
89
- result.errorClass === "retryable"
110
+ retryResult.transportError ||
111
+ retryResult.errorClass === "unavailable" ||
112
+ retryResult.errorClass === "retryable"
90
113
  ) {
91
114
  return this.fallbackResult(intent, "retryable_failed", "fallback_after_retry");
92
115
  }
116
+ result = retryResult;
93
117
  }
94
118
 
95
119
  if (result.errorClass === "permanent") {
@@ -129,7 +129,7 @@ export async function trainBundle(
129
129
  };
130
130
  }
131
131
 
132
- export { loadSessions } from "./sessionLoader.js";
132
+ export { loadSessionFile, loadSessions } from "./sessionLoader.js";
133
133
  export type { LoadedSession, SessionTurn, SessionLoaderOptions } from "./sessionLoader.js";
134
134
  export { extractFacts, extractFactsFromSessions, RELATION_CATALOG, ALL_RELATIONS } from "./extractFacts.js";
135
135
  export type {
@@ -5,6 +5,8 @@ export interface SchedulerConfig {
5
5
  interval: string | null;
6
6
  /** Passed through to trainBundle. */
7
7
  trainConfig?: Omit<TrainBundleConfig, "full" | "dryRun">;
8
+ /** Called after a successful train tick (no error). Use to notify MemoryService. */
9
+ onSuccess?: () => void;
8
10
  }
9
11
 
10
12
  export interface TrainScheduler {
@@ -69,6 +71,7 @@ export function createTrainScheduler(
69
71
  full: false,
70
72
  dryRun: false,
71
73
  });
74
+ config.onSuccess?.();
72
75
  } catch (err) {
73
76
  error = err instanceof Error ? err.message : String(err);
74
77
  }
@@ -1,6 +1,7 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import fs from "node:fs/promises";
3
3
  import path from "node:path";
4
+ import { messageText, parseJsonlSession as parseActiveBranchJsonl } from "../session/activeBranch.js";
4
5
 
5
6
  export interface SessionTurn {
6
7
  role: string;
@@ -14,6 +15,8 @@ export interface LoadedSession {
14
15
  createdAt: string;
15
16
  filePath: string;
16
17
  modifiedAt: Date;
18
+ parentSessionFile?: string;
19
+ parentSessionId?: string;
17
20
  turns: SessionTurn[];
18
21
  }
19
22
 
@@ -29,39 +32,6 @@ interface PiSessionFile {
29
32
  messages?: PiSessionMessage[];
30
33
  }
31
34
 
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
-
47
- function messageText(content: unknown): string {
48
- if (typeof content === "string") return content;
49
- if (!Array.isArray(content)) return "";
50
- const parts: string[] = [];
51
- for (const block of content) {
52
- if (typeof block === "string") {
53
- parts.push(block);
54
- continue;
55
- }
56
- if (block && typeof block === "object") {
57
- const b = block as Record<string, unknown>;
58
- if (typeof b.text === "string") parts.push(b.text);
59
- else if (typeof b.content === "string") parts.push(b.content);
60
- }
61
- }
62
- return parts.join("\n");
63
- }
64
-
65
35
  export interface SessionLoaderOptions {
66
36
  sessionsDir: string;
67
37
  modifiedAfter?: Date | null;
@@ -94,49 +64,6 @@ async function collectSessionFiles(dir: string): Promise<string[]> {
94
64
  return files;
95
65
  }
96
66
 
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;
100
-
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>;
107
- try {
108
- obj = JSON.parse(line) as Record<string, unknown>;
109
- } catch {
110
- continue;
111
- }
112
-
113
- if (obj.type === "session" && !header) {
114
- header = obj as unknown as JsonlSessionHeader;
115
- continue;
116
- }
117
-
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
67
  function parseJsonSession(raw: string, filePath: string): LoadedSession | null {
141
68
  let session: PiSessionFile;
142
69
  try {
@@ -167,6 +94,16 @@ function parseJsonSession(raw: string, filePath: string): LoadedSession | null {
167
94
  };
168
95
  }
169
96
 
97
+ function parseJsonlSession(raw: string, filePath: string): LoadedSession | null {
98
+ const parsed = parseActiveBranchJsonl(raw, filePath);
99
+ if (!parsed) return null;
100
+ return {
101
+ ...parsed,
102
+ filePath,
103
+ modifiedAt: new Date(),
104
+ };
105
+ }
106
+
170
107
  /**
171
108
  * Scan session files (JSON + JSONL, recursive subdirectories), parse Pi session
172
109
  * format, optionally filter by modified-after timestamp for incremental training.
@@ -190,17 +127,7 @@ export async function loadSessions(
190
127
  if (!st.isFile()) continue;
191
128
  if (modifiedAfter && st.mtime <= modifiedAfter) continue;
192
129
 
193
- let raw: string;
194
- try {
195
- raw = await fs.readFile(filePath, "utf8");
196
- } catch {
197
- continue;
198
- }
199
-
200
- const isJsonl = filePath.endsWith(".jsonl");
201
- const parsed = isJsonl
202
- ? parseJsonlSession(raw, filePath)
203
- : parseJsonSession(raw, filePath);
130
+ const parsed = await loadSessionFile(filePath);
204
131
 
205
132
  if (!parsed) continue;
206
133
  parsed.modifiedAt = st.mtime;
@@ -211,6 +138,30 @@ export async function loadSessions(
211
138
  return deduplicateSessions(sessions);
212
139
  }
213
140
 
141
+ export async function loadSessionFile(filePath: string): Promise<LoadedSession | null> {
142
+ let st: Awaited<ReturnType<typeof fs.stat>>;
143
+ try {
144
+ st = await fs.stat(filePath);
145
+ } catch {
146
+ return null;
147
+ }
148
+ if (!st.isFile()) return null;
149
+
150
+ let raw: string;
151
+ try {
152
+ raw = await fs.readFile(filePath, "utf8");
153
+ } catch {
154
+ return null;
155
+ }
156
+
157
+ const parsed = filePath.endsWith(".jsonl")
158
+ ? parseJsonlSession(raw, filePath)
159
+ : parseJsonSession(raw, filePath);
160
+ if (!parsed) return null;
161
+ parsed.modifiedAt = st.mtime;
162
+ return parsed;
163
+ }
164
+
214
165
  function deduplicateSessions(sessions: LoadedSession[]): LoadedSession[] {
215
166
  const seen = new Set<string>();
216
167
  return sessions.filter((s) => {