nlm-memory 0.4.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 (472) hide show
  1. package/.agents/plugins/marketplace.json +20 -0
  2. package/.github/workflows/ci.yml +30 -0
  3. package/LICENSE +151 -0
  4. package/README.md +119 -0
  5. package/dist/cli/classify-parity.d.ts +48 -0
  6. package/dist/cli/classify-parity.js +182 -0
  7. package/dist/cli/classify-parity.js.map +1 -0
  8. package/dist/cli/launchctl-helpers.d.ts +26 -0
  9. package/dist/cli/launchctl-helpers.js +42 -0
  10. package/dist/cli/launchctl-helpers.js.map +1 -0
  11. package/dist/cli/nlm.d.ts +25 -0
  12. package/dist/cli/nlm.js +832 -0
  13. package/dist/cli/nlm.js.map +1 -0
  14. package/dist/core/actions/actions-log.d.ts +40 -0
  15. package/dist/core/actions/actions-log.js +72 -0
  16. package/dist/core/actions/actions-log.js.map +1 -0
  17. package/dist/core/actions/overlay.d.ts +30 -0
  18. package/dist/core/actions/overlay.js +101 -0
  19. package/dist/core/actions/overlay.js.map +1 -0
  20. package/dist/core/adapters/aider.d.ts +33 -0
  21. package/dist/core/adapters/aider.js +167 -0
  22. package/dist/core/adapters/aider.js.map +1 -0
  23. package/dist/core/adapters/claude-code.d.ts +32 -0
  24. package/dist/core/adapters/claude-code.js +270 -0
  25. package/dist/core/adapters/claude-code.js.map +1 -0
  26. package/dist/core/adapters/common.d.ts +20 -0
  27. package/dist/core/adapters/common.js +60 -0
  28. package/dist/core/adapters/common.js.map +1 -0
  29. package/dist/core/adapters/from-source.d.ts +11 -0
  30. package/dist/core/adapters/from-source.js +55 -0
  31. package/dist/core/adapters/from-source.js.map +1 -0
  32. package/dist/core/adapters/hermes-agent.d.ts +34 -0
  33. package/dist/core/adapters/hermes-agent.js +192 -0
  34. package/dist/core/adapters/hermes-agent.js.map +1 -0
  35. package/dist/core/adapters/hermes.d.ts +31 -0
  36. package/dist/core/adapters/hermes.js +247 -0
  37. package/dist/core/adapters/hermes.js.map +1 -0
  38. package/dist/core/adapters/jsonl-generic.d.ts +56 -0
  39. package/dist/core/adapters/jsonl-generic.js +185 -0
  40. package/dist/core/adapters/jsonl-generic.js.map +1 -0
  41. package/dist/core/adapters/opencode.d.ts +36 -0
  42. package/dist/core/adapters/opencode.js +213 -0
  43. package/dist/core/adapters/opencode.js.map +1 -0
  44. package/dist/core/adapters/pi.d.ts +32 -0
  45. package/dist/core/adapters/pi.js +233 -0
  46. package/dist/core/adapters/pi.js.map +1 -0
  47. package/dist/core/classifier/prompt.d.ts +60 -0
  48. package/dist/core/classifier/prompt.js +178 -0
  49. package/dist/core/classifier/prompt.js.map +1 -0
  50. package/dist/core/dataset/build-dataset.d.ts +87 -0
  51. package/dist/core/dataset/build-dataset.js +335 -0
  52. package/dist/core/dataset/build-dataset.js.map +1 -0
  53. package/dist/core/embedding/chunk-body.d.ts +30 -0
  54. package/dist/core/embedding/chunk-body.js +60 -0
  55. package/dist/core/embedding/chunk-body.js.map +1 -0
  56. package/dist/core/embedding/embed-backfill.d.ts +36 -0
  57. package/dist/core/embedding/embed-backfill.js +168 -0
  58. package/dist/core/embedding/embed-backfill.js.map +1 -0
  59. package/dist/core/embedding/embed-normalize.d.ts +28 -0
  60. package/dist/core/embedding/embed-normalize.js +98 -0
  61. package/dist/core/embedding/embed-normalize.js.map +1 -0
  62. package/dist/core/facts/backfill-facts.d.ts +58 -0
  63. package/dist/core/facts/backfill-facts.js +169 -0
  64. package/dist/core/facts/backfill-facts.js.map +1 -0
  65. package/dist/core/facts/extract-facts.d.ts +20 -0
  66. package/dist/core/facts/extract-facts.js +37 -0
  67. package/dist/core/facts/extract-facts.js.map +1 -0
  68. package/dist/core/hook/citation-detect.d.ts +32 -0
  69. package/dist/core/hook/citation-detect.js +105 -0
  70. package/dist/core/hook/citation-detect.js.map +1 -0
  71. package/dist/core/hook/cite-memo.d.ts +20 -0
  72. package/dist/core/hook/cite-memo.js +68 -0
  73. package/dist/core/hook/cite-memo.js.map +1 -0
  74. package/dist/core/hook/claude-settings.d.ts +34 -0
  75. package/dist/core/hook/claude-settings.js +117 -0
  76. package/dist/core/hook/claude-settings.js.map +1 -0
  77. package/dist/core/hook/gate.d.ts +11 -0
  78. package/dist/core/hook/gate.js +19 -0
  79. package/dist/core/hook/gate.js.map +1 -0
  80. package/dist/core/hook/hook-log.d.ts +25 -0
  81. package/dist/core/hook/hook-log.js +28 -0
  82. package/dist/core/hook/hook-log.js.map +1 -0
  83. package/dist/core/hook/memo-sweep.d.ts +55 -0
  84. package/dist/core/hook/memo-sweep.js +134 -0
  85. package/dist/core/hook/memo-sweep.js.map +1 -0
  86. package/dist/core/hook/memo.d.ts +20 -0
  87. package/dist/core/hook/memo.js +66 -0
  88. package/dist/core/hook/memo.js.map +1 -0
  89. package/dist/core/hook/pointer-block.d.ts +14 -0
  90. package/dist/core/hook/pointer-block.js +19 -0
  91. package/dist/core/hook/pointer-block.js.map +1 -0
  92. package/dist/core/hook/select.d.ts +21 -0
  93. package/dist/core/hook/select.js +15 -0
  94. package/dist/core/hook/select.js.map +1 -0
  95. package/dist/core/hook/transcript.d.ts +31 -0
  96. package/dist/core/hook/transcript.js +103 -0
  97. package/dist/core/hook/transcript.js.map +1 -0
  98. package/dist/core/ingest/ingest-session.d.ts +40 -0
  99. package/dist/core/ingest/ingest-session.js +71 -0
  100. package/dist/core/ingest/ingest-session.js.map +1 -0
  101. package/dist/core/providers/provider-models.d.ts +24 -0
  102. package/dist/core/providers/provider-models.js +72 -0
  103. package/dist/core/providers/provider-models.js.map +1 -0
  104. package/dist/core/providers/provider-registry.d.ts +62 -0
  105. package/dist/core/providers/provider-registry.js +143 -0
  106. package/dist/core/providers/provider-registry.js.map +1 -0
  107. package/dist/core/recall/citation-log.d.ts +28 -0
  108. package/dist/core/recall/citation-log.js +90 -0
  109. package/dist/core/recall/citation-log.js.map +1 -0
  110. package/dist/core/recall/filter.d.ts +11 -0
  111. package/dist/core/recall/filter.js +20 -0
  112. package/dist/core/recall/filter.js.map +1 -0
  113. package/dist/core/recall/index.d.ts +6 -0
  114. package/dist/core/recall/index.js +5 -0
  115. package/dist/core/recall/index.js.map +1 -0
  116. package/dist/core/recall/match-fields.d.ts +10 -0
  117. package/dist/core/recall/match-fields.js +37 -0
  118. package/dist/core/recall/match-fields.js.map +1 -0
  119. package/dist/core/recall/query-log.d.ts +36 -0
  120. package/dist/core/recall/query-log.js +112 -0
  121. package/dist/core/recall/query-log.js.map +1 -0
  122. package/dist/core/recall/query-shape.d.ts +22 -0
  123. package/dist/core/recall/query-shape.js +64 -0
  124. package/dist/core/recall/query-shape.js.map +1 -0
  125. package/dist/core/recall/recall-service.d.ts +19 -0
  126. package/dist/core/recall/recall-service.js +252 -0
  127. package/dist/core/recall/recall-service.js.map +1 -0
  128. package/dist/core/recall/recent-log.d.ts +16 -0
  129. package/dist/core/recall/recent-log.js +46 -0
  130. package/dist/core/recall/recent-log.js.map +1 -0
  131. package/dist/core/recall/tokenize.d.ts +7 -0
  132. package/dist/core/recall/tokenize.js +18 -0
  133. package/dist/core/recall/tokenize.js.map +1 -0
  134. package/dist/core/recall/useful-scan.d.ts +52 -0
  135. package/dist/core/recall/useful-scan.js +300 -0
  136. package/dist/core/recall/useful-scan.js.map +1 -0
  137. package/dist/core/recall-facts/fact-query-log.d.ts +42 -0
  138. package/dist/core/recall-facts/fact-query-log.js +115 -0
  139. package/dist/core/recall-facts/fact-query-log.js.map +1 -0
  140. package/dist/core/recall-facts/fact-recall-service.d.ts +34 -0
  141. package/dist/core/recall-facts/fact-recall-service.js +246 -0
  142. package/dist/core/recall-facts/fact-recall-service.js.map +1 -0
  143. package/dist/core/scheduler/scan-once.d.ts +32 -0
  144. package/dist/core/scheduler/scan-once.js +100 -0
  145. package/dist/core/scheduler/scan-once.js.map +1 -0
  146. package/dist/core/scheduler/scheduler.d.ts +59 -0
  147. package/dist/core/scheduler/scheduler.js +158 -0
  148. package/dist/core/scheduler/scheduler.js.map +1 -0
  149. package/dist/core/sources/source-registry.d.ts +68 -0
  150. package/dist/core/sources/source-registry.js +208 -0
  151. package/dist/core/sources/source-registry.js.map +1 -0
  152. package/dist/core/storage/db-restore.d.ts +53 -0
  153. package/dist/core/storage/db-restore.js +113 -0
  154. package/dist/core/storage/db-restore.js.map +1 -0
  155. package/dist/core/storage/live-status.d.ts +15 -0
  156. package/dist/core/storage/live-status.js +43 -0
  157. package/dist/core/storage/live-status.js.map +1 -0
  158. package/dist/core/storage/migrate.d.ts +14 -0
  159. package/dist/core/storage/migrate.js +52 -0
  160. package/dist/core/storage/migrate.js.map +1 -0
  161. package/dist/core/storage/sqlite-fact-store.d.ts +50 -0
  162. package/dist/core/storage/sqlite-fact-store.js +256 -0
  163. package/dist/core/storage/sqlite-fact-store.js.map +1 -0
  164. package/dist/core/storage/sqlite-session-store.d.ts +152 -0
  165. package/dist/core/storage/sqlite-session-store.js +587 -0
  166. package/dist/core/storage/sqlite-session-store.js.map +1 -0
  167. package/dist/hook/pre-compact-hook.d.ts +26 -0
  168. package/dist/hook/pre-compact-hook.js +94 -0
  169. package/dist/hook/pre-compact-hook.js.map +1 -0
  170. package/dist/hook/prompt-recall-hook.d.ts +23 -0
  171. package/dist/hook/prompt-recall-hook.js +141 -0
  172. package/dist/hook/prompt-recall-hook.js.map +1 -0
  173. package/dist/hook/session-end-hook.d.ts +18 -0
  174. package/dist/hook/session-end-hook.js +67 -0
  175. package/dist/hook/session-end-hook.js.map +1 -0
  176. package/dist/hook/session-start-hook.d.ts +25 -0
  177. package/dist/hook/session-start-hook.js +129 -0
  178. package/dist/hook/session-start-hook.js.map +1 -0
  179. package/dist/hook/stop-hook.d.ts +38 -0
  180. package/dist/hook/stop-hook.js +171 -0
  181. package/dist/hook/stop-hook.js.map +1 -0
  182. package/dist/hook/subagent-start-hook.d.ts +30 -0
  183. package/dist/hook/subagent-start-hook.js +108 -0
  184. package/dist/hook/subagent-start-hook.js.map +1 -0
  185. package/dist/http/app.d.ts +65 -0
  186. package/dist/http/app.js +1009 -0
  187. package/dist/http/app.js.map +1 -0
  188. package/dist/install/claude-code.d.ts +57 -0
  189. package/dist/install/claude-code.js +76 -0
  190. package/dist/install/claude-code.js.map +1 -0
  191. package/dist/install/codex.d.ts +82 -0
  192. package/dist/install/codex.js +277 -0
  193. package/dist/install/codex.js.map +1 -0
  194. package/dist/install/hermes-agent.d.ts +35 -0
  195. package/dist/install/hermes-agent.js +48 -0
  196. package/dist/install/hermes-agent.js.map +1 -0
  197. package/dist/install/hermes.d.ts +29 -0
  198. package/dist/install/hermes.js +52 -0
  199. package/dist/install/hermes.js.map +1 -0
  200. package/dist/install/ollama.d.ts +54 -0
  201. package/dist/install/ollama.js +178 -0
  202. package/dist/install/ollama.js.map +1 -0
  203. package/dist/install/setup.d.ts +37 -0
  204. package/dist/install/setup.js +339 -0
  205. package/dist/install/setup.js.map +1 -0
  206. package/dist/llm/classifier-box.d.ts +29 -0
  207. package/dist/llm/classifier-box.js +43 -0
  208. package/dist/llm/classifier-box.js.map +1 -0
  209. package/dist/llm/deepseek-client.d.ts +40 -0
  210. package/dist/llm/deepseek-client.js +114 -0
  211. package/dist/llm/deepseek-client.js.map +1 -0
  212. package/dist/llm/env-autoload.d.ts +8 -0
  213. package/dist/llm/env-autoload.js +54 -0
  214. package/dist/llm/env-autoload.js.map +1 -0
  215. package/dist/llm/ollama-client.d.ts +47 -0
  216. package/dist/llm/ollama-client.js +156 -0
  217. package/dist/llm/ollama-client.js.map +1 -0
  218. package/dist/mcp/server.d.ts +64 -0
  219. package/dist/mcp/server.js +430 -0
  220. package/dist/mcp/server.js.map +1 -0
  221. package/dist/ports/fact-store.d.ts +82 -0
  222. package/dist/ports/fact-store.js +16 -0
  223. package/dist/ports/fact-store.js.map +1 -0
  224. package/dist/ports/llm-client.d.ts +42 -0
  225. package/dist/ports/llm-client.js +14 -0
  226. package/dist/ports/llm-client.js.map +1 -0
  227. package/dist/ports/logger.d.ts +13 -0
  228. package/dist/ports/logger.js +8 -0
  229. package/dist/ports/logger.js.map +1 -0
  230. package/dist/ports/session-store.d.ts +29 -0
  231. package/dist/ports/session-store.js +9 -0
  232. package/dist/ports/session-store.js.map +1 -0
  233. package/dist/ports/transcript-adapter.d.ts +48 -0
  234. package/dist/ports/transcript-adapter.js +15 -0
  235. package/dist/ports/transcript-adapter.js.map +1 -0
  236. package/dist/shared/types.d.ts +129 -0
  237. package/dist/shared/types.js +6 -0
  238. package/dist/shared/types.js.map +1 -0
  239. package/dist/ui/assets/index-BA6IpU8g.css +1 -0
  240. package/dist/ui/assets/index-B_qIVV0k.js +69 -0
  241. package/dist/ui/index.html +13 -0
  242. package/docs/methodology/re-derivation-rate.md +112 -0
  243. package/docs/methodology/useful-hit-rate.md +79 -0
  244. package/docs/plans/2026-05-20-fts5-lexical-recall.md +1088 -0
  245. package/docs/plans/2026-05-20-recall-daemon-wedge-fix.md +662 -0
  246. package/docs/plans/2026-05-20-recall-hook-design.md +131 -0
  247. package/docs/plans/2026-05-20-recall-hook-implementation.md +1222 -0
  248. package/docs/plans/desktop-product.md +69 -0
  249. package/docs/plans/factstore-design.md +236 -0
  250. package/logs/CHANGELOG/CHANGELOG-2026.md +1389 -0
  251. package/logs/CHANGELOG/CHANGELOG.md +320 -0
  252. package/migrations/000_initial_schema.sql +174 -0
  253. package/migrations/001_entity_type_rename.sql +17 -0
  254. package/migrations/002_adapter_state_extend.sql +12 -0
  255. package/migrations/003_session_embeddings.sql +11 -0
  256. package/migrations/004_facts.sql +46 -0
  257. package/migrations/005_sources.sql +31 -0
  258. package/migrations/006_providers.sql +33 -0
  259. package/migrations/007_source_tokens.sql +17 -0
  260. package/migrations/008_fts_rebuild.sql +9 -0
  261. package/migrations/009_session_embedding_chunks.sql +46 -0
  262. package/migrations/010_sources_opencode.sql +30 -0
  263. package/migrations/011_sources_hermes_agent.sql +30 -0
  264. package/migrations/012_sources_aider.sql +30 -0
  265. package/migrations/013_adapter_state_failure_count.sql +12 -0
  266. package/package.json +56 -0
  267. package/plugin/.codex-plugin/plugin.json +22 -0
  268. package/plugin/.mcp.json +8 -0
  269. package/plugin/README.md +51 -0
  270. package/plugin/hooks/hooks.json +25 -0
  271. package/plugin/scripts/prompt-recall-hook.mjs +202 -0
  272. package/plugin/scripts/stop-hook.mjs +306 -0
  273. package/plugin-hermes-agent/README.md +49 -0
  274. package/plugin-hermes-agent/__init__.py +75 -0
  275. package/plugin-hermes-agent/plugin.yaml +15 -0
  276. package/scripts/backfill-citations.mjs +0 -0
  277. package/scripts/build-codex-plugin.mjs +61 -0
  278. package/scripts/deepseek-probe.mjs +67 -0
  279. package/scripts/extract-triples.mjs +207 -0
  280. package/scripts/longmemeval/embedding-cache.ts +77 -0
  281. package/scripts/longmemeval/fetch-dataset.sh +25 -0
  282. package/scripts/longmemeval/run-harness.ts +315 -0
  283. package/scripts/longmemeval/scorer.ts +99 -0
  284. package/scripts/longmemeval/tsconfig.json +9 -0
  285. package/scripts/longmemeval/types.ts +35 -0
  286. package/scripts/nlm-daily-digest.py +239 -0
  287. package/scripts/nlm-daily-digest.sh +28 -0
  288. package/src/cli/classify-parity.ts +257 -0
  289. package/src/cli/launchctl-helpers.ts +49 -0
  290. package/src/cli/nlm.ts +885 -0
  291. package/src/core/actions/actions-log.ts +118 -0
  292. package/src/core/actions/overlay.ts +117 -0
  293. package/src/core/adapters/aider.ts +205 -0
  294. package/src/core/adapters/claude-code.ts +293 -0
  295. package/src/core/adapters/common.ts +54 -0
  296. package/src/core/adapters/from-source.ts +57 -0
  297. package/src/core/adapters/hermes-agent.ts +240 -0
  298. package/src/core/adapters/hermes.ts +277 -0
  299. package/src/core/adapters/jsonl-generic.ts +208 -0
  300. package/src/core/adapters/opencode.ts +281 -0
  301. package/src/core/adapters/pi.ts +264 -0
  302. package/src/core/classifier/prompt.ts +200 -0
  303. package/src/core/dataset/build-dataset.ts +463 -0
  304. package/src/core/embedding/chunk-body.ts +76 -0
  305. package/src/core/embedding/embed-backfill.ts +210 -0
  306. package/src/core/embedding/embed-normalize.ts +135 -0
  307. package/src/core/facts/backfill-facts.ts +254 -0
  308. package/src/core/facts/extract-facts.ts +50 -0
  309. package/src/core/hook/citation-detect.ts +124 -0
  310. package/src/core/hook/cite-memo.ts +68 -0
  311. package/src/core/hook/claude-settings.ts +166 -0
  312. package/src/core/hook/gate.ts +25 -0
  313. package/src/core/hook/hook-log.ts +41 -0
  314. package/src/core/hook/memo-sweep.ts +164 -0
  315. package/src/core/hook/memo.ts +67 -0
  316. package/src/core/hook/pointer-block.ts +26 -0
  317. package/src/core/hook/select.ts +32 -0
  318. package/src/core/hook/transcript.ts +121 -0
  319. package/src/core/ingest/ingest-session.ts +111 -0
  320. package/src/core/providers/provider-models.ts +100 -0
  321. package/src/core/providers/provider-registry.ts +196 -0
  322. package/src/core/recall/citation-log.ts +108 -0
  323. package/src/core/recall/filter.ts +27 -0
  324. package/src/core/recall/index.ts +6 -0
  325. package/src/core/recall/match-fields.ts +40 -0
  326. package/src/core/recall/query-log.ts +149 -0
  327. package/src/core/recall/query-shape.ts +66 -0
  328. package/src/core/recall/recall-service.ts +320 -0
  329. package/src/core/recall/recent-log.ts +59 -0
  330. package/src/core/recall/tokenize.ts +18 -0
  331. package/src/core/recall/useful-scan.ts +336 -0
  332. package/src/core/recall-facts/fact-query-log.ts +150 -0
  333. package/src/core/recall-facts/fact-recall-service.ts +327 -0
  334. package/src/core/scheduler/scan-once.ts +142 -0
  335. package/src/core/scheduler/scheduler.ts +225 -0
  336. package/src/core/sources/source-registry.ts +260 -0
  337. package/src/core/storage/db-restore.ts +133 -0
  338. package/src/core/storage/live-status.ts +45 -0
  339. package/src/core/storage/migrate.ts +72 -0
  340. package/src/core/storage/sqlite-fact-store.ts +304 -0
  341. package/src/core/storage/sqlite-session-store.ts +765 -0
  342. package/src/hook/prompt-recall-hook.ts +174 -0
  343. package/src/hook/session-end-hook.ts +81 -0
  344. package/src/hook/session-start-hook.ts +165 -0
  345. package/src/hook/stop-hook.ts +236 -0
  346. package/src/http/app.ts +1114 -0
  347. package/src/install/claude-code.ts +128 -0
  348. package/src/install/codex.ts +367 -0
  349. package/src/install/hermes-agent.ts +76 -0
  350. package/src/install/hermes.ts +78 -0
  351. package/src/install/ollama.ts +208 -0
  352. package/src/install/setup.ts +368 -0
  353. package/src/llm/classifier-box.ts +64 -0
  354. package/src/llm/deepseek-client.ts +150 -0
  355. package/src/llm/env-autoload.ts +55 -0
  356. package/src/llm/ollama-client.ts +189 -0
  357. package/src/mcp/server.ts +534 -0
  358. package/src/ports/fact-store.ts +102 -0
  359. package/src/ports/llm-client.ts +52 -0
  360. package/src/ports/logger.ts +16 -0
  361. package/src/ports/session-store.ts +45 -0
  362. package/src/ports/transcript-adapter.ts +55 -0
  363. package/src/shared/types.ts +145 -0
  364. package/src/ui/App.tsx +58 -0
  365. package/src/ui/components/PromoteOpenButton.tsx +65 -0
  366. package/src/ui/components/SessionDrawer.tsx +136 -0
  367. package/src/ui/components/SideNav.tsx +162 -0
  368. package/src/ui/components/Skeleton.tsx +107 -0
  369. package/src/ui/index.html +13 -0
  370. package/src/ui/lib/actions.ts +30 -0
  371. package/src/ui/lib/api.ts +92 -0
  372. package/src/ui/lib/dataset.ts +141 -0
  373. package/src/ui/lib/registries.ts +155 -0
  374. package/src/ui/lib/view-settings.ts +41 -0
  375. package/src/ui/main.tsx +15 -0
  376. package/src/ui/pages/Live.tsx +229 -0
  377. package/src/ui/pages/Pulse.tsx +415 -0
  378. package/src/ui/pages/Recall.tsx +190 -0
  379. package/src/ui/pages/River.tsx +308 -0
  380. package/src/ui/pages/Search.tsx +93 -0
  381. package/src/ui/pages/Stub.tsx +9 -0
  382. package/src/ui/pages/Thread.tsx +262 -0
  383. package/src/ui/pages/settings/Classifier.tsx +227 -0
  384. package/src/ui/pages/settings/Data.tsx +190 -0
  385. package/src/ui/pages/settings/Index.tsx +65 -0
  386. package/src/ui/pages/settings/Labels.tsx +224 -0
  387. package/src/ui/pages/settings/Providers.tsx +305 -0
  388. package/src/ui/pages/settings/SettingsSubnav.tsx +28 -0
  389. package/src/ui/pages/settings/Sources.tsx +326 -0
  390. package/src/ui/pages/settings/Views.tsx +96 -0
  391. package/src/ui/styles.css +1766 -0
  392. package/src/ui/tsconfig.json +21 -0
  393. package/src/ui/vite.config.ts +19 -0
  394. package/tests/fixtures/claude_code/short_session.jsonl +2 -0
  395. package/tests/fixtures/claude_code/standard_iso.jsonl +4 -0
  396. package/tests/fixtures/claude_code/tool_heavy.jsonl +8 -0
  397. package/tests/fixtures/claude_code/with_subagent.jsonl +7 -0
  398. package/tests/fixtures/facts.ts +17 -0
  399. package/tests/fixtures/golden-corpus.ts +85 -0
  400. package/tests/fixtures/hermes/paired_request_dump.json +24 -0
  401. package/tests/fixtures/hermes/paired_session.json +23 -0
  402. package/tests/fixtures/hermes/request_dump.json +28 -0
  403. package/tests/fixtures/hermes/session_iso.json +38 -0
  404. package/tests/fixtures/hermes/session_unix.json +38 -0
  405. package/tests/fixtures/hermes/system_only.json +18 -0
  406. package/tests/fixtures/pi/error-connection-abort.jsonl +8 -0
  407. package/tests/fixtures/pi/short-successful.jsonl +5 -0
  408. package/tests/fixtures/pi/with-custom-message.jsonl +6 -0
  409. package/tests/fixtures/sessions.ts +22 -0
  410. package/tests/integration/backfill-facts.test.ts +362 -0
  411. package/tests/integration/citation-explicit.test.ts +111 -0
  412. package/tests/integration/cite-event.test.ts +169 -0
  413. package/tests/integration/cite-memo.test.ts +87 -0
  414. package/tests/integration/db-restore.test.ts +153 -0
  415. package/tests/integration/embed-backfill.test.ts +176 -0
  416. package/tests/integration/fact-supersedence.test.ts +313 -0
  417. package/tests/integration/fts-index.test.ts +60 -0
  418. package/tests/integration/getbyids-sqlite.test.ts +60 -0
  419. package/tests/integration/hermes-agent-hooks.test.ts +248 -0
  420. package/tests/integration/hook-claude-settings.test.ts +205 -0
  421. package/tests/integration/hook-log.test.ts +54 -0
  422. package/tests/integration/hook-memo.test.ts +68 -0
  423. package/tests/integration/hook-pre-compact.test.ts +105 -0
  424. package/tests/integration/hook-subagent-start.test.ts +102 -0
  425. package/tests/integration/http.test.ts +401 -0
  426. package/tests/integration/keyword-search-fts.test.ts +66 -0
  427. package/tests/integration/mcp-recall-logging.test.ts +88 -0
  428. package/tests/integration/mcp.test.ts +248 -0
  429. package/tests/integration/memo-sweep.test.ts +91 -0
  430. package/tests/integration/prompt-recall-hook.test.ts +88 -0
  431. package/tests/integration/provider-registry.test.ts +107 -0
  432. package/tests/integration/recall-golden.test.ts +59 -0
  433. package/tests/integration/recall-sqlite.test.ts +169 -0
  434. package/tests/integration/scheduler.test.ts +391 -0
  435. package/tests/integration/session-end-hook.test.ts +48 -0
  436. package/tests/integration/session-start-hook.test.ts +126 -0
  437. package/tests/integration/source-registry.test.ts +120 -0
  438. package/tests/integration/sqlite-fact-store.test.ts +346 -0
  439. package/tests/integration/stop-hook.test.ts +560 -0
  440. package/tests/integration/wal-checkpoint.test.ts +49 -0
  441. package/tests/unit/cli/launchctl-helpers.test.ts +60 -0
  442. package/tests/unit/core/adapters/aider.test.ts +230 -0
  443. package/tests/unit/core/adapters/claude-code.test.ts +118 -0
  444. package/tests/unit/core/adapters/hermes-agent.test.ts +329 -0
  445. package/tests/unit/core/adapters/hermes.test.ts +81 -0
  446. package/tests/unit/core/adapters/jsonl-generic.test.ts +142 -0
  447. package/tests/unit/core/adapters/opencode.test.ts +354 -0
  448. package/tests/unit/core/adapters/pi.test.ts +110 -0
  449. package/tests/unit/core/classifier/prompt.test.ts +126 -0
  450. package/tests/unit/core/embedding/chunk-body.test.ts +100 -0
  451. package/tests/unit/core/facts/extract-facts.test.ts +117 -0
  452. package/tests/unit/core/filter.test.ts +40 -0
  453. package/tests/unit/core/hook/citation-detect-cite-session.test.ts +96 -0
  454. package/tests/unit/core/hook/citation-detect.test.ts +124 -0
  455. package/tests/unit/core/hook/gate.test.ts +29 -0
  456. package/tests/unit/core/hook/pointer-block.test.ts +22 -0
  457. package/tests/unit/core/hook/select.test.ts +66 -0
  458. package/tests/unit/core/match-fields.test.ts +39 -0
  459. package/tests/unit/core/mcp-cite-session.test.ts +51 -0
  460. package/tests/unit/core/providers/provider-models.test.ts +101 -0
  461. package/tests/unit/core/query-shape.test.ts +92 -0
  462. package/tests/unit/core/recall-facts/fact-recall-service.test.ts +258 -0
  463. package/tests/unit/core/recall-service.test.ts +200 -0
  464. package/tests/unit/core/storage/live-status.test.ts +54 -0
  465. package/tests/unit/core/tokenize.test.ts +32 -0
  466. package/tests/unit/core/useful-scan.test.ts +537 -0
  467. package/tests/unit/llm/embed.test.ts +93 -0
  468. package/tests/unit/llm/ollama-client.test.ts +124 -0
  469. package/tests/unit/scripts/longmemeval-scorer.test.ts +114 -0
  470. package/tsconfig.json +31 -0
  471. package/tsconfig.test.json +11 -0
  472. package/vitest.config.ts +22 -0
