@remnic/core 1.1.0 → 1.1.2

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 (308) hide show
  1. package/dist/access-audit.d.ts +56 -0
  2. package/dist/access-audit.js +9 -0
  3. package/dist/access-cli.js +70 -53
  4. package/dist/access-cli.js.map +1 -1
  5. package/dist/access-http.d.ts +16 -9
  6. package/dist/access-http.js +26 -18
  7. package/dist/access-mcp.d.ts +16 -9
  8. package/dist/access-mcp.js +30 -8
  9. package/dist/access-schema.d.ts +124 -33
  10. package/dist/access-schema.js +5 -1
  11. package/dist/{access-service-HmO1Trrx.d.ts → access-service-Br8ZydTK.d.ts} +158 -63
  12. package/dist/access-service.d.ts +13 -6
  13. package/dist/access-service.js +23 -14
  14. package/dist/bootstrap.d.ts +6 -3
  15. package/dist/briefing.d.ts +1 -0
  16. package/dist/briefing.js +8 -6
  17. package/dist/buffer-surprise-report.d.ts +70 -0
  18. package/dist/buffer-surprise-report.js +7 -0
  19. package/dist/buffer-surprise-report.js.map +1 -0
  20. package/dist/buffer-surprise.d.ts +98 -0
  21. package/dist/buffer-surprise.js +11 -0
  22. package/dist/buffer-surprise.js.map +1 -0
  23. package/dist/buffer.d.ts +100 -2
  24. package/dist/buffer.js +1 -1
  25. package/dist/calibration.js +6 -6
  26. package/dist/causal-behavior.js +4 -4
  27. package/dist/causal-chain.js +2 -2
  28. package/dist/causal-consolidation.js +19 -18
  29. package/dist/causal-consolidation.js.map +1 -1
  30. package/dist/causal-retrieval.js +4 -4
  31. package/dist/causal-trajectory.js +1 -1
  32. package/dist/{chunk-QNJMBKFK.js → chunk-2LGMW3DJ.js} +3 -2
  33. package/dist/chunk-2LGMW3DJ.js.map +1 -0
  34. package/dist/{chunk-QDYXG4CS.js → chunk-3FPTCC3Z.js} +4 -3
  35. package/dist/chunk-3FPTCC3Z.js.map +1 -0
  36. package/dist/chunk-3GPTTA4J.js +57 -0
  37. package/dist/chunk-3GPTTA4J.js.map +1 -0
  38. package/dist/{chunk-ITRLGI2T.js → chunk-3OGMS3PE.js} +2 -2
  39. package/dist/{chunk-DEPL3635.js → chunk-3YGHKTBF.js} +1446 -196
  40. package/dist/chunk-3YGHKTBF.js.map +1 -0
  41. package/dist/{chunk-BLKTA7MM.js → chunk-4HQS2HPX.js} +54 -21
  42. package/dist/chunk-4HQS2HPX.js.map +1 -0
  43. package/dist/chunk-54V4BZWP.js +139 -0
  44. package/dist/chunk-54V4BZWP.js.map +1 -0
  45. package/dist/chunk-5JRF2PZA.js +67 -0
  46. package/dist/chunk-5JRF2PZA.js.map +1 -0
  47. package/dist/chunk-64NJRYU2.js +332 -0
  48. package/dist/chunk-64NJRYU2.js.map +1 -0
  49. package/dist/{chunk-OIT5QGG4.js → chunk-6AUUAZEX.js} +72 -2
  50. package/dist/chunk-6AUUAZEX.js.map +1 -0
  51. package/dist/{chunk-3QHL5ABG.js → chunk-6YJHX2DL.js} +191 -10
  52. package/dist/chunk-6YJHX2DL.js.map +1 -0
  53. package/dist/chunk-AJU4PJGY.js +126 -0
  54. package/dist/chunk-AJU4PJGY.js.map +1 -0
  55. package/dist/chunk-ASAITVLA.js +64 -0
  56. package/dist/chunk-ASAITVLA.js.map +1 -0
  57. package/dist/{chunk-44ICJRF3.js → chunk-AYXIPSZO.js} +5 -5
  58. package/dist/{chunk-MBJHSA7F.js → chunk-BECYBZLX.js} +265 -20
  59. package/dist/chunk-BECYBZLX.js.map +1 -0
  60. package/dist/chunk-C4SQJZAF.js +486 -0
  61. package/dist/chunk-C4SQJZAF.js.map +1 -0
  62. package/dist/{chunk-6UJ47TVX.js → chunk-CUPFXL3J.js} +2 -2
  63. package/dist/chunk-DF3RVK3X.js +119 -0
  64. package/dist/chunk-DF3RVK3X.js.map +1 -0
  65. package/dist/{chunk-N42IWANG.js → chunk-DG6YMRDC.js} +3 -3
  66. package/dist/chunk-DGVM5SFL.js +69 -0
  67. package/dist/chunk-DGVM5SFL.js.map +1 -0
  68. package/dist/{chunk-3SV6CQHO.js → chunk-DIXB44VE.js} +102 -66
  69. package/dist/chunk-DIXB44VE.js.map +1 -0
  70. package/dist/chunk-EIR5VLIH.js +90 -0
  71. package/dist/chunk-EIR5VLIH.js.map +1 -0
  72. package/dist/{chunk-GV6NLQ4X.js → chunk-F5VP6YCB.js} +374 -16
  73. package/dist/chunk-F5VP6YCB.js.map +1 -0
  74. package/dist/{chunk-6ZH4TU6I.js → chunk-FAAFWE4G.js} +2 -1
  75. package/dist/chunk-FAAFWE4G.js.map +1 -0
  76. package/dist/{chunk-7WQ6SLIE.js → chunk-FVA6TGI3.js} +2 -2
  77. package/dist/{chunk-PAORGQRI.js → chunk-GA5P7RST.js} +37 -23
  78. package/dist/chunk-GA5P7RST.js.map +1 -0
  79. package/dist/chunk-GDFS42HT.js +206 -0
  80. package/dist/chunk-GDFS42HT.js.map +1 -0
  81. package/dist/chunk-IISBCCWR.js +52 -0
  82. package/dist/chunk-IISBCCWR.js.map +1 -0
  83. package/dist/chunk-JBMSGZEQ.js +441 -0
  84. package/dist/chunk-JBMSGZEQ.js.map +1 -0
  85. package/dist/{chunk-J4IYOZZ5.js → chunk-JXS5PDQ7.js} +3 -1
  86. package/dist/chunk-JXS5PDQ7.js.map +1 -0
  87. package/dist/chunk-KVBLZUKV.js +173 -0
  88. package/dist/chunk-KVBLZUKV.js.map +1 -0
  89. package/dist/{chunk-4LACOVZX.js → chunk-L7IXWRYE.js} +10 -5
  90. package/dist/chunk-L7IXWRYE.js.map +1 -0
  91. package/dist/chunk-LBLXEFWK.js +51 -0
  92. package/dist/chunk-LBLXEFWK.js.map +1 -0
  93. package/dist/{chunk-WBSAYXVI.js → chunk-LOIMBRDE.js} +201 -45
  94. package/dist/chunk-LOIMBRDE.js.map +1 -0
  95. package/dist/{chunk-3WHVNEN7.js → chunk-LTCGGW2D.js} +1 -1
  96. package/dist/chunk-LTCGGW2D.js.map +1 -0
  97. package/dist/{chunk-ZVBB3T7V.js → chunk-NBVAS5MT.js} +25 -23
  98. package/dist/chunk-NBVAS5MT.js.map +1 -0
  99. package/dist/{chunk-UEYA6UC7.js → chunk-NZLQTHS5.js} +25 -2
  100. package/dist/chunk-NZLQTHS5.js.map +1 -0
  101. package/dist/{chunk-NQEVYWX6.js → chunk-OC5OXUQ4.js} +211 -7
  102. package/dist/chunk-OC5OXUQ4.js.map +1 -0
  103. package/dist/{chunk-LK6SGL53.js → chunk-OR64ZGRZ.js} +3 -2
  104. package/dist/chunk-OR64ZGRZ.js.map +1 -0
  105. package/dist/{chunk-SYUK3VLY.js → chunk-PVICZTKG.js} +117 -5
  106. package/dist/chunk-PVICZTKG.js.map +1 -0
  107. package/dist/chunk-PVPWZSSI.js +37 -0
  108. package/dist/chunk-PVPWZSSI.js.map +1 -0
  109. package/dist/{chunk-JL2PU6AI.js → chunk-R2XRID2N.js} +2 -2
  110. package/dist/{chunk-4NRAJUDS.js → chunk-RBBWYEFJ.js} +1 -1
  111. package/dist/chunk-RFYAYKTD.js +146 -0
  112. package/dist/chunk-RFYAYKTD.js.map +1 -0
  113. package/dist/chunk-SOBJ6NEY.js +18 -0
  114. package/dist/chunk-SOBJ6NEY.js.map +1 -0
  115. package/dist/{chunk-JIU55F3X.js → chunk-SPI27QT6.js} +2 -2
  116. package/dist/{chunk-MVTHXUBX.js → chunk-STGWEHYR.js} +479 -20
  117. package/dist/chunk-STGWEHYR.js.map +1 -0
  118. package/dist/{chunk-6LX5ORAS.js → chunk-TMYO7B5P.js} +4 -4
  119. package/dist/chunk-TVVEYCNW.js +65 -0
  120. package/dist/chunk-TVVEYCNW.js.map +1 -0
  121. package/dist/chunk-ULYOGL6R.js +322 -0
  122. package/dist/chunk-ULYOGL6R.js.map +1 -0
  123. package/dist/{chunk-37UIFYWO.js → chunk-UWB5LMWY.js} +108 -9
  124. package/dist/chunk-UWB5LMWY.js.map +1 -0
  125. package/dist/{chunk-47UU5PU2.js → chunk-VBVG2M5G.js} +18 -3
  126. package/dist/chunk-VBVG2M5G.js.map +1 -0
  127. package/dist/{chunk-7ECD5ATE.js → chunk-VDX363PS.js} +2 -2
  128. package/dist/{chunk-O5ETUNBT.js → chunk-VTU2B4VF.js} +7 -3
  129. package/dist/chunk-VTU2B4VF.js.map +1 -0
  130. package/dist/{chunk-MTLYEMJB.js → chunk-WCLICCGB.js} +18 -3
  131. package/dist/chunk-WCLICCGB.js.map +1 -0
  132. package/dist/chunk-X6GF3FX2.js +26 -0
  133. package/dist/chunk-X6GF3FX2.js.map +1 -0
  134. package/dist/{chunk-3QFQGRHO.js → chunk-XMHBH5H6.js} +4 -4
  135. package/dist/{chunk-DHHP2Z4X.js → chunk-XXVWLXSG.js} +2 -2
  136. package/dist/{chunk-XZ2TIKGC.js → chunk-Y7R2XJ5Q.js} +25 -9
  137. package/dist/chunk-Y7R2XJ5Q.js.map +1 -0
  138. package/dist/{chunk-ALXMCZEU.js → chunk-Z2E7VW55.js} +6 -3
  139. package/dist/chunk-Z2E7VW55.js.map +1 -0
  140. package/dist/chunk-ZAIM4TUE.js +488 -0
  141. package/dist/chunk-ZAIM4TUE.js.map +1 -0
  142. package/dist/chunk-ZZTOURJI.js +91 -0
  143. package/dist/chunk-ZZTOURJI.js.map +1 -0
  144. package/dist/{cli-BneVIEvh.d.ts → cli-BkeRaYfk.d.ts} +2 -2
  145. package/dist/cli.d.ts +13 -6
  146. package/dist/cli.js +42 -31
  147. package/dist/config.js +2 -2
  148. package/dist/consolidation-operator.d.ts +41 -0
  149. package/dist/consolidation-operator.js +11 -0
  150. package/dist/consolidation-operator.js.map +1 -0
  151. package/dist/consolidation-provenance-check.d.ts +68 -0
  152. package/dist/consolidation-provenance-check.js +9 -0
  153. package/dist/consolidation-provenance-check.js.map +1 -0
  154. package/dist/consolidation-undo.d.ts +123 -0
  155. package/dist/consolidation-undo.js +426 -0
  156. package/dist/consolidation-undo.js.map +1 -0
  157. package/dist/{contradiction-scan-GR33PONM.js → contradiction-scan-E3GJTI4F.js} +43 -7
  158. package/dist/contradiction-scan-E3GJTI4F.js.map +1 -0
  159. package/dist/cross-namespace-budget.d.ts +133 -0
  160. package/dist/cross-namespace-budget.js +9 -0
  161. package/dist/cross-namespace-budget.js.map +1 -0
  162. package/dist/direct-answer-wiring.js +5 -70
  163. package/dist/direct-answer-wiring.js.map +1 -1
  164. package/dist/embedding-fallback.js +2 -1
  165. package/dist/{engine-5TIQBYZR.js → engine-72LSIWQP.js} +8 -7
  166. package/dist/engine-72LSIWQP.js.map +1 -0
  167. package/dist/entity-retrieval.d.ts +1 -0
  168. package/dist/entity-retrieval.js +7 -6
  169. package/dist/explicit-capture.d.ts +6 -3
  170. package/dist/explicit-capture.js +2 -2
  171. package/dist/extraction-judge-telemetry.d.ts +113 -0
  172. package/dist/extraction-judge-telemetry.js +14 -0
  173. package/dist/extraction-judge-telemetry.js.map +1 -0
  174. package/dist/extraction-judge-training.d.ts +85 -0
  175. package/dist/extraction-judge-training.js +16 -0
  176. package/dist/extraction-judge-training.js.map +1 -0
  177. package/dist/extraction-judge.d.ts +124 -2
  178. package/dist/extraction-judge.js +11 -1
  179. package/dist/extraction.js +10 -9
  180. package/dist/fallback-llm.js +3 -3
  181. package/dist/graph-recall.d.ts +100 -0
  182. package/dist/graph-recall.js +8 -0
  183. package/dist/graph-recall.js.map +1 -0
  184. package/dist/graph-retrieval.d.ts +271 -0
  185. package/dist/graph-retrieval.js +21 -0
  186. package/dist/graph-retrieval.js.map +1 -0
  187. package/dist/importance.js +1 -1
  188. package/dist/index.d.ts +585 -20
  189. package/dist/index.js +542 -344
  190. package/dist/index.js.map +1 -1
  191. package/dist/local-llm.js +2 -2
  192. package/dist/memory-worth-bench.d.ts +51 -0
  193. package/dist/memory-worth-bench.js +131 -0
  194. package/dist/memory-worth-bench.js.map +1 -0
  195. package/dist/memory-worth-filter.d.ts +128 -0
  196. package/dist/memory-worth-filter.js +10 -0
  197. package/dist/memory-worth-filter.js.map +1 -0
  198. package/dist/memory-worth-outcomes.d.ts +118 -0
  199. package/dist/memory-worth-outcomes.js +9 -0
  200. package/dist/memory-worth-outcomes.js.map +1 -0
  201. package/dist/memory-worth.d.ts +102 -0
  202. package/dist/memory-worth.js +7 -0
  203. package/dist/memory-worth.js.map +1 -0
  204. package/dist/operator-toolkit.d.ts +40 -1
  205. package/dist/operator-toolkit.js +25 -16
  206. package/dist/{orchestrator-DRYA6_lW.d.ts → orchestrator-CmJ-NTdJ.d.ts} +233 -8
  207. package/dist/orchestrator.d.ts +6 -3
  208. package/dist/orchestrator.js +54 -44
  209. package/dist/page-versioning.d.ts +12 -1
  210. package/dist/page-versioning.js +5 -3
  211. package/dist/{port-C1GZFv8h.d.ts → port-BADbLZU5.d.ts} +2 -2
  212. package/dist/qmd-recall-cache.d.ts +1 -1
  213. package/dist/qmd.d.ts +5 -3
  214. package/dist/qmd.js +3 -3
  215. package/dist/reasoning-trace-recall.d.ts +90 -0
  216. package/dist/reasoning-trace-recall.js +13 -0
  217. package/dist/reasoning-trace-recall.js.map +1 -0
  218. package/dist/reasoning-trace-types.d.ts +54 -0
  219. package/dist/reasoning-trace-types.js +17 -0
  220. package/dist/reasoning-trace-types.js.map +1 -0
  221. package/dist/recall-audit-anomaly.d.ts +112 -0
  222. package/dist/recall-audit-anomaly.js +11 -0
  223. package/dist/recall-audit-anomaly.js.map +1 -0
  224. package/dist/recall-audit.js +5 -44
  225. package/dist/recall-audit.js.map +1 -1
  226. package/dist/recall-explain-renderer.d.ts +49 -0
  227. package/dist/recall-explain-renderer.js +18 -0
  228. package/dist/recall-explain-renderer.js.map +1 -0
  229. package/dist/recall-state.d.ts +12 -1
  230. package/dist/recall-state.js +1 -1
  231. package/dist/recall-xray-cli.d.ts +40 -0
  232. package/dist/recall-xray-cli.js +11 -0
  233. package/dist/recall-xray-cli.js.map +1 -0
  234. package/dist/recall-xray-renderer.d.ts +44 -0
  235. package/dist/recall-xray-renderer.js +18 -0
  236. package/dist/recall-xray-renderer.js.map +1 -0
  237. package/dist/recall-xray.d.ts +179 -0
  238. package/dist/recall-xray.js +13 -0
  239. package/dist/recall-xray.js.map +1 -0
  240. package/dist/resolve-provider-secret.d.ts +5 -1
  241. package/dist/resolve-provider-secret.js +3 -1
  242. package/dist/resume-bundles.js +6 -6
  243. package/dist/retrieval-agents.d.ts +1 -1
  244. package/dist/retrieval-tiers.d.ts +17 -0
  245. package/dist/retrieval-tiers.js +9 -0
  246. package/dist/retrieval-tiers.js.map +1 -0
  247. package/dist/schemas.d.ts +309 -53
  248. package/dist/schemas.js +1 -1
  249. package/dist/{semantic-consolidation-DrvSYRdB.d.ts → semantic-consolidation-CxJU6MJk.d.ts} +62 -1
  250. package/dist/semantic-consolidation.d.ts +2 -1
  251. package/dist/semantic-consolidation.js +22 -7
  252. package/dist/semantic-rule-promotion.js +7 -6
  253. package/dist/semantic-rule-verifier.js +7 -6
  254. package/dist/storage.d.ts +82 -1
  255. package/dist/storage.js +6 -5
  256. package/dist/summarizer.js +6 -6
  257. package/dist/temporal-supersession.d.ts +1 -0
  258. package/dist/tier-migration.d.ts +2 -1
  259. package/dist/tokens.js +2 -1
  260. package/dist/types.d.ts +276 -2
  261. package/dist/types.js +1 -1
  262. package/dist/verified-recall.js +7 -6
  263. package/package.json +1 -1
  264. package/dist/chunk-37UIFYWO.js.map +0 -1
  265. package/dist/chunk-3QHL5ABG.js.map +0 -1
  266. package/dist/chunk-3SV6CQHO.js.map +0 -1
  267. package/dist/chunk-3WHVNEN7.js.map +0 -1
  268. package/dist/chunk-47UU5PU2.js.map +0 -1
  269. package/dist/chunk-4LACOVZX.js.map +0 -1
  270. package/dist/chunk-6ZH4TU6I.js.map +0 -1
  271. package/dist/chunk-ALXMCZEU.js.map +0 -1
  272. package/dist/chunk-BLKTA7MM.js.map +0 -1
  273. package/dist/chunk-DEPL3635.js.map +0 -1
  274. package/dist/chunk-GV6NLQ4X.js.map +0 -1
  275. package/dist/chunk-J4IYOZZ5.js.map +0 -1
  276. package/dist/chunk-LAYN4LDC.js +0 -267
  277. package/dist/chunk-LAYN4LDC.js.map +0 -1
  278. package/dist/chunk-LK6SGL53.js.map +0 -1
  279. package/dist/chunk-MBJHSA7F.js.map +0 -1
  280. package/dist/chunk-MTLYEMJB.js.map +0 -1
  281. package/dist/chunk-MVTHXUBX.js.map +0 -1
  282. package/dist/chunk-NQEVYWX6.js.map +0 -1
  283. package/dist/chunk-O5ETUNBT.js.map +0 -1
  284. package/dist/chunk-OIT5QGG4.js.map +0 -1
  285. package/dist/chunk-PAORGQRI.js.map +0 -1
  286. package/dist/chunk-QDYXG4CS.js.map +0 -1
  287. package/dist/chunk-QNJMBKFK.js.map +0 -1
  288. package/dist/chunk-SYUK3VLY.js.map +0 -1
  289. package/dist/chunk-UEYA6UC7.js.map +0 -1
  290. package/dist/chunk-UVJFDP7P.js +0 -202
  291. package/dist/chunk-UVJFDP7P.js.map +0 -1
  292. package/dist/chunk-WBSAYXVI.js.map +0 -1
  293. package/dist/chunk-XZ2TIKGC.js.map +0 -1
  294. package/dist/chunk-ZVBB3T7V.js.map +0 -1
  295. package/dist/contradiction-scan-GR33PONM.js.map +0 -1
  296. /package/dist/{engine-5TIQBYZR.js.map → access-audit.js.map} +0 -0
  297. /package/dist/{chunk-ITRLGI2T.js.map → chunk-3OGMS3PE.js.map} +0 -0
  298. /package/dist/{chunk-44ICJRF3.js.map → chunk-AYXIPSZO.js.map} +0 -0
  299. /package/dist/{chunk-6UJ47TVX.js.map → chunk-CUPFXL3J.js.map} +0 -0
  300. /package/dist/{chunk-N42IWANG.js.map → chunk-DG6YMRDC.js.map} +0 -0
  301. /package/dist/{chunk-7WQ6SLIE.js.map → chunk-FVA6TGI3.js.map} +0 -0
  302. /package/dist/{chunk-JL2PU6AI.js.map → chunk-R2XRID2N.js.map} +0 -0
  303. /package/dist/{chunk-4NRAJUDS.js.map → chunk-RBBWYEFJ.js.map} +0 -0
  304. /package/dist/{chunk-JIU55F3X.js.map → chunk-SPI27QT6.js.map} +0 -0
  305. /package/dist/{chunk-6LX5ORAS.js.map → chunk-TMYO7B5P.js.map} +0 -0
  306. /package/dist/{chunk-7ECD5ATE.js.map → chunk-VDX363PS.js.map} +0 -0
  307. /package/dist/{chunk-3QFQGRHO.js.map → chunk-XMHBH5H6.js.map} +0 -0
  308. /package/dist/{chunk-DHHP2Z4X.js.map → chunk-XXVWLXSG.js.map} +0 -0
