@remnic/core 9.57.6 → 9.58.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 (471) hide show
  1. package/dist/access-admin-ops-surface.d.ts +12 -11
  2. package/dist/access-admin-ops-surface.js +25 -24
  3. package/dist/access-authorization-probe.d.ts +12 -11
  4. package/dist/access-authorization-probe.js +26 -25
  5. package/dist/access-boundary.d.ts +12 -11
  6. package/dist/access-boundary.js +25 -24
  7. package/dist/access-cli.js +77 -76
  8. package/dist/access-cli.js.map +1 -1
  9. package/dist/access-coding-context-resolution.d.ts +1 -1
  10. package/dist/access-extraction-force-flush.d.ts +12 -11
  11. package/dist/access-extraction-force-flush.js +25 -24
  12. package/dist/access-health-types.d.ts +1 -1
  13. package/dist/access-http-lcm-compaction.d.ts +12 -11
  14. package/dist/access-http-lifecycle-flush.d.ts +12 -11
  15. package/dist/access-http-offline-stream.d.ts +12 -11
  16. package/dist/access-http-query.js +25 -24
  17. package/dist/access-http.d.ts +13 -12
  18. package/dist/access-http.js +36 -35
  19. package/dist/access-identity-continuity-surface.d.ts +10 -9
  20. package/dist/access-identity-continuity-surface.js +25 -24
  21. package/dist/access-lcm-surface.d.ts +12 -11
  22. package/dist/access-lcm-surface.js +25 -24
  23. package/dist/access-mcp.d.ts +12 -11
  24. package/dist/access-mcp.js +32 -31
  25. package/dist/access-memory-search-fanout.d.ts +2 -2
  26. package/dist/{access-namespace-preflight-Bpt-SYP0.d.ts → access-namespace-preflight-TBXz-8s_.d.ts} +1 -1
  27. package/dist/access-namespace-preflight.d.ts +3 -3
  28. package/dist/access-namespace-preflight.js +25 -24
  29. package/dist/access-observe-write-surface.d.ts +12 -11
  30. package/dist/access-observe-write-surface.js +25 -24
  31. package/dist/access-offline-manifest.d.ts +10 -9
  32. package/dist/access-operations-batch.js +27 -26
  33. package/dist/access-operations.d.ts +12 -11
  34. package/dist/access-operations.js +31 -30
  35. package/dist/access-recall-concurrency.d.ts +12 -11
  36. package/dist/access-recall-concurrency.js +25 -24
  37. package/dist/access-recall-response.d.ts +12 -11
  38. package/dist/access-recall-response.js +25 -24
  39. package/dist/access-recall-surface.d.ts +12 -11
  40. package/dist/access-recall-surface.js +25 -24
  41. package/dist/access-schema.js +3 -3
  42. package/dist/{access-service-4DO8XMq6.d.ts → access-service-BQkRul4d.d.ts} +7 -7
  43. package/dist/access-service-helpers.d.ts +12 -11
  44. package/dist/access-service.d.ts +12 -11
  45. package/dist/access-service.js +25 -24
  46. package/dist/access-surface-catalog.d.ts +12 -11
  47. package/dist/access-surface-catalog.js +27 -26
  48. package/dist/access-surface-catalog.js.map +1 -1
  49. package/dist/access-wearables-meetings-surface.d.ts +3 -3
  50. package/dist/action-confidence.d.ts +1 -1
  51. package/dist/active-memory-bridge.d.ts +1 -1
  52. package/dist/active-recall.d.ts +1 -1
  53. package/dist/active-recall.js +1 -1
  54. package/dist/ambient-provenance.d.ts +1 -1
  55. package/dist/artifact-search.d.ts +1 -1
  56. package/dist/behavior-learner.d.ts +1 -1
  57. package/dist/behavior-signals.d.ts +1 -1
  58. package/dist/bootstrap.d.ts +10 -9
  59. package/dist/briefing-window.d.ts +2 -2
  60. package/dist/briefing.d.ts +2 -2
  61. package/dist/briefing.js +10 -10
  62. package/dist/buffer-surprise-report.d.ts +1 -1
  63. package/dist/buffer-surprise.js +4 -3
  64. package/dist/buffer-turn-helpers.d.ts +1 -1
  65. package/dist/buffer.d.ts +2 -2
  66. package/dist/bulk-import/index.d.ts +3 -3
  67. package/dist/calibration.d.ts +1 -1
  68. package/dist/capabilities.d.ts +1 -1
  69. package/dist/{capsule-crypto-YO5QJ6L3.js → capsule-crypto-GWVG7LGC.js} +2 -2
  70. package/dist/{catalog-BasnO3Bw.d.ts → catalog-z1UerMbh.d.ts} +1 -1
  71. package/dist/causal-behavior.d.ts +2 -1
  72. package/dist/causal-consolidation.d.ts +1 -1
  73. package/dist/causal-consolidation.js +14 -13
  74. package/dist/causal-consolidation.js.map +1 -1
  75. package/dist/causal-trajectory-graph.d.ts +1 -1
  76. package/dist/{chunk-P6WBAR27.js → chunk-2GBFKHBZ.js} +9 -9
  77. package/dist/chunk-2GBFKHBZ.js.map +1 -0
  78. package/dist/{chunk-YTZA7U6Q.js → chunk-2VHCLHZ5.js} +3 -3
  79. package/dist/{chunk-NEJQ65NV.js → chunk-4CLE2LXB.js} +2 -2
  80. package/dist/{chunk-3VFVQDZO.js → chunk-4IIKJOYM.js} +1 -1
  81. package/dist/{chunk-3VFVQDZO.js.map → chunk-4IIKJOYM.js.map} +1 -1
  82. package/dist/{chunk-JLNBQWZ2.js → chunk-5JMAGU26.js} +7 -6
  83. package/dist/{chunk-JLNBQWZ2.js.map → chunk-5JMAGU26.js.map} +1 -1
  84. package/dist/{chunk-VDX2J7OX.js → chunk-5W7CTZ2E.js} +4 -4
  85. package/dist/{chunk-CZUHNJHX.js → chunk-6C7Q6NG3.js} +13 -13
  86. package/dist/{chunk-LMXUWAXW.js → chunk-6TBIWIAG.js} +9 -8
  87. package/dist/chunk-6TBIWIAG.js.map +1 -0
  88. package/dist/{chunk-4O6R3GPK.js → chunk-6W2ISYUV.js} +4 -4
  89. package/dist/{chunk-3AJOJOFP.js → chunk-77DAFTUU.js} +3 -3
  90. package/dist/{chunk-IKT6ZVO7.js → chunk-7DHFB5SG.js} +3 -3
  91. package/dist/{chunk-SFR7J2G6.js → chunk-7G34CBKS.js} +2 -2
  92. package/dist/{chunk-IT57AOMC.js → chunk-AH2NX6S7.js} +3 -3
  93. package/dist/{chunk-HYQJQGD3.js → chunk-AM4T6CHX.js} +49 -35
  94. package/dist/chunk-AM4T6CHX.js.map +1 -0
  95. package/dist/{chunk-Z54EM5YZ.js → chunk-B2QHVLJO.js} +60 -30
  96. package/dist/chunk-B2QHVLJO.js.map +1 -0
  97. package/dist/{chunk-FI3PIOOI.js → chunk-B4CSPFYN.js} +8 -8
  98. package/dist/{chunk-7PJARJS4.js → chunk-BPM6UHUL.js} +2 -2
  99. package/dist/{chunk-PPUTUOC3.js → chunk-BTHDQOF4.js} +5 -5
  100. package/dist/{chunk-DUHMYBVE.js → chunk-CZGOLAEW.js} +41 -41
  101. package/dist/{chunk-5PGYCWA7.js → chunk-DE6YCZEE.js} +2 -2
  102. package/dist/{chunk-JOA4Q765.js → chunk-EUYWQ7TS.js} +2 -2
  103. package/dist/{chunk-O5AFC5KM.js → chunk-EWL5DICB.js} +2 -2
  104. package/dist/{chunk-4YZCIK5E.js → chunk-EZXGJJXM.js} +2 -2
  105. package/dist/{chunk-GXIY65UH.js → chunk-FI5EAP4N.js} +2 -2
  106. package/dist/{chunk-YOKSRIFI.js → chunk-FUWS2MNJ.js} +4 -4
  107. package/dist/{chunk-6RK2X7BR.js → chunk-G3JFFUKR.js} +2 -2
  108. package/dist/{chunk-VANZKHTN.js → chunk-G5OB4XZX.js} +4 -4
  109. package/dist/{chunk-TEHPIMXD.js → chunk-HK6KYIF6.js} +2 -2
  110. package/dist/{chunk-XBGLEOID.js → chunk-I4BLRM7X.js} +9 -9
  111. package/dist/{chunk-5ZIWRMZM.js → chunk-IVKIODHO.js} +7 -6
  112. package/dist/chunk-IVKIODHO.js.map +1 -0
  113. package/dist/{chunk-6SX4MY5Q.js → chunk-J65AKOD6.js} +2 -2
  114. package/dist/{chunk-BPWZDHFM.js → chunk-J6QIAYNR.js} +2 -2
  115. package/dist/{chunk-XL3TATMJ.js → chunk-JHMAYABJ.js} +4 -4
  116. package/dist/{chunk-F7M36OG2.js → chunk-JNWISMYG.js} +10 -10
  117. package/dist/{chunk-VYIXCMUP.js → chunk-KBIIE57I.js} +3 -3
  118. package/dist/{chunk-QKPDN325.js → chunk-KF75ANST.js} +6 -3
  119. package/dist/chunk-KF75ANST.js.map +1 -0
  120. package/dist/{chunk-24PNX2GF.js → chunk-KXYMU4IN.js} +3 -3
  121. package/dist/{chunk-FKTD7IVS.js → chunk-L3JVAM5Q.js} +2 -2
  122. package/dist/{chunk-5QHDXMXP.js → chunk-LLZKOSGM.js} +1 -1
  123. package/dist/{chunk-5QHDXMXP.js.map → chunk-LLZKOSGM.js.map} +1 -1
  124. package/dist/{chunk-LIF6DO6U.js → chunk-MG7GI4TT.js} +2 -2
  125. package/dist/{chunk-RUZM6Z52.js → chunk-MMZK2DWN.js} +1 -1
  126. package/dist/{chunk-NTJ6TY44.js → chunk-ODAE36PG.js} +3 -3
  127. package/dist/{chunk-QFN4U7AG.js → chunk-ODZKV2F7.js} +3 -3
  128. package/dist/{chunk-QFN4U7AG.js.map → chunk-ODZKV2F7.js.map} +1 -1
  129. package/dist/{chunk-YLHM5BQQ.js → chunk-OLX2HJFW.js} +4 -4
  130. package/dist/{chunk-DNC4A7RS.js → chunk-OU7RWWW6.js} +2 -2
  131. package/dist/{chunk-L3LRWDJW.js → chunk-OV36CROW.js} +3 -3
  132. package/dist/{chunk-BXLOS5AJ.js → chunk-OWHERGF2.js} +2 -2
  133. package/dist/{chunk-CSV34HIW.js → chunk-P5OYNV7B.js} +2 -2
  134. package/dist/{chunk-SNW3YRO6.js → chunk-P6GOYLHN.js} +2 -2
  135. package/dist/{chunk-REVMY7RA.js → chunk-PFPNUFKZ.js} +109 -59
  136. package/dist/chunk-PFPNUFKZ.js.map +1 -0
  137. package/dist/{chunk-USGKHF36.js → chunk-QUJC22CT.js} +2 -2
  138. package/dist/{chunk-BVRREV3N.js → chunk-R7IV3EUJ.js} +2 -2
  139. package/dist/{chunk-KSXJOC4F.js → chunk-RDFF2NAU.js} +18 -8
  140. package/dist/chunk-RDFF2NAU.js.map +1 -0
  141. package/dist/{chunk-D4LVHPOA.js → chunk-RXN7Z2V5.js} +3 -3
  142. package/dist/{chunk-7VSHDLJQ.js → chunk-T2VJ2EO5.js} +6 -6
  143. package/dist/chunk-TOTT6S2V.js +20 -0
  144. package/dist/chunk-TOTT6S2V.js.map +1 -0
  145. package/dist/{chunk-U7BMN33M.js → chunk-UBSM6TZP.js} +2 -2
  146. package/dist/{chunk-DQEMWVMT.js → chunk-UVYI6VIX.js} +1 -1
  147. package/dist/{chunk-U7XLOSG2.js → chunk-VIM6QH4D.js} +2 -2
  148. package/dist/{chunk-LJJWFV2Q.js → chunk-VJXSAJFW.js} +2 -2
  149. package/dist/{chunk-HZC3OECF.js → chunk-VOKK4WCG.js} +2 -2
  150. package/dist/{chunk-LTAVSOZW.js → chunk-WVP2I6C5.js} +5 -4
  151. package/dist/chunk-WVP2I6C5.js.map +1 -0
  152. package/dist/{chunk-LRDP2AC2.js → chunk-YBCZTBN2.js} +2 -2
  153. package/dist/{chunk-USAUBGBC.js → chunk-YKZFX2HQ.js} +2 -2
  154. package/dist/{chunk-I2GIYTRU.js → chunk-ZOGOOSB3.js} +2 -2
  155. package/dist/{chunk-PBGWYM3H.js → chunk-ZPUIRTIZ.js} +27 -15
  156. package/dist/chunk-ZPUIRTIZ.js.map +1 -0
  157. package/dist/chunking.js +2 -1
  158. package/dist/{cli-CQdYilcx.d.ts → cli-DsyVvOFP.d.ts} +5 -5
  159. package/dist/cli.d.ts +14 -13
  160. package/dist/cli.js +57 -56
  161. package/dist/coding/pre-action-gate.d.ts +1 -1
  162. package/dist/compounding/engine.d.ts +2 -2
  163. package/dist/compounding/engine.js +10 -10
  164. package/dist/compounding/preference-consolidator.d.ts +2 -1
  165. package/dist/compression-optimizer.d.ts +1 -1
  166. package/dist/config.d.ts +1 -1
  167. package/dist/config.js +1 -1
  168. package/dist/connectors/codex-materialize-runner.d.ts +1 -1
  169. package/dist/connectors/codex-materialize-runner.js +12 -11
  170. package/dist/connectors/codex-materialize.d.ts +7 -12
  171. package/dist/connectors/codex-materialize.js +2 -1
  172. package/dist/connectors/index.d.ts +1 -1
  173. package/dist/connectors/index.js +12 -11
  174. package/dist/consolidation-provenance-check.d.ts +2 -2
  175. package/dist/consolidation-undo.d.ts +2 -2
  176. package/dist/contradiction/index.d.ts +2 -2
  177. package/dist/contradiction/index.js +4 -4
  178. package/dist/converge-config.d.ts +1 -1
  179. package/dist/convergence-refresh.d.ts +2 -2
  180. package/dist/convergence-refresh.js +12 -12
  181. package/dist/conversation-index/backend.d.ts +1 -1
  182. package/dist/conversation-index/chunker.d.ts +1 -1
  183. package/dist/conversation-index/faiss-adapter.d.ts +1 -1
  184. package/dist/conversation-index/indexer.d.ts +1 -1
  185. package/dist/conversation-index/search.d.ts +1 -1
  186. package/dist/corpus-watermark.d.ts +1 -1
  187. package/dist/corpus-watermark.js +12 -12
  188. package/dist/day-summary.d.ts +1 -1
  189. package/dist/day-summary.js +3 -2
  190. package/dist/delinearize.d.ts +1 -1
  191. package/dist/dependency-propagation-config.d.ts +1 -1
  192. package/dist/{dependency-propagation-delivery-C_K_AHcv.d.ts → dependency-propagation-delivery-DdwSc1qh.d.ts} +1 -1
  193. package/dist/direct-answer-wiring.d.ts +1 -1
  194. package/dist/direct-answer.d.ts +1 -1
  195. package/dist/embedding-fallback.d.ts +1 -1
  196. package/dist/enrichment/index.d.ts +1 -1
  197. package/dist/entity-retrieval.d.ts +2 -2
  198. package/dist/entity-retrieval.js +10 -10
  199. package/dist/entity-schema.d.ts +1 -1
  200. package/dist/explicit-capture.d.ts +10 -9
  201. package/dist/external-wiki-access.d.ts +12 -11
  202. package/dist/external-wiki-access.js +26 -25
  203. package/dist/external-wiki-collection-registration.d.ts +1 -1
  204. package/dist/external-wiki-collection.d.ts +1 -1
  205. package/dist/external-wiki-mcp-tools.d.ts +12 -11
  206. package/dist/extraction-error-classification.d.ts +1 -1
  207. package/dist/extraction-faithfulness.d.ts +1 -1
  208. package/dist/extraction-judge-telemetry.d.ts +1 -1
  209. package/dist/extraction-judge-training.d.ts +1 -1
  210. package/dist/extraction-judge.d.ts +1 -1
  211. package/dist/extraction-liveness.d.ts +1 -1
  212. package/dist/extraction-normalization.d.ts +1 -1
  213. package/dist/extraction-prompt.d.ts +1 -1
  214. package/dist/extraction-source-grounding-rules.d.ts +1 -1
  215. package/dist/extraction-source-grounding.d.ts +1 -1
  216. package/dist/extraction.d.ts +1 -1
  217. package/dist/extraction.js +16 -15
  218. package/dist/fallback-llm.d.ts +1 -1
  219. package/dist/graph-dashboard-diff.d.ts +1 -1
  220. package/dist/graph-dashboard-key.d.ts +1 -1
  221. package/dist/graph-dashboard-parser.d.ts +1 -1
  222. package/dist/graph-edge-reinforcement.d.ts +1 -1
  223. package/dist/graph-path-reconstruction.d.ts +1 -1
  224. package/dist/graph-path-scoring.d.ts +1 -1
  225. package/dist/graph-snapshot.d.ts +1 -1
  226. package/dist/graph.d.ts +1 -1
  227. package/dist/harmonic-retrieval.js +1 -1
  228. package/dist/importance.d.ts +1 -1
  229. package/dist/importers/index.d.ts +1 -1
  230. package/dist/in-flight-reads.d.ts +1 -1
  231. package/dist/index.d.ts +22 -21
  232. package/dist/index.js +81 -80
  233. package/dist/intent.d.ts +1 -1
  234. package/dist/lcm/archive.d.ts +3 -2
  235. package/dist/lcm/archive.js +2 -1
  236. package/dist/lcm/engine.d.ts +2 -1
  237. package/dist/lcm/engine.js +5 -4
  238. package/dist/lcm/index.d.ts +2 -1
  239. package/dist/lcm/index.js +7 -6
  240. package/dist/lcm/recall.d.ts +1 -0
  241. package/dist/lcm/summarizer.d.ts +1 -0
  242. package/dist/lcm/summarizer.js +3 -2
  243. package/dist/lcm/tools.d.ts +2 -1
  244. package/dist/lifecycle.d.ts +1 -1
  245. package/dist/live-connectors-runner.d.ts +1 -1
  246. package/dist/local-llm.d.ts +2 -3
  247. package/dist/local-llm.js +2 -1
  248. package/dist/local-model-endpoint.d.ts +1 -1
  249. package/dist/maintenance/memory-governance.d.ts +1 -1
  250. package/dist/maintenance/memory-governance.js +10 -10
  251. package/dist/maintenance/rebuild-memory-lifecycle-ledger.d.ts +2 -2
  252. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +10 -10
  253. package/dist/maintenance/rebuild-memory-projection.d.ts +2 -2
  254. package/dist/maintenance/rebuild-memory-projection.js +11 -11
  255. package/dist/{maintenance-DdP931i9.d.ts → maintenance-DvkiF7fm.d.ts} +3 -3
  256. package/dist/mcp-memory-inspector-app.d.ts +12 -11
  257. package/dist/memory-action-policy.d.ts +1 -1
  258. package/dist/memory-cache.d.ts +1 -1
  259. package/dist/memory-lifecycle-ledger-utils.d.ts +1 -1
  260. package/dist/memory-projection-store.d.ts +1 -1
  261. package/dist/memory-provenance.d.ts +1 -1
  262. package/dist/memory-snapshot.d.ts +1 -1
  263. package/dist/memory-worth-outcomes.d.ts +2 -2
  264. package/dist/models-json.d.ts +1 -1
  265. package/dist/namespaces/migrate.d.ts +3 -3
  266. package/dist/namespaces/migrate.js +11 -11
  267. package/dist/namespaces/principal.d.ts +1 -1
  268. package/dist/namespaces/search.d.ts +1 -1
  269. package/dist/namespaces/search.js +7 -7
  270. package/dist/namespaces/storage.d.ts +3 -3
  271. package/dist/namespaces/storage.js +10 -10
  272. package/dist/native-knowledge.d.ts +1 -1
  273. package/dist/offline-sync-impression-drain.d.ts +1 -1
  274. package/dist/operator-doctor-corpus.d.ts +1 -1
  275. package/dist/operator-doctor-corpus.js +13 -13
  276. package/dist/operator-doctor-replica.d.ts +1 -1
  277. package/dist/operator-toolkit.d.ts +3 -3
  278. package/dist/operator-toolkit.js +18 -18
  279. package/dist/orchestration/compression-guideline-coordinator.d.ts +2 -2
  280. package/dist/orchestration/maintenance.d.ts +4 -4
  281. package/dist/orchestration/maintenance.js +15 -15
  282. package/dist/{orchestrator-DxUWX7gs.d.ts → orchestrator-CXPIzs3D.d.ts} +9 -9
  283. package/dist/orchestrator.d.ts +10 -9
  284. package/dist/orchestrator.js +77 -76
  285. package/dist/patterns-cli.d.ts +1 -1
  286. package/dist/{pipeline-BKUMU1vU.d.ts → pipeline-BuYWU9rt.d.ts} +1 -1
  287. package/dist/policy-runtime.d.ts +1 -1
  288. package/dist/proactive-contention.d.ts +1 -1
  289. package/dist/provenance.d.ts +1 -1
  290. package/dist/{public-http-Dw0bJhcC.d.ts → public-http-m9x1Qyzn.d.ts} +1 -1
  291. package/dist/{qmd-DJP-GLFe.d.ts → qmd-DYnrPw-m.d.ts} +1 -1
  292. package/dist/qmd-preflight.d.ts +2 -2
  293. package/dist/qmd-recall-cache.d.ts +1 -1
  294. package/dist/qmd.d.ts +2 -2
  295. package/dist/recall-concurrency-config.d.ts +1 -1
  296. package/dist/recall-disclosure-escalation.d.ts +1 -1
  297. package/dist/recall-explain-renderer.d.ts +1 -1
  298. package/dist/recall-explain-renderer.js +4 -3
  299. package/dist/recall-memory-map.d.ts +1 -1
  300. package/dist/recall-planner-llm.d.ts +1 -1
  301. package/dist/recall-state.d.ts +1 -1
  302. package/dist/recall-tag-filter.d.ts +1 -1
  303. package/dist/recall-timings.d.ts +1 -1
  304. package/dist/recall-xray-cli.d.ts +1 -1
  305. package/dist/recall-xray-cli.js +5 -4
  306. package/dist/recall-xray-renderer.d.ts +1 -1
  307. package/dist/recall-xray-renderer.js +4 -3
  308. package/dist/recall-xray.d.ts +2 -7
  309. package/dist/recall-xray.js +3 -2
  310. package/dist/reconcile/cursor.d.ts +1 -1
  311. package/dist/reconcile/manifest.d.ts +1 -1
  312. package/dist/reconcile/plan.d.ts +1 -1
  313. package/dist/replay/normalizers/chatgpt.d.ts +1 -1
  314. package/dist/replay/normalizers/claude.d.ts +1 -1
  315. package/dist/replay/normalizers/openclaw.d.ts +1 -1
  316. package/dist/replay/normalizers/shared.d.ts +1 -1
  317. package/dist/replay/runner.d.ts +1 -1
  318. package/dist/replay/types.d.ts +1 -1
  319. package/dist/replica-divergence.d.ts +1 -1
  320. package/dist/replica-peers-config.d.ts +1 -1
  321. package/dist/resolve-auth-token.d.ts +1 -1
  322. package/dist/resume-bundles.js +4 -3
  323. package/dist/retrieval-agents.d.ts +2 -2
  324. package/dist/retrieval-tiers.d.ts +1 -1
  325. package/dist/routing/engine.d.ts +1 -1
  326. package/dist/routing/store.d.ts +1 -1
  327. package/dist/salvage-envelope.d.ts +1 -1
  328. package/dist/{scope-profiles-CaKcnB-6.d.ts → scope-profiles-C1QG4Cmv.d.ts} +1 -1
  329. package/dist/search/embed-helper.d.ts +1 -1
  330. package/dist/search/factory.d.ts +1 -1
  331. package/dist/search/factory.js +6 -6
  332. package/dist/search/index.d.ts +1 -1
  333. package/dist/search/index.js +11 -11
  334. package/dist/search/lancedb-backend.d.ts +1 -1
  335. package/dist/search/lancedb-backend.js +2 -2
  336. package/dist/search/meilisearch-backend.d.ts +1 -1
  337. package/dist/search/meilisearch-backend.js +2 -2
  338. package/dist/search/noop-backend.d.ts +1 -1
  339. package/dist/search/orama-backend.d.ts +1 -1
  340. package/dist/search/orama-backend.js +2 -2
  341. package/dist/search/port.d.ts +1 -1
  342. package/dist/search/remote-backend.d.ts +1 -1
  343. package/dist/semantic-chunking.js +3 -2
  344. package/dist/{semantic-consolidation-P0eMWqh-.d.ts → semantic-consolidation-DMeX8XS0.d.ts} +1 -1
  345. package/dist/semantic-consolidation.d.ts +2 -2
  346. package/dist/semantic-consolidation.js +14 -13
  347. package/dist/semantic-rule-promotion.js +10 -10
  348. package/dist/semantic-rule-verifier.d.ts +1 -1
  349. package/dist/semantic-rule-verifier.js +10 -10
  350. package/dist/{service-D5GQLxq_.d.ts → service-CT3ENiTO.d.ts} +2 -2
  351. package/dist/session-observer-bands.d.ts +1 -1
  352. package/dist/session-observer-state.d.ts +1 -1
  353. package/dist/session-observer-state.js +1 -1
  354. package/dist/shared-context/manager.d.ts +1 -1
  355. package/dist/signal.d.ts +1 -1
  356. package/dist/source-agent-qualifier.d.ts +1 -1
  357. package/dist/source-agent-qualifier.js +12 -11
  358. package/dist/{storage-DIyil2ll.d.ts → storage-B0L76y7N.d.ts} +1 -1
  359. package/dist/storage.d.ts +2 -2
  360. package/dist/storage.js +9 -9
  361. package/dist/summarizer.d.ts +1 -1
  362. package/dist/summarizer.js +3 -2
  363. package/dist/summary-snapshot.d.ts +1 -1
  364. package/dist/support-passport/index.d.ts +15 -14
  365. package/dist/support-passport/index.js +28 -27
  366. package/dist/temporal-supersession.d.ts +2 -2
  367. package/dist/temporal-timeline-recall.d.ts +1 -1
  368. package/dist/temporal-validity.d.ts +1 -1
  369. package/dist/threading.d.ts +1 -1
  370. package/dist/tier-migration.d.ts +2 -2
  371. package/dist/tier-routing.d.ts +1 -1
  372. package/dist/token-estimate.d.ts +10 -0
  373. package/dist/token-estimate.js +8 -0
  374. package/dist/token-estimate.js.map +1 -0
  375. package/dist/topics.d.ts +1 -1
  376. package/dist/transcript.d.ts +1 -6
  377. package/dist/transcript.js +2 -1
  378. package/dist/transfer/backup.js +2 -2
  379. package/dist/transfer/capsule-export.js +2 -2
  380. package/dist/transfer/capsule-import.js +2 -2
  381. package/dist/trust-score-stage.d.ts +1 -1
  382. package/dist/trust-score.d.ts +1 -1
  383. package/dist/{types-CbXieHKr.d.ts → types-D7pB0QwO.d.ts} +1 -1
  384. package/dist/types.d.ts +1 -1
  385. package/dist/types.js +1 -1
  386. package/dist/utility-runtime.d.ts +1 -1
  387. package/dist/verified-recall.js +10 -10
  388. package/dist/write-envelope.d.ts +1 -1
  389. package/package.json +12 -2
  390. package/src/chunking.ts +6 -12
  391. package/src/config.ts +2 -0
  392. package/src/connectors/codex-materialize.ts +21 -26
  393. package/src/lcm/archive.ts +4 -5
  394. package/src/lcm/engine.ts +55 -34
  395. package/src/lcm/summarizer.ts +27 -16
  396. package/src/lcm-engine.test.ts +82 -2
  397. package/src/local-llm.ts +4 -6
  398. package/src/memory-extension-host/render-extensions-block.ts +3 -9
  399. package/src/orchestration/recall-section-coordinator.ts +93 -27
  400. package/src/recall-token-budget.test.ts +51 -0
  401. package/src/recall-xray.ts +4 -11
  402. package/src/semantic-chunking.ts +5 -12
  403. package/src/session-observer-state.ts +3 -3
  404. package/src/testing/subjects/recall-budget.test.ts +21 -5
  405. package/src/token-estimate.test.ts +38 -0
  406. package/src/token-estimate.ts +22 -0
  407. package/src/transcript.test.ts +38 -3
  408. package/src/transcript.ts +58 -59
  409. package/src/types.ts +1 -1
  410. package/dist/chunk-5ZIWRMZM.js.map +0 -1
  411. package/dist/chunk-HYQJQGD3.js.map +0 -1
  412. package/dist/chunk-KSXJOC4F.js.map +0 -1
  413. package/dist/chunk-LMXUWAXW.js.map +0 -1
  414. package/dist/chunk-LTAVSOZW.js.map +0 -1
  415. package/dist/chunk-P6WBAR27.js.map +0 -1
  416. package/dist/chunk-PBGWYM3H.js.map +0 -1
  417. package/dist/chunk-QKPDN325.js.map +0 -1
  418. package/dist/chunk-REVMY7RA.js.map +0 -1
  419. package/dist/chunk-Z54EM5YZ.js.map +0 -1
  420. /package/dist/{capsule-crypto-YO5QJ6L3.js.map → capsule-crypto-GWVG7LGC.js.map} +0 -0
  421. /package/dist/{chunk-YTZA7U6Q.js.map → chunk-2VHCLHZ5.js.map} +0 -0
  422. /package/dist/{chunk-NEJQ65NV.js.map → chunk-4CLE2LXB.js.map} +0 -0
  423. /package/dist/{chunk-VDX2J7OX.js.map → chunk-5W7CTZ2E.js.map} +0 -0
  424. /package/dist/{chunk-CZUHNJHX.js.map → chunk-6C7Q6NG3.js.map} +0 -0
  425. /package/dist/{chunk-4O6R3GPK.js.map → chunk-6W2ISYUV.js.map} +0 -0
  426. /package/dist/{chunk-3AJOJOFP.js.map → chunk-77DAFTUU.js.map} +0 -0
  427. /package/dist/{chunk-IKT6ZVO7.js.map → chunk-7DHFB5SG.js.map} +0 -0
  428. /package/dist/{chunk-SFR7J2G6.js.map → chunk-7G34CBKS.js.map} +0 -0
  429. /package/dist/{chunk-IT57AOMC.js.map → chunk-AH2NX6S7.js.map} +0 -0
  430. /package/dist/{chunk-FI3PIOOI.js.map → chunk-B4CSPFYN.js.map} +0 -0
  431. /package/dist/{chunk-7PJARJS4.js.map → chunk-BPM6UHUL.js.map} +0 -0
  432. /package/dist/{chunk-PPUTUOC3.js.map → chunk-BTHDQOF4.js.map} +0 -0
  433. /package/dist/{chunk-DUHMYBVE.js.map → chunk-CZGOLAEW.js.map} +0 -0
  434. /package/dist/{chunk-5PGYCWA7.js.map → chunk-DE6YCZEE.js.map} +0 -0
  435. /package/dist/{chunk-JOA4Q765.js.map → chunk-EUYWQ7TS.js.map} +0 -0
  436. /package/dist/{chunk-O5AFC5KM.js.map → chunk-EWL5DICB.js.map} +0 -0
  437. /package/dist/{chunk-4YZCIK5E.js.map → chunk-EZXGJJXM.js.map} +0 -0
  438. /package/dist/{chunk-GXIY65UH.js.map → chunk-FI5EAP4N.js.map} +0 -0
  439. /package/dist/{chunk-YOKSRIFI.js.map → chunk-FUWS2MNJ.js.map} +0 -0
  440. /package/dist/{chunk-6RK2X7BR.js.map → chunk-G3JFFUKR.js.map} +0 -0
  441. /package/dist/{chunk-VANZKHTN.js.map → chunk-G5OB4XZX.js.map} +0 -0
  442. /package/dist/{chunk-TEHPIMXD.js.map → chunk-HK6KYIF6.js.map} +0 -0
  443. /package/dist/{chunk-XBGLEOID.js.map → chunk-I4BLRM7X.js.map} +0 -0
  444. /package/dist/{chunk-6SX4MY5Q.js.map → chunk-J65AKOD6.js.map} +0 -0
  445. /package/dist/{chunk-BPWZDHFM.js.map → chunk-J6QIAYNR.js.map} +0 -0
  446. /package/dist/{chunk-XL3TATMJ.js.map → chunk-JHMAYABJ.js.map} +0 -0
  447. /package/dist/{chunk-F7M36OG2.js.map → chunk-JNWISMYG.js.map} +0 -0
  448. /package/dist/{chunk-VYIXCMUP.js.map → chunk-KBIIE57I.js.map} +0 -0
  449. /package/dist/{chunk-24PNX2GF.js.map → chunk-KXYMU4IN.js.map} +0 -0
  450. /package/dist/{chunk-FKTD7IVS.js.map → chunk-L3JVAM5Q.js.map} +0 -0
  451. /package/dist/{chunk-LIF6DO6U.js.map → chunk-MG7GI4TT.js.map} +0 -0
  452. /package/dist/{chunk-RUZM6Z52.js.map → chunk-MMZK2DWN.js.map} +0 -0
  453. /package/dist/{chunk-NTJ6TY44.js.map → chunk-ODAE36PG.js.map} +0 -0
  454. /package/dist/{chunk-YLHM5BQQ.js.map → chunk-OLX2HJFW.js.map} +0 -0
  455. /package/dist/{chunk-DNC4A7RS.js.map → chunk-OU7RWWW6.js.map} +0 -0
  456. /package/dist/{chunk-L3LRWDJW.js.map → chunk-OV36CROW.js.map} +0 -0
  457. /package/dist/{chunk-BXLOS5AJ.js.map → chunk-OWHERGF2.js.map} +0 -0
  458. /package/dist/{chunk-CSV34HIW.js.map → chunk-P5OYNV7B.js.map} +0 -0
  459. /package/dist/{chunk-SNW3YRO6.js.map → chunk-P6GOYLHN.js.map} +0 -0
  460. /package/dist/{chunk-USGKHF36.js.map → chunk-QUJC22CT.js.map} +0 -0
  461. /package/dist/{chunk-BVRREV3N.js.map → chunk-R7IV3EUJ.js.map} +0 -0
  462. /package/dist/{chunk-D4LVHPOA.js.map → chunk-RXN7Z2V5.js.map} +0 -0
  463. /package/dist/{chunk-7VSHDLJQ.js.map → chunk-T2VJ2EO5.js.map} +0 -0
  464. /package/dist/{chunk-U7BMN33M.js.map → chunk-UBSM6TZP.js.map} +0 -0
  465. /package/dist/{chunk-DQEMWVMT.js.map → chunk-UVYI6VIX.js.map} +0 -0
  466. /package/dist/{chunk-U7XLOSG2.js.map → chunk-VIM6QH4D.js.map} +0 -0
  467. /package/dist/{chunk-LJJWFV2Q.js.map → chunk-VJXSAJFW.js.map} +0 -0
  468. /package/dist/{chunk-HZC3OECF.js.map → chunk-VOKK4WCG.js.map} +0 -0
  469. /package/dist/{chunk-LRDP2AC2.js.map → chunk-YBCZTBN2.js.map} +0 -0
  470. /package/dist/{chunk-USAUBGBC.js.map → chunk-YKZFX2HQ.js.map} +0 -0
  471. /package/dist/{chunk-I2GIYTRU.js.map → chunk-ZOGOOSB3.js.map} +0 -0
