@chendpoc/pi-memory 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (177) hide show
  1. package/README.md +180 -0
  2. package/dist/adapters/piComplete.d.ts +17 -0
  3. package/dist/adapters/piComplete.d.ts.map +1 -0
  4. package/dist/adapters/piComplete.js +169 -0
  5. package/dist/adapters/piComplete.js.map +1 -0
  6. package/dist/bundle/install.d.ts +34 -0
  7. package/dist/bundle/install.d.ts.map +1 -0
  8. package/dist/bundle/install.js +183 -0
  9. package/dist/bundle/install.js.map +1 -0
  10. package/dist/cli.d.ts +3 -0
  11. package/dist/cli.d.ts.map +1 -0
  12. package/dist/cli.js +245 -0
  13. package/dist/cli.js.map +1 -0
  14. package/dist/config.d.ts +27 -0
  15. package/dist/config.d.ts.map +1 -0
  16. package/dist/config.js +49 -0
  17. package/dist/config.js.map +1 -0
  18. package/dist/errclass.d.ts +7 -0
  19. package/dist/errclass.d.ts.map +1 -0
  20. package/dist/errclass.js +32 -0
  21. package/dist/errclass.js.map +1 -0
  22. package/dist/extension.d.ts +24 -0
  23. package/dist/extension.d.ts.map +1 -0
  24. package/dist/extension.js +7 -0
  25. package/dist/extension.js.map +1 -0
  26. package/dist/fallback/index.d.ts +11 -0
  27. package/dist/fallback/index.d.ts.map +1 -0
  28. package/dist/fallback/index.js +16 -0
  29. package/dist/fallback/index.js.map +1 -0
  30. package/dist/fallback/llmRerank.d.ts +19 -0
  31. package/dist/fallback/llmRerank.d.ts.map +1 -0
  32. package/dist/fallback/llmRerank.js +60 -0
  33. package/dist/fallback/llmRerank.js.map +1 -0
  34. package/dist/fallback/memoryMd.d.ts +6 -0
  35. package/dist/fallback/memoryMd.d.ts.map +1 -0
  36. package/dist/fallback/memoryMd.js +35 -0
  37. package/dist/fallback/memoryMd.js.map +1 -0
  38. package/dist/fallback/sessionIndex.d.ts +35 -0
  39. package/dist/fallback/sessionIndex.d.ts.map +1 -0
  40. package/dist/fallback/sessionIndex.js +222 -0
  41. package/dist/fallback/sessionIndex.js.map +1 -0
  42. package/dist/fallback/sessionSearch.d.ts +18 -0
  43. package/dist/fallback/sessionSearch.d.ts.map +1 -0
  44. package/dist/fallback/sessionSearch.js +161 -0
  45. package/dist/fallback/sessionSearch.js.map +1 -0
  46. package/dist/index.d.ts +25 -0
  47. package/dist/index.d.ts.map +1 -0
  48. package/dist/index.js +24 -0
  49. package/dist/index.js.map +1 -0
  50. package/dist/paths.d.ts +7 -0
  51. package/dist/paths.d.ts.map +1 -0
  52. package/dist/paths.js +26 -0
  53. package/dist/paths.js.map +1 -0
  54. package/dist/pi-extension.d.ts +6 -0
  55. package/dist/pi-extension.d.ts.map +1 -0
  56. package/dist/pi-extension.js +224 -0
  57. package/dist/pi-extension.js.map +1 -0
  58. package/dist/preflight/detectIntents.d.ts +102 -0
  59. package/dist/preflight/detectIntents.d.ts.map +1 -0
  60. package/dist/preflight/detectIntents.js +624 -0
  61. package/dist/preflight/detectIntents.js.map +1 -0
  62. package/dist/preflight/hook.d.ts +58 -0
  63. package/dist/preflight/hook.d.ts.map +1 -0
  64. package/dist/preflight/hook.js +77 -0
  65. package/dist/preflight/hook.js.map +1 -0
  66. package/dist/preflight/render.d.ts +21 -0
  67. package/dist/preflight/render.d.ts.map +1 -0
  68. package/dist/preflight/render.js +132 -0
  69. package/dist/preflight/render.js.map +1 -0
  70. package/dist/preflight/strip.d.ts +11 -0
  71. package/dist/preflight/strip.d.ts.map +1 -0
  72. package/dist/preflight/strip.js +46 -0
  73. package/dist/preflight/strip.js.map +1 -0
  74. package/dist/service.d.ts +56 -0
  75. package/dist/service.d.ts.map +1 -0
  76. package/dist/service.js +158 -0
  77. package/dist/service.js.map +1 -0
  78. package/dist/sidecar/bundle.d.ts +19 -0
  79. package/dist/sidecar/bundle.d.ts.map +1 -0
  80. package/dist/sidecar/bundle.js +39 -0
  81. package/dist/sidecar/bundle.js.map +1 -0
  82. package/dist/sidecar/client.d.ts +17 -0
  83. package/dist/sidecar/client.d.ts.map +1 -0
  84. package/dist/sidecar/client.js +107 -0
  85. package/dist/sidecar/client.js.map +1 -0
  86. package/dist/sidecar/process.d.ts +14 -0
  87. package/dist/sidecar/process.d.ts.map +1 -0
  88. package/dist/sidecar/process.js +126 -0
  89. package/dist/sidecar/process.js.map +1 -0
  90. package/dist/tools/memoryAppend.d.ts +37 -0
  91. package/dist/tools/memoryAppend.d.ts.map +1 -0
  92. package/dist/tools/memoryAppend.js +99 -0
  93. package/dist/tools/memoryAppend.js.map +1 -0
  94. package/dist/tools/memoryRecall.d.ts +113 -0
  95. package/dist/tools/memoryRecall.d.ts.map +1 -0
  96. package/dist/tools/memoryRecall.js +325 -0
  97. package/dist/tools/memoryRecall.js.map +1 -0
  98. package/dist/trainer/bundleBuilder.d.ts +30 -0
  99. package/dist/trainer/bundleBuilder.d.ts.map +1 -0
  100. package/dist/trainer/bundleBuilder.js +106 -0
  101. package/dist/trainer/bundleBuilder.js.map +1 -0
  102. package/dist/trainer/bundleLoader.d.ts +12 -0
  103. package/dist/trainer/bundleLoader.d.ts.map +1 -0
  104. package/dist/trainer/bundleLoader.js +59 -0
  105. package/dist/trainer/bundleLoader.js.map +1 -0
  106. package/dist/trainer/deltaMerge.d.ts +38 -0
  107. package/dist/trainer/deltaMerge.d.ts.map +1 -0
  108. package/dist/trainer/deltaMerge.js +183 -0
  109. package/dist/trainer/deltaMerge.js.map +1 -0
  110. package/dist/trainer/entityResolver.d.ts +27 -0
  111. package/dist/trainer/entityResolver.d.ts.map +1 -0
  112. package/dist/trainer/entityResolver.js +92 -0
  113. package/dist/trainer/entityResolver.js.map +1 -0
  114. package/dist/trainer/extractFacts.d.ts +67 -0
  115. package/dist/trainer/extractFacts.d.ts.map +1 -0
  116. package/dist/trainer/extractFacts.js +213 -0
  117. package/dist/trainer/extractFacts.js.map +1 -0
  118. package/dist/trainer/index.d.ts +54 -0
  119. package/dist/trainer/index.d.ts.map +1 -0
  120. package/dist/trainer/index.js +82 -0
  121. package/dist/trainer/index.js.map +1 -0
  122. package/dist/trainer/llmExtractor.d.ts +16 -0
  123. package/dist/trainer/llmExtractor.d.ts.map +1 -0
  124. package/dist/trainer/llmExtractor.js +146 -0
  125. package/dist/trainer/llmExtractor.js.map +1 -0
  126. package/dist/trainer/marker.d.ts +10 -0
  127. package/dist/trainer/marker.d.ts.map +1 -0
  128. package/dist/trainer/marker.js +28 -0
  129. package/dist/trainer/marker.js.map +1 -0
  130. package/dist/trainer/scheduler.d.ts +31 -0
  131. package/dist/trainer/scheduler.d.ts.map +1 -0
  132. package/dist/trainer/scheduler.js +72 -0
  133. package/dist/trainer/scheduler.js.map +1 -0
  134. package/dist/trainer/sessionLoader.d.ts +23 -0
  135. package/dist/trainer/sessionLoader.d.ts.map +1 -0
  136. package/dist/trainer/sessionLoader.js +106 -0
  137. package/dist/trainer/sessionLoader.js.map +1 -0
  138. package/dist/types.d.ts +135 -0
  139. package/dist/types.d.ts.map +1 -0
  140. package/dist/types.js +8 -0
  141. package/dist/types.js.map +1 -0
  142. package/package.json +78 -0
  143. package/src/adapters/piComplete.ts +233 -0
  144. package/src/bundle/install.ts +206 -0
  145. package/src/cli.ts +254 -0
  146. package/src/config.ts +92 -0
  147. package/src/errclass.ts +37 -0
  148. package/src/extension.ts +23 -0
  149. package/src/fallback/index.ts +24 -0
  150. package/src/fallback/llmRerank.ts +90 -0
  151. package/src/fallback/memoryMd.ts +36 -0
  152. package/src/fallback/sessionIndex.ts +289 -0
  153. package/src/fallback/sessionSearch.ts +181 -0
  154. package/src/index.ts +213 -0
  155. package/src/paths.ts +28 -0
  156. package/src/pi-extension.ts +276 -0
  157. package/src/preflight/detectIntents.ts +654 -0
  158. package/src/preflight/hook.ts +136 -0
  159. package/src/preflight/render.ts +185 -0
  160. package/src/preflight/strip.ts +50 -0
  161. package/src/service.ts +202 -0
  162. package/src/sidecar/bundle.ts +52 -0
  163. package/src/sidecar/client.ts +166 -0
  164. package/src/sidecar/process.ts +145 -0
  165. package/src/tools/memoryAppend.ts +113 -0
  166. package/src/tools/memoryRecall.ts +364 -0
  167. package/src/trainer/bundleBuilder.ts +192 -0
  168. package/src/trainer/bundleLoader.ts +105 -0
  169. package/src/trainer/deltaMerge.ts +221 -0
  170. package/src/trainer/entityResolver.ts +140 -0
  171. package/src/trainer/extractFacts.ts +312 -0
  172. package/src/trainer/index.ts +147 -0
  173. package/src/trainer/llmExtractor.ts +206 -0
  174. package/src/trainer/marker.ts +30 -0
  175. package/src/trainer/scheduler.ts +104 -0
  176. package/src/trainer/sessionLoader.ts +139 -0
  177. package/src/types.ts +168 -0