@@ -0,0 +1,119 @@
1
+ import {
2
+ log
3
+ } from "./chunk-2ODBA7MQ.js";
4
+
5
+ // src/extraction-judge-training.ts
6
+ import path from "path";
7
+ import { homedir } from "os";
8
+ import { appendFile, mkdir, readFile, readdir } from "fs/promises";
9
+ function expandTilde(p) {
10
+ const home = homedir();
11
+ if (p === "~" || p.startsWith("~/") || p.startsWith("~\\")) {
12
+ return home + p.slice(1);
13
+ }
14
+ if (p === "$HOME" || p.startsWith("$HOME/") || p.startsWith("$HOME\\")) {
15
+ return home + p.slice(5);
16
+ }
17
+ if (p === "${HOME}" || p.startsWith("${HOME}/") || p.startsWith("${HOME}\\")) {
18
+ return home + p.slice(7);
19
+ }
20
+ return p;
21
+ }
22
+ function resolveTrainingDir(options) {
23
+ if (options.directory && options.directory.length > 0) {
24
+ return expandTilde(options.directory);
25
+ }
26
+ return path.join(homedir(), ".remnic", "judge-training");
27
+ }
28
+ function dateStamp(iso) {
29
+ const ms = Date.parse(iso);
30
+ const d = Number.isFinite(ms) ? new Date(ms) : /* @__PURE__ */ new Date();
31
+ const yyyy = d.getUTCFullYear().toString().padStart(4, "0");
32
+ const mm = (d.getUTCMonth() + 1).toString().padStart(2, "0");
33
+ const dd = d.getUTCDate().toString().padStart(2, "0");
34
+ return `${yyyy}-${mm}-${dd}`;
35
+ }
36
+ function trainingFilePathFor(directory, iso) {
37
+ return path.join(directory, `${dateStamp(iso)}.jsonl`);
38
+ }
39
+ async function recordJudgeTrainingPair(row, options) {
40
+ if (!options.enabled) return;
41
+ const dir = resolveTrainingDir(options);
42
+ const filePath = trainingFilePathFor(dir, row.ts);
43
+ try {
44
+ await mkdir(dir, { recursive: true });
45
+ await appendFile(filePath, `${JSON.stringify(row)}
46
+ `, "utf-8");
47
+ } catch (err) {
48
+ log.debug(
49
+ `extraction-judge-training: append failed (non-fatal): ${err instanceof Error ? err.message : String(err)}`
50
+ );
51
+ }
52
+ }
53
+ async function readJudgeTrainingPairs(options) {
54
+ const dir = resolveTrainingDir({ enabled: true, ...options });
55
+ let entries;
56
+ try {
57
+ entries = await readdir(dir);
58
+ } catch (err) {
59
+ const code = err.code;
60
+ if (code === "ENOENT") return { rows: [], malformed: 0 };
61
+ throw err;
62
+ }
63
+ const rows = [];
64
+ let malformed = 0;
65
+ entries.sort();
66
+ for (const name of entries) {
67
+ if (!name.endsWith(".jsonl")) continue;
68
+ const raw = await readFile(path.join(dir, name), "utf-8");
69
+ for (const line of raw.split("\n")) {
70
+ if (!line.trim()) continue;
71
+ let parsed;
72
+ try {
73
+ parsed = JSON.parse(line);
74
+ } catch {
75
+ malformed += 1;
76
+ continue;
77
+ }
78
+ if (!isValidTrainingPair(parsed)) {
79
+ malformed += 1;
80
+ continue;
81
+ }
82
+ rows.push(parsed);
83
+ }
84
+ }
85
+ return { rows, malformed };
86
+ }
87
+ function isValidTrainingPair(value) {
88
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
89
+ return false;
90
+ }
91
+ const p = value;
92
+ if (p.version !== 1) return false;
93
+ if (typeof p.ts !== "string") return false;
94
+ if (typeof p.candidateText !== "string") return false;
95
+ if (typeof p.candidateCategory !== "string") return false;
96
+ if (p.verdictKind !== "accept" && p.verdictKind !== "reject" && p.verdictKind !== "defer") {
97
+ return false;
98
+ }
99
+ if (typeof p.reason !== "string") return false;
100
+ if (p.candidateConfidence !== void 0 && typeof p.candidateConfidence !== "number") {
101
+ return false;
102
+ }
103
+ if (p.priorDeferrals !== void 0 && typeof p.priorDeferrals !== "number") {
104
+ return false;
105
+ }
106
+ if (p.groundTruthLabel !== void 0 && p.groundTruthLabel !== "accept" && p.groundTruthLabel !== "reject" && p.groundTruthLabel !== "defer") {
107
+ return false;
108
+ }
109
+ return true;
110
+ }
111
+
112
+ export {
113
+ resolveTrainingDir,
114
+ trainingFilePathFor,
115
+ recordJudgeTrainingPair,
116
+ readJudgeTrainingPairs,
117
+ isValidTrainingPair
118
+ };
119
+ //# sourceMappingURL=chunk-DF3RVK3X.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/extraction-judge-training.ts"],"sourcesContent":["/**\n * Extraction Judge Training Data Shim (issue #562, PR 4).\n *\n * Opt-in collector for `(candidate_text, verdict_kind, reason,\n * ground_truth_label?)` tuples. Rows are appended to JSONL files under\n * `~/.remnic/judge-training/<YYYY-MM-DD>.jsonl` so operators can ship the\n * data into a future GRPO training pipeline without exfiltrating live\n * memory content through the regular observation ledger.\n *\n * Gating:\n * - Off by default. Must be explicitly enabled via\n * `collectJudgeTrainingPairs: true` in plugin config.\n * - The ground-truth label is always optional — labels are added out-of-\n * band once reviewers disambiguate the candidate's fate.\n *\n * Privacy: the row carries only what the judge already sees — the\n * candidate text and its metadata. It does NOT carry session keys,\n * principal IDs, or any user identifiers. The file lives in the user's\n * home directory rather than the shared memory directory so it is never\n * committed, sync'd, or bundled into exports.\n */\n\nimport path from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { appendFile, mkdir, readFile, readdir } from \"node:fs/promises\";\nimport { log } from \"./logger.js\";\nimport type { JudgeVerdictKind } from \"./extraction-judge.js\";\n\n/**\n * Persisted training row. Intentionally minimal: just the signal needed\n * to train a judge replacement policy. Schema version is tagged so future\n * readers can migrate older rows.\n */\nexport interface JudgeTrainingPair {\n version: 1;\n ts: string; // ISO-8601\n candidateText: string;\n candidateCategory: string;\n candidateConfidence?: number;\n verdictKind: JudgeVerdictKind;\n reason: string;\n /**\n * Number of prior deferrals when the verdict was resolved. `0` for the\n * first resolution; only set when known (defer pathway).\n */\n priorDeferrals?: number;\n /**\n * Optional human-applied ground-truth label. Added after the fact by a\n * reviewer / labelling script; not present on fresh rows.\n */\n groundTruthLabel?: JudgeVerdictKind;\n}\n\nexport interface JudgeTrainingOptions {\n enabled: boolean;\n /**\n * Override for the output directory. Defaults to\n * `~/.remnic/judge-training`. Tests pass a temp path here.\n */\n directory?: string;\n}\n\n/**\n * Expand a leading `~` / `~/` / `$HOME/` / `${HOME}/` to the process home\n * directory. Node's `fs` APIs do not expand `~` themselves (CLAUDE.md\n * gotcha 17), so every user-facing path input must be funnelled through\n * this helper before it reaches the filesystem.\n */\nfunction expandTilde(p: string): string {\n const home = homedir();\n if (p === \"~\" || p.startsWith(\"~/\") || p.startsWith(\"~\\\\\")) {\n return home + p.slice(1);\n }\n if (p === \"$HOME\" || p.startsWith(\"$HOME/\") || p.startsWith(\"$HOME\\\\\")) {\n return home + p.slice(5);\n }\n if (p === \"${HOME}\" || p.startsWith(\"${HOME}/\") || p.startsWith(\"${HOME}\\\\\")) {\n return home + p.slice(7);\n }\n return p;\n}\n\nexport function resolveTrainingDir(options: JudgeTrainingOptions): string {\n if (options.directory && options.directory.length > 0) {\n // Expand `~` / `$HOME` in the override so operators can write the\n // config as the user sees it (CLAUDE.md gotcha 17).\n return expandTilde(options.directory);\n }\n return path.join(homedir(), \".remnic\", \"judge-training\");\n}\n\nfunction dateStamp(iso: string): string {\n // `YYYY-MM-DD` from an ISO-8601 string. Falls back to today on a parse\n // failure rather than throwing — the caller already wrote a row and the\n // timestamp is best-effort.\n const ms = Date.parse(iso);\n const d = Number.isFinite(ms) ? new Date(ms) : new Date();\n const yyyy = d.getUTCFullYear().toString().padStart(4, \"0\");\n const mm = (d.getUTCMonth() + 1).toString().padStart(2, \"0\");\n const dd = d.getUTCDate().toString().padStart(2, \"0\");\n return `${yyyy}-${mm}-${dd}`;\n}\n\nexport function trainingFilePathFor(\n directory: string,\n iso: string,\n): string {\n return path.join(directory, `${dateStamp(iso)}.jsonl`);\n}\n\n/**\n * Append a single training row. Fails open — write errors are logged at\n * debug level and swallowed, same policy as the telemetry emitter.\n * No-op when `options.enabled` is false.\n */\nexport async function recordJudgeTrainingPair(\n row: JudgeTrainingPair,\n options: JudgeTrainingOptions,\n): Promise<void> {\n if (!options.enabled) return;\n const dir = resolveTrainingDir(options);\n const filePath = trainingFilePathFor(dir, row.ts);\n try {\n await mkdir(dir, { recursive: true });\n await appendFile(filePath, `${JSON.stringify(row)}\\n`, \"utf-8\");\n } catch (err) {\n log.debug(\n `extraction-judge-training: append failed (non-fatal): ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n}\n\n/**\n * Read all training rows from the configured directory. Returns an empty\n * array when the directory is missing. Malformed lines are skipped and\n * counted in the returned `malformed` tally.\n */\nexport async function readJudgeTrainingPairs(\n options: Pick<JudgeTrainingOptions, \"directory\">,\n): Promise<{ rows: JudgeTrainingPair[]; malformed: number }> {\n const dir = resolveTrainingDir({ enabled: true, ...options });\n let entries: string[];\n try {\n entries = await readdir(dir);\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code;\n if (code === \"ENOENT\") return { rows: [], malformed: 0 };\n throw err;\n }\n\n const rows: JudgeTrainingPair[] = [];\n let malformed = 0;\n // Sort so reads are deterministic across platforms.\n entries.sort();\n for (const name of entries) {\n if (!name.endsWith(\".jsonl\")) continue;\n const raw = await readFile(path.join(dir, name), \"utf-8\");\n for (const line of raw.split(\"\\n\")) {\n if (!line.trim()) continue;\n let parsed: unknown;\n try {\n parsed = JSON.parse(line);\n } catch {\n malformed += 1;\n continue;\n }\n if (!isValidTrainingPair(parsed)) {\n malformed += 1;\n continue;\n }\n rows.push(parsed);\n }\n }\n return { rows, malformed };\n}\n\n/**\n * Structural validator matching the persisted schema. Forward-compat: an\n * unknown `verdictKind` string is treated as malformed (strict training\n * signal — we do not want to admit unlabelled gibberish into a trainer).\n */\nexport function isValidTrainingPair(value: unknown): value is JudgeTrainingPair {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n return false;\n }\n const p = value as Record<string, unknown>;\n if (p.version !== 1) return false;\n if (typeof p.ts !== \"string\") return false;\n if (typeof p.candidateText !== \"string\") return false;\n if (typeof p.candidateCategory !== \"string\") return false;\n if (\n p.verdictKind !== \"accept\" &&\n p.verdictKind !== \"reject\" &&\n p.verdictKind !== \"defer\"\n ) {\n return false;\n }\n if (typeof p.reason !== \"string\") return false;\n if (\n p.candidateConfidence !== undefined &&\n typeof p.candidateConfidence !== \"number\"\n ) {\n return false;\n }\n if (p.priorDeferrals !== undefined && typeof p.priorDeferrals !== \"number\") {\n return false;\n }\n if (\n p.groundTruthLabel !== undefined &&\n p.groundTruthLabel !== \"accept\" &&\n p.groundTruthLabel !== \"reject\" &&\n p.groundTruthLabel !== \"defer\"\n ) {\n return false;\n }\n return true;\n}\n"],"mappings":"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","names":[]}
@@ -8,10 +8,10 @@ import {
8
8
  } from "./chunk-FEMOX5AD.js";