@@ -1,3 +1,7 @@
1
+ import {
2
+ estimateTokenCount
3
+ } from "./chunk-TOTT6S2V.js";
4
+
1
5
  // src/memory-extension-host/host-discovery.ts
2
6
  import { readdir, readFile, lstat, realpath } from "fs/promises";
3
7
  import path from "path";
@@ -225,9 +229,6 @@ function resolveExtensionsRoot(config) {
225
229
  }
226
230
 
227
231
  // src/memory-extension-host/render-extensions-block.ts
228
- function estimateTokens(text) {
229
- return Math.ceil(text.length / 4);
230
- }
231
232
  function renderExtensionsBlock(extensions) {
232
233
  if (extensions.length === 0) return "";
233
234
  const header = `## Active memory extensions
@@ -238,7 +239,7 @@ extension produces or curates.
238
239
 
239
240
  `;
240
241
  let budget = REMNIC_EXTENSIONS_TOTAL_TOKEN_LIMIT;
241
- budget -= estimateTokens(header);
242
+ budget -= estimateTokenCount(header);
242
243
  const inlined = [];
243
244
  const omitted = [];
244
245
  for (const ext of extensions) {
@@ -248,7 +249,7 @@ ${ext.instructions}
248
249
  \`\`\`
249
250
 
250
251
  `;
251
- const cost = estimateTokens(block);
252
+ const cost = estimateTokenCount(block);
252
253
  if (cost <= budget) {
253
254
  inlined.push(block);
254
255
  budget -= cost;
@@ -277,4 +278,4 @@ export {
277
278
  renderExtensionsBlock,
278
279
  renderExtensionsFooter
279
280
  };
280
- //# sourceMappingURL=chunk-JLNBQWZ2.js.map
281
+ //# sourceMappingURL=chunk-5JMAGU26.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/memory-extension-host/host-discovery.ts","../src/memory-extension-host/render-extensions-block.ts"],"sourcesContent":["/**\n * memory-extension-host/host-discovery.ts — Discover third-party memory extensions.\n *\n * Scans a root directory (typically ~/.remnic/memory_extensions/) for valid\n * extension subdirectories. Each extension must contain an instructions.md.\n * The discovery process is read-only and NEVER reads or executes files under\n * any extension's scripts/ directory.\n */\n\nimport { readdir, readFile, lstat, realpath } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport type { LoggerBackend } from \"../logger.js\";\nimport type { PluginConfig } from \"../types.js\";\nimport type { DiscoveredExtension, ExtensionSchema } from \"./types.js\";\n\n/** Total token budget for all discovered extension instructions combined. */\nexport const REMNIC_EXTENSIONS_TOTAL_TOKEN_LIMIT = 5_000;\n\n/** Per-extension instructions read cap, using the same 4 chars/token budget heuristic. */\nexport const REMNIC_EXTENSION_INSTRUCTIONS_BYTE_LIMIT =\n REMNIC_EXTENSIONS_TOTAL_TOKEN_LIMIT * 4;\n\n/** Optional schemas are metadata only; refuse unusually large schema files. */\nexport const REMNIC_EXTENSION_SCHEMA_BYTE_LIMIT = 64 * 1024;\n\n/** Maximum number of example files collected per extension. */\nconst MAX_EXAMPLES_PER_EXTENSION = 10;\n\n/** Slug validation: lowercase letters, digits, hyphens, 1-64 chars. */\nconst VALID_SLUG_RE = /^[a-z0-9][a-z0-9-]{0,63}$/;\n\nconst VALID_MEMORY_TYPES = new Set([\"fact\", \"preference\", \"procedure\", \"reference\"]);\n\n/**\n * Discover all valid memory extensions under the given root directory.\n *\n * Returns extensions sorted by name. Skips entries with warnings when:\n * - The slug is invalid (not lowercase alphanumeric + hyphens, or > 64 chars)\n * - instructions.md is missing\n * - schema.json is malformed (extension still returned but schema is undefined)\n *\n * NEVER reads files under any extension's scripts/ directory.\n */\nexport async function discoverMemoryExtensions(\n root: string,\n log: Pick<LoggerBackend, \"warn\" | \"debug\">,\n): Promise<DiscoveredExtension[]> {\n // If root doesn't exist, return empty silently (not even a warning).\n // Use lstat() for root — a symlinked extensions root could redirect\n // discovery to an untrusted directory tree (#428 P2). When the root\n // IS a symlink, resolve it and verify it still lives under the parent\n // memory directory so that an attacker-controlled symlink can't point\n // discovery at /etc or another user's home.\n let rootStat;\n try {\n rootStat = await lstat(root);\n } catch {\n return [];\n }\n if (rootStat.isSymbolicLink()) {\n // Resolve and verify the real path is inside the expected parent.\n let resolved: string;\n try {\n resolved = await realpath(root);\n } catch {\n return [];\n }\n // Normalize the parent path through realpath so that:\n // 1. Relative roots (e.g. \"memory_extensions\") become absolute (#431 Finding 1)\n // 2. Intermediate symlinks (e.g. macOS /var -> /private/var) are resolved (#431 Finding 2)\n let expectedParent: string;\n try {\n expectedParent = await realpath(path.resolve(path.dirname(root)));\n } catch {\n // Parent directory doesn't exist or is inaccessible — reject.\n return [];\n }\n if (!resolved.startsWith(expectedParent + path.sep) && resolved !== expectedParent) {\n log.warn?.(\n `[memory-extensions] root \"${root}\" is a symlink resolving outside the expected parent directory, skipping`,\n );\n return [];\n }\n // Re-check the resolved path is a directory.\n try {\n rootStat = await lstat(resolved);\n } catch {\n return [];\n }\n }\n if (!rootStat.isDirectory()) {\n return [];\n }\n\n let entries: string[];\n try {\n entries = await readdir(root);\n } catch {\n return [];\n }\n\n const extensions: DiscoveredExtension[] = [];\n\n for (const entry of entries) {\n const entryPath = path.join(root, entry);\n\n // Must be a real directory (not a symlink) — lstat() blocks symlink\n // traversal that could escape the extensions root (#382 P2).\n let entryStat;\n try {\n entryStat = await lstat(entryPath);\n } catch {\n continue;\n }\n if (entryStat.isSymbolicLink()) {\n log.warn?.(\n `[memory-extensions] skipping \"${entry}\": symlinks are not followed for security`,\n );\n continue;\n }\n if (!entryStat.isDirectory()) continue;\n\n // Validate slug\n if (!VALID_SLUG_RE.test(entry)) {\n log.warn?.(\n `[memory-extensions] skipping \"${entry}\": invalid slug (must be lowercase alphanumeric + hyphens, 1-64 chars)`,\n );\n continue;\n }\n\n // Require instructions.md — reject symlinked files (#428 P1)\n const instructionsPath = path.join(entryPath, \"instructions.md\");\n if (await isSymlink(instructionsPath)) {\n log.warn?.(\n `[memory-extensions] skipping \"${entry}\": instructions.md is a symlink`,\n );\n continue;\n }\n let instructions: string;\n try {\n instructions = await readUtf8FileWithinLimit(\n instructionsPath,\n REMNIC_EXTENSION_INSTRUCTIONS_BYTE_LIMIT,\n );\n } catch (err) {\n const reason = err instanceof FileTooLargeError\n ? `instructions.md exceeds ${REMNIC_EXTENSION_INSTRUCTIONS_BYTE_LIMIT} bytes`\n : \"missing instructions.md\";\n log.warn?.(`[memory-extensions] skipping \"${entry}\": ${reason}`);\n continue;\n }\n\n // Read optional schema.json — reject symlinked files (#428 P1)\n let schema: ExtensionSchema | undefined;\n const schemaPath = path.join(entryPath, \"schema.json\");\n if (await isSymlink(schemaPath)) {\n log.warn?.(\n `[memory-extensions] \"${entry}\": schema.json is a symlink, ignoring schema`,\n );\n } else {\n try {\n const schemaRaw = await readUtf8FileWithinLimit(\n schemaPath,\n REMNIC_EXTENSION_SCHEMA_BYTE_LIMIT,\n );\n const parsed = JSON.parse(schemaRaw);\n if (typeof parsed === \"object\" && parsed !== null && !Array.isArray(parsed)) {\n schema = validateSchema(parsed);\n } else {\n log.warn?.(\n `[memory-extensions] \"${entry}\": schema.json is not a valid object, ignoring schema`,\n );\n }\n } catch (err) {\n // File doesn't exist → fine, no warning needed\n if (isFileNotFoundError(err)) {\n // schema remains undefined\n } else if (err instanceof FileTooLargeError) {\n log.warn?.(\n `[memory-extensions] \"${entry}\": schema.json exceeds ${REMNIC_EXTENSION_SCHEMA_BYTE_LIMIT} bytes, ignoring schema`,\n );\n } else {\n log.warn?.(\n `[memory-extensions] \"${entry}\": malformed schema.json, ignoring schema`,\n );\n }\n }\n }\n\n // Collect examples/*.md (cap at MAX_EXAMPLES_PER_EXTENSION)\n // NEVER read from scripts/ directory\n const examplesPaths: string[] = [];\n const examplesDir = path.join(entryPath, \"examples\");\n try {\n const examplesStat = await lstat(examplesDir);\n if (examplesStat.isSymbolicLink()) {\n log.warn?.(\n `[memory-extensions] \"${entry}\": examples/ is a symlink, ignoring examples`,\n );\n throw new Error(\"skip symlinked examples directory\");\n }\n if (!examplesStat.isDirectory()) {\n throw new Error(\"skip non-directory examples path\");\n }\n const exampleEntries = await readdir(examplesDir);\n const mdFiles = exampleEntries\n .filter((f) => f.endsWith(\".md\"))\n .sort()\n .slice(0, MAX_EXAMPLES_PER_EXTENSION);\n for (const f of mdFiles) {\n const examplePath = path.join(examplesDir, f);\n const exampleStat = await lstat(examplePath);\n if (exampleStat.isSymbolicLink()) {\n log.warn?.(\n `[memory-extensions] \"${entry}\": examples/${f} is a symlink, ignoring example`,\n );\n continue;\n }\n if (!exampleStat.isFile()) {\n continue;\n }\n examplesPaths.push(examplePath);\n }\n } catch {\n // No examples dir — fine\n }\n\n extensions.push({\n name: entry,\n root: entryPath,\n instructionsPath,\n instructions,\n schema,\n examplesPaths,\n });\n }\n\n // Sort by name for deterministic ordering\n extensions.sort((a, b) => {\n if (a.name < b.name) return -1;\n if (a.name > b.name) return 1;\n return 0;\n });\n\n return extensions;\n}\n\nclass FileTooLargeError extends Error {\n constructor(filePath: string, limitBytes: number, actualBytes: number) {\n super(`${filePath} is ${actualBytes} bytes; limit is ${limitBytes} bytes`);\n this.name = \"FileTooLargeError\";\n }\n}\n\nasync function readUtf8FileWithinLimit(filePath: string, limitBytes: number): Promise<string> {\n const stat = await lstat(filePath);\n if (!stat.isFile()) {\n throw new Error(`${filePath} is not a file`);\n }\n if (stat.size > limitBytes) {\n throw new FileTooLargeError(filePath, limitBytes, stat.size);\n }\n return readFile(filePath, \"utf-8\");\n}\n\nfunction validateSchema(raw: Record<string, unknown>): ExtensionSchema {\n const memoryTypes: ExtensionSchema[\"memoryTypes\"] = (() => {\n if (!Array.isArray(raw.memoryTypes)) return undefined;\n const valid = raw.memoryTypes.filter(\n (t): t is \"fact\" | \"preference\" | \"procedure\" | \"reference\" =>\n typeof t === \"string\" && VALID_MEMORY_TYPES.has(t),\n );\n return valid.length > 0 ? valid : undefined;\n })();\n\n const groupingHints: ExtensionSchema[\"groupingHints\"] = (() => {\n if (!Array.isArray(raw.groupingHints)) return undefined;\n const valid = raw.groupingHints.filter(\n (h): h is string => typeof h === \"string\" && h.length > 0,\n );\n return valid.length > 0 ? valid : undefined;\n })();\n\n const version: ExtensionSchema[\"version\"] =\n typeof raw.version === \"string\" && raw.version.length > 0\n ? raw.version\n : undefined;\n\n return {\n ...(memoryTypes ? { memoryTypes } : {}),\n ...(groupingHints ? { groupingHints } : {}),\n ...(version ? { version } : {}),\n };\n}\n\nfunction isFileNotFoundError(err: unknown): boolean {\n return (\n typeof err === \"object\" &&\n err !== null &&\n \"code\" in err &&\n (err as { code: string }).code === \"ENOENT\"\n );\n}\n\n/**\n * Returns true if the path exists and is a symlink.\n * Returns false if the path does not exist or is not a symlink.\n */\nasync function isSymlink(filePath: string): Promise<boolean> {\n try {\n const s = await lstat(filePath);\n return s.isSymbolicLink();\n } catch {\n return false;\n }\n}\n\n/**\n * Resolve the memory extensions root directory from config.\n * If memoryExtensionsRoot is empty, derive from memoryDir by going up to\n * the Remnic home dir and appending memory_extensions.\n *\n * Moved here from semantic-consolidation.ts (#428 Finding 3) because this\n * is a generic config-to-path resolver with no consolidation logic.\n */\nexport function resolveExtensionsRoot(config: PluginConfig): string {\n if (config.memoryExtensionsRoot.length > 0) {\n return config.memoryExtensionsRoot;\n }\n // Default: memoryDir is typically ~/.openclaw/workspace/memory/local\n // Go up to the parent that owns the memory tree and append memory_extensions\n return path.join(path.dirname(config.memoryDir), \"memory_extensions\");\n}\n","/**\n * memory-extension-host/render-extensions-block.ts — Render discovered extensions\n * into a markdown block for injection into consolidation prompts.\n *\n * Respects the global token budget (REMNIC_EXTENSIONS_TOTAL_TOKEN_LIMIT) and\n * truncates with a footer listing omitted extensions when over budget.\n */\n\nimport { REMNIC_EXTENSIONS_TOTAL_TOKEN_LIMIT } from \"./host-discovery.js\";\nimport type { DiscoveredExtension } from \"./types.js\";\n\n/**\n * Approximate token count using the 4 chars per token heuristic.\n */\nfunction estimateTokens(text: string): number {\n return Math.ceil(text.length / 4);\n}\n\n/**\n * Render a markdown block containing extension instructions for injection\n * into consolidation prompts.\n *\n * If the list is empty, returns \"\".\n * Inlines extensions in name order until the token budget is exhausted.\n * If the budget is exceeded, appends a truncation footer listing omitted extensions.\n */\nexport function renderExtensionsBlock(extensions: DiscoveredExtension[]): string {\n if (extensions.length === 0) return \"\";\n\n const header = `## Active memory extensions\n\nYou are running with the following third-party memory extensions. Each\nextension's \\`instructions.md\\` tells you how to interpret memories that\nextension produces or curates.\n\n`;\n\n let budget = REMNIC_EXTENSIONS_TOTAL_TOKEN_LIMIT;\n budget -= estimateTokens(header);\n\n const inlined: string[] = [];\n const omitted: string[] = [];\n\n for (const ext of extensions) {\n const block = `### remnic-extension/${ext.name}\\n\\`\\`\\`\\n${ext.instructions}\\n\\`\\`\\`\\n\\n`;\n const cost = estimateTokens(block);\n if (cost <= budget) {\n inlined.push(block);\n budget -= cost;\n } else {\n omitted.push(ext.name);\n }\n }\n\n let result = header;\n result += inlined.join(\"\");\n\n if (omitted.length > 0) {\n result += `> **Note:** ${omitted.length} extension(s) omitted due to token budget: ${omitted.join(\", \")}\\n`;\n }\n\n return result;\n}\n\n/**\n * Render a compact one-line footer listing active extension names.\n * Used by day-summary and summary-snapshot where full instructions are not needed.\n */\nexport function renderExtensionsFooter(extensions: DiscoveredExtension[]): string {\n if (extensions.length === 0) return \"\";\n const names = extensions.map((ext) => ext.name).join(\", \");\n return `Active extensions: ${names}`;\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/memory-extension-host/host-discovery.ts","../src/memory-extension-host/render-extensions-block.ts"],"sourcesContent":["/**\n * memory-extension-host/host-discovery.ts — Discover third-party memory extensions.\n *\n * Scans a root directory (typically ~/.remnic/memory_extensions/) for valid\n * extension subdirectories. Each extension must contain an instructions.md.\n * The discovery process is read-only and NEVER reads or executes files under\n * any extension's scripts/ directory.\n */\n\nimport { readdir, readFile, lstat, realpath } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport type { LoggerBackend } from \"../logger.js\";\nimport type { PluginConfig } from \"../types.js\";\nimport type { DiscoveredExtension, ExtensionSchema } from \"./types.js\";\n\n/** Total token budget for all discovered extension instructions combined. */\nexport const REMNIC_EXTENSIONS_TOTAL_TOKEN_LIMIT = 5_000;\n\n/** Per-extension instructions read cap, using the same 4 chars/token budget heuristic. */\nexport const REMNIC_EXTENSION_INSTRUCTIONS_BYTE_LIMIT =\n REMNIC_EXTENSIONS_TOTAL_TOKEN_LIMIT * 4;\n\n/** Optional schemas are metadata only; refuse unusually large schema files. */\nexport const REMNIC_EXTENSION_SCHEMA_BYTE_LIMIT = 64 * 1024;\n\n/** Maximum number of example files collected per extension. */\nconst MAX_EXAMPLES_PER_EXTENSION = 10;\n\n/** Slug validation: lowercase letters, digits, hyphens, 1-64 chars. */\nconst VALID_SLUG_RE = /^[a-z0-9][a-z0-9-]{0,63}$/;\n\nconst VALID_MEMORY_TYPES = new Set([\"fact\", \"preference\", \"procedure\", \"reference\"]);\n\n/**\n * Discover all valid memory extensions under the given root directory.\n *\n * Returns extensions sorted by name. Skips entries with warnings when:\n * - The slug is invalid (not lowercase alphanumeric + hyphens, or > 64 chars)\n * - instructions.md is missing\n * - schema.json is malformed (extension still returned but schema is undefined)\n *\n * NEVER reads files under any extension's scripts/ directory.\n */\nexport async function discoverMemoryExtensions(\n root: string,\n log: Pick<LoggerBackend, \"warn\" | \"debug\">,\n): Promise<DiscoveredExtension[]> {\n // If root doesn't exist, return empty silently (not even a warning).\n // Use lstat() for root — a symlinked extensions root could redirect\n // discovery to an untrusted directory tree (#428 P2). When the root\n // IS a symlink, resolve it and verify it still lives under the parent\n // memory directory so that an attacker-controlled symlink can't point\n // discovery at /etc or another user's home.\n let rootStat;\n try {\n rootStat = await lstat(root);\n } catch {\n return [];\n }\n if (rootStat.isSymbolicLink()) {\n // Resolve and verify the real path is inside the expected parent.\n let resolved: string;\n try {\n resolved = await realpath(root);\n } catch {\n return [];\n }\n // Normalize the parent path through realpath so that:\n // 1. Relative roots (e.g. \"memory_extensions\") become absolute (#431 Finding 1)\n // 2. Intermediate symlinks (e.g. macOS /var -> /private/var) are resolved (#431 Finding 2)\n let expectedParent: string;\n try {\n expectedParent = await realpath(path.resolve(path.dirname(root)));\n } catch {\n // Parent directory doesn't exist or is inaccessible — reject.\n return [];\n }\n if (!resolved.startsWith(expectedParent + path.sep) && resolved !== expectedParent) {\n log.warn?.(\n `[memory-extensions] root \"${root}\" is a symlink resolving outside the expected parent directory, skipping`,\n );\n return [];\n }\n // Re-check the resolved path is a directory.\n try {\n rootStat = await lstat(resolved);\n } catch {\n return [];\n }\n }\n if (!rootStat.isDirectory()) {\n return [];\n }\n\n let entries: string[];\n try {\n entries = await readdir(root);\n } catch {\n return [];\n }\n\n const extensions: DiscoveredExtension[] = [];\n\n for (const entry of entries) {\n const entryPath = path.join(root, entry);\n\n // Must be a real directory (not a symlink) — lstat() blocks symlink\n // traversal that could escape the extensions root (#382 P2).\n let entryStat;\n try {\n entryStat = await lstat(entryPath);\n } catch {\n continue;\n }\n if (entryStat.isSymbolicLink()) {\n log.warn?.(\n `[memory-extensions] skipping \"${entry}\": symlinks are not followed for security`,\n );\n continue;\n }\n if (!entryStat.isDirectory()) continue;\n\n // Validate slug\n if (!VALID_SLUG_RE.test(entry)) {\n log.warn?.(\n `[memory-extensions] skipping \"${entry}\": invalid slug (must be lowercase alphanumeric + hyphens, 1-64 chars)`,\n );\n continue;\n }\n\n // Require instructions.md — reject symlinked files (#428 P1)\n const instructionsPath = path.join(entryPath, \"instructions.md\");\n if (await isSymlink(instructionsPath)) {\n log.warn?.(\n `[memory-extensions] skipping \"${entry}\": instructions.md is a symlink`,\n );\n continue;\n }\n let instructions: string;\n try {\n instructions = await readUtf8FileWithinLimit(\n instructionsPath,\n REMNIC_EXTENSION_INSTRUCTIONS_BYTE_LIMIT,\n );\n } catch (err) {\n const reason = err instanceof FileTooLargeError\n ? `instructions.md exceeds ${REMNIC_EXTENSION_INSTRUCTIONS_BYTE_LIMIT} bytes`\n : \"missing instructions.md\";\n log.warn?.(`[memory-extensions] skipping \"${entry}\": ${reason}`);\n continue;\n }\n\n // Read optional schema.json — reject symlinked files (#428 P1)\n let schema: ExtensionSchema | undefined;\n const schemaPath = path.join(entryPath, \"schema.json\");\n if (await isSymlink(schemaPath)) {\n log.warn?.(\n `[memory-extensions] \"${entry}\": schema.json is a symlink, ignoring schema`,\n );\n } else {\n try {\n const schemaRaw = await readUtf8FileWithinLimit(\n schemaPath,\n REMNIC_EXTENSION_SCHEMA_BYTE_LIMIT,\n );\n const parsed = JSON.parse(schemaRaw);\n if (typeof parsed === \"object\" && parsed !== null && !Array.isArray(parsed)) {\n schema = validateSchema(parsed);\n } else {\n log.warn?.(\n `[memory-extensions] \"${entry}\": schema.json is not a valid object, ignoring schema`,\n );\n }\n } catch (err) {\n // File doesn't exist → fine, no warning needed\n if (isFileNotFoundError(err)) {\n // schema remains undefined\n } else if (err instanceof FileTooLargeError) {\n log.warn?.(\n `[memory-extensions] \"${entry}\": schema.json exceeds ${REMNIC_EXTENSION_SCHEMA_BYTE_LIMIT} bytes, ignoring schema`,\n );\n } else {\n log.warn?.(\n `[memory-extensions] \"${entry}\": malformed schema.json, ignoring schema`,\n );\n }\n }\n }\n\n // Collect examples/*.md (cap at MAX_EXAMPLES_PER_EXTENSION)\n // NEVER read from scripts/ directory\n const examplesPaths: string[] = [];\n const examplesDir = path.join(entryPath, \"examples\");\n try {\n const examplesStat = await lstat(examplesDir);\n if (examplesStat.isSymbolicLink()) {\n log.warn?.(\n `[memory-extensions] \"${entry}\": examples/ is a symlink, ignoring examples`,\n );\n throw new Error(\"skip symlinked examples directory\");\n }\n if (!examplesStat.isDirectory()) {\n throw new Error(\"skip non-directory examples path\");\n }\n const exampleEntries = await readdir(examplesDir);\n const mdFiles = exampleEntries\n .filter((f) => f.endsWith(\".md\"))\n .sort()\n .slice(0, MAX_EXAMPLES_PER_EXTENSION);\n for (const f of mdFiles) {\n const examplePath = path.join(examplesDir, f);\n const exampleStat = await lstat(examplePath);\n if (exampleStat.isSymbolicLink()) {\n log.warn?.(\n `[memory-extensions] \"${entry}\": examples/${f} is a symlink, ignoring example`,\n );\n continue;\n }\n if (!exampleStat.isFile()) {\n continue;\n }\n examplesPaths.push(examplePath);\n }\n } catch {\n // No examples dir — fine\n }\n\n extensions.push({\n name: entry,\n root: entryPath,\n instructionsPath,\n instructions,\n schema,\n examplesPaths,\n });\n }\n\n // Sort by name for deterministic ordering\n extensions.sort((a, b) => {\n if (a.name < b.name) return -1;\n if (a.name > b.name) return 1;\n return 0;\n });\n\n return extensions;\n}\n\nclass FileTooLargeError extends Error {\n constructor(filePath: string, limitBytes: number, actualBytes: number) {\n super(`${filePath} is ${actualBytes} bytes; limit is ${limitBytes} bytes`);\n this.name = \"FileTooLargeError\";\n }\n}\n\nasync function readUtf8FileWithinLimit(filePath: string, limitBytes: number): Promise<string> {\n const stat = await lstat(filePath);\n if (!stat.isFile()) {\n throw new Error(`${filePath} is not a file`);\n }\n if (stat.size > limitBytes) {\n throw new FileTooLargeError(filePath, limitBytes, stat.size);\n }\n return readFile(filePath, \"utf-8\");\n}\n\nfunction validateSchema(raw: Record<string, unknown>): ExtensionSchema {\n const memoryTypes: ExtensionSchema[\"memoryTypes\"] = (() => {\n if (!Array.isArray(raw.memoryTypes)) return undefined;\n const valid = raw.memoryTypes.filter(\n (t): t is \"fact\" | \"preference\" | \"procedure\" | \"reference\" =>\n typeof t === \"string\" && VALID_MEMORY_TYPES.has(t),\n );\n return valid.length > 0 ? valid : undefined;\n })();\n\n const groupingHints: ExtensionSchema[\"groupingHints\"] = (() => {\n if (!Array.isArray(raw.groupingHints)) return undefined;\n const valid = raw.groupingHints.filter(\n (h): h is string => typeof h === \"string\" && h.length > 0,\n );\n return valid.length > 0 ? valid : undefined;\n })();\n\n const version: ExtensionSchema[\"version\"] =\n typeof raw.version === \"string\" && raw.version.length > 0\n ? raw.version\n : undefined;\n\n return {\n ...(memoryTypes ? { memoryTypes } : {}),\n ...(groupingHints ? { groupingHints } : {}),\n ...(version ? { version } : {}),\n };\n}\n\nfunction isFileNotFoundError(err: unknown): boolean {\n return (\n typeof err === \"object\" &&\n err !== null &&\n \"code\" in err &&\n (err as { code: string }).code === \"ENOENT\"\n );\n}\n\n/**\n * Returns true if the path exists and is a symlink.\n * Returns false if the path does not exist or is not a symlink.\n */\nasync function isSymlink(filePath: string): Promise<boolean> {\n try {\n const s = await lstat(filePath);\n return s.isSymbolicLink();\n } catch {\n return false;\n }\n}\n\n/**\n * Resolve the memory extensions root directory from config.\n * If memoryExtensionsRoot is empty, derive from memoryDir by going up to\n * the Remnic home dir and appending memory_extensions.\n *\n * Moved here from semantic-consolidation.ts (#428 Finding 3) because this\n * is a generic config-to-path resolver with no consolidation logic.\n */\nexport function resolveExtensionsRoot(config: PluginConfig): string {\n if (config.memoryExtensionsRoot.length > 0) {\n return config.memoryExtensionsRoot;\n }\n // Default: memoryDir is typically ~/.openclaw/workspace/memory/local\n // Go up to the parent that owns the memory tree and append memory_extensions\n return path.join(path.dirname(config.memoryDir), \"memory_extensions\");\n}\n","/**\n * memory-extension-host/render-extensions-block.ts — Render discovered extensions\n * into a markdown block for injection into consolidation prompts.\n *\n * Respects the global token budget (REMNIC_EXTENSIONS_TOTAL_TOKEN_LIMIT) and\n * truncates with a footer listing omitted extensions when over budget.\n */\n\nimport { estimateTokenCount } from \"../token-estimate.js\";\nimport { REMNIC_EXTENSIONS_TOTAL_TOKEN_LIMIT } from \"./host-discovery.js\";\nimport type { DiscoveredExtension } from \"./types.js\";\n\n/**\n * Render a markdown block containing extension instructions for injection\n * into consolidation prompts.\n *\n * If the list is empty, returns \"\".\n * Inlines extensions in name order until the token budget is exhausted.\n * If the budget is exceeded, appends a truncation footer listing omitted extensions.\n */\nexport function renderExtensionsBlock(extensions: DiscoveredExtension[]): string {\n if (extensions.length === 0) return \"\";\n\n const header = `## Active memory extensions\n\nYou are running with the following third-party memory extensions. Each\nextension's \\`instructions.md\\` tells you how to interpret memories that\nextension produces or curates.\n\n`;\n\n let budget = REMNIC_EXTENSIONS_TOTAL_TOKEN_LIMIT;\n budget -= estimateTokenCount(header);\n\n const inlined: string[] = [];\n const omitted: string[] = [];\n\n for (const ext of extensions) {\n const block = `### remnic-extension/${ext.name}\\n\\`\\`\\`\\n${ext.instructions}\\n\\`\\`\\`\\n\\n`;\n const cost = estimateTokenCount(block);\n if (cost <= budget) {\n inlined.push(block);\n budget -= cost;\n } else {\n omitted.push(ext.name);\n }\n }\n\n let result = header;\n result += inlined.join(\"\");\n\n if (omitted.length > 0) {\n result += `> **Note:** ${omitted.length} extension(s) omitted due to token budget: ${omitted.join(\", \")}\\n`;\n }\n\n return result;\n}\n\n/**\n * Render a compact one-line footer listing active extension names.\n * Used by day-summary and summary-snapshot where full instructions are not needed.\n */\nexport function renderExtensionsFooter(extensions: DiscoveredExtension[]): string {\n if (extensions.length === 0) return \"\";\n const names = extensions.map((ext) => ext.name).join(\", \");\n return `Active extensions: ${names}`;\n}\n"],"mappings":"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","names":[]}
@@ -1,12 +1,12 @@
1
+ import {
2
+ encryptCapsuleFile
3
+ } from "./chunk-UVYI6VIX.js";
1
4
  import {
2
5
  CAPSULE_ID_PATTERN,
3
6
  CapsuleBlockSchema,
4
7
  ExportBundleV2Schema,
5
8
  ExportManifestV2Schema
6
9
  } from "./chunk-WEHSQBFR.js";
7
- import {
8
- encryptCapsuleFile
9
- } from "./chunk-DQEMWVMT.js";
10
10
  import {
11
11
  CAPSULE_SCHEMA_VERSION,
12
12
  EXPORT_FORMAT
@@ -261,4 +261,4 @@ export {
261
261
  exportCapsule,
262
262
  isValidCapsuleSince
263
263
  };
264
- //# sourceMappingURL=chunk-VDX2J7OX.js.map
264
+ //# sourceMappingURL=chunk-5W7CTZ2E.js.map
@@ -8,7 +8,7 @@ import {
8
8
  } from "./chunk-GDB4J2H3.js";
9
9
  import {
10
10
  importCapsule
11
- } from "./chunk-PPUTUOC3.js";
11
+ } from "./chunk-BTHDQOF4.js";
12
12
  import {
13
13
  WriteQuarantineStore
14
14
  } from "./chunk-PJSK6OJM.js";
@@ -35,13 +35,13 @@ import {
35
35
  } from "./chunk-H7XKCNR6.js";
36
36
  import {
37
37
  toRecallExplainJson
38
- } from "./chunk-U7BMN33M.js";
38
+ } from "./chunk-UBSM6TZP.js";
39
39
  import {
40
40
  estimateRecallTokens
41
- } from "./chunk-QKPDN325.js";
41
+ } from "./chunk-KF75ANST.js";
42
42
  import {
43
43
  readAggregateExtractionWatermark
44
- } from "./chunk-5PGYCWA7.js";
44
+ } from "./chunk-DE6YCZEE.js";
45
45
  import {
46
46
  ReplicaDivergenceMonitor
47
47
  } from "./chunk-CVILPI77.js";