@@ -0,0 +1,276 @@
1
+ import type { AgentMessage } from "@earendil-works/pi-agent-core";
2
+ import type { UserMessage } from "@earendil-works/pi-ai";
3
+ import { StringEnum } from "@earendil-works/pi-ai";
4
+ import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
5
+ import { truncateHead } from "@earendil-works/pi-coding-agent";
6
+ import { Type } from "typebox";
7
+
8
+ import {
9
+ createPiLLMClient,
10
+ DEFAULT_HELPER_MODEL,
11
+ DEFAULT_HELPER_PROVIDER,
12
+ resolveMemoryHelperLLM,
13
+ } from "./adapters/piComplete.js";
14
+ import { defaultMemoryConfig, type MemoryConfig } from "./config.js";
15
+ import { createFallbackQuery } from "./fallback/index.js";
16
+ import type { RerankOptions } from "./fallback/llmRerank.js";
17
+ import type { MemoryHelperLLM } from "./preflight/detectIntents.js";
18
+ import { runMemoryPreflight } from "./preflight/hook.js";
19
+ import { injectPrivateMemoryContext } from "./preflight/strip.js";
20
+ import { MemoryService } from "./service.js";
21
+ import type { LLMClient } from "./trainer/llmExtractor.js";
22
+ import {
23
+ createMemoryAppendTool,
24
+ MEMORY_APPEND_DESCRIPTION,
25
+ MEMORY_APPEND_NAME,
26
+ MEMORY_APPEND_PROMPT_GUIDELINES,
27
+ MEMORY_APPEND_PROMPT_SNIPPET,
28
+ } from "./tools/memoryAppend.js";
29
+ import {
30
+ createMemoryRecallTool,
31
+ MEMORY_RECALL_DESCRIPTION,
32
+ MEMORY_RECALL_NAME,
33
+ MEMORY_RECALL_PROMPT_GUIDELINES,
34
+ MEMORY_RECALL_PROMPT_SNIPPET,
35
+ } from "./tools/memoryRecall.js";
36
+
37
+ const MemoryRecallParams = Type.Object({
38
+ mode: Type.Optional(
39
+ StringEnum(["direct_relation", "path_query", "typed_neighborhood"] as const),
40
+ ),
41
+ anchor_mentions: Type.Array(Type.String()),
42
+ relation_constraints: Type.Optional(Type.Array(Type.String())),
43
+ candidate_type: Type.Optional(Type.String()),
44
+ scope_filter: Type.Optional(Type.Array(Type.String())),
45
+ target_slot: Type.Optional(StringEnum(["head", "tail"] as const)),
46
+ time_window: Type.Optional(Type.String()),
47
+ evidence_budget: Type.Optional(Type.Number()),
48
+ result_limit: Type.Optional(Type.Number()),
49
+ });
50
+
51
+ const MemoryAppendParams = Type.Object({
52
+ content: Type.String({ description: "New entries to append (markdown bullet points)." }),
53
+ });
54
+
55
+ const RECALL_MAX_LINES = 200;
56
+ const RECALL_MAX_BYTES = 32_000;
57
+
58
+ let sharedService: MemoryService | null = null;
59
+ let sessionCfg: MemoryConfig | null = null;
60
+ let sharedHelper: MemoryHelperLLM | null = null;
61
+ let sharedLLMClient: LLMClient | null = null;
62
+ let preflightCache: { userText: string; privateContext: string } | null = null;
63
+
64
+ export function getSharedMemoryService(): MemoryService | null {
65
+ return sharedService;
66
+ }
67
+
68
+ async function refreshMemoryHelper(ctx: ExtensionContext, pi: ExtensionAPI): Promise<void> {
69
+ sharedHelper = await resolveMemoryHelperLLM(ctx, pi.getFlag("memory-helper-model"));
70
+ sharedLLMClient = createPiLLMClient(ctx, pi.getFlag("memory-helper-model"));
71
+ }
72
+
73
+ function getRerankOpts(): RerankOptions | null {
74
+ if (!sharedLLMClient) return null;
75
+ return { client: sharedLLMClient };
76
+ }
77
+
78
+ function getUserMessageText(message: AgentMessage): string | null {
79
+ if (message.role !== "user") return null;
80
+ const user = message as UserMessage;
81
+ if (typeof user.content === "string") return user.content;
82
+ return user.content
83
+ .filter((block): block is { type: "text"; text: string } => block.type === "text")
84
+ .map((block) => block.text)
85
+ .join("\n");
86
+ }
87
+
88
+ function setUserMessageText(message: UserMessage, text: string): UserMessage {
89
+ return { ...message, content: text };
90
+ }
91
+
92
+ function findLastUserMessageIndex(messages: AgentMessage[]): number {
93
+ for (let i = messages.length - 1; i >= 0; i--) {
94
+ if (messages[i]?.role === "user") return i;
95
+ }
96
+ return -1;
97
+ }
98
+
99
+ function formatMemoryStatus(service: MemoryService): string {
100
+ const snap = service.getStatus();
101
+ const lines = [
102
+ `status: ${snap.status}`,
103
+ snap.reason ? `reason: ${snap.reason}` : null,
104
+ snap.health ? `health: ${JSON.stringify(snap.health)}` : null,
105
+ ].filter(Boolean);
106
+ return lines.join("\n");
107
+ }
108
+
109
+ export default function piMemoryExtension(pi: ExtensionAPI): void {
110
+ pi.registerFlag("memory-helper-model", {
111
+ description: `Model for memory intent helper (default: ${DEFAULT_HELPER_PROVIDER}/${DEFAULT_HELPER_MODEL})`,
112
+ type: "string",
113
+ });
114
+
115
+ pi.on("session_start", async (_event, ctx) => {
116
+ const cfg = defaultMemoryConfig();
117
+ sessionCfg = cfg;
118
+ preflightCache = null;
119
+ sharedHelper = null;
120
+
121
+ if (cfg.provider === "disabled") return;
122
+
123
+ const service = new MemoryService(cfg);
124
+ sharedService = service;
125
+
126
+ try {
127
+ await service.start();
128
+ service.startSessionIndex();
129
+ if (cfg.trainer.auto_interval) {
130
+ service.startAutoTrainer();
131
+ }
132
+ await refreshMemoryHelper(ctx, pi);
133
+ } catch (err) {
134
+ const message = err instanceof Error ? err.message : String(err);
135
+ if (ctx.hasUI) {
136
+ ctx.ui.notify(`pi-memory: sidecar start failed (${message}) — fallback mode active`, "warning");
137
+ }
138
+ }
139
+ });
140
+
141
+ pi.on("session_shutdown", async () => {
142
+ if (sharedService) {
143
+ await sharedService.stop();
144
+ sharedService = null;
145
+ }
146
+ sessionCfg = null;
147
+ sharedHelper = null;
148
+ sharedLLMClient = null;
149
+ preflightCache = null;
150
+ });
151
+
152
+ pi.on("model_select", async (_event, ctx) => {
153
+ await refreshMemoryHelper(ctx, pi);
154
+ });
155
+
156
+ pi.on("agent_start", () => {
157
+ preflightCache = null;
158
+ });
159
+
160
+ const fallback = createFallbackQuery({
161
+ sessionsDir: defaultMemoryConfig().sessionsDir,
162
+ memoryMdPaths: defaultMemoryConfig().memoryMdPaths,
163
+ });
164
+
165
+ pi.registerCommand("memory", {
166
+ description: "Show pi-memory sidecar status and bundle info",
167
+ handler: async (_args, ctx) => {
168
+ const service = sharedService;
169
+ if (!service) {
170
+ ctx.ui.notify("pi-memory: service not started", "warning");
171
+ return;
172
+ }
173
+ ctx.ui.notify(formatMemoryStatus(service), "info");
174
+ },
175
+ });
176
+
177
+ pi.registerTool({
178
+ name: MEMORY_RECALL_NAME,
179
+ label: "Memory Recall",
180
+ description: MEMORY_RECALL_DESCRIPTION,
181
+ promptSnippet: MEMORY_RECALL_PROMPT_SNIPPET,
182
+ promptGuidelines: [...MEMORY_RECALL_PROMPT_GUIDELINES],
183
+ parameters: MemoryRecallParams,
184
+ async execute(_toolCallId, params, signal) {
185
+ const service = sharedService;
186
+ if (!service) {
187
+ return {
188
+ content: [{ type: "text", text: "Memory service not started." }],
189
+ details: { truncated: false },
190
+ isError: true,
191
+ };
192
+ }
193
+ const tool = createMemoryRecallTool(service, fallback, getRerankOpts());
194
+ const result = await tool.run(JSON.stringify(params), signal);
195
+ const truncated = truncateHead(result.content, {
196
+ maxLines: RECALL_MAX_LINES,
197
+ maxBytes: RECALL_MAX_BYTES,
198
+ });
199
+ let text = truncated.content;
200
+ if (truncated.truncated) {
201
+ text += `\n\n[truncated: ${truncated.totalLines} lines, ${truncated.totalBytes} bytes]`;
202
+ }
203
+ return {
204
+ content: [{ type: "text", text }],
205
+ details: { truncated: truncated.truncated },
206
+ isError: result.isError ?? false,
207
+ };
208
+ },
209
+ });
210
+
211
+ const memoryMdPath = defaultMemoryConfig().memoryMdPaths[0];
212
+ if (memoryMdPath) {
213
+ const appendTool = createMemoryAppendTool(memoryMdPath);
214
+ pi.registerTool({
215
+ name: MEMORY_APPEND_NAME,
216
+ label: "Memory Append",
217
+ description: MEMORY_APPEND_DESCRIPTION,
218
+ promptSnippet: MEMORY_APPEND_PROMPT_SNIPPET,
219
+ promptGuidelines: [...MEMORY_APPEND_PROMPT_GUIDELINES],
220
+ parameters: MemoryAppendParams,
221
+ async execute(_toolCallId, params) {
222
+ const result = await appendTool.run(JSON.stringify(params));
223
+ return {
224
+ content: [{ type: "text", text: result.content }],
225
+ details: {},
226
+ isError: result.isError ?? false,
227
+ };
228
+ },
229
+ });
230
+ }
231
+
232
+ pi.on("context", async (event, ctx) => {
233
+ const service = sharedService;
234
+ const cfg = sessionCfg;
235
+ if (!service || !cfg || cfg.provider === "disabled") return;
236
+
237
+ const userIndex = findLastUserMessageIndex(event.messages);
238
+ if (userIndex < 0) return;
239
+
240
+ const userText = getUserMessageText(event.messages[userIndex]!);
241
+ if (!userText?.trim()) return;
242
+
243
+ let privateContext: string | undefined;
244
+ if (preflightCache?.userText === userText) {
245
+ privateContext = preflightCache.privateContext;
246
+ } else {
247
+ const userTurnCount = event.messages.filter((m) => m.role === "user").length;
248
+ const preflight = await runMemoryPreflight(userText, service, {
249
+ helper: sharedHelper,
250
+ forceHelper: userTurnCount === 1,
251
+ fallback,
252
+ signal: ctx.signal,
253
+ rerankOpts: getRerankOpts(),
254
+ });
255
+ if (!preflight?.privateContext) return;
256
+ privateContext = preflight.privateContext;
257
+ preflightCache = { userText, privateContext };
258
+ }
259
+
260
+ const injectedText = injectPrivateMemoryContext(
261
+ userText,
262
+ userText,
263
+ privateContext,
264
+ );
265
+
266
+ const messages = [...event.messages];
267
+ messages[userIndex] = setUserMessageText(
268
+ messages[userIndex] as UserMessage,
269
+ injectedText,
270
+ );
271
+
272
+ return { messages };
273
+ });
274
+ }
275
+
276
+ export { piMemoryExtension };