9
9
  import {
10
10
  LocalLlmClient
11
- } from "./chunk-JL2PU6AI.js";
11
+ } from "./chunk-R2XRID2N.js";
12
12
  import {
13
13
  FallbackLlmClient
14
- } from "./chunk-44ICJRF3.js";
14
+ } from "./chunk-AYXIPSZO.js";
15
15
  import {
16
16
  extractJsonCandidates
17
17
  } from "./chunk-UZB5KHKX.js";
@@ -618,4 +618,4 @@ ${truncatedConversation}`;
618
618
  export {
619
619
  HourlySummarizer
620
620
  };
621
- //# sourceMappingURL=chunk-N42IWANG.js.map
621
+ //# sourceMappingURL=chunk-DG6YMRDC.js.map
@@ -0,0 +1,69 @@
1
+ import {
2
+ DEFAULT_PPR_DAMPING,
3
+ DEFAULT_PPR_ITERATIONS,
4
+ buildGraphFromMemories,
5
+ queryGraph
6
+ } from "./chunk-64NJRYU2.js";
7
+
8
+ // src/graph-recall.ts
9
+ function runGraphRecall(config, options) {
10
+ if (!config.recallGraphEnabled) {
11
+ return {
12
+ ran: false,
13
+ results: [],
14
+ graph: null,
15
+ reason: "disabled",
16
+ iterations: 0,
17
+ converged: true
18
+ };
19
+ }
20
+ const topK = typeof config.recallGraphTopK === "number" && Number.isFinite(config.recallGraphTopK) ? config.recallGraphTopK : 50;
21
+ if (topK <= 0) {
22
+ return {
23
+ ran: false,
24
+ results: [],
25
+ graph: null,
26
+ reason: "topk-zero",
27
+ iterations: 0,
28
+ converged: true
29
+ };
30
+ }
31
+ if (options.memories.length === 0) {
32
+ return {
33
+ ran: false,
34
+ results: [],
35
+ graph: null,
36
+ reason: "empty-input",
37
+ iterations: 0,
38
+ converged: true
39
+ };
40
+ }
41
+ const graph = buildGraphFromMemories(options.memories);
42
+ const damping = typeof config.recallGraphDamping === "number" && Number.isFinite(config.recallGraphDamping) ? config.recallGraphDamping : DEFAULT_PPR_DAMPING;
43
+ const iterations = typeof config.recallGraphIterations === "number" && Number.isFinite(config.recallGraphIterations) ? config.recallGraphIterations : DEFAULT_PPR_ITERATIONS;
44
+ const ppr = queryGraph(graph, options.seedIds, {
45
+ damping,
46
+ iterations,
47
+ seedWeights: options.seedWeights
48
+ });
49
+ const memoryResults = [];
50
+ for (const node of ppr.rankedNodes) {
51
+ const graphNode = graph.nodes.get(node.id);
52
+ if (!graphNode || graphNode.type !== "memory") continue;
53
+ memoryResults.push({ id: node.id, score: node.score });
54
+ if (memoryResults.length >= topK) break;
55
+ }
56
+ return {
57
+ ran: true,
58
+ results: memoryResults,
59
+ graph,
60
+ reason: "ran",
61
+ iterations: ppr.iterations,
62
+ converged: ppr.converged
63
+ };
64
+ }
65
+
66
+ export {
67
+ runGraphRecall
68
+ };
69
+ //# sourceMappingURL=chunk-DGVM5SFL.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/graph-recall.ts"],"sourcesContent":["/**\n * Graph-based retrieval integration (issue #559 PR 4 of 5).\n *\n * Pure helper that composes `extractGraphEdges` (PR 2) and `queryGraph`\n * (PR 3) into a single retrieval surface. Operators opt in via the\n * `recallGraphEnabled` config flag; until the `retrieval-graph` bench in\n * PR 5 justifies flipping the default, this tier ships disabled.\n *\n * Kept as a pure function so the orchestrator can call it with whatever\n * candidate pool it has (hot cache, recent window, QMD first-pass, etc.)\n * without forcing a specific storage contract on this module.\n */\n\nimport {\n DEFAULT_PPR_DAMPING,\n DEFAULT_PPR_ITERATIONS,\n type MemoryEdgeSource,\n type RemnicGraph,\n buildGraphFromMemories,\n queryGraph,\n} from \"./graph-retrieval.js\";\n\n/**\n * Subset of `PluginConfig` that governs the graph retrieval tier. Kept\n * as a local interface so this module does not pull in the full\n * `PluginConfig` import — `orchestrator.ts` can pass the fields directly.\n */\nexport interface GraphRecallConfig {\n /** Master enable flag. When false, `runGraphRecall` is a no-op. */\n recallGraphEnabled: boolean;\n /** PPR damping factor (default 0.85). */\n recallGraphDamping: number;\n /** PPR power-iteration cap (default 20). */\n recallGraphIterations: number;\n /**\n * Max memories the graph tier returns. `0` disables the tier's\n * contribution without touching `recallGraphEnabled`.\n */\n recallGraphTopK: number;\n}\n\n/** Per-invocation options for `runGraphRecall`. */\nexport interface GraphRecallOptions {\n /**\n * Candidate memories to build the graph from. Typically the caller's\n * recall candidate pool (hot cache + QMD first-pass). The extractor\n * reads only the fields declared on `MemoryEdgeSource` — callers can\n * safely pass richer memory objects.\n */\n memories: readonly MemoryEdgeSource[];\n /**\n * Seed memory / entity ids produced by the query-to-graph matcher.\n * Typically the ids of the top QMD hits plus any entity-exact matches.\n * If empty, PPR falls back to a uniform distribution over graph nodes.\n */\n seedIds: readonly string[];\n /**\n * Optional per-seed weights. When provided, PPR starts from the\n * weighted distribution instead of uniform-over-seeds.\n */\n seedWeights?: ReadonlyMap<string, number> | Readonly<Record<string, number>>;\n}\n\n/** A single result from the graph tier. */\nexport interface GraphRecallResult {\n /** Memory id (the `to` of the highest-scoring `memory`-typed node). */\n id: string;\n /** PPR score in [0, 1]. Higher is better. */\n score: number;\n}\n\n/** The full shape returned by `runGraphRecall`. */\nexport interface GraphRecallRun {\n /**\n * Whether the graph tier actually ran. `false` when `recallGraphEnabled`\n * is `false` or `recallGraphTopK <= 0` — in both cases `results` is `[]`\n * and `reason` indicates which gate short-circuited.\n */\n ran: boolean;\n /**\n * Memory-typed ranked results. Entity / agent nodes are filtered out\n * because the orchestrator merges this list with memory-typed QMD\n * results via MMR.\n */\n results: GraphRecallResult[];\n /** The graph that was built (or `null` if the tier did not run). */\n graph: RemnicGraph | null;\n /** Debugging tag for tier-explain surfaces. */\n reason: \"ran\" | \"disabled\" | \"topk-zero\" | \"empty-input\";\n /** Number of power-iteration rounds that executed. */\n iterations: number;\n /** Whether PPR's L1 delta fell below tolerance before the iter cap. */\n converged: boolean;\n}\n\n/**\n * Pure graph retrieval run.\n *\n * 1. Short-circuits to `{ ran: false }` when the feature flag is off,\n * `topK <= 0`, or the memory pool is empty. No graph is built, no\n * PPR runs — this preserves the zero-cost guarantee for\n * `recallGraphEnabled: false` (the default).\n * 2. Otherwise builds the retrieval graph from the candidate pool via\n * `buildGraphFromMemories` (PR 2 extractor).\n * 3. Runs Personalized PageRank via `queryGraph` (PR 3).\n * 4. Projects ranked nodes to memory-typed ids only — entity and agent\n * nodes never appear in the recall result set.\n */\nexport function runGraphRecall(\n config: GraphRecallConfig,\n options: GraphRecallOptions,\n): GraphRecallRun {\n if (!config.recallGraphEnabled) {\n return {\n ran: false,\n results: [],\n graph: null,\n reason: \"disabled\",\n iterations: 0,\n converged: true,\n };\n }\n\n // `isFinite` guard on `topK` mirrors the checks on `damping` and\n // `iterations` below. Without it a `NaN` topK would pass the typeof\n // check, then `NaN <= 0` is false (bypassing the short-circuit) and\n // the downstream `memoryResults.length >= topK` never triggers.\n const topK =\n typeof config.recallGraphTopK === \"number\" && Number.isFinite(config.recallGraphTopK)\n ? config.recallGraphTopK\n : 50;\n if (topK <= 0) {\n return {\n ran: false,\n results: [],\n graph: null,\n reason: \"topk-zero\",\n iterations: 0,\n converged: true,\n };\n }\n\n if (options.memories.length === 0) {\n return {\n ran: false,\n results: [],\n graph: null,\n reason: \"empty-input\",\n iterations: 0,\n converged: true,\n };\n }\n\n const graph = buildGraphFromMemories(options.memories);\n\n const damping =\n typeof config.recallGraphDamping === \"number\" && Number.isFinite(config.recallGraphDamping)\n ? config.recallGraphDamping\n : DEFAULT_PPR_DAMPING;\n const iterations =\n typeof config.recallGraphIterations === \"number\" && Number.isFinite(config.recallGraphIterations)\n ? config.recallGraphIterations\n : DEFAULT_PPR_ITERATIONS;\n\n // PPR runs without a topK so we can post-filter non-memory nodes without\n // the trim dropping memory-typed results. We apply topK after projection.\n const ppr = queryGraph(graph, options.seedIds, {\n damping,\n iterations,\n seedWeights: options.seedWeights,\n });\n\n const memoryResults: GraphRecallResult[] = [];\n for (const node of ppr.rankedNodes) {\n const graphNode = graph.nodes.get(node.id);\n if (!graphNode || graphNode.type !== \"memory\") continue;\n memoryResults.push({ id: node.id, score: node.score });\n if (memoryResults.length >= topK) break;\n }\n\n return {\n ran: true,\n results: memoryResults,\n graph,\n reason: \"ran\",\n iterations: ppr.iterations,\n converged: ppr.converged,\n };\n}\n"],"mappings":";;;;;;;;AA4GO,SAAS,eACd,QACA,SACgB;AAChB,MAAI,CAAC,OAAO,oBAAoB;AAC9B,WAAO;AAAA,MACL,KAAK;AAAA,MACL,SAAS,CAAC;AAAA,MACV,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,YAAY;AAAA,MACZ,WAAW;AAAA,IACb;AAAA,EACF;AAMA,QAAM,OACJ,OAAO,OAAO,oBAAoB,YAAY,OAAO,SAAS,OAAO,eAAe,IAChF,OAAO,kBACP;AACN,MAAI,QAAQ,GAAG;AACb,WAAO;AAAA,MACL,KAAK;AAAA,MACL,SAAS,CAAC;AAAA,MACV,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,YAAY;AAAA,MACZ,WAAW;AAAA,IACb;AAAA,EACF;AAEA,MAAI,QAAQ,SAAS,WAAW,GAAG;AACjC,WAAO;AAAA,MACL,KAAK;AAAA,MACL,SAAS,CAAC;AAAA,MACV,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,YAAY;AAAA,MACZ,WAAW;AAAA,IACb;AAAA,EACF;AAEA,QAAM,QAAQ,uBAAuB,QAAQ,QAAQ;AAErD,QAAM,UACJ,OAAO,OAAO,uBAAuB,YAAY,OAAO,SAAS,OAAO,kBAAkB,IACtF,OAAO,qBACP;AACN,QAAM,aACJ,OAAO,OAAO,0BAA0B,YAAY,OAAO,SAAS,OAAO,qBAAqB,IAC5F,OAAO,wBACP;AAIN,QAAM,MAAM,WAAW,OAAO,QAAQ,SAAS;AAAA,IAC7C;AAAA,IACA;AAAA,IACA,aAAa,QAAQ;AAAA,EACvB,CAAC;AAED,QAAM,gBAAqC,CAAC;AAC5C,aAAW,QAAQ,IAAI,aAAa;AAClC,UAAM,YAAY,MAAM,MAAM,IAAI,KAAK,EAAE;AACzC,QAAI,CAAC,aAAa,UAAU,SAAS,SAAU;AAC/C,kBAAc,KAAK,EAAE,IAAI,KAAK,IAAI,OAAO,KAAK,MAAM,CAAC;AACrD,QAAI,cAAc,UAAU,KAAM;AAAA,EACpC;AAEA,SAAO;AAAA,IACL,KAAK;AAAA,IACL,SAAS;AAAA,IACT;AAAA,IACA,QAAQ;AAAA,IACR,YAAY,IAAI;AAAA,IAChB,WAAW,IAAI;AAAA,EACjB;AACF;","names":[]}
@@ -6,7 +6,10 @@ import {
6
6
  ProactiveExtractionResultSchema,
7
7
  ProactiveQuestionsResultSchema,
8
8
  buildProfileConsolidationResultSchema
9
- } from "./chunk-UEYA6UC7.js";
9
+ } from "./chunk-NZLQTHS5.js";
10
+ import {
11
+ normalizeReasoningTrace
12
+ } from "./chunk-54V4BZWP.js";
10
13
  import {
11
14
  ProfilingCollector
12
15
  } from "./chunk-NBNN5GOB.js";
@@ -15,18 +18,18 @@ import {
15
18
  } from "./chunk-FEMOX5AD.js";
16
19
  import {
17
20
  LocalLlmClient
18
- } from "./chunk-JL2PU6AI.js";
21
+ } from "./chunk-R2XRID2N.js";
22
+ import {
23
+ delinearize
24
+ } from "./chunk-VEWZZM3H.js";
19
25
  import {
20
26
  buildExtensionsFooterForSummary,
21
27
  formatDaySummaryMemories,
22
28
  loadDaySummaryPrompt
23
29
  } from "./chunk-GZCUW5IC.js";
24
- import {
25
- delinearize
26
- } from "./chunk-VEWZZM3H.js";
27
30
  import {
28
31
  FallbackLlmClient
29
- } from "./chunk-44ICJRF3.js";
32
+ } from "./chunk-AYXIPSZO.js";
30
33
  import {
31
34
  buildChatCompletionTokenLimit,
32
35
  shouldAssumeOpenAiChatCompletions
@@ -50,6 +53,22 @@ import {
50
53
  // src/extraction.ts
51
54
  import OpenAI from "openai";
52
55
  var PROACTIVE_MIN_CONFIDENCE = 0.8;
56
+ var CONSOLIDATION_RESPONSE_SCHEMA = `{
57
+ "items": [
58
+ {
59
+ "existingId": "id",
60
+ "action": "ADD",
61
+ "mergeWith": "optional-existing-id",
62
+ "updatedContent": "optional replacement content",
63
+ "reason": "brief reason for this action"
64
+ }
65
+ ],
66
+ "profileUpdates": ["optional profile update"],
67
+ "entityUpdates": [{"name": "person-jane-doe", "type": "person", "facts": ["Now leads the backend team", "Recently migrated the user service to TypeScript"]}]
68
+ }`;
69
+ function isPlainRecord(value) {
70
+ return typeof value === "object" && value !== null && !Array.isArray(value);
71
+ }
53
72
  function normalizeQuestion(question) {
54
73
  const priority = Number.isFinite(question.priority) ? Math.max(0, Math.min(1, question.priority)) : 0.5;
55
74
  return {
@@ -168,7 +187,11 @@ var ExtractionEngine = class {
168
187
  structuredAttributes: f?.structuredAttributes && typeof f.structuredAttributes === "object" && !Array.isArray(f.structuredAttributes) ? Object.fromEntries(
169
188
  Object.entries(f.structuredAttributes).filter(([k, v]) => typeof k === "string" && typeof v === "string")
170
189
  ) : void 0,
171
- procedureSteps: Array.isArray(f?.procedureSteps) ? normalizeProcedureSteps(f.procedureSteps) : void 0
190
+ procedureSteps: Array.isArray(f?.procedureSteps) ? normalizeProcedureSteps(f.procedureSteps) : void 0,
191
+ reasoningTrace: (() => {
192
+ const candidate = f?.reasoningTrace && typeof f.reasoningTrace === "object" && !Array.isArray(f.reasoningTrace) ? f.reasoningTrace : f?.reasoning_trace && typeof f.reasoning_trace === "object" && !Array.isArray(f.reasoning_trace) ? f.reasoning_trace : null;
193
+ return candidate ? normalizeReasoningTrace(candidate) ?? void 0 : void 0;
194
+ })()
172
195
  })).filter((f) => f.content.length > 0) : [];
173
196
  const questions = Array.isArray(parsed?.questions) ? parsed.questions.map((q) => {
174
197
  if (typeof q === "string") return { question: q, context: "", priority: 0.5 };
@@ -195,16 +218,32 @@ var ExtractionEngine = class {
195
218
  };
196
219
  }
197
220
  normalizeEntityUpdate(entity) {
221
+ const rawUpdates = isPlainRecord(entity?.updates) ? entity.updates : null;
222
+ const directFacts = Array.isArray(entity?.facts) ? entity.facts.filter((fact) => typeof fact === "string").map((fact) => fact.trim()).filter((fact) => fact.length > 0) : [];
223
+ const updateFacts = rawUpdates && Array.isArray(rawUpdates.facts) ? rawUpdates.facts.filter((fact) => typeof fact === "string").map((fact) => fact.trim()).filter((fact) => fact.length > 0) : [];
224
+ const scalarUpdateFacts = rawUpdates ? Object.keys(rawUpdates).sort((a, b) => a.localeCompare(b)).filter((key) => !["facts", "name", "promptedByQuestion", "structuredSections", "type"].includes(key)).flatMap((key) => {
225
+ const value = rawUpdates[key];
226
+ if (typeof value === "string" && value.trim().length > 0) {
227
+ return [`${key}: ${value.trim()}`];
228
+ }
229
+ if (typeof value === "number" || typeof value === "boolean") {
230
+ return [`${key}: ${String(value)}`];
231
+ }
232
+ return [];
233
+ }) : [];
234
+ const structuredSectionsSource = Array.isArray(entity?.structuredSections) ? entity.structuredSections : Array.isArray(rawUpdates?.structuredSections) ? rawUpdates.structuredSections : [];
235
+ const name = typeof entity?.name === "string" ? entity.name.trim() : typeof entity?.entityId === "string" ? entity.entityId.trim() : typeof rawUpdates?.name === "string" ? rawUpdates.name.trim() : "";
236
+ const type = typeof entity?.type === "string" && entity.type.trim().length > 0 ? entity.type.trim() : typeof rawUpdates?.type === "string" && rawUpdates.type.trim().length > 0 ? rawUpdates.type.trim() : "other";
198
237
  return {
199
- name: typeof entity?.name === "string" ? entity.name : "",
200
- type: typeof entity?.type === "string" ? entity.type : "other",
201
- facts: Array.isArray(entity?.facts) ? entity.facts.filter((fact) => typeof fact === "string") : [],
202
- structuredSections: Array.isArray(entity?.structuredSections) ? entity.structuredSections.map((section) => ({
238
+ name,
239
+ type,
240
+ facts: [...directFacts, ...updateFacts, ...scalarUpdateFacts],
241
+ structuredSections: structuredSectionsSource.length > 0 ? structuredSectionsSource.map((section) => ({
203
242
  key: typeof section?.key === "string" ? section.key.trim() : "",
204
243
  title: typeof section?.title === "string" ? section.title.trim() : "",
205
244
  facts: Array.isArray(section?.facts) ? section.facts.filter((fact) => typeof fact === "string").map((fact) => fact.trim()).filter((fact) => fact.length > 0) : []
206
245
  })).filter((section) => section.key.length > 0 && section.title.length > 0 && section.facts.length > 0) : void 0,
207
- promptedByQuestion: typeof entity?.promptedByQuestion === "string" ? entity.promptedByQuestion : void 0
246
+ promptedByQuestion: typeof entity?.promptedByQuestion === "string" ? entity.promptedByQuestion : typeof rawUpdates?.promptedByQuestion === "string" ? rawUpdates.promptedByQuestion : void 0
208
247
  };
209
248
  }
210
249
  parseJsonObject(content) {
@@ -264,15 +303,36 @@ var ExtractionEngine = class {
264
303
  return normalized.summary.length > 0 ? normalized : null;
265
304
  }
266
305
  sanitizeConsolidationResult(result) {
267
- const items = result.items.map((item) => {
268
- if (!item.updatedContent) return item;
269
- const sanitized = sanitizeMemoryContent(item.updatedContent);
306
+ const items = [];
307
+ for (const item of Array.isArray(result.items) ? result.items : []) {
308
+ const rawAction = typeof item?.action === "string" ? item.action.toUpperCase() : "SKIP";
309
+ const action = rawAction === "ADD" || rawAction === "MERGE" || rawAction === "UPDATE" || rawAction === "INVALIDATE" || rawAction === "SKIP" ? rawAction : "SKIP";
310
+ const existingId = typeof item?.existingId === "string" ? item.existingId.trim() : typeof item?.newMemoryId === "string" ? item.newMemoryId.trim() : typeof item?.memoryId === "string" ? item.memoryId.trim() : "";
311
+ if (!existingId) continue;
312
+ const mergeWith = typeof item?.mergeWith === "string" ? item.mergeWith : void 0;
313
+ const reason = typeof item?.reason === "string" ? item.reason : "";
314
+ const rawUpdatedContent = typeof item?.updatedContent === "string" ? item.updatedContent : void 0;
315
+ if (!rawUpdatedContent) {
316
+ items.push({ existingId, action, mergeWith, updatedContent: void 0, reason });
317
+ continue;
318
+ }
319
+ const sanitized = sanitizeMemoryContent(rawUpdatedContent);
270
320
  if (!sanitized.clean) {
271
- log.warn(`consolidation item sanitized (${item.existingId}); violations=${sanitized.violations.join(", ")}`);
321
+ log.warn(`consolidation item sanitized (${existingId}); violations=${sanitized.violations.join(", ")}`);
272
322
  }
273
- return { ...item, updatedContent: sanitized.text };
274
- });
275
- return { ...result, items };
323
+ items.push({
324
+ existingId,
325
+ action,
326
+ mergeWith,
327
+ updatedContent: sanitized.text,
328
+ reason
329
+ });
330
+ }
331
+ const profileUpdates = (Array.isArray(result.profileUpdates) ? result.profileUpdates : []).map(
332
+ (update) => typeof update === "string" ? update.trim() : typeof update?.content === "string" ? update.content.trim() : ""
333
+ ).filter((update) => update.length > 0);
334
+ const entityUpdates = (Array.isArray(result.entityUpdates) ? result.entityUpdates : []).map((entity) => this.normalizeEntityUpdate(entity)).filter((entity) => entity.name.length > 0);
335
+ return { items, profileUpdates, entityUpdates };
276
336
  }
277
337
  async applyProactiveQuestionPass(conversation, base) {
278
338
  if (!this.config.proactiveExtractionEnabled) return base;
@@ -762,8 +822,16 @@ var ExtractionEngine = class {
762
822
  log.debug(
763
823
  `extracted ${result.facts.length} facts, ${result.entities.length} entities, ${(result.questions ?? []).length} questions via fallback (${detailed.modelUsed})`
764
824
  );
825
+ const normalizedFacts = result.facts.map((f) => {
826
+ if (!f?.reasoningTrace) return f;
827
+ return {
828
+ ...f,
829
+ reasoningTrace: normalizeReasoningTrace(f.reasoningTrace) ?? void 0
830
+ };
831
+ });
765
832
  const sanitized = this.sanitizeExtractionResult({
766
833
  ...result,
834
+ facts: normalizedFacts,
767
835
  questions: result.questions ?? [],
768
836
  identityReflection: result.identityReflection ?? void 0
769
837
  }, messageTimestamp);
@@ -831,6 +899,7 @@ Memory categories \u2014 use the MOST SPECIFIC category that fits:
831
899
  - skill: Demonstrated capabilities
832
900
  - rule: Explicit operational rules or constraints
833
901
  - procedure: Repeatable workflows \u2014 use when the user describes a multi-step play (\u22652 ordered steps). Put the human-readable trigger/context in "content" (e.g. "When you deploy\u2026") and list steps in "procedureSteps" as [{"order":1,"intent":"\u2026"}, \u2026] mirroring the gateway extraction schema.
902
+ - reasoning_trace: Stored solution chains \u2014 use when the user narrates HOW they solved a specific problem step-by-step ("here's how I figured out\u2026", "the debugging went like this\u2026"). Put a short title in "content" (e.g. "How I debugged the staging latency spike") and the chain in "reasoningTrace": {"steps":[{"order":1,"description":"\u2026"}, \u2026], "finalAnswer":"\u2026", "observedOutcome":"\u2026" (optional)}. Require \u22652 ordered steps and a finalAnswer. Do NOT use for ordinary decisions (prefer "decision") or reusable workflows (prefer "procedure").
834
903
 
835
904
  IMPORTANT: Do NOT label everything as "fact". Use "decision" for architectural choices, "commitment" for deadlines/promises, "principle" for reusable rules, "correction" for when the user rejects a suggestion, etc.
836
905
 
@@ -892,7 +961,7 @@ Also generate:
892
961
 
893
962
  Output JSON:
894
963
  {
895
- "facts": [{"category": "decision", "content": "Chose PostgreSQL over MongoDB for the user service", "importance": 8, "confidence": 0.9, "structuredAttributes": {"chosen": "PostgreSQL", "rejected": "MongoDB"}}, {"category": "procedure", "content": "When you cut a hotfix release, follow the checklist", "importance": 8, "confidence": 0.9, "procedureSteps": [{"order": 1, "intent": "Branch from main and cherry-pick the fix"}, {"order": 2, "intent": "Run CI and tag the release"}]}, {"category": "commitment", "content": "Must ship v2.0 API by end of March", "importance": 10, "confidence": 1.0, "structuredAttributes": {"deadline": "end of March", "deliverable": "v2.0 API"}}, {"category": "fact", "content": "The store backend uses Redis for session caching", "importance": 6, "confidence": 0.95, "entityRef": "project-acme-store"}, {"category": "principle", "content": "Always run migrations in a transaction to avoid partial schema updates", "importance": 8, "confidence": 0.9}],
964
+ "facts": [{"category": "decision", "content": "Chose PostgreSQL over MongoDB for the user service", "importance": 8, "confidence": 0.9, "structuredAttributes": {"chosen": "PostgreSQL", "rejected": "MongoDB"}}, {"category": "procedure", "content": "When you cut a hotfix release, follow the checklist", "importance": 8, "confidence": 0.9, "procedureSteps": [{"order": 1, "intent": "Branch from main and cherry-pick the fix"}, {"order": 2, "intent": "Run CI and tag the release"}]}, {"category": "reasoning_trace", "content": "How I debugged the staging latency spike", "importance": 7, "confidence": 0.9, "reasoningTrace": {"steps": [{"order": 1, "description": "Checked CPU/memory dashboards \u2014 both were flat"}, {"order": 2, "description": "Ran a traceroute and saw retries against the cache tier"}, {"order": 3, "description": "Tailed cache-tier logs and spotted eviction storms"}], "finalAnswer": "Root cause was an undersized eviction policy on the session cache", "observedOutcome": "Increased cache size, p95 returned to baseline within 10 minutes"}}, {"category": "commitment", "content": "Must ship v2.0 API by end of March", "importance": 10, "confidence": 1.0, "structuredAttributes": {"deadline": "end of March", "deliverable": "v2.0 API"}}, {"category": "fact", "content": "The store backend uses Redis for session caching", "importance": 6, "confidence": 0.95, "entityRef": "project-acme-store"}, {"category": "principle", "content": "Always run migrations in a transaction to avoid partial schema updates", "importance": 8, "confidence": 0.9}],
896
965
  "entities": [{"name": "person-jane-doe", "type": "person", "facts": ["Works at Acme Corp", "Prefers Python over JavaScript"], "structuredSections": [{"key": "beliefs", "title": "Beliefs", "facts": ["Python is a better fit than JavaScript for backend work."]}]}, {"name": "project-acme-store", "type": "project", "facts": ["Built with Next.js", "Deployed on Vercel"]}],
897
966
  "profileUpdates": ["User prefers dark mode in all editors"],
898
967
  "questions": [{"question": "Which cloud provider hosts the staging environment?", "context": "Came up during deployment discussion", "priority": 0.5}],
@@ -969,7 +1038,7 @@ ${truncatedConversation}`;
969
1038
 