@@ -71,7 +71,7 @@ import {
71
71
  } from "./chunk-NLE6TSLG.js";
72
72
  import {
73
73
  LocalLlmClient
74
- } from "./chunk-5ZIWRMZM.js";
74
+ } from "./chunk-IVKIODHO.js";
75
75
  import {
76
76
  buildProcedurePersistBody,
77
77
  normalizeProcedureSteps
@@ -99,11 +99,11 @@ import {
99
99
  listMemoryGovernanceRuns,
100
100
  readMemoryGovernanceRunArtifact,
101
101
  runMemoryGovernance
102
- } from "./chunk-6RK2X7BR.js";
102
+ } from "./chunk-G3JFFUKR.js";
103
103
  import {
104
104
  CorpusWatermarkCache,
105
105
  computeServiceCorpusCensus
106
- } from "./chunk-LJJWFV2Q.js";
106
+ } from "./chunk-VJXSAJFW.js";
107
107
  import {
108
108
  CrossNamespaceBudget,
109
109
  toBudgetWarning
@@ -127,21 +127,21 @@ import {
127
127
  enqueuePassiveCorrectionNotification,
128
128
  parseBriefingFocus,
129
129
  parseBriefingWindow
130
- } from "./chunk-CSV34HIW.js";
130
+ } from "./chunk-P5OYNV7B.js";
131
131
  import {
132
132
  parseEntityFile,
133
133
  parseFrontmatter,
134
134
  qmdCollectionPathParts,
135
135
  qmdResultPathCandidates,
136
136
  selfDeps
137
- } from "./chunk-YTZA7U6Q.js";
137
+ } from "./chunk-2VHCLHZ5.js";
138
138
  import {
139
139
  namespaceCollectionName
140
- } from "./chunk-SNW3YRO6.js";
140
+ } from "./chunk-P6GOYLHN.js";
141
141
  import {
142
142
  DEFAULT_RECALL_DISCLOSURE,
143
143
  isRecallDisclosure
144
- } from "./chunk-5QHDXMXP.js";
144
+ } from "./chunk-LLZKOSGM.js";
145
145
  import {
146
146
  normalizeProjectionPreview,
147
147
  normalizeProjectionTags
@@ -179,7 +179,7 @@ import {
179
179
  } from "./chunk-JV3C2MNQ.js";
180
180
  import {
181
181
  exportCapsule
182
- } from "./chunk-VDX2J7OX.js";
182
+ } from "./chunk-5W7CTZ2E.js";
183
183
  import {
184
184
  evaluateActionConfidence
185
185
  } from "./chunk-Z2YFLXHU.js";