@@ -0,0 +1,213 @@
1
+ /**
2
+ * OpenCode adapter.
3
+ *
4
+ * Reads the OpenCode SQLite database at:
5
+ * macOS: ~/Library/Application Support/opencode/opencode.db
6
+ * Linux: $XDG_DATA_HOME/opencode/opencode.db (default ~/.local/share/opencode/opencode.db)
7
+ *
8
+ * Unlike the JSONL-based adapters, OpenCode stores all sessions and messages
9
+ * in a single SQLite file. `discover()` queries the sessions table and returns
10
+ * session IDs (not file paths). `parseSession()` treats its string argument as
11
+ * a session ID and reconstructs a SessionChunk from the messages and parts tables.
12
+ *
13
+ * Part types extracted:
14
+ * - text (non-ignored): the conversational prose
15
+ * - tool : summarized as [tool: <name>]
16
+ * All other part types (reasoning, step-start/finish, snapshot, patch,
17
+ * compaction, agent, retry, subtask) are structural and skipped.
18
+ *
19
+ * Format reference: verified against sst/opencode migration
20
+ * 20260127222353_familiar_lady_ursula and session.sql.ts, 2026-05-28.
21
+ */
22
+ import { existsSync, readFileSync } from "node:fs";
23
+ import { homedir } from "node:os";
24
+ import { join } from "node:path";
25
+ import Database from "better-sqlite3";
26
+ import { durationMinutes, normalizeTimestamp } from "./common.js";
27
+ const TOOL_OUTPUT_PREVIEW_CHARS = 240;
28
+ export function defaultDbPath() {
29
+ if (process.env["OPENCODE_DB_PATH"])
30
+ return process.env["OPENCODE_DB_PATH"];
31
+ if (process.platform === "darwin") {
32
+ return join(homedir(), "Library", "Application Support", "opencode", "opencode.db");
33
+ }
34
+ const xdg = process.env["XDG_DATA_HOME"] ?? join(homedir(), ".local", "share");
35
+ return join(xdg, "opencode", "opencode.db");
36
+ }
37
+ function readGitBranch(directory) {
38
+ try {
39
+ const head = readFileSync(join(directory, ".git", "HEAD"), "utf8").trim();
40
+ const match = /^ref: refs\/heads\/(.+)$/.exec(head);
41
+ return match ? (match[1] ?? "") : "";
42
+ }
43
+ catch {
44
+ return "";
45
+ }
46
+ }
47
+ function extractTurns(messages, partsByMessage) {
48
+ const turns = [];
49
+ for (const msg of messages) {
50
+ let info;
51
+ try {
52
+ info = JSON.parse(msg.data);
53
+ }
54
+ catch {
55
+ continue;
56
+ }
57
+ if (info.role !== "user" && info.role !== "assistant")
58
+ continue;
59
+ const parts = partsByMessage.get(msg.id) ?? [];
60
+ const segments = [];
61
+ for (const p of parts) {
62
+ if (p.type === "text") {
63
+ const tp = p;
64
+ if (!tp.ignored && tp.text)
65
+ segments.push(tp.text);
66
+ }
67
+ else if (p.type === "tool") {
68
+ const tp = p;
69
+ const output = extractToolOutput(tp);
70
+ segments.push(output ? `[tool: ${tp.tool}] ${output}` : `[tool: ${tp.tool}]`);
71
+ }
72
+ }
73
+ const text = segments.join("\n").trim();
74
+ if (!text)
75
+ continue;
76
+ turns.push({ role: info.role, text, timestamp: normalizeTimestamp(msg.time_created) });
77
+ }
78
+ return turns;
79
+ }
80
+ function extractToolOutput(part) {
81
+ const state = part.state;
82
+ if (!state)
83
+ return "";
84
+ const output = state["output"] ??
85
+ state["result"] ??
86
+ "";
87
+ if (typeof output !== "string" || !output)
88
+ return "";
89
+ const preview = output.slice(0, TOOL_OUTPUT_PREVIEW_CHARS);
90
+ return output.length > TOOL_OUTPUT_PREVIEW_CHARS ? `${preview}…` : preview;
91
+ }
92
+ function provisionalLabel(turns) {
93
+ for (const t of turns) {
94
+ if (t.role !== "user")
95
+ continue;
96
+ const first = t.text.split("\n", 1)[0]?.trim();
97
+ if (first)
98
+ return first.slice(0, 80);
99
+ }
100
+ return "Untitled session";
101
+ }
102
+ export class OpenCodeAdapter {
103
+ name = "opencode";
104
+ runtimeVersion = "opencode/1.0";
105
+ transcriptKind = "opencode-sqlite";
106
+ dbPath;
107
+ constructor(opts = {}) {
108
+ this.dbPath = opts.dbPath ?? defaultDbPath();
109
+ }
110
+ detect() {
111
+ if (existsSync(this.dbPath)) {
112
+ return { adapterName: this.name, enabled: true, path: this.dbPath, hint: null };
113
+ }
114
+ return { adapterName: this.name, enabled: false, path: null, hint: "opencode.db not found" };
115
+ }
116
+ async discover(options) {
117
+ if (!existsSync(this.dbPath))
118
+ return [];
119
+ let db;
120
+ try {
121
+ db = new Database(this.dbPath, { readonly: true });
122
+ let rows;
123
+ if (options?.since) {
124
+ const sinceMs = options.since.getTime();
125
+ rows = db
126
+ .prepare(`SELECT id FROM session WHERE time_archived IS NULL AND time_updated >= ?`)
127
+ .all(sinceMs);
128
+ }
129
+ else {
130
+ rows = db
131
+ .prepare(`SELECT id FROM session WHERE time_archived IS NULL`)
132
+ .all();
133
+ }
134
+ return rows.map((r) => r.id);
135
+ }
136
+ catch {
137
+ return [];
138
+ }
139
+ finally {
140
+ db?.close();
141
+ }
142
+ }
143
+ async parseSession(sessionId) {
144
+ if (!existsSync(this.dbPath))
145
+ return null;
146
+ let db;
147
+ try {
148
+ db = new Database(this.dbPath, { readonly: true });
149
+ const session = db
150
+ .prepare(`SELECT id, directory, title, time_created, time_updated
151
+ FROM session WHERE id = ?`)
152
+ .get(sessionId);
153
+ if (!session)
154
+ return null;
155
+ const messages = db
156
+ .prepare(`SELECT id, time_created, data FROM message
157
+ WHERE session_id = ? ORDER BY time_created ASC`)
158
+ .all(sessionId);
159
+ const partRows = db
160
+ .prepare(`SELECT message_id, time_created, data FROM part
161
+ WHERE session_id = ? ORDER BY time_created ASC`)
162
+ .all(sessionId);
163
+ const partsByMessage = new Map();
164
+ for (const row of partRows) {
165
+ let data;
166
+ try {
167
+ data = JSON.parse(row.data);
168
+ }
169
+ catch {
170
+ continue;
171
+ }
172
+ const bucket = partsByMessage.get(row.message_id);
173
+ if (bucket) {
174
+ bucket.push(data);
175
+ }
176
+ else {
177
+ partsByMessage.set(row.message_id, [data]);
178
+ }
179
+ }
180
+ const turns = extractTurns(messages, partsByMessage);
181
+ if (turns.length === 0)
182
+ return null;
183
+ const startedAt = normalizeTimestamp(session.time_created);
184
+ const endedAt = normalizeTimestamp(session.time_updated);
185
+ const transcript = turns.map((t) => `${t.role}: ${t.text}`).join("\n\n");
186
+ const label = session.title && session.title !== "New session"
187
+ ? session.title.slice(0, 80)
188
+ : provisionalLabel(turns);
189
+ return {
190
+ id: `oc_${sessionId}`,
191
+ runtime: this.runtimeVersion,
192
+ runtimeSessionId: sessionId,
193
+ sourcePath: `${this.dbPath}::${sessionId}`,
194
+ startedAt,
195
+ endedAt,
196
+ durationMin: durationMinutes(startedAt, endedAt),
197
+ turnCount: turns.length,
198
+ byteRange: [0, Buffer.byteLength(transcript, "utf8")],
199
+ projectDir: session.directory,
200
+ gitBranch: readGitBranch(session.directory),
201
+ text: transcript,
202
+ label,
203
+ };
204
+ }
205
+ catch {
206
+ return null;
207
+ }
208
+ finally {
209
+ db?.close();
210
+ }
211
+ }
212
+ }
213
+ //# sourceMappingURL=opencode.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"opencode.js","sourceRoot":"","sources":["../../../src/core/adapters/opencode.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Pi adapter.
3
+ *
4
+ * Reads ~/.pi/agent/sessions/<cwd-slug>/<timestamp>_<uuid>.jsonl. Pi writes
5
+ * session files even when a run aborts — those still ingest, but the adapter
6
+ * flags them via the `gitBranch: "aborted"` sentinel (SessionChunk has no
7
+ * status field; storage layer decodes the sentinel later).
8
+ *
9
+ * File shape (v3, confirmed 2026-05-18): one JSON object per line. Five
10
+ * event types: session, model_change, thinking_level_change, message,
11
+ * custom_message. Only `message` produces turns; the rest are configuration
12
+ * or extension-injected (custom_message must be excluded).
13
+ *
14
+ * Discovery is recursive (`<sessions>/<cwd-slug>/<file>.jsonl`).
15
+ * $PI_SESSIONS_PATH overrides the default path.
16
+ */
17
+ import type { DetectionResult, DiscoverOptions, SessionChunk, TranscriptAdapter } from "../../ports/transcript-adapter.js";
18
+ export interface PiAdapterOptions {
19
+ readonly sessionsPath?: string;
20
+ readonly idleMinutes?: number;
21
+ }
22
+ export declare class PiAdapter implements TranscriptAdapter {
23
+ readonly name = "pi";
24
+ readonly runtimeVersion = "pi/1.0";
25
+ readonly transcriptKind = "pi-jsonl";
26
+ private readonly sessionsPath;
27
+ readonly idleMinutes: number;
28
+ constructor(opts?: PiAdapterOptions);
29
+ detect(): DetectionResult;
30
+ discover(options?: DiscoverOptions): Promise<ReadonlyArray<string>>;
31
+ parseSession(path: string): Promise<SessionChunk | null>;
32
+ }
@@ -0,0 +1,233 @@
1
+ /**
2
+ * Pi adapter.
3
+ *
4
+ * Reads ~/.pi/agent/sessions/<cwd-slug>/<timestamp>_<uuid>.jsonl. Pi writes
5
+ * session files even when a run aborts — those still ingest, but the adapter
6
+ * flags them via the `gitBranch: "aborted"` sentinel (SessionChunk has no
7
+ * status field; storage layer decodes the sentinel later).
8
+ *
9
+ * File shape (v3, confirmed 2026-05-18): one JSON object per line. Five
10
+ * event types: session, model_change, thinking_level_change, message,
11
+ * custom_message. Only `message` produces turns; the rest are configuration
12
+ * or extension-injected (custom_message must be excluded).
13
+ *
14
+ * Discovery is recursive (`<sessions>/<cwd-slug>/<file>.jsonl`).
15
+ * $PI_SESSIONS_PATH overrides the default path.
16
+ */
17
+ import { promises as fs, existsSync, statSync } from "node:fs";
18
+ import { extname, join } from "node:path";
19
+ import { homedir } from "node:os";
20
+ import { durationMinutes, normalizeTimestamp, safeSessionId, } from "./common.js";
21
+ const TOOL_RESULT_PREVIEW_CHARS = 240;
22
+ function defaultSessionsPath() {
23
+ return (process.env["PI_SESSIONS_PATH"] ??
24
+ join(homedir(), ".pi", "agent", "sessions"));
25
+ }
26
+ export class PiAdapter {
27
+ name = "pi";
28
+ runtimeVersion = "pi/1.0";
29
+ transcriptKind = "pi-jsonl";
30
+ sessionsPath;
31
+ idleMinutes;
32
+ constructor(opts = {}) {
33
+ this.sessionsPath = opts.sessionsPath ?? defaultSessionsPath();
34
+ this.idleMinutes = opts.idleMinutes ?? 15;
35
+ }
36
+ detect() {
37
+ const p = defaultSessionsPath();
38
+ if (existsSync(p) && statSync(p).isDirectory()) {
39
+ return { adapterName: this.name, enabled: true, path: p, hint: null };
40
+ }
41
+ return {
42
+ adapterName: this.name,
43
+ enabled: false,
44
+ path: null,
45
+ hint: "Pi not detected — ~/.pi/agent/sessions/ missing.",
46
+ };
47
+ }
48
+ async discover(options = {}) {
49
+ if (!existsSync(this.sessionsPath))
50
+ return [];
51
+ const found = [];
52
+ await walk(this.sessionsPath, async (full, st) => {
53
+ if (st.size === 0)
54
+ return;
55
+ if (extname(full) !== ".jsonl")
56
+ return;
57
+ if (options.since && st.mtime < options.since)
58
+ return;
59
+ found.push({ mtime: st.mtimeMs, path: full });
60
+ });
61
+ found.sort((a, b) => a.mtime - b.mtime);
62
+ return found.map((f) => f.path);
63
+ }
64
+ async parseSession(path) {
65
+ let raw;
66
+ try {
67
+ raw = await fs.readFile(path, "utf8");
68
+ }
69
+ catch {
70
+ return null;
71
+ }
72
+ const turns = [];
73
+ let sessionId = "";
74
+ let projectDir = "";
75
+ let startedAt = "";
76
+ let endedAt = "";
77
+ let totalBytes = 0;
78
+ let allAssistantErrors = true;
79
+ for (const line of raw.split("\n")) {
80
+ totalBytes += Buffer.byteLength(line, "utf8") + 1;
81
+ const trimmed = line.trim();
82
+ if (!trimmed)
83
+ continue;
84
+ let evt;
85
+ try {
86
+ evt = JSON.parse(trimmed);
87
+ }
88
+ catch {
89
+ continue;
90
+ }
91
+ const evtType = evt["type"];
92
+ if (evtType === "session") {
93
+ if (typeof evt["id"] === "string")
94
+ sessionId = evt["id"];
95
+ if (typeof evt["cwd"] === "string")
96
+ projectDir = evt["cwd"];
97
+ continue;
98
+ }
99
+ if (evtType === "model_change" ||
100
+ evtType === "thinking_level_change" ||
101
+ evtType === "custom_message") {
102
+ continue;
103
+ }
104
+ if (evtType !== "message")
105
+ continue;
106
+ const msg = isRecord(evt["message"]) ? evt["message"] : {};
107
+ const role = msg["role"];
108
+ if (role !== "user" && role !== "assistant")
109
+ continue;
110
+ const innerTs = msg["timestamp"];
111
+ const outerTs = evt["timestamp"];
112
+ const ts = innerTs
113
+ ? normalizeTimestamp(innerTs)
114
+ : normalizeTimestamp(outerTs);
115
+ if (ts) {
116
+ if (!startedAt)
117
+ startedAt = ts;
118
+ endedAt = ts;
119
+ }
120
+ const text = extractPiText(msg["content"]);
121
+ if (role === "assistant") {
122
+ const stop = typeof msg["stopReason"] === "string" ? msg["stopReason"] : "";
123
+ if (stop !== "error")
124
+ allAssistantErrors = false;
125
+ if (!text.trim())
126
+ continue; // error turns have empty content
127
+ }
128
+ else if (!text.trim()) {
129
+ continue;
130
+ }
131
+ turns.push({ role, text, timestamp: ts });
132
+ }
133
+ if (turns.length === 0)
134
+ return null;
135
+ const hasUser = turns.some((t) => t.role === "user");
136
+ const hasSuccessfulAssistant = turns.some((t) => t.role === "assistant");
137
+ const isAborted = hasUser && (!hasSuccessfulAssistant || allAssistantErrors);
138
+ const transcript = turns.map((t) => `[${t.role}] ${t.text}`).join("\n\n");
139
+ const duration = durationMinutes(startedAt, endedAt);
140
+ const label = provisionalLabel(turns);
141
+ const rawId = sessionId || path.split("/").pop().replace(".jsonl", "");
142
+ return {
143
+ id: safeSessionId("pi", rawId),
144
+ runtime: this.runtimeVersion,
145
+ runtimeSessionId: rawId,
146
+ sourcePath: path,
147
+ startedAt,
148
+ endedAt,
149
+ durationMin: duration,
150
+ turnCount: turns.length,
151
+ byteRange: [0, totalBytes],
152
+ projectDir,
153
+ gitBranch: isAborted ? "aborted" : "",
154
+ text: transcript,
155
+ label,
156
+ };
157
+ }
158
+ }
159
+ // ── helpers ──────────────────────────────────────────────────────────────
160
+ function isRecord(v) {
161
+ return !!v && typeof v === "object" && !Array.isArray(v);
162
+ }
163
+ async function walk(dir, onFile) {
164
+ let entries;
165
+ try {
166
+ entries = await fs.readdir(dir, { withFileTypes: true });
167
+ }
168
+ catch {
169
+ return;
170
+ }
171
+ for (const ent of entries) {
172
+ const full = join(dir, ent.name);
173
+ if (ent.isDirectory()) {
174
+ await walk(full, onFile);
175
+ }
176
+ else if (ent.isFile()) {
177
+ try {
178
+ const st = await fs.stat(full);
179
+ await onFile(full, st);
180
+ }
181
+ catch {
182
+ continue;
183
+ }
184
+ }
185
+ }
186
+ }
187
+ function extractPiText(content) {
188
+ if (typeof content === "string")
189
+ return content.trim();
190
+ if (!Array.isArray(content))
191
+ return "";
192
+ const parts = [];
193
+ for (const block of content) {
194
+ if (!isRecord(block))
195
+ continue;
196
+ const btype = block["type"];
197
+ if (btype === "text") {
198
+ const t = block["text"];
199
+ if (typeof t === "string" && t.trim())
200
+ parts.push(t.trim());
201
+ }
202
+ else if (btype === "tool_use") {
203
+ const name = typeof block["name"] === "string" ? block["name"] : "tool";
204
+ parts.push(`[tool_use: ${name}]`);
205
+ }
206
+ else if (btype === "tool_result") {
207
+ let res = block["content"];
208
+ if (Array.isArray(res)) {
209
+ res = res
210
+ .filter(isRecord)
211
+ .map((b) => (typeof b["text"] === "string" ? b["text"] : ""))
212
+ .join("\n");
213
+ }
214
+ if (typeof res === "string" && res) {
215
+ const preview = res.slice(0, TOOL_RESULT_PREVIEW_CHARS);
216
+ const ellipsis = res.length > TOOL_RESULT_PREVIEW_CHARS ? "..." : "";
217
+ parts.push(`[tool_result: ${preview}${ellipsis}]`);
218
+ }
219
+ }
220
+ }
221
+ return parts.filter((p) => p).join("\n");
222
+ }
223
+ function provisionalLabel(turns) {
224
+ for (const t of turns) {
225
+ if (t.role !== "user")
226
+ continue;
227
+ const firstLine = t.text.split("\n", 1)[0]?.trim();
228
+ if (firstLine)
229
+ return firstLine.slice(0, 80);
230
+ }
231
+ return "Untitled session";
232
+ }
233
+ //# sourceMappingURL=pi.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pi.js","sourceRoot":"","sources":["../../../src/core/adapters/pi.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Classifier prompt + transcript helpers. Centralized so every LLMClient
3
+ * implementation hits the same prompt (parity with the Python daemon).
4
+ *
5
+ * Hard cap at 15K chars matches `classifier.py` MAX_TRANSCRIPT_CHARS:
6
+ * smaller models (phi4-mini, qwen) pattern-match JSON from the transcript
7
+ * above that size. Long sessions get first-half + last-half with a
8
+ * separator to preserve opening intent + closing decisions.
9
+ *
10
+ * Phase B.2: prompt now also asks for a `facts` array of normalized
11
+ * (subject, predicate, value) triples for the FactStore. The closed
12
+ * predicate vocabulary is embedded in the prompt so deterministic
13
+ * supersedence (Phase B.4) actually catches collisions instead of
14
+ * fragmenting on synonymous predicates. See docs/plans/factstore-design.md.
15
+ */
16
+ /**
17
+ * Closed predicate vocabulary. Approximately 25 high-leverage predicates
18
+ * covering the most common (subject, predicate, value) shapes Edward
19
+ * actually writes about in sessions.
20
+ *
21
+ * Vocab evolution (Phase B.5 backfill, 2026-05-19): the 168-session pilot
22
+ * showed `other` getting used 43% of the time — it became a catch-all for
23
+ * narrative observations that don't fit the (subject, predicate, value)
24
+ * shape at all. Removed. The classifier prompt now instructs the model to
25
+ * SKIP facts that don't fit (leave them in decisions[]/open[] instead).
26
+ * Added `description`, `commit`, `cost` from observed high-frequency
27
+ * patterns in the pilot batch's `other` bucket.
28
+ *
29
+ * Adding entries here is cheap and forwards-compatible: old facts stay,
30
+ * new ingests can use the new predicate. Removing entries is not — old
31
+ * facts referencing a retired predicate would stop matching by deterministic
32
+ * supersedence, so prefer to mark deprecated rather than delete. (Existing
33
+ * `other`-predicate facts from the pilot stay in the DB and are filterable
34
+ * at query time; the coercer will drop new `other` writes.)
35
+ */
36
+ export declare const PREDICATE_VOCABULARY: readonly ["framework", "endpoint", "model", "port", "host", "owner", "pricing", "cost", "deadline", "status", "stack", "runtime", "library", "version", "dependency", "schema", "integration", "deployment", "repo", "branch", "commit", "description", "decided-on", "assumption", "blocker"];
37
+ export type PredicateVocab = (typeof PREDICATE_VOCABULARY)[number];
38
+ export declare const CLASSIFIER_SYSTEM_PROMPT: string;
39
+ export declare const MAX_TRANSCRIPT_CHARS = 15000;
40
+ export declare function truncateTranscript(text: string, maxChars?: number): string;
41
+ export declare function stripJsonFences(text: string): string;
42
+ export declare function validateClassifierJson(data: unknown): data is Record<string, unknown>;
43
+ export declare function buildUserPrompt(transcript: string, priorContext: string): string;
44
+ interface CoercedFact {
45
+ kind: "decision" | "open" | "attribute";
46
+ subject: string;
47
+ predicate: string;
48
+ value: string;
49
+ sourceQuote?: string;
50
+ }
51
+ export declare function coerceClassifyResult(data: Record<string, unknown>): {
52
+ label: string;
53
+ summary: string;
54
+ entities: string[];
55
+ decisions: string[];
56
+ open: string[];
57
+ confidence: number;
58
+ facts: CoercedFact[];
59
+ };
60
+ export {};