970
1039
  Respond with valid JSON matching this schema:
971
1040
  {
972
- "facts": [{"category": "decision", "content": "Chose React over Vue for the dashboard rewrite", "importance": 8, "confidence": 0.9, "tags": ["frontend"], "structuredAttributes": {"chosen": "React", "rejected": "Vue"}}, {"category": "fact", "content": "The API gateway uses rate limiting at 1000 req/min", "importance": 6, "confidence": 0.95, "tags": ["infra"], "entityRef": "project-dashboard", "structuredAttributes": {"rate_limit": "1000 req/min"}}],
1041
+ "facts": [{"category": "decision", "content": "Chose React over Vue for the dashboard rewrite", "importance": 8, "confidence": 0.9, "tags": ["frontend"], "structuredAttributes": {"chosen": "React", "rejected": "Vue"}}, {"category": "fact", "content": "The API gateway uses rate limiting at 1000 req/min", "importance": 6, "confidence": 0.95, "tags": ["infra"], "entityRef": "project-dashboard", "structuredAttributes": {"rate_limit": "1000 req/min"}}, {"category": "reasoning_trace", "content": "How I chose the dashboard rewrite framework", "confidence": 0.9, "tags": ["frontend"], "reasoningTrace": {"steps": [{"order": 1, "description": "Listed constraints: SSR needed, team mostly JS"}, {"order": 2, "description": "Ran a spike in Vue 3 \u2014 worked, but ecosystem felt thin for our needs"}, {"order": 3, "description": "Ran the same spike in React \u2014 integrated faster with Next.js"}], "finalAnswer": "Picked React with Next.js for SSR + ecosystem fit"}}],
973
1042
  "entities": [{"name": "person-sarah-chen", "type": "person", "facts": ["Leads the backend team", "Joined from Google in 2024"], "structuredSections": [{"key": "beliefs", "title": "Beliefs", "facts": ["Small teams should own whole systems."]}]}, {"name": "project-dashboard", "type": "project", "facts": ["React-based admin panel", "Deployed on AWS ECS"]}],
974
1043
  "profileUpdates": ["User prefers TypeScript over plain JavaScript"],
975
1044
  "questions": [{"question": "What database does the analytics service use?", "context": "Came up during discussion of migration plan", "priority": 0.5}],
@@ -1015,7 +1084,8 @@ Respond with valid JSON matching this schema:
1015
1084
  "moment",
1016
1085
  "skill",
1017
1086
  "rule",
1018
- "procedure"
1087
+ "procedure",
1088
+ "reasoning_trace"
1019
1089
  ]);