@@ -17672,4 +17672,4 @@ export {
17672
17672
  EngramAccessService,
17673
17673
  AccessAdminOpsSurface
17674
17674
  };
17675
- //# sourceMappingURL=chunk-CZUHNJHX.js.map
17675
+ //# sourceMappingURL=chunk-6C7Q6NG3.js.map
@@ -1,12 +1,13 @@
1
+ import {
2
+ estimateTokenCount
3
+ } from "./chunk-TOTT6S2V.js";
4
+
1
5
  // src/chunking.ts
2
6
  var DEFAULT_CHUNKING_CONFIG = {
3
7
  targetTokens: 200,
4
8
  minTokens: 150,
5
9
  overlapSentences: 2
6
10
  };
7
- function estimateTokens(text) {
8
- return Math.ceil(text.length / 4);
9
- }
10
11
  var UNICODE_SENTENCE_TERMINATORS = "\u3002\uFF0E\uFF01\uFF1F\u061F\u06D4\u0964\u0965\uFF61\u2026";
11
12
  var ASCII_SENTENCE_TERMINATORS = ".!?";
12
13
  var CJK_NO_SPACE_TERMINATORS = "\u3002\uFF0E\uFF01\uFF1F\uFF61";
@@ -106,7 +107,7 @@ function joinSentences(sentences) {
106
107
  return result;
107
108
  }
