@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
package/README.md ADDED
@@ -0,0 +1,180 @@
1
+ # @chendpoc/pi-memory
2
+
3
+ Pi Agent 本地情景记忆(**模式 B**)— TLM sidecar + `memory_recall` 工具 + **隐式 episodic preflight**。
4
+
5
+ 与 [Kocoro](https://github.com/Kocoro-lab/Kocoro) 的 TLM 协议对齐:`QueryIntent` → Unix socket `POST /query` → `ResponseEnvelope`。
6
+
7
+ ## 前置条件
8
+
9
+ 1. **安装 `tlm` sidecar**(与 Kocoro 相同,建议 v0.6.x+)并加入 `PATH`,或配置 `tlmPath` 绝对路径。
10
+ 2. **准备本地 bundle**(自行训练或从测试环境拷贝):
11
+
12
+ ```text
13
+ ~/.pi/memory/
14
+ ├── current → bundles/2026-06-01T00-00-00Z # symlink
15
+ └── bundles/
16
+ └── 2026-06-01T00-00-00Z/
17
+ ├── manifest.json
18
+ └── …
19
+ ```
20
+
21
+ `current` 目录下必须有可解析的 `manifest.json`(与 Kocoro `currentBundleReadable` 一致)。
22
+
23
+ 3. (可选)`~/.pi/MEMORY.md` — sidecar 不可用时的 fallback 关键词匹配;`~/.pi/sessions/*.json` — session 关键词 fallback。
24
+
25
+ ## 安装
26
+
27
+ ### 作为 Pi package(推荐)
28
+
29
+ 本地开发(settings 相对路径):
30
+
31
+ ```json
32
+ {
33
+ "packages": ["./extensions/pi-memory"]
34
+ }
35
+ ```
36
+
37
+ 或发布后:
38
+
39
+ ```bash
40
+ pi install npm:@chendpoc/pi-memory
41
+ ```
42
+
43
+ `package.json` 中已声明 `pi.extensions`,Pi 会自动加载 `src/pi-extension.ts`。
44
+
45
+ ### 构建 CLI / 库
46
+
47
+ ```bash
48
+ cd agent/extensions/pi-memory
49
+ npm install
50
+ npm run build
51
+ ```
52
+
53
+ ## CLI 手动测试
54
+
55
+ ```bash
56
+ # 检查 sidecar 健康(会尝试 spawn tlm)
57
+ npx pi-memory health
58
+
59
+ # 结构化查询
60
+ npx pi-memory query '{"mode":"direct_relation","anchor_mentions":["Alice"]}'
61
+
62
+ # 安装本地 bundle(staging → bundles/<ts> → current symlink,sidecar 在跑时会 /bundle/reload)
63
+ npx pi-memory install-bundle ./path/to/bundle-dir
64
+ ```
65
+
66
+ ## 在 Pi Agent 中启用
67
+
68
+ 本包已对接 `@earendil-works/pi-coding-agent` 的 `ExtensionAPI`:
69
+
70
+ | 旧 stub API | Pi 真实 API |
71
+ |-------------|-------------|
72
+ | factory 内 `service.start()` | `session_start` |
73
+ | `onUnload` | `session_shutdown` |
74
+ | `registerTool` | `pi.registerTool`(TypeBox 参数) |
75
+ | `onBeforeTurn` | `context` 事件(LLM 调用前注入,不写入 session) |
76
+
77
+ 扩展加载后:
78
+
79
+ 1. **`session_start`** — `MemoryService.start()` spawn `tlm serve`
80
+ 2. 注册 **`memory_recall`** 工具(sidecar 不可用时 fallback 到 session JSON + MEMORY.md)
81
+ 3. 注册 **`memory_append`** 工具(追加 `~/.pi/MEMORY.md`)
82
+ 4. **`context` 事件** — 隐式 preflight,注入 `<private_memory>` 到当次 LLM 请求的 messages 副本
83
+
84
+ Helper 小模型通过 `@earendil-works/pi-ai/compat` 的 `complete()` 调用,默认 `deepseek/deepseek-v4-flash`:
85
+
86
+ ```bash
87
+ pi --memory-helper-model deepseek/deepseek-v4-flash
88
+ ```
89
+
90
+ ### Pi 命令
91
+
92
+ - `/memory` — 显示 sidecar 状态、reason 与 health 摘要
93
+
94
+ 也可程序化 import:
95
+
96
+ ```typescript
97
+ import piMemory from "@chendpoc/pi-memory/extension";
98
+ export default piMemory;
99
+ ```
100
+
101
+ **持久化:** `context` 事件修改的是 messages 深拷贝,`<private_memory>` 不会写入 session JSON,无需额外 `stripPrivateMemory`。
102
+
103
+ ## 隐式 Episodic Preflight
104
+
105
+ 在每轮 LLM 调用**之前**,通过 `context` 事件检测是否需要召回私人情景记忆,并将结果注入 `<private_memory>` 块。
106
+
107
+ ### 工作流程
108
+
109
+ 1. **Intent 检测** — 中/英/日关系问句正则快路径;可选 `MemoryHelperLLM`(`compile_memory_intents` forced tool_use,由 `complete()` 驱动)。
110
+ 2. **批量查询** — 对最多 3 个 intent 并发调用 sidecar,`2s` 超时,失败静默跳过。
111
+ 3. **渲染** — 组装 `<private_memory>` 正文,**8192 字节**上限。
112
+ 4. **注入** — 通过 `context` 事件写回最后一条 user message。
113
+
114
+ ### Helper LLM(Pi 集成)
115
+
116
+ ```typescript
117
+ import { resolveMemoryHelperLLM } from "@chendpoc/pi-memory";
118
+
119
+ // 在 extension 的 context handler 中:
120
+ const helper = await resolveMemoryHelperLLM(ctx, pi.getFlag("memory-helper-model"));
121
+ ```
122
+
123
+ `helper` 为 `null` 时仅走正则快路径(fail-silent)。
124
+
125
+ ## 架构
126
+
127
+ | 模块 | 作用 |
128
+ |------|------|
129
+ | `src/pi-extension.ts` | Pi ExtensionAPI 入口 |
130
+ | `src/adapters/piComplete.ts` | `complete()` 适配(helper + trainer LLM) |
131
+ | `SidecarProcess` | spawn / waitReady / stop |
132
+ | `SidecarClient` | `/health`, `/query`, `/bundle/reload` |
133
+ | `MemoryService` | 本地模式生命周期 + query / queryBatch |
134
+ | `MemoryRecallTool` | Agent 工具 + session/MEMORY.md fallback |
135
+ | `memory_append` | 追加 MEMORY.md |
136
+ | `preflight/*` | 隐式 preflight detect → query → render → inject/strip |
137
+ | `bundle/install` | `install-bundle` CLI |
138
+
139
+ ## LLM 深度提取(Trainer)
140
+
141
+ 使用 `@earendil-works/pi-ai/compat` 的 `complete()`,不绑定具体 LLM 厂商:
142
+
143
+ ```typescript
144
+ import { createLLMFactExtractor, createStandaloneLLMClient, trainBundle } from "@chendpoc/pi-memory";
145
+
146
+ const extractor = createLLMFactExtractor({
147
+ client: createStandaloneLLMClient("deepseek/deepseek-v4-flash"),
148
+ batchSize: 10,
149
+ });
150
+ ```
151
+
152
+ CLI:
153
+
154
+ ```bash
155
+ pi-memory train --extractor llm
156
+ pi-memory train --extractor llm --model deepseek/deepseek-v4-flash
157
+ pi-memory train --extractor regex
158
+ ```
159
+
160
+ 需配置对应 provider 的环境变量(如 `DEEPSEEK_API_KEY`)。LLM 不可用时 CLI 自动回退 regex。
161
+
162
+ ## peerDependencies
163
+
164
+ Pi 核心包由 host 提供,列在 `peerDependencies` 中,勿 bundle:
165
+
166
+ - `@earendil-works/pi-ai`
167
+ - `@earendil-works/pi-coding-agent`
168
+ - `typebox`
169
+
170
+ ## 开发
171
+
172
+ ```bash
173
+ npm test
174
+ npm run typecheck
175
+ npm run build
176
+ ```
177
+
178
+ ## 许可
179
+
180
+ MIT
@@ -0,0 +1,17 @@
1
+ import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
2
+ import { type MemoryHelperLLM } from "../preflight/detectIntents.js";
3
+ import type { LLMClient } from "../trainer/llmExtractor.js";
4
+ export declare const DEFAULT_HELPER_PROVIDER = "deepseek";
5
+ export declare const DEFAULT_HELPER_MODEL = "deepseek-v4-flash";
6
+ export declare function parseModelSpec(spec: string | boolean | undefined, defaultProvider?: string, defaultModelId?: string): {
7
+ provider: string;
8
+ modelId: string;
9
+ };
10
+ /** Resolve helper LLM when model + auth are available; otherwise null (regex-only preflight). */
11
+ export declare function resolveMemoryHelperLLM(ctx: ExtensionContext, modelSpec: string | boolean | undefined): Promise<MemoryHelperLLM | null>;
12
+ /** @alias resolveMemoryHelperLLM */
13
+ export declare const createMemoryHelperLLM: typeof resolveMemoryHelperLLM;
14
+ export declare function createPiLLMClient(ctx: ExtensionContext, modelSpec: string | boolean | undefined): LLMClient | null;
15
+ /** Standalone LLM client for CLI usage without ExtensionContext. */
16
+ export declare function createStandaloneLLMClient(modelSpec?: string, env?: NodeJS.ProcessEnv): LLMClient;
17
+ //# sourceMappingURL=piComplete.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"piComplete.d.ts","sourceRoot":"","sources":["../../src/adapters/piComplete.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AAGxE,OAAO,EAIL,KAAK,eAAe,EACrB,MAAM,+BAA+B,CAAC;AACvC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAE5D,eAAO,MAAM,uBAAuB,aAAa,CAAC;AAClD,eAAO,MAAM,oBAAoB,sBAAsB,CAAC;AAIxD,wBAAgB,cAAc,CAC5B,IAAI,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,EAClC,eAAe,SAA0B,EACzC,cAAc,SAAuB,GACpC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAavC;AA4FD,iGAAiG;AACjG,wBAAsB,sBAAsB,CAC1C,GAAG,EAAE,gBAAgB,EACrB,SAAS,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,GACtC,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAKjC;AAED,oCAAoC;AACpC,eAAO,MAAM,qBAAqB,+BAAyB,CAAC;AAE5D,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,gBAAgB,EACrB,SAAS,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,GACtC,SAAS,GAAG,IAAI,CAoClB;AAED,oEAAoE;AACpE,wBAAgB,yBAAyB,CACvC,SAAS,CAAC,EAAE,MAAM,EAClB,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,SAAS,CAwCX"}
@@ -0,0 +1,169 @@
1
+ import { complete, getEnvApiKey, getModels } from "@earendil-works/pi-ai/compat";
2
+ import { Type } from "typebox";
3
+ import { COMPILE_MEMORY_INTENTS_PARAMETERS, MEMORY_HELPER_TOOL_NAME, } from "../preflight/detectIntents.js";
4
+ export const DEFAULT_HELPER_PROVIDER = "deepseek";
5
+ export const DEFAULT_HELPER_MODEL = "deepseek-v4-flash";
6
+ const CompileMemoryIntentsParams = Type.Unsafe(COMPILE_MEMORY_INTENTS_PARAMETERS);
7
+ export function parseModelSpec(spec, defaultProvider = DEFAULT_HELPER_PROVIDER, defaultModelId = DEFAULT_HELPER_MODEL) {
8
+ if (typeof spec !== "string" || !spec.trim()) {
9
+ return { provider: defaultProvider, modelId: defaultModelId };
10
+ }
11
+ const trimmed = spec.trim();
12
+ const slash = trimmed.indexOf("/");
13
+ if (slash === -1) {
14
+ return { provider: defaultProvider, modelId: trimmed };
15
+ }
16
+ return {
17
+ provider: trimmed.slice(0, slash),
18
+ modelId: trimmed.slice(slash + 1),
19
+ };
20
+ }
21
+ async function resolveModelAuth(ctx, provider, modelId) {
22
+ const model = ctx.modelRegistry.find(provider, modelId);
23
+ if (!model)
24
+ return null;
25
+ const auth = await ctx.modelRegistry.getApiKeyAndHeaders(model);
26
+ if (!auth.ok || !auth.apiKey)
27
+ return null;
28
+ return {
29
+ model,
30
+ apiKey: auth.apiKey,
31
+ headers: auth.headers,
32
+ env: auth.env,
33
+ };
34
+ }
35
+ function extractTextFromResponse(content) {
36
+ return content
37
+ .filter((block) => block.type === "text")
38
+ .map((block) => block.text)
39
+ .join("\n");
40
+ }
41
+ function findToolCall(content, toolName) {
42
+ for (const block of content) {
43
+ if (block.type === "toolCall" && block.name === toolName && block.arguments) {
44
+ return block;
45
+ }
46
+ }
47
+ return null;
48
+ }
49
+ function buildMemoryHelperLLM(resolved) {
50
+ return {
51
+ async compileIntents(text, signal) {
52
+ const response = await complete(resolved.model, {
53
+ messages: [
54
+ {
55
+ role: "user",
56
+ content: [
57
+ {
58
+ type: "text",
59
+ text: `Analyze whether the user message requires recalling private episodic memory.\n\n<message>\n${text}\n</message>`,
60
+ },
61
+ ],
62
+ timestamp: Date.now(),
63
+ },
64
+ ],
65
+ tools: [
66
+ {
67
+ name: MEMORY_HELPER_TOOL_NAME,
68
+ description: "Decide whether to recall private episodic memory and compile structured query intents.",
69
+ parameters: CompileMemoryIntentsParams,
70
+ },
71
+ ],
72
+ }, {
73
+ apiKey: resolved.apiKey,
74
+ headers: resolved.headers,
75
+ env: resolved.env,
76
+ signal,
77
+ toolChoice: { type: "tool", name: MEMORY_HELPER_TOOL_NAME },
78
+ });
79
+ const toolCall = findToolCall(response.content, MEMORY_HELPER_TOOL_NAME);
80
+ if (toolCall?.arguments) {
81
+ return toolCall.arguments;
82
+ }
83
+ const raw = extractTextFromResponse(response.content);
84
+ if (!raw.trim()) {
85
+ return { should_recall: false, intents: [] };
86
+ }
87
+ return JSON.parse(raw);
88
+ },
89
+ };
90
+ }
91
+ /** Resolve helper LLM when model + auth are available; otherwise null (regex-only preflight). */
92
+ export async function resolveMemoryHelperLLM(ctx, modelSpec) {
93
+ const { provider, modelId } = parseModelSpec(modelSpec);
94
+ const resolved = await resolveModelAuth(ctx, provider, modelId);
95
+ if (!resolved)
96
+ return null;
97
+ return buildMemoryHelperLLM(resolved);
98
+ }
99
+ /** @alias resolveMemoryHelperLLM */
100
+ export const createMemoryHelperLLM = resolveMemoryHelperLLM;
101
+ export function createPiLLMClient(ctx, modelSpec) {
102
+ const { provider, modelId } = parseModelSpec(modelSpec);
103
+ return {
104
+ async complete(prompt) {
105
+ const resolved = await resolveModelAuth(ctx, provider, modelId);
106
+ if (!resolved) {
107
+ throw new Error(`LLM model not available: ${provider}/${modelId}`);
108
+ }
109
+ const response = await complete(resolved.model, {
110
+ messages: [
111
+ {
112
+ role: "user",
113
+ content: [{ type: "text", text: prompt }],
114
+ timestamp: Date.now(),
115
+ },
116
+ ],
117
+ }, {
118
+ apiKey: resolved.apiKey,
119
+ headers: resolved.headers,
120
+ env: resolved.env,
121
+ maxTokens: 8192,
122
+ });
123
+ const text = extractTextFromResponse(response.content);
124
+ if (!text.trim()) {
125
+ throw new Error("LLM response was empty");
126
+ }
127
+ return text;
128
+ },
129
+ };
130
+ }
131
+ /** Standalone LLM client for CLI usage without ExtensionContext. */
132
+ export function createStandaloneLLMClient(modelSpec, env = process.env) {
133
+ const { provider, modelId } = parseModelSpec(modelSpec);
134
+ const providerEnv = toProviderEnv(env);
135
+ const model = getModels(provider).find((m) => m.id === modelId);
136
+ if (!model) {
137
+ throw new Error(`Model not found: ${provider}/${modelId}`);
138
+ }
139
+ const apiKey = getEnvApiKey(provider, providerEnv);
140
+ if (!apiKey) {
141
+ throw new Error(`No API key for ${provider} — set the provider env var or use regex extractor`);
142
+ }
143
+ return {
144
+ async complete(prompt) {
145
+ const response = await complete(model, {
146
+ messages: [
147
+ {
148
+ role: "user",
149
+ content: [{ type: "text", text: prompt }],
150
+ timestamp: Date.now(),
151
+ },
152
+ ],
153
+ }, {
154
+ apiKey,
155
+ maxTokens: 8192,
156
+ env: providerEnv,
157
+ });
158
+ const text = extractTextFromResponse(response.content);
159
+ if (!text.trim()) {
160
+ throw new Error("LLM response was empty");
161
+ }
162
+ return text;
163
+ },
164
+ };
165
+ }
166
+ function toProviderEnv(env) {
167
+ return Object.fromEntries(Object.entries(env).filter((entry) => entry[1] !== undefined));
168
+ }
169
+ //# sourceMappingURL=piComplete.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"piComplete.js","sourceRoot":"","sources":["../../src/adapters/piComplete.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ export interface InstallBundleOptions {
2
+ bundleRoot: string;
3
+ sourceDir: string;
4
+ /** Number of old bundles to keep after install (default 3). */
5
+ retain?: number;
6
+ }
7
+ export interface InstallBundleResult {
8
+ bundle_ts: string;
9
+ bundle_version: string;
10
+ installed_dir: string;
11
+ files_copied: number;
12
+ }
13
+ /**
14
+ * Enforces [0.4.0, 0.7.0) — producers guarantee additive minor bumps in this
15
+ * range; breaking schema changes move to 0.7.0+ to trip this gate.
16
+ * Ported from Kocoro internal/memory/bundle.go versionInRange.
17
+ */
18
+ export declare function versionInRange(v: string): boolean;
19
+ /**
20
+ * Keep the newest `keep` bundle dirs plus the current symlink target.
21
+ * Best-effort — failures are silently ignored.
22
+ * Ported from Kocoro internal/memory/bundle.go Puller.retain.
23
+ */
24
+ export declare function retainBundles(bundleRoot: string, keep: number): Promise<void>;
25
+ /**
26
+ * Path sandboxing rules from Kocoro bundle.go validateManifestPath (§4.2).
27
+ */
28
+ export declare function validateManifestPath(rel: string, stagingDir: string): string | null;
29
+ /**
30
+ * Install a local bundle directory into bundleRoot (staging → bundles/<ts> → current).
31
+ * Verifies per-file sha256 when manifest lists hashes.
32
+ */
33
+ export declare function installBundle(opts: InstallBundleOptions): Promise<InstallBundleResult>;
34
+ //# sourceMappingURL=install.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../src/bundle/install.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,oBAAoB;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,+DAA+D;IAC/D,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAOjD;AAED;;;;GAIG;AACH,wBAAsB,aAAa,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAmCnF;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAmBnF;AA4BD;;;GAGG;AACH,wBAAsB,aAAa,CACjC,IAAI,EAAE,oBAAoB,GACzB,OAAO,CAAC,mBAAmB,CAAC,CAyE9B"}
@@ -0,0 +1,183 @@
1
+ import { createHash } from "node:crypto";
2
+ import fs from "node:fs/promises";
3
+ import path from "node:path";
4
+ /**
5
+ * Enforces [0.4.0, 0.7.0) — producers guarantee additive minor bumps in this
6
+ * range; breaking schema changes move to 0.7.0+ to trip this gate.
7
+ * Ported from Kocoro internal/memory/bundle.go versionInRange.
8
+ */
9
+ export function versionInRange(v) {
10
+ const parts = v.split(".");
11
+ if (parts.length !== 3)
12
+ return false;
13
+ const [maj, min, pat] = parts.map(Number);
14
+ if (maj !== 0 || isNaN(min) || isNaN(pat))
15
+ return false;
16
+ if (min < 4 || min >= 7)
17
+ return false;
18
+ return pat >= 0;
19
+ }
20
+ /**
21
+ * Keep the newest `keep` bundle dirs plus the current symlink target.
22
+ * Best-effort — failures are silently ignored.
23
+ * Ported from Kocoro internal/memory/bundle.go Puller.retain.
24
+ */
25
+ export async function retainBundles(bundleRoot, keep) {
26
+ const bundlesDir = path.join(bundleRoot, "bundles");
27
+ let names;
28
+ try {
29
+ names = await fs.readdir(bundlesDir);
30
+ }
31
+ catch {
32
+ return;
33
+ }
34
+ const dirs = [];
35
+ for (const name of names) {
36
+ try {
37
+ const st = await fs.stat(path.join(bundlesDir, name));
38
+ if (st.isDirectory())
39
+ dirs.push(name);
40
+ }
41
+ catch { /* skip unreadable */ }
42
+ }
43
+ dirs.sort().reverse();
44
+ if (dirs.length <= keep)
45
+ return;
46
+ let currentTarget = "";
47
+ try {
48
+ const target = await fs.readlink(path.join(bundleRoot, "current"));
49
+ currentTarget = path.basename(target);
50
+ }
51
+ catch { /* no current pointer */ }
52
+ const keepSet = new Set();
53
+ for (let i = 0; i < Math.min(keep, dirs.length); i++) {
54
+ keepSet.add(dirs[i]);
55
+ }
56
+ if (currentTarget)
57
+ keepSet.add(currentTarget);
58
+ for (const d of dirs) {
59
+ if (!keepSet.has(d)) {
60
+ await fs.rm(path.join(bundlesDir, d), { recursive: true, force: true }).catch(() => { });
61
+ }
62
+ }
63
+ }
64
+ /**
65
+ * Path sandboxing rules from Kocoro bundle.go validateManifestPath (§4.2).
66
+ */
67
+ export function validateManifestPath(rel, stagingDir) {
68
+ if (!rel)
69
+ return "empty path";
70
+ if (rel.includes("\0"))
71
+ return "null byte in path";
72
+ if (path.isAbsolute(rel))
73
+ return "absolute path";
74
+ const cleaned = path.normalize(rel);
75
+ if (cleaned === ".." ||
76
+ cleaned.startsWith(`..${path.sep}`) ||
77
+ cleaned.includes(`${path.sep}..`)) {
78
+ return "contains parent traversal";
79
+ }
80
+ const abs = path.join(stagingDir, cleaned);
81
+ const cleanedAbs = path.normalize(abs);
82
+ const prefix = path.normalize(stagingDir) + path.sep;
83
+ if (!cleanedAbs.startsWith(prefix)) {
84
+ return "escapes staging dir";
85
+ }
86
+ return null;
87
+ }
88
+ async function sha256File(filePath) {
89
+ const data = await fs.readFile(filePath);
90
+ return createHash("sha256").update(data).digest("hex");
91
+ }
92
+ /**
93
+ * Points <bundleRoot>/current at finalDir. On Windows uses a directory junction
94
+ * (unprivileged, no Developer Mode needed — os.Symlink would fail with
95
+ * ERROR_PRIVILEGE_NOT_HELD on stock hosts). On POSIX uses atomic tmp-symlink +
96
+ * rename. Ported from Kocoro bundle_link_{unix,windows}.go.
97
+ */
98
+ async function swapCurrent(bundleRoot, finalDir) {
99
+ const currentLink = path.join(bundleRoot, "current");
100
+ if (process.platform === "win32") {
101
+ try {
102
+ await fs.rm(currentLink, { recursive: true, force: true });
103
+ }
104
+ catch { /* absent */ }
105
+ await fs.symlink(finalDir, currentLink, "junction");
106
+ return;
107
+ }
108
+ const tmpLink = path.join(bundleRoot, "current.tmp");
109
+ try {
110
+ await fs.unlink(tmpLink);
111
+ }
112
+ catch { /* absent */ }
113
+ await fs.symlink(finalDir, tmpLink);
114
+ await fs.rename(tmpLink, currentLink);
115
+ }
116
+ /**
117
+ * Install a local bundle directory into bundleRoot (staging → bundles/<ts> → current).
118
+ * Verifies per-file sha256 when manifest lists hashes.
119
+ */
120
+ export async function installBundle(opts) {
121
+ const sourceDir = path.resolve(opts.sourceDir);
122
+ const bundleRoot = path.resolve(opts.bundleRoot);
123
+ const manifestPath = path.join(sourceDir, "manifest.json");
124
+ const raw = await fs.readFile(manifestPath, "utf8");
125
+ const manifest = JSON.parse(raw);
126
+ if (!manifest.bundle_ts?.trim()) {
127
+ throw new Error("manifest missing bundle_ts");
128
+ }
129
+ if (!versionInRange(manifest.bundle_version ?? "")) {
130
+ throw new Error(`bundle_version "${manifest.bundle_version}" outside supported range [0.4.0, 0.7.0)`);
131
+ }
132
+ await fs.mkdir(bundleRoot, { recursive: true, mode: 0o700 });
133
+ const staging = path.join(bundleRoot, "staging", manifest.bundle_ts);
134
+ await fs.rm(staging, { recursive: true, force: true });
135
+ await fs.mkdir(staging, { recursive: true, mode: 0o700 });
136
+ let filesCopied = 0;
137
+ for (const f of manifest.files ?? []) {
138
+ const err = validateManifestPath(f.path, staging);
139
+ if (err) {
140
+ await fs.rm(staging, { recursive: true, force: true });
141
+ throw new Error(`unsafe manifest path ${f.path}: ${err}`);
142
+ }
143
+ const src = path.join(sourceDir, path.normalize(f.path));
144
+ const dest = path.join(staging, path.normalize(f.path));
145
+ await fs.mkdir(path.dirname(dest), { recursive: true, mode: 0o700 });
146
+ await fs.copyFile(src, dest, fs.constants.COPYFILE_EXCL).catch(async () => {
147
+ await fs.copyFile(src, dest);
148
+ });
149
+ if (f.sha256) {
150
+ const got = await sha256File(dest);
151
+ if (got !== f.sha256) {
152
+ await fs.rm(staging, { recursive: true, force: true });
153
+ throw new Error(`sha256 mismatch on ${f.path}: got ${got} want ${f.sha256}`);
154
+ }
155
+ }
156
+ filesCopied++;
157
+ }
158
+ // Copy manifest itself if not listed in files[]
159
+ const manifestDest = path.join(staging, "manifest.json");
160
+ try {
161
+ await fs.access(manifestDest);
162
+ }
163
+ catch {
164
+ await fs.copyFile(manifestPath, manifestDest);
165
+ }
166
+ const bundlesDir = path.join(bundleRoot, "bundles");
167
+ await fs.mkdir(bundlesDir, { recursive: true, mode: 0o700 });
168
+ const finalDir = path.join(bundlesDir, manifest.bundle_ts);
169
+ await fs.rm(finalDir, { recursive: true, force: true });
170
+ await fs.rename(staging, finalDir);
171
+ await swapCurrent(bundleRoot, finalDir);
172
+ const keep = opts.retain ?? 3;
173
+ if (keep > 0) {
174
+ await retainBundles(bundleRoot, keep);
175
+ }
176
+ return {
177
+ bundle_ts: manifest.bundle_ts,
178
+ bundle_version: manifest.bundle_version,
179
+ installed_dir: finalDir,
180
+ files_copied: filesCopied,
181
+ };
182
+ }
183
+ //# sourceMappingURL=install.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"install.js","sourceRoot":"","sources":["../../src/bundle/install.ts"],"names":[],"mappings":"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"}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}