1020
1090
  const allowedEntityTypes = /* @__PURE__ */ new Set([
1021
1091
  "person",
@@ -1073,6 +1143,7 @@ Memory categories:
1073
1143
  - skill: Capabilities the user or agent has demonstrated (e.g., "user is proficient with Kubernetes")${this.config.causalRuleExtractionEnabled ? `
1074
1144
  - rule: Causal rules discovered through experience (format: "IF <condition> THEN <action/outcome>", e.g., "IF Shopify API returns 401 THEN the admin token is missing read_products scope")` : ""}
1075
1145
  - procedure: A reusable workflow the user wants remembered the same way across sessions. Set category to "procedure". Use "content" for a short title that includes explicit trigger phrasing (e.g. "When you deploy to production\u2026", "Whenever you ship a release\u2026"). Add "procedureSteps": an array of at least two objects {"order": number, "intent": "concrete step description"} in execution order. Optional per-step "toolCall": {"kind": "\u2026", "signature": "\u2026"}, "expectedOutcome", "optional": true.
1146
+ - reasoning_trace: A stored solution chain / chain-of-thought the user walked through to solve a problem (e.g. "Here's how I debugged the latency spike: first I checked\u2026, then I\u2026, finally I\u2026"). Set category to "reasoning_trace". Use "content" for a short title summarising the problem (e.g. "How I debugged the staging latency spike"). Add "reasoningTrace": {"steps": [{"order": number, "description": "what happened at this step"}, \u2026], "finalAnswer": "the conclusion or answer", "observedOutcome": "optional confirmation of how it played out"}. Require at least two ordered steps AND a finalAnswer. Use this category only when the user explicitly narrates their reasoning \u2014 not for ordinary decisions (use "decision") or reusable workflows (use "procedure").
1076
1147
 
1077
1148
  Rules:
1078
1149
  - Only extract genuinely NEW information worth remembering across sessions
@@ -1193,15 +1264,10 @@ Consolidate the new memories against existing ones.`
1193
1264
  );
1194
1265
  if (fallbackResult) {
1195
1266
  log.debug(`consolidation: ${fallbackResult.items.length} decisions via fallback`);
1196
- const normalizedEntityUpdates = fallbackResult.entityUpdates.map((entity) => this.normalizeEntityUpdate(entity));
1197
1267
  return this.sanitizeConsolidationResult({
1198
- items: fallbackResult.items.map((item) => ({
1199
- ...item,
1200
- mergeWith: item.mergeWith ?? void 0,
1201
- updatedContent: item.updatedContent ?? void 0
1202
- })),
1268
+ items: fallbackResult.items,
1203
1269
  profileUpdates: fallbackResult.profileUpdates,
1204
- entityUpdates: normalizedEntityUpdates
1270
+ entityUpdates: fallbackResult.entityUpdates
1205
1271
  });
1206
1272
  }
1207
1273
  if (!this.client) {
@@ -1233,19 +1299,7 @@ New memories to consolidate:
1233
1299
  ${newList}
1234
1300
 
1235
1301
  Respond with valid JSON only, matching this schema:
1236
- {
1237
- "items": [
1238
- {
1239
- "existingId": "id",
1240
- "action": "ADD",
1241
- "mergeWith": "optional-existing-id",
1242
- "updatedContent": "optional replacement content",
1243
- "reason": "brief reason for this action"
1244
- }
1245
- ],
1246
- "profileUpdates": ["optional profile update"],
1247
- "entityUpdates": [{"name": "person-jane-doe", "type": "person", "facts": ["Now leads the backend team", "Recently migrated the user service to TypeScript"]}]
1248
- }`;
1302
+ ${CONSOLIDATION_RESPONSE_SCHEMA}`;
1249
1303
  const response = await this.client.chat.completions.create({
1250
1304
  model: this.config.model,
1251
1305
  messages: [
@@ -1280,25 +1334,13 @@ Respond with valid JSON only, matching this schema:
1280
1334
  tokenUsage: cUsage ? { input: cUsage.prompt_tokens, output: cUsage.completion_tokens, total: cUsage.total_tokens } : void 0
1281
1335
  });
1282
1336
  if (parsed && Array.isArray(parsed.items)) {
1283
- const normalizedItems = parsed.items.map((item) => {
1284
- const rawAction = typeof item?.action === "string" ? item.action.toUpperCase() : "SKIP";
1285
- const action = rawAction === "ADD" || rawAction === "MERGE" || rawAction === "UPDATE" || rawAction === "INVALIDATE" || rawAction === "SKIP" ? rawAction : "SKIP";
1286
- return {
1287
- existingId: typeof item?.existingId === "string" ? item.existingId : typeof item?.newMemoryId === "string" ? item.newMemoryId : "",
1288
- action,
1289
- mergeWith: typeof item?.mergeWith === "string" ? item.mergeWith : void 0,
1290
- updatedContent: typeof item?.updatedContent === "string" ? item.updatedContent : void 0,
1291
- reason: typeof item?.reason === "string" ? item.reason : ""
1292
- };
1293
- }).filter((item) => item.existingId.length > 0);
1294
- const normalizedEntityUpdates = Array.isArray(parsed.entityUpdates) ? parsed.entityUpdates.map((entity) => this.normalizeEntityUpdate(entity)).filter((entity) => entity.name.length > 0) : [];
1295
1337
  log.debug(
1296
- `consolidation: ${normalizedItems.length} decisions`
1338
+ `consolidation: ${parsed.items.length} decisions`
1297
1339
  );
1298
1340
  return this.sanitizeConsolidationResult({
1299
- items: normalizedItems,
1300
- profileUpdates: Array.isArray(parsed.profileUpdates) ? parsed.profileUpdates.filter((update) => typeof update === "string" && update.trim().length > 0) : [],
1301
- entityUpdates: normalizedEntityUpdates
1341
+ items: parsed.items,
1342
+ profileUpdates: Array.isArray(parsed.profileUpdates) ? parsed.profileUpdates : [],
1343
+ entityUpdates: Array.isArray(parsed.entityUpdates) ? parsed.entityUpdates : []
1302
1344
  });
1303
1345
  }
1304
1346
  log.warn("consolidation returned no parsed output");
@@ -1349,13 +1391,7 @@ New memories to consolidate:
1349
1391
  ${newList}
1350
1392
 
1351
1393
  Respond with valid JSON matching this schema:
1352
- {
1353
- "items": [
1354
- {"memoryId": "id", "action": "ADD|MERGE|UPDATE|INVALIDATE|SKIP", "reason": "why", "updatedContent": "optional new content"}
1355
- ],
1356
- "profileUpdates": [{"section": "section name", "content": "new bullet"}],
1357
- "entityUpdates": [{"entityId": "id", "updates": {"field": "value"}}]
1358
- }`;
1394
+ ${CONSOLIDATION_RESPONSE_SCHEMA}`;
1359
1395
  const response = await this.localLlm.chatCompletion(
1360
1396
  [
1361
1397
  { role: "system", content: "You are a memory consolidation system. Output valid JSON only." },
@@ -2224,4 +2260,4 @@ ${memoryList}` }
2224
2260
  export {
2225
2261
  ExtractionEngine
2226
2262
  };
2227
- //# sourceMappingURL=chunk-3SV6CQHO.js.map
2263
+ //# sourceMappingURL=chunk-DIXB44VE.js.map