108
109
  function chunkContent(content, config = DEFAULT_CHUNKING_CONFIG) {
109
- const totalTokens = estimateTokens(content);
110
+ const totalTokens = estimateTokenCount(content);
110
111
  if (totalTokens < config.minTokens) {
111
112
  return {
112
113
  chunked: false,
@@ -134,7 +135,7 @@ function chunkContent(content, config = DEFAULT_CHUNKING_CONFIG) {
134
135
  let chunkIndex = 0;
135
136
  for (let i = 0; i < sentences.length; i++) {
136
137
  const sentence = sentences[i];
137
- const sentenceTokens = estimateTokens(sentence);
138
+ const sentenceTokens = estimateTokenCount(sentence);
138
139
  currentChunkSentences.push(sentence);
139
140
  currentTokens += sentenceTokens;
140
141
  const atTarget = currentTokens >= config.targetTokens;
@@ -144,7 +145,7 @@ function chunkContent(content, config = DEFAULT_CHUNKING_CONFIG) {
144
145
  chunks.push({
145
146
  content: chunkContent2,
146
147
  index: chunkIndex,
147
- tokenCount: estimateTokens(chunkContent2)
148
+ tokenCount: estimateTokenCount(chunkContent2)
148
149
  });
149
150
  chunkIndex++;
150
151
  if (!isLastSentence) {
@@ -154,7 +155,7 @@ function chunkContent(content, config = DEFAULT_CHUNKING_CONFIG) {
154
155
  currentTokens = 0;
155
156
  } else {
156
157
  currentChunkSentences = currentChunkSentences.slice(-overlapCount);
157
- currentTokens = currentChunkSentences.reduce((sum, s) => sum + estimateTokens(s), 0);
158
+ currentTokens = currentChunkSentences.reduce((sum, s) => sum + estimateTokenCount(s), 0);
158
159
  }
159
160
  }
160
161
  }
@@ -197,4 +198,4 @@ export {
197
198
  chunkContent,
198
199
  reassembleChunks
199
200
  };
200
- //# sourceMappingURL=chunk-LMXUWAXW.js.map
201
+ //# sourceMappingURL=chunk-6TBIWIAG.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/chunking.ts"],"sourcesContent":["/**\n * Automatic Chunking with Overlap (Phase 2A)\n *\n * Sentence-boundary chunking for long memories.\n * Preserves coherent thoughts by never splitting mid-sentence.\n */\n\nimport { estimateTokenCount } from \"./token-estimate.js\";\n\nexport interface ChunkingConfig {\n /** Target tokens per chunk (default 200) */\n targetTokens: number;\n /** Minimum tokens to trigger chunking (default 150) */\n minTokens: number;\n /** Number of sentences to overlap between chunks (default 2) */\n overlapSentences: number;\n}\n\nexport interface Chunk {\n /** Chunk content */\n content: string;\n /** 0-based index */\n index: number;\n /** Approximate token count */\n tokenCount: number;\n}\n\nexport interface ChunkResult {\n /** Whether content was chunked */\n chunked: boolean;\n /** Array of chunks (length 1 if not chunked) */\n chunks: Chunk[];\n}\n\n/** Default chunking configuration */\nexport const DEFAULT_CHUNKING_CONFIG: ChunkingConfig = {\n targetTokens: 200,\n minTokens: 150,\n overlapSentences: 2,\n};\n\n/**\n * Split text into sentences.\n *\n * The scan stays linear and does not use a backtracking regular expression.\n * ASCII punctuation keeps its old whitespace rule. Unicode terminators also\n * split when the next sentence starts immediately, as in CJK text.\n */\nconst UNICODE_SENTENCE_TERMINATORS = \"。.!?؟۔।॥。…\";\nconst ASCII_SENTENCE_TERMINATORS = \".!?\";\nconst CJK_NO_SPACE_TERMINATORS = \"。.!?。\";\nconst CLOSING_PUNCTUATION = \"\\\"'”’»」』)]】〉》)]}\";\nconst DIGITS = \"01234567890123456789\";\nconst SENTENCE_SEGMENTER =\n typeof Intl.Segmenter === \"function\"\n ? new Intl.Segmenter(undefined, { granularity: \"sentence\" })\n : undefined;\n\nfunction isSentenceTerminator(character: string): boolean {\n return (\n ASCII_SENTENCE_TERMINATORS.includes(character) ||\n UNICODE_SENTENCE_TERMINATORS.includes(character)\n );\n}\n\nfunction isCjk(character: string): boolean {\n return /[\\u3000-\\u9fff\\uf900-\\ufaff\\uac00-\\ud7af]/u.test(character);\n}\n\ntype SentenceList = string[] & { separators: string[] };\n\nfunction splitSentencesFallback(text: string): SentenceList {\n const sentences = [] as unknown as SentenceList;\n Object.defineProperty(sentences, \"separators\", {\n value: [],\n writable: true,\n });\n let start = 0;\n let separator = \"\";\n for (let i = 0; i < text.length; i++) {\n const ch = text[i];\n if (!isSentenceTerminator(ch)) continue;\n\n let end = i;\n let hasUnicodeTerminator = UNICODE_SENTENCE_TERMINATORS.includes(ch);\n while (end + 1 < text.length && isSentenceTerminator(text[end + 1])) {\n end++;\n hasUnicodeTerminator ||= UNICODE_SENTENCE_TERMINATORS.includes(text[end]);\n }\n\n const isFullwidthDecimal =\n ch === \".\" &&\n DIGITS.includes(text[i - 1] ?? \"\") &&\n DIGITS.includes(text[end + 1] ?? \"\");\n while (end + 1 < text.length && CLOSING_PUNCTUATION.includes(text[end + 1])) {\n end++;\n }\n\n const after = text[end + 1];\n const boundary =\n !isFullwidthDecimal &&\n (hasUnicodeTerminator || after === undefined || /\\s/u.test(after));\n if (boundary) {\n const sentence = text.slice(start, end + 1).trim();\n if (sentence.length > 0) {\n sentences.push(sentence);\n if (sentences.length > 1) sentences.separators.push(separator);\n }\n let nextStart = end + 1;\n while (nextStart < text.length && /\\s/u.test(text[nextStart])) nextStart++;\n separator = text.slice(end + 1, nextStart);\n start = nextStart;\n }\n i = end;\n }\n\n const remaining = text.slice(start).trim();\n if (remaining.length > 0) {\n sentences.push(remaining);\n if (sentences.length > 1) sentences.separators.push(separator);\n }\n return sentences;\n}\nexport function splitSentences(text: string): string[] {\n const canUseSegmenter =\n SENTENCE_SEGMENTER !== undefined &&\n !/\\s/u.test(text) &&\n [...text].some((character) => UNICODE_SENTENCE_TERMINATORS.includes(character));\n if (canUseSegmenter) {\n const segments = Array.from(SENTENCE_SEGMENTER.segment(text), ({ segment }) =>\n segment.trim(),\n ).filter((segment) => segment.length > 0) as SentenceList;\n if (\n segments.length > 1 &&\n segments.slice(0, -1).every((segment) =>\n [...segment].some((character) =>\n UNICODE_SENTENCE_TERMINATORS.includes(character),\n ),\n )\n ) {\n Object.defineProperty(segments, \"separators\", {\n value: Array(segments.length - 1).fill(\"\"),\n writable: true,\n });\n return segments;\n }\n }\n return splitSentencesFallback(text);\n}\n\nfunction endsWithoutSpace(text: string): boolean {\n let index = text.length - 1;\n while (index >= 0 && CLOSING_PUNCTUATION.includes(text[index])) index--;\n const terminator = text[index] ?? \"\";\n if (CJK_NO_SPACE_TERMINATORS.includes(terminator)) {\n return isCjk(text[index - 1] ?? \"\");\n }\n return terminator === \"…\" && isCjk(text[index - 1] ?? \"\");\n}\nexport function joinSentences(sentences: string[]): string {\n let result = \"\";\n const separators = (sentences as Partial<SentenceList>).separators;\n for (let i = 0; i < sentences.length; i++) {\n const sentence = sentences[i];\n if (result.length > 0) {\n const separator = separators?.[i - 1];\n result += separator ?? (endsWithoutSpace(result) ? \"\" : \" \");\n }\n result += sentence;\n }\n return result;\n}\n\n\n/**\n * Chunk content into overlapping segments at sentence boundaries.\n *\n * @param content - The text content to chunk\n * @param config - Chunking configuration\n * @returns ChunkResult with chunks array\n */\nexport function chunkContent(\n content: string,\n config: ChunkingConfig = DEFAULT_CHUNKING_CONFIG,\n): ChunkResult {\n const totalTokens = estimateTokenCount(content);\n\n // Don't chunk if below minimum threshold\n if (totalTokens < config.minTokens) {\n return {\n chunked: false,\n chunks: [{\n content,\n index: 0,\n tokenCount: totalTokens,\n }],\n };\n }\n\n const sentences = splitSentences(content);\n\n // If we couldn't split into multiple sentences, don't chunk\n if (sentences.length <= 1) {\n return {\n chunked: false,\n chunks: [{\n content,\n index: 0,\n tokenCount: totalTokens,\n }],\n };\n }\n\n const chunks: Chunk[] = [];\n let currentChunkSentences: string[] = [];\n let currentTokens = 0;\n let chunkIndex = 0;\n\n for (let i = 0; i < sentences.length; i++) {\n const sentence = sentences[i];\n const sentenceTokens = estimateTokenCount(sentence);\n\n // Add sentence to current chunk\n currentChunkSentences.push(sentence);\n currentTokens += sentenceTokens;\n\n // Check if we've reached target size (with some flexibility)\n // Allow going over by up to 50% to avoid tiny final chunks\n const atTarget = currentTokens >= config.targetTokens;\n const isLastSentence = i === sentences.length - 1;\n\n if (atTarget || isLastSentence) {\n // Create chunk from accumulated sentences\n const chunkContent = joinSentences(currentChunkSentences);\n chunks.push({\n content: chunkContent,\n index: chunkIndex,\n tokenCount: estimateTokenCount(chunkContent),\n });\n chunkIndex++;\n\n // Start new chunk with overlap (if not at end)\n if (!isLastSentence) {\n // Keep last N sentences for overlap.\n // Guard: slice(-0) === slice(0), which returns the ENTIRE array\n // (CLAUDE.md gotcha #27). When overlapSentences is 0, clear fully.\n const overlapCount = Math.min(config.overlapSentences, currentChunkSentences.length);\n if (overlapCount <= 0) {\n currentChunkSentences = [];\n currentTokens = 0;\n } else {\n currentChunkSentences = currentChunkSentences.slice(-overlapCount);\n currentTokens = currentChunkSentences.reduce((sum, s) => sum + estimateTokenCount(s), 0);\n }\n }\n }\n }\n\n // Only consider it \"chunked\" if we got multiple chunks\n return {\n chunked: chunks.length > 1,\n chunks,\n };\n}\n\n/**\n * Get parent content by reassembling chunks.\n * Useful for displaying full context when a chunk is retrieved.\n *\n * @param chunks - Array of chunk contents in order\n * @returns Reassembled parent content (with overlap removed)\n */\nexport function reassembleChunks(chunks: string[]): string {\n if (chunks.length === 0) return \"\";\n if (chunks.length === 1) return chunks[0];\n\n // For overlapping chunks, we need to deduplicate\n // Simple approach: use full first chunk, then non-overlapping parts of subsequent chunks\n // This is imperfect but handles most cases\n const result: string[] = [chunks[0]];\n\n for (let i = 1; i < chunks.length; i++) {\n const prevChunk = chunks[i - 1];\n const currChunk = chunks[i];\n\n // Find overlap by looking for common suffix/prefix\n // Try to find where the previous chunk ends in the current chunk\n const prevSentences = splitSentences(prevChunk);\n const currSentences = splitSentences(currChunk);\n\n // Find how many sentences from prev are at the start of curr\n let overlapCount = 0;\n for (let j = 0; j < Math.min(prevSentences.length, currSentences.length); j++) {\n // Check if last N sentences of prev match first N sentences of curr\n const prevEnd = prevSentences.slice(-(j + 1));\n const currStart = currSentences.slice(0, j + 1);\n\n if (joinSentences(prevEnd) === joinSentences(currStart)) {\n overlapCount = j + 1;\n }\n }\n\n // Add non-overlapping portion\n if (overlapCount > 0 && overlapCount < currSentences.length) {\n result.push(joinSentences(currSentences.slice(overlapCount)));\n } else if (overlapCount === 0) {\n // No detected overlap, add full chunk\n result.push(currChunk);\n }\n // If overlapCount === currSentences.length, skip (fully contained)\n }\n\n return joinSentences(result);\n}\n"],"mappings":";;;;;AAmCO,IAAM,0BAA0C;AAAA,EACrD,cAAc;AAAA,EACd,WAAW;AAAA,EACX,kBAAkB;AACpB;AASA,IAAM,+BAA+B;AACrC,IAAM,6BAA6B;AACnC,IAAM,2BAA2B;AACjC,IAAM,sBAAsB;AAC5B,IAAM,SAAS;AACf,IAAM,qBACJ,OAAO,KAAK,cAAc,aACtB,IAAI,KAAK,UAAU,QAAW,EAAE,aAAa,WAAW,CAAC,IACzD;AAEN,SAAS,qBAAqB,WAA4B;AACxD,SACE,2BAA2B,SAAS,SAAS,KAC7C,6BAA6B,SAAS,SAAS;AAEnD;AAEA,SAAS,MAAM,WAA4B;AACzC,SAAO,6CAA6C,KAAK,SAAS;AACpE;AAIA,SAAS,uBAAuB,MAA4B;AAC1D,QAAM,YAAY,CAAC;AACnB,SAAO,eAAe,WAAW,cAAc;AAAA,IAC7C,OAAO,CAAC;AAAA,IACR,UAAU;AAAA,EACZ,CAAC;AACD,MAAI,QAAQ;AACZ,MAAI,YAAY;AAChB,WAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACpC,UAAM,KAAK,KAAK,CAAC;AACjB,QAAI,CAAC,qBAAqB,EAAE,EAAG;AAE/B,QAAI,MAAM;AACV,QAAI,uBAAuB,6BAA6B,SAAS,EAAE;AACnE,WAAO,MAAM,IAAI,KAAK,UAAU,qBAAqB,KAAK,MAAM,CAAC,CAAC,GAAG;AACnE;AACA,+BAAyB,6BAA6B,SAAS,KAAK,GAAG,CAAC;AAAA,IAC1E;AAEA,UAAM,qBACJ,OAAO,YACP,OAAO,SAAS,KAAK,IAAI,CAAC,KAAK,EAAE,KACjC,OAAO,SAAS,KAAK,MAAM,CAAC,KAAK,EAAE;AACrC,WAAO,MAAM,IAAI,KAAK,UAAU,oBAAoB,SAAS,KAAK,MAAM,CAAC,CAAC,GAAG;AAC3E;AAAA,IACF;AAEA,UAAM,QAAQ,KAAK,MAAM,CAAC;AAC1B,UAAM,WACJ,CAAC,uBACA,wBAAwB,UAAU,UAAa,MAAM,KAAK,KAAK;AAClE,QAAI,UAAU;AACZ,YAAM,WAAW,KAAK,MAAM,OAAO,MAAM,CAAC,EAAE,KAAK;AACjD,UAAI,SAAS,SAAS,GAAG;AACvB,kBAAU,KAAK,QAAQ;AACvB,YAAI,UAAU,SAAS,EAAG,WAAU,WAAW,KAAK,SAAS;AAAA,MAC/D;AACA,UAAI,YAAY,MAAM;AACtB,aAAO,YAAY,KAAK,UAAU,MAAM,KAAK,KAAK,SAAS,CAAC,EAAG;AAC/D,kBAAY,KAAK,MAAM,MAAM,GAAG,SAAS;AACzC,cAAQ;AAAA,IACV;AACA,QAAI;AAAA,EACN;AAEA,QAAM,YAAY,KAAK,MAAM,KAAK,EAAE,KAAK;AACzC,MAAI,UAAU,SAAS,GAAG;AACxB,cAAU,KAAK,SAAS;AACxB,QAAI,UAAU,SAAS,EAAG,WAAU,WAAW,KAAK,SAAS;AAAA,EAC/D;AACA,SAAO;AACT;AACO,SAAS,eAAe,MAAwB;AACrD,QAAM,kBACJ,uBAAuB,UACvB,CAAC,MAAM,KAAK,IAAI,KAChB,CAAC,GAAG,IAAI,EAAE,KAAK,CAAC,cAAc,6BAA6B,SAAS,SAAS,CAAC;AAChF,MAAI,iBAAiB;AACnB,UAAM,WAAW,MAAM;AAAA,MAAK,mBAAmB,QAAQ,IAAI;AAAA,MAAG,CAAC,EAAE,QAAQ,MACvE,QAAQ,KAAK;AAAA,IACf,EAAE,OAAO,CAAC,YAAY,QAAQ,SAAS,CAAC;AACxC,QACE,SAAS,SAAS,KAClB,SAAS,MAAM,GAAG,EAAE,EAAE;AAAA,MAAM,CAAC,YAC3B,CAAC,GAAG,OAAO,EAAE;AAAA,QAAK,CAAC,cACjB,6BAA6B,SAAS,SAAS;AAAA,MACjD;AAAA,IACF,GACA;AACA,aAAO,eAAe,UAAU,cAAc;AAAA,QAC5C,OAAO,MAAM,SAAS,SAAS,CAAC,EAAE,KAAK,EAAE;AAAA,QACzC,UAAU;AAAA,MACZ,CAAC;AACD,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO,uBAAuB,IAAI;AACpC;AAEA,SAAS,iBAAiB,MAAuB;AAC/C,MAAI,QAAQ,KAAK,SAAS;AAC1B,SAAO,SAAS,KAAK,oBAAoB,SAAS,KAAK,KAAK,CAAC,EAAG;AAChE,QAAM,aAAa,KAAK,KAAK,KAAK;AAClC,MAAI,yBAAyB,SAAS,UAAU,GAAG;AACjD,WAAO,MAAM,KAAK,QAAQ,CAAC,KAAK,EAAE;AAAA,EACpC;AACA,SAAO,eAAe,YAAO,MAAM,KAAK,QAAQ,CAAC,KAAK,EAAE;AAC1D;AACO,SAAS,cAAc,WAA6B;AACzD,MAAI,SAAS;AACb,QAAM,aAAc,UAAoC;AACxD,WAAS,IAAI,GAAG,IAAI,UAAU,QAAQ,KAAK;AACzC,UAAM,WAAW,UAAU,CAAC;AAC5B,QAAI,OAAO,SAAS,GAAG;AACrB,YAAM,YAAY,aAAa,IAAI,CAAC;AACpC,gBAAU,cAAc,iBAAiB,MAAM,IAAI,KAAK;AAAA,IAC1D;AACA,cAAU;AAAA,EACZ;AACA,SAAO;AACT;AAUO,SAAS,aACd,SACA,SAAyB,yBACZ;AACb,QAAM,cAAc,mBAAmB,OAAO;AAG9C,MAAI,cAAc,OAAO,WAAW;AAClC,WAAO;AAAA,MACL,SAAS;AAAA,MACT,QAAQ,CAAC;AAAA,QACP;AAAA,QACA,OAAO;AAAA,QACP,YAAY;AAAA,MACd,CAAC;AAAA,IACH;AAAA,EACF;AAEA,QAAM,YAAY,eAAe,OAAO;AAGxC,MAAI,UAAU,UAAU,GAAG;AACzB,WAAO;AAAA,MACL,SAAS;AAAA,MACT,QAAQ,CAAC;AAAA,QACP;AAAA,QACA,OAAO;AAAA,QACP,YAAY;AAAA,MACd,CAAC;AAAA,IACH;AAAA,EACF;AAEA,QAAM,SAAkB,CAAC;AACzB,MAAI,wBAAkC,CAAC;AACvC,MAAI,gBAAgB;AACpB,MAAI,aAAa;AAEjB,WAAS,IAAI,GAAG,IAAI,UAAU,QAAQ,KAAK;AACzC,UAAM,WAAW,UAAU,CAAC;AAC5B,UAAM,iBAAiB,mBAAmB,QAAQ;AAGlD,0BAAsB,KAAK,QAAQ;AACnC,qBAAiB;AAIjB,UAAM,WAAW,iBAAiB,OAAO;AACzC,UAAM,iBAAiB,MAAM,UAAU,SAAS;AAEhD,QAAI,YAAY,gBAAgB;AAE9B,YAAMA,gBAAe,cAAc,qBAAqB;AACxD,aAAO,KAAK;AAAA,QACV,SAASA;AAAA,QACT,OAAO;AAAA,QACP,YAAY,mBAAmBA,aAAY;AAAA,MAC7C,CAAC;AACD;AAGA,UAAI,CAAC,gBAAgB;AAInB,cAAM,eAAe,KAAK,IAAI,OAAO,kBAAkB,sBAAsB,MAAM;AACnF,YAAI,gBAAgB,GAAG;AACrB,kCAAwB,CAAC;AACzB,0BAAgB;AAAA,QAClB,OAAO;AACL,kCAAwB,sBAAsB,MAAM,CAAC,YAAY;AACjE,0BAAgB,sBAAsB,OAAO,CAAC,KAAK,MAAM,MAAM,mBAAmB,CAAC,GAAG,CAAC;AAAA,QACzF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAGA,SAAO;AAAA,IACL,SAAS,OAAO,SAAS;AAAA,IACzB;AAAA,EACF;AACF;AASO,SAAS,iBAAiB,QAA0B;AACzD,MAAI,OAAO,WAAW,EAAG,QAAO;AAChC,MAAI,OAAO,WAAW,EAAG,QAAO,OAAO,CAAC;AAKxC,QAAM,SAAmB,CAAC,OAAO,CAAC,CAAC;AAEnC,WAAS,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK;AACtC,UAAM,YAAY,OAAO,IAAI,CAAC;AAC9B,UAAM,YAAY,OAAO,CAAC;AAI1B,UAAM,gBAAgB,eAAe,SAAS;AAC9C,UAAM,gBAAgB,eAAe,SAAS;AAG9C,QAAI,eAAe;AACnB,aAAS,IAAI,GAAG,IAAI,KAAK,IAAI,cAAc,QAAQ,cAAc,MAAM,GAAG,KAAK;AAE7E,YAAM,UAAU,cAAc,MAAM,EAAE,IAAI,EAAE;AAC5C,YAAM,YAAY,cAAc,MAAM,GAAG,IAAI,CAAC;AAE9C,UAAI,cAAc,OAAO,MAAM,cAAc,SAAS,GAAG;AACvD,uBAAe,IAAI;AAAA,MACrB;AAAA,IACF;AAGA,QAAI,eAAe,KAAK,eAAe,cAAc,QAAQ;AAC3D,aAAO,KAAK,cAAc,cAAc,MAAM,YAAY,CAAC,CAAC;AAAA,IAC9D,WAAW,iBAAiB,GAAG;AAE7B,aAAO,KAAK,SAAS;AAAA,IACvB;AAAA,EAEF;AAEA,SAAO,cAAc,MAAM;AAC7B;","names":["chunkContent"]}
@@ -9,14 +9,14 @@ import {
9
9
  } from "./chunk-666A3MOW.js";
10
10
  import {
11
11
  rebuildMemoryLifecycleLedger
12
- } from "./chunk-DNC4A7RS.js";
12
+ } from "./chunk-OU7RWWW6.js";
13
13
  import {
14
14
  planNamespaceMaintenance,
15
15
  runNamespaceMaintenanceBatchPlan
16
- } from "./chunk-4YZCIK5E.js";
16
+ } from "./chunk-EZXGJJXM.js";
17
17
  import {
18
18
  rebuildMemoryProjection
19
- } from "./chunk-IT57AOMC.js";
19
+ } from "./chunk-AH2NX6S7.js";
20
20
  import {
21
21
  activityDayWindow,
22
22
  activityDigestPath,
@@ -1719,4 +1719,4 @@ export {
1719
1719
  ActivitySyncScheduler,
1720
1720
  MaintenanceScheduler
1721
1721
  };
1722
- //# sourceMappingURL=chunk-4O6R3GPK.js.map
1722
+ //# sourceMappingURL=chunk-6W2ISYUV.js.map
@@ -2,10 +2,10 @@ import {
2
2
  discoverMemoryExtensions,
3
3
  renderExtensionsBlock,
4
4
  resolveExtensionsRoot
5
- } from "./chunk-JLNBQWZ2.js";
5
+ } from "./chunk-5JMAGU26.js";
6
6
  import {
7
7
  runPostConsolidationMaterialize
8
- } from "./chunk-VYIXCMUP.js";
8
+ } from "./chunk-KBIIE57I.js";
9
9
  import {
10
10
  isSemanticConsolidationLlmOperator
11
11
  } from "./chunk-G7D6GZ5J.js";
@@ -220,4 +220,4 @@ export {
220
220
  buildExtensionsBlockForConsolidation,
221
221
  materializeAfterSemanticConsolidation
222
222
  };
223
- //# sourceMappingURL=chunk-3AJOJOFP.js.map
223
+ //# sourceMappingURL=chunk-77DAFTUU.js.map
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  parseXrayFormat
3
- } from "./chunk-U7BMN33M.js";
3
+ } from "./chunk-UBSM6TZP.js";
4
4
  import {
5
5
  RECALL_DISCLOSURE_LEVELS,
6
6
  isRecallDisclosure
7
- } from "./chunk-5QHDXMXP.js";
7
+ } from "./chunk-LLZKOSGM.js";
8
8
 
9
9
  // src/recall-xray-cli.ts
10
10
  function parseXrayDisclosureFlag(value) {
@@ -50,4 +50,4 @@ export {
50
50
  parseXrayBudgetFlag,
51
51
  parseXrayCliOptions
52
52
  };
53
- //# sourceMappingURL=chunk-IKT6ZVO7.js.map
53
+ //# sourceMappingURL=chunk-7DHFB5SG.js.map
@@ -2,7 +2,7 @@ import {
2
2
  SupportPassportError,
3
3
  SupportPassportOwnerCreateGrantRequestSchema,
4
4
  getOperation
5
- } from "./chunk-CZUHNJHX.js";
5
+ } from "./chunk-6C7Q6NG3.js";
6
6
 
7
7
  // src/support-passport/access-http.ts
8
8
  var OWNER_ROOT = "/engram/v1/support-passport";
@@ -137,4 +137,4 @@ export {
137
137
  SUPPORT_PASSPORT_OWNER_HTTP_ROUTES,
138
138
  handleSupportPassportOwnerHttp
139
139
  };
140
- //# sourceMappingURL=chunk-SFR7J2G6.js.map
140
+ //# sourceMappingURL=chunk-7G34CBKS.js.map
@@ -7,10 +7,10 @@ import {
7
7
  import {
8
8
  listMemoryGovernanceRuns,
9
9
  readMemoryGovernanceRunArtifact
10
- } from "./chunk-6RK2X7BR.js";
10
+ } from "./chunk-G3JFFUKR.js";
11
11
  import {
12
12
  StorageManager
13
- } from "./chunk-YTZA7U6Q.js";
13
+ } from "./chunk-2VHCLHZ5.js";
14
14
  import {
15
15
  normalizeProjectionPreview,
16
16
  normalizeProjectionTags
@@ -1005,4 +1005,4 @@ export {
1005
1005
  verifyMemoryProjection,
1006
1006
  repairMemoryProjection
1007
1007
  };
1008
- //# sourceMappingURL=chunk-IT57AOMC.js.map
1008
+ //# sourceMappingURL=chunk-AH2NX6S7.js.map
@@ -4,6 +4,9 @@ import {
4
4
  import {
5
5
  sessionStoragePaths
6
6
  } from "./chunk-S4DDLTPX.js";
7
+ import {
8
+ estimateTokenCount
9
+ } from "./chunk-TOTT6S2V.js";
7
10
  import {
8
11
  resolveSafeStoragePath
9
12
  } from "./chunk-FF4KLI5W.js";
@@ -42,8 +45,6 @@ var TranscriptManager = class _TranscriptManager {
42
45
  directoryOwnershipCache = /* @__PURE__ */ new Map();
43
46
  /** Default checkpoint TTL in hours */
44
47
  static DEFAULT_CHECKPOINT_TTL_HOURS = 24;
45
- /** Approximate characters per token for rough estimation */
46
- static CHARS_PER_TOKEN = 4;
47
48
  constructor(config) {
48
49
  this.config = config;
49
50
  this.transcriptsDir = path.join(config.memoryDir, "transcripts");
@@ -327,6 +328,7 @@ var TranscriptManager = class _TranscriptManager {
327
328
  async estimateSessionFootprint(sessionKey) {
328
329
  const { dir, alternateDir, legacyDir, readbackDirs } = this.getTranscriptPath(sessionKey);
329
330
  let bytes = 0;
331
+ let tokens = 0;
330
332
  try {
331
333
  const files = await this.getSessionStorageFiles(
332
334
  this.transcriptsDir,
@@ -339,27 +341,30 @@ var TranscriptManager = class _TranscriptManager {
339
341
  if (!cached) {
340
342
  const fileBytes = /* @__PURE__ */ new Map();
341
343
  const fileSizes = /* @__PURE__ */ new Map();
344
+ const fileTokens = /* @__PURE__ */ new Map();
342
345
  for (const file of files) {
343
346
  try {
344
347
  const fileInfo = await stat(file.path);
345
- const sessionBytes = await this.estimateSessionBytesInFile(
346
- file.path,
347
- sessionKey
348
- );
349
- fileBytes.set(file.cacheKey, sessionBytes);
348
+ const estimate = await this.estimateSessionBytesInFile(file.path, sessionKey);
349
+ fileBytes.set(file.cacheKey, estimate.bytes);
350
+ fileTokens.set(file.cacheKey, estimate.tokens);
350
351
  fileSizes.set(file.cacheKey, Math.max(0, fileInfo.size));
351
- bytes += sessionBytes;
352
+ bytes += estimate.bytes;
353
+ tokens += estimate.tokens;
352
354
  } catch {
353
355
  }
354
356
  }
355
- this.sessionFootprintCache.set(sessionKey, { totalBytes: bytes, fileBytes, fileSizes });
357
+ this.sessionFootprintCache.set(sessionKey, { totalBytes: bytes, totalTokens: tokens, fileBytes, fileSizes, fileTokens });
356
358
  } else {
357
359
  bytes = cached.totalBytes;
360
+ tokens = cached.totalTokens;
358
361
  const seen = new Set(files.map((file) => file.cacheKey));
359
362
  for (const [cachedFile, cachedSessionBytes] of cached.fileBytes.entries()) {
360
363
  if (!seen.has(cachedFile)) {
361
364
  bytes -= cachedSessionBytes;
365
+ tokens -= cached.fileTokens.get(cachedFile) ?? 0;
362
366
  cached.fileBytes.delete(cachedFile);
367
+ cached.fileTokens.delete(cachedFile);
363
368
  cached.fileSizes.delete(cachedFile);
364
369
  }
365
370
  }
@@ -367,10 +372,12 @@ var TranscriptManager = class _TranscriptManager {
367
372
  if (cached.fileBytes.has(file.cacheKey)) continue;
368
373
  try {
369
374
  const fileInfo = await stat(file.path);
370
- const sessionBytes = await this.estimateSessionBytesInFile(file.path, sessionKey);
371
- cached.fileBytes.set(file.cacheKey, sessionBytes);
375
+ const estimate = await this.estimateSessionBytesInFile(file.path, sessionKey);
376
+ cached.fileBytes.set(file.cacheKey, estimate.bytes);
377
+ cached.fileTokens.set(file.cacheKey, estimate.tokens);
372
378
  cached.fileSizes.set(file.cacheKey, Math.max(0, fileInfo.size));
373
- bytes += sessionBytes;
379
+ bytes += estimate.bytes;
380
+ tokens += estimate.tokens;
374
381
  } catch {
375
382
  }
376
383
  }
@@ -378,51 +385,54 @@ var TranscriptManager = class _TranscriptManager {
378
385
  try {
379
386
  const fileInfo = await stat(file.path);
380
387
  const size = Math.max(0, fileInfo.size);
381
- const previousSessionBytes = cached.fileBytes.get(file.cacheKey) ?? 0;
388
+ const previousEstimate = {
389
+ bytes: cached.fileBytes.get(file.cacheKey) ?? 0,
390
+ tokens: cached.fileTokens.get(file.cacheKey) ?? 0
391
+ };
382
392
  const previousSize = cached.fileSizes.get(file.cacheKey) ?? -1;
383
393
  if (size !== previousSize) {
384
- const sessionBytes = await this.estimateSessionBytesInFile(file.path, sessionKey);
385
- cached.fileBytes.set(file.cacheKey, sessionBytes);
394
+ const estimate = await this.estimateSessionBytesInFile(file.path, sessionKey);
395
+ cached.fileBytes.set(file.cacheKey, estimate.bytes);
396
+ cached.fileTokens.set(file.cacheKey, estimate.tokens);
386
397
  cached.fileSizes.set(file.cacheKey, size);
387
- bytes += sessionBytes - previousSessionBytes;
398
+ bytes += estimate.bytes - previousEstimate.bytes;
399
+ tokens += estimate.tokens - previousEstimate.tokens;
388
400
  }
389
401
  } catch {
390
402
  }
391
403
  }
392
- if (bytes < 0) bytes = 0;
404
+ bytes = Math.max(0, bytes);
405
+ tokens = Math.max(0, tokens);
393
406
  cached.totalBytes = bytes;
407
+ cached.totalTokens = tokens;
394
408
  }
395
409
  } catch {
396
410
  this.sessionFootprintCache.delete(sessionKey);
397
411
  }
398
- return {
399
- bytes,
400
- tokens: Math.floor(bytes / _TranscriptManager.CHARS_PER_TOKEN)
401
- };
412
+ return { bytes, tokens };
402
413
  }
403
414
  async estimateSessionBytesInFile(filePath, sessionKey) {
404
415
  try {
405
416
  const raw = await readFile(filePath, "utf-8");
406
- let total = 0;
417
+ let bytes = 0;
418
+ let tokens = 0;
407
419
  for (const line of raw.split("\n")) {
408
420
  if (!line.trim()) continue;
409
421
  try {
410
422
  const parsed = JSON.parse(line);
411
423
  if (parsed.sessionKey === sessionKey) {
412
- total += Buffer.byteLength(`${line}
424
+ bytes += Buffer.byteLength(`${line}
413
425
  `, "utf-8");
426
+ if (typeof parsed.content === "string") tokens += estimateTokenCount(parsed.content);
414
427
  }
415
428
  } catch {
416
429
  }
417
430
  }
418
- return total;
431
+ return { bytes, tokens };
419
432
  } catch {
420
- return 0;
433
+ return { bytes: 0, tokens: 0 };
421
434
  }
422
435
  }
423
- /**
424
- * Check if a file is a legacy flat transcript file (YYYY-MM-DD.jsonl format).
425
- */
426
436
  isLegacyTranscriptFile(filename) {
427
437
  return /^\d{4}-\d{2}-\d{2}\.jsonl$/.test(filename);
428
438
  }
@@ -808,7 +818,8 @@ var TranscriptManager = class _TranscriptManager {
808
818
  if (entries.length === 0) {
809
819
  return "";
810
820
  }
811
- const maxChars = maxTokens * _TranscriptManager.CHARS_PER_TOKEN;
821
+ const maxTokenBudget = Math.max(0, Math.floor(maxTokens));
822
+ if (maxTokenBudget === 0) return "";
812
823
  const lines = [];
813
824
  const firstEntry = new Date(entries[0].timestamp);
814
825
  const lastEntry = new Date(entries[entries.length - 1].timestamp);
@@ -819,6 +830,9 @@ var TranscriptManager = class _TranscriptManager {
819
830
  lines.push(`## Recent Conversation (last ${hoursDiff} hour${hoursDiff === 1 ? "" : "s"})`);
820
831
  }
821
832
  lines.push("");
833
+ if (estimateTokenCount(lines.join("\n")) > maxTokenBudget) {
834
+ return "";
835
+ }
822
836
  const formattedEntries = [];
823
837
  for (const entry of entries) {
824
838
  const time = new Date(entry.timestamp);
@@ -830,17 +844,17 @@ var TranscriptManager = class _TranscriptManager {
830
844
  const roleLabel = entry.role === "user" ? "User" : "Assistant";
831
845
  formattedEntries.push(`[${timeStr}] ${roleLabel}: ${entry.content}`);
832
846
  }
833
- let totalChars = lines.join("\n").length;
834
847
  const selectedEntries = [];
835
848
  for (let i = formattedEntries.length - 1; i >= 0; i--) {
836
849
  const entry = formattedEntries[i];
837
- const entryChars = entry.length + 1;
838
- if (totalChars + entryChars > maxChars && selectedEntries.length > 0) {
839
- break;
850
+ const candidate = [...lines, entry, ...selectedEntries, ""].join("\n");
851
+ if (estimateTokenCount(candidate) > maxTokenBudget) {
852
+ if (selectedEntries.length > 0) break;
853
+ return "";
840
854
  }
841
855
  selectedEntries.unshift(entry);
842
- totalChars += entryChars;
843
856
  }
857
+ if (selectedEntries.length === 0) return "";
844
858
  lines.push(...selectedEntries);
845
859
  lines.push("");
846
860
  const result = lines.join("\n");
@@ -938,4 +952,4 @@ export {
938
952
  utcDayRange,
939
953
  TranscriptManager
940
954
  };
941
- //# sourceMappingURL=chunk-HYQJQGD3.js.map
955
+ //# sourceMappingURL=chunk-AM4T6CHX.js.map