@remnic/core 9.57.5 → 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 (476) 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 +15 -14
  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.d.ts +88 -88
  42. package/dist/access-schema.js +3 -3
  43. package/dist/{access-service-D9xXmKvd.d.ts → access-service-BQkRul4d.d.ts} +71 -71
  44. package/dist/access-service-helpers.d.ts +12 -11
  45. package/dist/access-service.d.ts +12 -11
  46. package/dist/access-service.js +25 -24
  47. package/dist/access-surface-catalog.d.ts +12 -11
  48. package/dist/access-surface-catalog.js +27 -26
  49. package/dist/access-surface-catalog.js.map +1 -1
  50. package/dist/access-wearables-meetings-surface.d.ts +3 -3
  51. package/dist/action-confidence.d.ts +1 -1
  52. package/dist/active-memory-bridge.d.ts +1 -1
  53. package/dist/active-recall.d.ts +1 -1
  54. package/dist/active-recall.js +1 -1
  55. package/dist/ambient-provenance.d.ts +1 -1
  56. package/dist/artifact-search.d.ts +1 -1
  57. package/dist/behavior-learner.d.ts +1 -1
  58. package/dist/behavior-signals.d.ts +1 -1
  59. package/dist/bootstrap.d.ts +10 -9
  60. package/dist/briefing-window.d.ts +2 -2
  61. package/dist/briefing.d.ts +2 -2
  62. package/dist/briefing.js +10 -10
  63. package/dist/buffer-surprise-report.d.ts +1 -1
  64. package/dist/buffer-surprise.js +4 -3
  65. package/dist/buffer-turn-helpers.d.ts +1 -1
  66. package/dist/buffer.d.ts +2 -2
  67. package/dist/bulk-import/index.d.ts +3 -3
  68. package/dist/calibration.d.ts +1 -1
  69. package/dist/capabilities.d.ts +1 -1
  70. package/dist/{capsule-crypto-YO5QJ6L3.js → capsule-crypto-GWVG7LGC.js} +2 -2
  71. package/dist/{catalog-BasnO3Bw.d.ts → catalog-z1UerMbh.d.ts} +1 -1
  72. package/dist/causal-behavior.d.ts +2 -1
  73. package/dist/causal-consolidation.d.ts +1 -1
  74. package/dist/causal-consolidation.js +14 -13
  75. package/dist/causal-consolidation.js.map +1 -1
  76. package/dist/causal-trajectory-graph.d.ts +1 -1
  77. package/dist/{chunk-P4BC54KI.js → chunk-2GBFKHBZ.js} +14 -38
  78. package/dist/chunk-2GBFKHBZ.js.map +1 -0
  79. package/dist/{chunk-YTZA7U6Q.js → chunk-2VHCLHZ5.js} +3 -3
  80. package/dist/{chunk-NEJQ65NV.js → chunk-4CLE2LXB.js} +2 -2
  81. package/dist/{chunk-3VFVQDZO.js → chunk-4IIKJOYM.js} +1 -1
  82. package/dist/{chunk-3VFVQDZO.js.map → chunk-4IIKJOYM.js.map} +1 -1
  83. package/dist/{chunk-JLNBQWZ2.js → chunk-5JMAGU26.js} +7 -6
  84. package/dist/{chunk-JLNBQWZ2.js.map → chunk-5JMAGU26.js.map} +1 -1
  85. package/dist/{chunk-VDX2J7OX.js → chunk-5W7CTZ2E.js} +4 -4
  86. package/dist/{chunk-CZUHNJHX.js → chunk-6C7Q6NG3.js} +13 -13
  87. package/dist/chunk-6TBIWIAG.js +201 -0
  88. package/dist/chunk-6TBIWIAG.js.map +1 -0
  89. package/dist/{chunk-4O6R3GPK.js → chunk-6W2ISYUV.js} +4 -4
  90. package/dist/{chunk-3AJOJOFP.js → chunk-77DAFTUU.js} +3 -3
  91. package/dist/{chunk-IKT6ZVO7.js → chunk-7DHFB5SG.js} +3 -3
  92. package/dist/{chunk-SFR7J2G6.js → chunk-7G34CBKS.js} +2 -2
  93. package/dist/{chunk-IT57AOMC.js → chunk-AH2NX6S7.js} +3 -3
  94. package/dist/{chunk-HYQJQGD3.js → chunk-AM4T6CHX.js} +49 -35
  95. package/dist/chunk-AM4T6CHX.js.map +1 -0
  96. package/dist/{chunk-Z54EM5YZ.js → chunk-B2QHVLJO.js} +60 -30
  97. package/dist/chunk-B2QHVLJO.js.map +1 -0
  98. package/dist/{chunk-FI3PIOOI.js → chunk-B4CSPFYN.js} +8 -8
  99. package/dist/{chunk-7PJARJS4.js → chunk-BPM6UHUL.js} +2 -2
  100. package/dist/{chunk-PPUTUOC3.js → chunk-BTHDQOF4.js} +5 -5
  101. package/dist/{chunk-AW4A4XOP.js → chunk-CZGOLAEW.js} +41 -41
  102. package/dist/{chunk-5PGYCWA7.js → chunk-DE6YCZEE.js} +2 -2
  103. package/dist/{chunk-JOA4Q765.js → chunk-EUYWQ7TS.js} +2 -2
  104. package/dist/{chunk-O5AFC5KM.js → chunk-EWL5DICB.js} +2 -2
  105. package/dist/{chunk-4YZCIK5E.js → chunk-EZXGJJXM.js} +2 -2
  106. package/dist/{chunk-GXIY65UH.js → chunk-FI5EAP4N.js} +2 -2
  107. package/dist/{chunk-YOKSRIFI.js → chunk-FUWS2MNJ.js} +4 -4
  108. package/dist/{chunk-6RK2X7BR.js → chunk-G3JFFUKR.js} +2 -2
  109. package/dist/{chunk-VANZKHTN.js → chunk-G5OB4XZX.js} +4 -4
  110. package/dist/{chunk-66SLUXKM.js → chunk-HK6KYIF6.js} +2 -2
  111. package/dist/{chunk-XBGLEOID.js → chunk-I4BLRM7X.js} +9 -9
  112. package/dist/{chunk-5ZIWRMZM.js → chunk-IVKIODHO.js} +7 -6
  113. package/dist/chunk-IVKIODHO.js.map +1 -0
  114. package/dist/{chunk-6SX4MY5Q.js → chunk-J65AKOD6.js} +2 -2
  115. package/dist/{chunk-BPWZDHFM.js → chunk-J6QIAYNR.js} +2 -2
  116. package/dist/{chunk-XL3TATMJ.js → chunk-JHMAYABJ.js} +4 -4
  117. package/dist/{chunk-F7M36OG2.js → chunk-JNWISMYG.js} +10 -10
  118. package/dist/{chunk-VYIXCMUP.js → chunk-KBIIE57I.js} +3 -3
  119. package/dist/{chunk-QKPDN325.js → chunk-KF75ANST.js} +6 -3
  120. package/dist/chunk-KF75ANST.js.map +1 -0
  121. package/dist/{chunk-24PNX2GF.js → chunk-KXYMU4IN.js} +3 -3
  122. package/dist/{chunk-FKTD7IVS.js → chunk-L3JVAM5Q.js} +2 -2
  123. package/dist/{chunk-5QHDXMXP.js → chunk-LLZKOSGM.js} +1 -1
  124. package/dist/{chunk-5QHDXMXP.js.map → chunk-LLZKOSGM.js.map} +1 -1
  125. package/dist/{chunk-LIF6DO6U.js → chunk-MG7GI4TT.js} +2 -2
  126. package/dist/{chunk-RUZM6Z52.js → chunk-MMZK2DWN.js} +1 -1
  127. package/dist/{chunk-NTJ6TY44.js → chunk-ODAE36PG.js} +3 -3
  128. package/dist/{chunk-QFN4U7AG.js → chunk-ODZKV2F7.js} +3 -3
  129. package/dist/{chunk-QFN4U7AG.js.map → chunk-ODZKV2F7.js.map} +1 -1
  130. package/dist/{chunk-YLHM5BQQ.js → chunk-OLX2HJFW.js} +4 -4
  131. package/dist/{chunk-DNC4A7RS.js → chunk-OU7RWWW6.js} +2 -2
  132. package/dist/{chunk-L3LRWDJW.js → chunk-OV36CROW.js} +3 -3
  133. package/dist/{chunk-BXLOS5AJ.js → chunk-OWHERGF2.js} +2 -2
  134. package/dist/{chunk-CSV34HIW.js → chunk-P5OYNV7B.js} +2 -2
  135. package/dist/{chunk-SNW3YRO6.js → chunk-P6GOYLHN.js} +2 -2
  136. package/dist/{chunk-C5B3CIMJ.js → chunk-PFPNUFKZ.js} +109 -59
  137. package/dist/chunk-PFPNUFKZ.js.map +1 -0
  138. package/dist/{chunk-USGKHF36.js → chunk-QUJC22CT.js} +2 -2
  139. package/dist/{chunk-BVRREV3N.js → chunk-R7IV3EUJ.js} +2 -2
  140. package/dist/{chunk-KSXJOC4F.js → chunk-RDFF2NAU.js} +18 -8
  141. package/dist/chunk-RDFF2NAU.js.map +1 -0
  142. package/dist/{chunk-D4LVHPOA.js → chunk-RXN7Z2V5.js} +3 -3
  143. package/dist/{chunk-7VSHDLJQ.js → chunk-T2VJ2EO5.js} +6 -6
  144. package/dist/chunk-TOTT6S2V.js +20 -0
  145. package/dist/chunk-TOTT6S2V.js.map +1 -0
  146. package/dist/{chunk-U7BMN33M.js → chunk-UBSM6TZP.js} +2 -2
  147. package/dist/{chunk-DQEMWVMT.js → chunk-UVYI6VIX.js} +1 -1
  148. package/dist/{chunk-U7XLOSG2.js → chunk-VIM6QH4D.js} +2 -2
  149. package/dist/{chunk-LJJWFV2Q.js → chunk-VJXSAJFW.js} +2 -2
  150. package/dist/{chunk-HZC3OECF.js → chunk-VOKK4WCG.js} +2 -2
  151. package/dist/{chunk-LTAVSOZW.js → chunk-WVP2I6C5.js} +5 -4
  152. package/dist/chunk-WVP2I6C5.js.map +1 -0
  153. package/dist/{chunk-LRDP2AC2.js → chunk-YBCZTBN2.js} +2 -2
  154. package/dist/{chunk-USAUBGBC.js → chunk-YKZFX2HQ.js} +2 -2
  155. package/dist/{chunk-I2GIYTRU.js → chunk-ZOGOOSB3.js} +2 -2
  156. package/dist/{chunk-PBGWYM3H.js → chunk-ZPUIRTIZ.js} +27 -15
  157. package/dist/chunk-ZPUIRTIZ.js.map +1 -0
  158. package/dist/chunking.d.ts +3 -1
  159. package/dist/chunking.js +8 -3
  160. package/dist/{cli-DTOkYhKI.d.ts → cli-DsyVvOFP.d.ts} +5 -5
  161. package/dist/cli.d.ts +14 -13
  162. package/dist/cli.js +57 -56
  163. package/dist/coding/pre-action-gate.d.ts +1 -1
  164. package/dist/compounding/engine.d.ts +2 -2
  165. package/dist/compounding/engine.js +10 -10
  166. package/dist/compounding/preference-consolidator.d.ts +2 -1
  167. package/dist/compression-optimizer.d.ts +1 -1
  168. package/dist/config.d.ts +1 -1
  169. package/dist/config.js +1 -1
  170. package/dist/connectors/codex-materialize-runner.d.ts +1 -1
  171. package/dist/connectors/codex-materialize-runner.js +12 -11
  172. package/dist/connectors/codex-materialize.d.ts +7 -12
  173. package/dist/connectors/codex-materialize.js +2 -1
  174. package/dist/connectors/index.d.ts +1 -1
  175. package/dist/connectors/index.js +12 -11
  176. package/dist/consolidation-provenance-check.d.ts +2 -2
  177. package/dist/consolidation-undo.d.ts +2 -2
  178. package/dist/contradiction/index.d.ts +2 -2
  179. package/dist/contradiction/index.js +4 -4
  180. package/dist/converge-config.d.ts +1 -1
  181. package/dist/convergence-refresh.d.ts +2 -2
  182. package/dist/convergence-refresh.js +12 -12
  183. package/dist/conversation-index/backend.d.ts +1 -1
  184. package/dist/conversation-index/chunker.d.ts +1 -1
  185. package/dist/conversation-index/faiss-adapter.d.ts +1 -1
  186. package/dist/conversation-index/indexer.d.ts +1 -1
  187. package/dist/conversation-index/search.d.ts +1 -1
  188. package/dist/corpus-watermark.d.ts +1 -1
  189. package/dist/corpus-watermark.js +12 -12
  190. package/dist/day-summary.d.ts +1 -1
  191. package/dist/day-summary.js +3 -2
  192. package/dist/delinearize.d.ts +1 -1
  193. package/dist/dependency-propagation-config.d.ts +1 -1
  194. package/dist/{dependency-propagation-delivery-C_K_AHcv.d.ts → dependency-propagation-delivery-DdwSc1qh.d.ts} +1 -1
  195. package/dist/direct-answer-wiring.d.ts +1 -1
  196. package/dist/direct-answer.d.ts +1 -1
  197. package/dist/embedding-fallback.d.ts +1 -1
  198. package/dist/enrichment/index.d.ts +1 -1
  199. package/dist/entity-retrieval.d.ts +2 -2
  200. package/dist/entity-retrieval.js +10 -10
  201. package/dist/entity-schema.d.ts +1 -1
  202. package/dist/explicit-capture.d.ts +10 -9
  203. package/dist/external-wiki-access.d.ts +12 -11
  204. package/dist/external-wiki-access.js +26 -25
  205. package/dist/external-wiki-collection-registration.d.ts +1 -1
  206. package/dist/external-wiki-collection.d.ts +1 -1
  207. package/dist/external-wiki-mcp-tools.d.ts +12 -11
  208. package/dist/extraction-error-classification.d.ts +1 -1
  209. package/dist/extraction-faithfulness.d.ts +1 -1
  210. package/dist/extraction-judge-telemetry.d.ts +1 -1
  211. package/dist/extraction-judge-training.d.ts +1 -1
  212. package/dist/extraction-judge.d.ts +1 -1
  213. package/dist/extraction-liveness.d.ts +1 -1
  214. package/dist/extraction-normalization.d.ts +1 -1
  215. package/dist/extraction-prompt.d.ts +1 -1
  216. package/dist/extraction-source-grounding-rules.d.ts +1 -1
  217. package/dist/extraction-source-grounding.d.ts +1 -1
  218. package/dist/extraction.d.ts +1 -1
  219. package/dist/extraction.js +16 -15
  220. package/dist/fallback-llm.d.ts +1 -1
  221. package/dist/graph-dashboard-diff.d.ts +1 -1
  222. package/dist/graph-dashboard-key.d.ts +1 -1
  223. package/dist/graph-dashboard-parser.d.ts +1 -1
  224. package/dist/graph-edge-reinforcement.d.ts +1 -1
  225. package/dist/graph-path-reconstruction.d.ts +1 -1
  226. package/dist/graph-path-scoring.d.ts +1 -1
  227. package/dist/graph-snapshot.d.ts +1 -1
  228. package/dist/graph.d.ts +1 -1
  229. package/dist/harmonic-retrieval.js +1 -1
  230. package/dist/importance.d.ts +1 -1
  231. package/dist/importers/index.d.ts +1 -1
  232. package/dist/in-flight-reads.d.ts +1 -1
  233. package/dist/index.d.ts +698 -697
  234. package/dist/index.js +81 -80
  235. package/dist/intent.d.ts +1 -1
  236. package/dist/lcm/archive.d.ts +3 -2
  237. package/dist/lcm/archive.js +2 -1
  238. package/dist/lcm/engine.d.ts +2 -1
  239. package/dist/lcm/engine.js +5 -4
  240. package/dist/lcm/index.d.ts +2 -1
  241. package/dist/lcm/index.js +7 -6
  242. package/dist/lcm/recall.d.ts +1 -0
  243. package/dist/lcm/summarizer.d.ts +1 -0
  244. package/dist/lcm/summarizer.js +3 -2
  245. package/dist/lcm/tools.d.ts +2 -1
  246. package/dist/lifecycle.d.ts +1 -1
  247. package/dist/live-connectors-runner.d.ts +1 -1
  248. package/dist/local-llm.d.ts +2 -3
  249. package/dist/local-llm.js +2 -1
  250. package/dist/local-model-endpoint.d.ts +1 -1
  251. package/dist/maintenance/memory-governance.d.ts +1 -1
  252. package/dist/maintenance/memory-governance.js +10 -10
  253. package/dist/maintenance/rebuild-memory-lifecycle-ledger.d.ts +2 -2
  254. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +10 -10
  255. package/dist/maintenance/rebuild-memory-projection.d.ts +2 -2
  256. package/dist/maintenance/rebuild-memory-projection.js +11 -11
  257. package/dist/{maintenance-DdP931i9.d.ts → maintenance-DvkiF7fm.d.ts} +3 -3
  258. package/dist/mcp-memory-inspector-app.d.ts +12 -11
  259. package/dist/memory-action-policy.d.ts +1 -1
  260. package/dist/memory-cache.d.ts +1 -1
  261. package/dist/memory-lifecycle-ledger-utils.d.ts +1 -1
  262. package/dist/memory-projection-store.d.ts +1 -1
  263. package/dist/memory-provenance.d.ts +1 -1
  264. package/dist/memory-snapshot.d.ts +1 -1
  265. package/dist/memory-worth-outcomes.d.ts +2 -2
  266. package/dist/models-json.d.ts +1 -1
  267. package/dist/namespaces/migrate.d.ts +3 -3
  268. package/dist/namespaces/migrate.js +11 -11
  269. package/dist/namespaces/principal.d.ts +1 -1
  270. package/dist/namespaces/search.d.ts +1 -1
  271. package/dist/namespaces/search.js +7 -7
  272. package/dist/namespaces/storage.d.ts +3 -3
  273. package/dist/namespaces/storage.js +10 -10
  274. package/dist/native-knowledge.d.ts +1 -1
  275. package/dist/offline-sync-impression-drain.d.ts +1 -1
  276. package/dist/operator-doctor-corpus.d.ts +1 -1
  277. package/dist/operator-doctor-corpus.js +13 -13
  278. package/dist/operator-doctor-replica.d.ts +1 -1
  279. package/dist/operator-toolkit.d.ts +3 -3
  280. package/dist/operator-toolkit.js +18 -18
  281. package/dist/orchestration/compression-guideline-coordinator.d.ts +2 -2
  282. package/dist/orchestration/maintenance.d.ts +4 -4
  283. package/dist/orchestration/maintenance.js +15 -15
  284. package/dist/{orchestrator-DxUWX7gs.d.ts → orchestrator-CXPIzs3D.d.ts} +9 -9
  285. package/dist/orchestrator.d.ts +10 -9
  286. package/dist/orchestrator.js +77 -76
  287. package/dist/patterns-cli.d.ts +1 -1
  288. package/dist/{pipeline-BKUMU1vU.d.ts → pipeline-BuYWU9rt.d.ts} +1 -1
  289. package/dist/policy-runtime.d.ts +1 -1
  290. package/dist/proactive-contention.d.ts +1 -1
  291. package/dist/provenance.d.ts +1 -1
  292. package/dist/{public-http-C01DLhAp.d.ts → public-http-m9x1Qyzn.d.ts} +1 -1
  293. package/dist/{qmd-DJP-GLFe.d.ts → qmd-DYnrPw-m.d.ts} +1 -1
  294. package/dist/qmd-preflight.d.ts +2 -2
  295. package/dist/qmd-recall-cache.d.ts +1 -1
  296. package/dist/qmd.d.ts +2 -2
  297. package/dist/recall-concurrency-config.d.ts +1 -1
  298. package/dist/recall-disclosure-escalation.d.ts +1 -1
  299. package/dist/recall-explain-renderer.d.ts +1 -1
  300. package/dist/recall-explain-renderer.js +4 -3
  301. package/dist/recall-memory-map.d.ts +1 -1
  302. package/dist/recall-planner-llm.d.ts +1 -1
  303. package/dist/recall-state.d.ts +1 -1
  304. package/dist/recall-tag-filter.d.ts +1 -1
  305. package/dist/recall-timings.d.ts +1 -1
  306. package/dist/recall-xray-cli.d.ts +1 -1
  307. package/dist/recall-xray-cli.js +5 -4
  308. package/dist/recall-xray-renderer.d.ts +1 -1
  309. package/dist/recall-xray-renderer.js +4 -3
  310. package/dist/recall-xray.d.ts +2 -7
  311. package/dist/recall-xray.js +3 -2
  312. package/dist/reconcile/cursor.d.ts +1 -1
  313. package/dist/reconcile/manifest.d.ts +1 -1
  314. package/dist/reconcile/plan.d.ts +1 -1
  315. package/dist/replay/normalizers/chatgpt.d.ts +1 -1
  316. package/dist/replay/normalizers/claude.d.ts +1 -1
  317. package/dist/replay/normalizers/openclaw.d.ts +1 -1
  318. package/dist/replay/normalizers/shared.d.ts +1 -1
  319. package/dist/replay/runner.d.ts +1 -1
  320. package/dist/replay/types.d.ts +1 -1
  321. package/dist/replica-divergence.d.ts +1 -1
  322. package/dist/replica-peers-config.d.ts +1 -1
  323. package/dist/resolve-auth-token.d.ts +1 -1
  324. package/dist/resume-bundles.js +4 -3
  325. package/dist/retrieval-agents.d.ts +2 -2
  326. package/dist/retrieval-tiers.d.ts +1 -1
  327. package/dist/routing/engine.d.ts +1 -1
  328. package/dist/routing/store.d.ts +1 -1
  329. package/dist/salvage-envelope.d.ts +1 -1
  330. package/dist/schemas.d.ts +138 -138
  331. package/dist/{scope-profiles-CaKcnB-6.d.ts → scope-profiles-C1QG4Cmv.d.ts} +1 -1
  332. package/dist/search/embed-helper.d.ts +1 -1
  333. package/dist/search/factory.d.ts +1 -1
  334. package/dist/search/factory.js +6 -6
  335. package/dist/search/index.d.ts +1 -1
  336. package/dist/search/index.js +11 -11
  337. package/dist/search/lancedb-backend.d.ts +1 -1
  338. package/dist/search/lancedb-backend.js +2 -2
  339. package/dist/search/meilisearch-backend.d.ts +1 -1
  340. package/dist/search/meilisearch-backend.js +2 -2
  341. package/dist/search/noop-backend.d.ts +1 -1
  342. package/dist/search/orama-backend.d.ts +1 -1
  343. package/dist/search/orama-backend.js +2 -2
  344. package/dist/search/port.d.ts +1 -1
  345. package/dist/search/remote-backend.d.ts +1 -1
  346. package/dist/semantic-chunking.js +3 -2
  347. package/dist/{semantic-consolidation-P0eMWqh-.d.ts → semantic-consolidation-DMeX8XS0.d.ts} +1 -1
  348. package/dist/semantic-consolidation.d.ts +2 -2
  349. package/dist/semantic-consolidation.js +14 -13
  350. package/dist/semantic-rule-promotion.js +10 -10
  351. package/dist/semantic-rule-verifier.d.ts +1 -1
  352. package/dist/semantic-rule-verifier.js +10 -10
  353. package/dist/{service-D5GQLxq_.d.ts → service-CT3ENiTO.d.ts} +2 -2
  354. package/dist/session-observer-bands.d.ts +1 -1
  355. package/dist/session-observer-state.d.ts +1 -1
  356. package/dist/session-observer-state.js +1 -1
  357. package/dist/shared-context/manager.d.ts +9 -9
  358. package/dist/signal.d.ts +1 -1
  359. package/dist/source-agent-qualifier.d.ts +1 -1
  360. package/dist/source-agent-qualifier.js +12 -11
  361. package/dist/{storage-DIyil2ll.d.ts → storage-B0L76y7N.d.ts} +1 -1
  362. package/dist/storage.d.ts +2 -2
  363. package/dist/storage.js +9 -9
  364. package/dist/summarizer.d.ts +1 -1
  365. package/dist/summarizer.js +3 -2
  366. package/dist/summary-snapshot.d.ts +1 -1
  367. package/dist/support-passport/index.d.ts +15 -14
  368. package/dist/support-passport/index.js +28 -27
  369. package/dist/temporal-supersession.d.ts +2 -2
  370. package/dist/temporal-timeline-recall.d.ts +1 -1
  371. package/dist/temporal-validity.d.ts +1 -1
  372. package/dist/threading.d.ts +1 -1
  373. package/dist/tier-migration.d.ts +2 -2
  374. package/dist/tier-routing.d.ts +1 -1
  375. package/dist/token-estimate.d.ts +10 -0
  376. package/dist/token-estimate.js +8 -0
  377. package/dist/token-estimate.js.map +1 -0
  378. package/dist/topics.d.ts +1 -1
  379. package/dist/transcript.d.ts +1 -6
  380. package/dist/transcript.js +2 -1
  381. package/dist/transfer/backup.js +2 -2
  382. package/dist/transfer/capsule-export.js +2 -2
  383. package/dist/transfer/capsule-import.js +2 -2
  384. package/dist/transfer/types.d.ts +66 -66
  385. package/dist/trust-score-stage.d.ts +1 -1
  386. package/dist/trust-score.d.ts +1 -1
  387. package/dist/{types-CbXieHKr.d.ts → types-D7pB0QwO.d.ts} +1 -1
  388. package/dist/types.d.ts +1 -1
  389. package/dist/types.js +1 -1
  390. package/dist/utility-runtime.d.ts +1 -1
  391. package/dist/verified-recall.js +10 -10
  392. package/dist/write-envelope.d.ts +1 -1
  393. package/package.json +12 -2
  394. package/src/chunking.ts +124 -45
  395. package/src/config.ts +2 -0
  396. package/src/connectors/codex-materialize.ts +21 -26
  397. package/src/lcm/archive.ts +4 -5
  398. package/src/lcm/engine.ts +55 -34
  399. package/src/lcm/summarizer.ts +27 -16
  400. package/src/lcm-engine.test.ts +82 -2
  401. package/src/local-llm.ts +4 -6
  402. package/src/memory-extension-host/render-extensions-block.ts +3 -9
  403. package/src/orchestration/recall-section-coordinator.ts +93 -27
  404. package/src/recall-token-budget.test.ts +51 -0
  405. package/src/recall-xray.ts +4 -11
  406. package/src/semantic-chunking.ts +14 -59
  407. package/src/session-observer-state.ts +3 -3
  408. package/src/testing/subjects/recall-budget.test.ts +21 -5
  409. package/src/token-estimate.test.ts +38 -0
  410. package/src/token-estimate.ts +22 -0
  411. package/src/transcript.test.ts +38 -3
  412. package/src/transcript.ts +58 -59
  413. package/src/types.ts +1 -1
  414. package/dist/chunk-5ZIWRMZM.js.map +0 -1
  415. package/dist/chunk-C5B3CIMJ.js.map +0 -1
  416. package/dist/chunk-HYQJQGD3.js.map +0 -1
  417. package/dist/chunk-KSXJOC4F.js.map +0 -1
  418. package/dist/chunk-LTAVSOZW.js.map +0 -1
  419. package/dist/chunk-P4BC54KI.js.map +0 -1
  420. package/dist/chunk-PBGWYM3H.js.map +0 -1
  421. package/dist/chunk-QKPDN325.js.map +0 -1
  422. package/dist/chunk-UQ7RN5HK.js +0 -126
  423. package/dist/chunk-UQ7RN5HK.js.map +0 -1
  424. package/dist/chunk-Z54EM5YZ.js.map +0 -1
  425. /package/dist/{capsule-crypto-YO5QJ6L3.js.map → capsule-crypto-GWVG7LGC.js.map} +0 -0
  426. /package/dist/{chunk-YTZA7U6Q.js.map → chunk-2VHCLHZ5.js.map} +0 -0
  427. /package/dist/{chunk-NEJQ65NV.js.map → chunk-4CLE2LXB.js.map} +0 -0
  428. /package/dist/{chunk-VDX2J7OX.js.map → chunk-5W7CTZ2E.js.map} +0 -0
  429. /package/dist/{chunk-CZUHNJHX.js.map → chunk-6C7Q6NG3.js.map} +0 -0
  430. /package/dist/{chunk-4O6R3GPK.js.map → chunk-6W2ISYUV.js.map} +0 -0
  431. /package/dist/{chunk-3AJOJOFP.js.map → chunk-77DAFTUU.js.map} +0 -0
  432. /package/dist/{chunk-IKT6ZVO7.js.map → chunk-7DHFB5SG.js.map} +0 -0
  433. /package/dist/{chunk-SFR7J2G6.js.map → chunk-7G34CBKS.js.map} +0 -0
  434. /package/dist/{chunk-IT57AOMC.js.map → chunk-AH2NX6S7.js.map} +0 -0
  435. /package/dist/{chunk-FI3PIOOI.js.map → chunk-B4CSPFYN.js.map} +0 -0
  436. /package/dist/{chunk-7PJARJS4.js.map → chunk-BPM6UHUL.js.map} +0 -0
  437. /package/dist/{chunk-PPUTUOC3.js.map → chunk-BTHDQOF4.js.map} +0 -0
  438. /package/dist/{chunk-AW4A4XOP.js.map → chunk-CZGOLAEW.js.map} +0 -0
  439. /package/dist/{chunk-5PGYCWA7.js.map → chunk-DE6YCZEE.js.map} +0 -0
  440. /package/dist/{chunk-JOA4Q765.js.map → chunk-EUYWQ7TS.js.map} +0 -0
  441. /package/dist/{chunk-O5AFC5KM.js.map → chunk-EWL5DICB.js.map} +0 -0
  442. /package/dist/{chunk-4YZCIK5E.js.map → chunk-EZXGJJXM.js.map} +0 -0
  443. /package/dist/{chunk-GXIY65UH.js.map → chunk-FI5EAP4N.js.map} +0 -0
  444. /package/dist/{chunk-YOKSRIFI.js.map → chunk-FUWS2MNJ.js.map} +0 -0
  445. /package/dist/{chunk-6RK2X7BR.js.map → chunk-G3JFFUKR.js.map} +0 -0
  446. /package/dist/{chunk-VANZKHTN.js.map → chunk-G5OB4XZX.js.map} +0 -0
  447. /package/dist/{chunk-66SLUXKM.js.map → chunk-HK6KYIF6.js.map} +0 -0
  448. /package/dist/{chunk-XBGLEOID.js.map → chunk-I4BLRM7X.js.map} +0 -0
  449. /package/dist/{chunk-6SX4MY5Q.js.map → chunk-J65AKOD6.js.map} +0 -0
  450. /package/dist/{chunk-BPWZDHFM.js.map → chunk-J6QIAYNR.js.map} +0 -0
  451. /package/dist/{chunk-XL3TATMJ.js.map → chunk-JHMAYABJ.js.map} +0 -0
  452. /package/dist/{chunk-F7M36OG2.js.map → chunk-JNWISMYG.js.map} +0 -0
  453. /package/dist/{chunk-VYIXCMUP.js.map → chunk-KBIIE57I.js.map} +0 -0
  454. /package/dist/{chunk-24PNX2GF.js.map → chunk-KXYMU4IN.js.map} +0 -0
  455. /package/dist/{chunk-FKTD7IVS.js.map → chunk-L3JVAM5Q.js.map} +0 -0
  456. /package/dist/{chunk-LIF6DO6U.js.map → chunk-MG7GI4TT.js.map} +0 -0
  457. /package/dist/{chunk-RUZM6Z52.js.map → chunk-MMZK2DWN.js.map} +0 -0
  458. /package/dist/{chunk-NTJ6TY44.js.map → chunk-ODAE36PG.js.map} +0 -0
  459. /package/dist/{chunk-YLHM5BQQ.js.map → chunk-OLX2HJFW.js.map} +0 -0
  460. /package/dist/{chunk-DNC4A7RS.js.map → chunk-OU7RWWW6.js.map} +0 -0
  461. /package/dist/{chunk-L3LRWDJW.js.map → chunk-OV36CROW.js.map} +0 -0
  462. /package/dist/{chunk-BXLOS5AJ.js.map → chunk-OWHERGF2.js.map} +0 -0
  463. /package/dist/{chunk-CSV34HIW.js.map → chunk-P5OYNV7B.js.map} +0 -0
  464. /package/dist/{chunk-SNW3YRO6.js.map → chunk-P6GOYLHN.js.map} +0 -0
  465. /package/dist/{chunk-USGKHF36.js.map → chunk-QUJC22CT.js.map} +0 -0
  466. /package/dist/{chunk-BVRREV3N.js.map → chunk-R7IV3EUJ.js.map} +0 -0
  467. /package/dist/{chunk-D4LVHPOA.js.map → chunk-RXN7Z2V5.js.map} +0 -0
  468. /package/dist/{chunk-7VSHDLJQ.js.map → chunk-T2VJ2EO5.js.map} +0 -0
  469. /package/dist/{chunk-U7BMN33M.js.map → chunk-UBSM6TZP.js.map} +0 -0
  470. /package/dist/{chunk-DQEMWVMT.js.map → chunk-UVYI6VIX.js.map} +0 -0
  471. /package/dist/{chunk-U7XLOSG2.js.map → chunk-VIM6QH4D.js.map} +0 -0
  472. /package/dist/{chunk-LJJWFV2Q.js.map → chunk-VJXSAJFW.js.map} +0 -0
  473. /package/dist/{chunk-HZC3OECF.js.map → chunk-VOKK4WCG.js.map} +0 -0
  474. /package/dist/{chunk-LRDP2AC2.js.map → chunk-YBCZTBN2.js.map} +0 -0
  475. /package/dist/{chunk-USAUBGBC.js.map → chunk-YKZFX2HQ.js.map} +0 -0
  476. /package/dist/{chunk-I2GIYTRU.js.map → chunk-ZOGOOSB3.js.map} +0 -0
package/src/chunking.ts CHANGED
@@ -5,6 +5,8 @@
5
5
  * Preserves coherent thoughts by never splitting mid-sentence.
6
6
  */
7
7
 
8
+ import { estimateTokenCount } from "./token-estimate.js";
9
+
8
10
  export interface ChunkingConfig {
9
11
  /** Target tokens per chunk (default 200) */
10
12
  targetTokens: number;
@@ -38,60 +40,137 @@ export const DEFAULT_CHUNKING_CONFIG: ChunkingConfig = {
38
40
  };
39
41
 
40
42
  /**
41
- * Estimate token count for text.
42
- * Rough approximation: ~4 characters per token for English.
43
+ * Split text into sentences.
44
+ *
45
+ * The scan stays linear and does not use a backtracking regular expression.
46
+ * ASCII punctuation keeps its old whitespace rule. Unicode terminators also
47
+ * split when the next sentence starts immediately, as in CJK text.
43
48
  */
44
- function estimateTokens(text: string): number {
45
- return Math.ceil(text.length / 4);
49
+ const UNICODE_SENTENCE_TERMINATORS = "。.!?؟۔।॥。…";
50
+ const ASCII_SENTENCE_TERMINATORS = ".!?";
51
+ const CJK_NO_SPACE_TERMINATORS = "。.!?。";
52
+ const CLOSING_PUNCTUATION = "\"'”’»」』)]】〉》)]}";
53
+ const DIGITS = "01234567890123456789";
54
+ const SENTENCE_SEGMENTER =
55
+ typeof Intl.Segmenter === "function"
56
+ ? new Intl.Segmenter(undefined, { granularity: "sentence" })
57
+ : undefined;
58
+
59
+ function isSentenceTerminator(character: string): boolean {
60
+ return (
61
+ ASCII_SENTENCE_TERMINATORS.includes(character) ||
62
+ UNICODE_SENTENCE_TERMINATORS.includes(character)
63
+ );
46
64
  }
47
65
 
48
- /**
49
- * Split text into sentences.
50
- * Handles common abbreviations and edge cases.
51
- */
52
- function splitSentences(text: string): string[] {
53
- // Split on sentence-ending punctuation (. ! ?) that is followed by whitespace
54
- // or end of string; the punctuation stays with the sentence.
55
- //
56
- // Implemented as a single linear scan rather than a regex. Every regex form of
57
- // this split is either polynomial (CodeQL js/polynomial-redos) or — once
58
- // bounded/anchored to satisfy CodeQL — mishandles long runs or non-boundary
59
- // punctuation (a global match silently drops a skipped prefix; a sticky match
60
- // stops at the first interior `.` that is not a real boundary, e.g. "v1.2.3"
61
- // or "example.com", emitting the whole document as one chunk). A character
62
- // scan is O(n), allocation-free, drops nothing, and treats interior
63
- // punctuation correctly. Normal prose splits identically to the previous
64
- // /[^.!?]*[.!?]+(?:\s+|$)/g form.
65
- const sentences: string[] = [];
66
+ function isCjk(character: string): boolean {
67
+ return /[\u3000-\u9fff\uf900-\ufaff\uac00-\ud7af]/u.test(character);
68
+ }
69
+
70
+ type SentenceList = string[] & { separators: string[] };
71
+
72
+ function splitSentencesFallback(text: string): SentenceList {
73
+ const sentences = [] as unknown as SentenceList;
74
+ Object.defineProperty(sentences, "separators", {
75
+ value: [],
76
+ writable: true,
77
+ });
66
78
  let start = 0;
79
+ let separator = "";
67
80
  for (let i = 0; i < text.length; i++) {
68
81
  const ch = text[i];
69
- if (ch !== "." && ch !== "!" && ch !== "?") continue;
70
- // Consume a run of terminators (e.g. "?!", "...").
82
+ if (!isSentenceTerminator(ch)) continue;
83
+
71
84
  let end = i;
72
- while (end + 1 < text.length) {
73
- const n = text[end + 1];
74
- if (n !== "." && n !== "!" && n !== "?") break;
85
+ let hasUnicodeTerminator = UNICODE_SENTENCE_TERMINATORS.includes(ch);
86
+ while (end + 1 < text.length && isSentenceTerminator(text[end + 1])) {
75
87
  end++;
88
+ hasUnicodeTerminator ||= UNICODE_SENTENCE_TERMINATORS.includes(text[end]);
76
89
  }
90
+
91
+ const isFullwidthDecimal =
92
+ ch === "." &&
93
+ DIGITS.includes(text[i - 1] ?? "") &&
94
+ DIGITS.includes(text[end + 1] ?? "");
95
+ while (end + 1 < text.length && CLOSING_PUNCTUATION.includes(text[end + 1])) {
96
+ end++;
97
+ }
98
+
77
99
  const after = text[end + 1];
78
- // A real boundary only if the terminator run ends the string or is followed
79
- // by whitespace. Interior punctuation (no following whitespace) is left in
80
- // place and the scan continues.
81
- if (after === undefined || /\s/.test(after)) {
100
+ const boundary =
101
+ !isFullwidthDecimal &&
102
+ (hasUnicodeTerminator || after === undefined || /\s/u.test(after));
103
+ if (boundary) {
82
104
  const sentence = text.slice(start, end + 1).trim();
83
- if (sentence.length > 0) sentences.push(sentence);
84
- start = end + 1;
105
+ if (sentence.length > 0) {
106
+ sentences.push(sentence);
107
+ if (sentences.length > 1) sentences.separators.push(separator);
108
+ }
109
+ let nextStart = end + 1;
110
+ while (nextStart < text.length && /\s/u.test(text[nextStart])) nextStart++;
111
+ separator = text.slice(end + 1, nextStart);
112
+ start = nextStart;
85
113
  }
86
114
  i = end;
87
115
  }
88
- // Trailing text without a closing terminator.
89
- if (start < text.length) {
90
- const remaining = text.slice(start).trim();
91
- if (remaining.length > 0) sentences.push(remaining);
116
+
117
+ const remaining = text.slice(start).trim();
118
+ if (remaining.length > 0) {
119
+ sentences.push(remaining);
120
+ if (sentences.length > 1) sentences.separators.push(separator);
92
121
  }
93
122
  return sentences;
94
123
  }
124
+ export function splitSentences(text: string): string[] {
125
+ const canUseSegmenter =
126
+ SENTENCE_SEGMENTER !== undefined &&
127
+ !/\s/u.test(text) &&
128
+ [...text].some((character) => UNICODE_SENTENCE_TERMINATORS.includes(character));
129
+ if (canUseSegmenter) {
130
+ const segments = Array.from(SENTENCE_SEGMENTER.segment(text), ({ segment }) =>
131
+ segment.trim(),
132
+ ).filter((segment) => segment.length > 0) as SentenceList;
133
+ if (
134
+ segments.length > 1 &&
135
+ segments.slice(0, -1).every((segment) =>
136
+ [...segment].some((character) =>
137
+ UNICODE_SENTENCE_TERMINATORS.includes(character),
138
+ ),
139
+ )
140
+ ) {
141
+ Object.defineProperty(segments, "separators", {
142
+ value: Array(segments.length - 1).fill(""),
143
+ writable: true,
144
+ });
145
+ return segments;
146
+ }
147
+ }
148
+ return splitSentencesFallback(text);
149
+ }
150
+
151
+ function endsWithoutSpace(text: string): boolean {
152
+ let index = text.length - 1;
153
+ while (index >= 0 && CLOSING_PUNCTUATION.includes(text[index])) index--;
154
+ const terminator = text[index] ?? "";
155
+ if (CJK_NO_SPACE_TERMINATORS.includes(terminator)) {
156
+ return isCjk(text[index - 1] ?? "");
157
+ }
158
+ return terminator === "…" && isCjk(text[index - 1] ?? "");
159
+ }
160
+ export function joinSentences(sentences: string[]): string {
161
+ let result = "";
162
+ const separators = (sentences as Partial<SentenceList>).separators;
163
+ for (let i = 0; i < sentences.length; i++) {
164
+ const sentence = sentences[i];
165
+ if (result.length > 0) {
166
+ const separator = separators?.[i - 1];
167
+ result += separator ?? (endsWithoutSpace(result) ? "" : " ");
168
+ }
169
+ result += sentence;
170
+ }
171
+ return result;
172
+ }
173
+
95
174
 
96
175
  /**
97
176
  * Chunk content into overlapping segments at sentence boundaries.
@@ -104,7 +183,7 @@ export function chunkContent(
104
183
  content: string,
105
184
  config: ChunkingConfig = DEFAULT_CHUNKING_CONFIG,
106
185
  ): ChunkResult {
107
- const totalTokens = estimateTokens(content);
186
+ const totalTokens = estimateTokenCount(content);
108
187
 
109
188
  // Don't chunk if below minimum threshold
110
189
  if (totalTokens < config.minTokens) {
@@ -139,7 +218,7 @@ export function chunkContent(
139
218
 
140
219
  for (let i = 0; i < sentences.length; i++) {
141
220
  const sentence = sentences[i];
142
- const sentenceTokens = estimateTokens(sentence);
221
+ const sentenceTokens = estimateTokenCount(sentence);
143
222
 
144
223
  // Add sentence to current chunk
145
224
  currentChunkSentences.push(sentence);
@@ -152,11 +231,11 @@ export function chunkContent(
152
231
 
153
232
  if (atTarget || isLastSentence) {
154
233
  // Create chunk from accumulated sentences
155
- const chunkContent = currentChunkSentences.join(" ");
234
+ const chunkContent = joinSentences(currentChunkSentences);
156
235
  chunks.push({
157
236
  content: chunkContent,
158
237
  index: chunkIndex,
159
- tokenCount: estimateTokens(chunkContent),
238
+ tokenCount: estimateTokenCount(chunkContent),
160
239
  });
161
240
  chunkIndex++;
162
241
 
@@ -171,7 +250,7 @@ export function chunkContent(
171
250
  currentTokens = 0;
172
251
  } else {
173
252
  currentChunkSentences = currentChunkSentences.slice(-overlapCount);
174
- currentTokens = currentChunkSentences.reduce((sum, s) => sum + estimateTokens(s), 0);
253
+ currentTokens = currentChunkSentences.reduce((sum, s) => sum + estimateTokenCount(s), 0);
175
254
  }
176
255
  }
177
256
  }
@@ -216,14 +295,14 @@ export function reassembleChunks(chunks: string[]): string {
216
295
  const prevEnd = prevSentences.slice(-(j + 1));
217
296
  const currStart = currSentences.slice(0, j + 1);
218
297
 
219
- if (prevEnd.join(" ") === currStart.join(" ")) {
298
+ if (joinSentences(prevEnd) === joinSentences(currStart)) {
220
299
  overlapCount = j + 1;
221
300
  }
222
301
  }
223
302
 
224
303
  // Add non-overlapping portion
225
304
  if (overlapCount > 0 && overlapCount < currSentences.length) {
226
- result.push(currSentences.slice(overlapCount).join(" "));
305
+ result.push(joinSentences(currSentences.slice(overlapCount)));
227
306
  } else if (overlapCount === 0) {
228
307
  // No detected overlap, add full chunk
229
308
  result.push(currChunk);
@@ -231,5 +310,5 @@ export function reassembleChunks(chunks: string[]): string {
231
310
  // If overlapCount === currSentences.length, skip (fully contained)
232
311
  }
233
312
 
234
- return result.join(" ");
313
+ return joinSentences(result);
235
314
  }
package/src/config.ts CHANGED
@@ -4508,6 +4508,8 @@ function buildRecallPipelineConfig(cfg: Record<string, unknown>): RecallPipeline
4508
4508
  ? Math.max(0, Math.floor(cfg.maxMemoryTokens))
4509
4509
  : 2000;
4510
4510
  const recallBudgetCharsRaw = clampNonNegativeNumber(cfg.recallBudgetChars);
4511
+ // Keep four characters per token as Latin-script headroom. Assemblers enforce
4512
+ // maxMemoryTokens with the shared script-aware estimator.
4511
4513
  const recallBudgetChars = recallBudgetCharsRaw ?? maxMemoryTokens * 4;
4512
4514
 
4513
4515
  const rawPipeline = cfg.recallPipeline;
@@ -28,8 +28,8 @@
28
28
  * - **Idempotent no-ops.** A content hash is written into the sentinel. If
29
29
  * the re-rendered hash matches the previous run, we skip writes entirely.
30
30
  * - **Token budget.** `memory_summary.md` is truncated to fit under the
31
- * configured token budget (whitespace-tokenized approximation), leaving
32
- * headroom under Codex's 5000-token summary cap.
31
+ * configured script-aware token estimate, leaving headroom under Codex's
32
+ * 5000-token summary cap.
33
33
  *
34
34
  * Privacy
35
35
  * ───────
@@ -47,6 +47,7 @@ import path from "node:path";
47
47
  import { log } from "../logger.js";
48
48
  import { readEnvVar, resolveHomeDir } from "../runtime/env.js";
49
49
  import type { MemoryFile } from "../types.js";
50
+ import { estimateTokenCount } from "../token-estimate.js";
50
51
  import { expandTildePath } from "../utils/path.js";
51
52
 
52
53
  // ─── Types ─────────────────────────────────────────────────────────────────
@@ -60,7 +61,7 @@ export interface MaterializeOptions {
60
61
  memories: MemoryFile[];
61
62
  /** Override `<codex_home>`. Defaults to `$CODEX_HOME` or `~/.codex`. */
62
63
  codexHome?: string;
63
- /** Maximum whitespace-tokenized size of memory_summary.md. Default 4500. */
64
+ /** Maximum estimated token size of memory_summary.md. Default 4500. */
64
65
  maxSummaryTokens?: number;
65
66
  /** Maximum age of rollout_summaries/*.md in days. Default 30. */
66
67
  rolloutRetentionDays?: number;
@@ -990,35 +991,23 @@ function trimHyphenEdges(value: string): string {
990
991
  while (end > start && value[end - 1] === "-") end -= 1;
991
992
  return value.slice(start, end);
992
993
  }
993
-
994
- /**
995
- * Whitespace-tokenized approximation used by the budget check. Matches the
996
- * simple heuristic Codex's usage.rs reporting uses for the "5000 token"
997
- * memory_summary cap.
998
- */
994
+ /** Shared script-aware approximation used by the budget check. */
999
995
  export function approximateTokenCount(text: string): number {
1000
- const trimmed = text.trim();
1001
- if (trimmed.length === 0) return 0;
1002
- return trimmed.split(/\s+/u).length;
996
+ return estimateTokenCount(text);
1003
997
  }
1004
998
 
1005
999
  /**
1006
- * Truncate `text` so it fits under `maxTokens` whitespace tokens. We drop
1007
- * trailing lines until we're under the budget and then append an ellipsis
1008
- * marker so downstream readers can see that truncation happened.
1000
+ * Truncate `text` so it fits under `maxTokens`. We drop trailing lines until
1001
+ * under budget, then append a marker so readers can see that truncation occurred.
1009
1002
  */
1010
1003
  export function truncateToTokenBudget(text: string, maxTokens: number): string {
1011
1004
  if (maxTokens <= 0) return "";
1012
1005
  if (approximateTokenCount(text) <= maxTokens) return text;
1013
1006
 
1014
- // Reserve headroom for the truncation marker so the line-preserving path
1015
- // can actually fit the marker without flipping to the hard-cut fallback.
1016
- // Both markers are counted with the same whitespace heuristic the budget
1017
- // check uses, so the arithmetic stays consistent.
1018
1007
  const lineMarker = "_[truncated for summary budget]_";
1019
1008
  const tailMarker = "[truncated]";
1009
+ if (approximateTokenCount(tailMarker) > maxTokens) return "";
1020
1010
  const lineMarkerTokens = approximateTokenCount(lineMarker);
1021
- const tailMarkerTokens = approximateTokenCount(tailMarker);
1022
1011
 
1023
1012
  const lines = text.split(/\r?\n/u);
1024
1013
  const lineBudget = Math.max(0, maxTokens - lineMarkerTokens);
@@ -1028,13 +1017,19 @@ export function truncateToTokenBudget(text: string, maxTokens: number): string {
1028
1017
  lines.push(lineMarker);
1029
1018
  let result = lines.join("\n");
1030
1019
 
1031
- // If a single huge line still blows the budget, hard-cut tokens. Reserve
1032
- // space for the tail marker's own token count so the final string stays
1033
- // within maxTokens rather than sneaking over by a few tokens.
1034
1020
  if (approximateTokenCount(result) > maxTokens) {
1035
- const tokens = result.split(/\s+/u);
1036
- const keep = Math.max(0, maxTokens - tailMarkerTokens);
1037
- result = keep > 0 ? `${tokens.slice(0, keep).join(" ")} ${tailMarker}` : tailMarker;
1021
+ const codePoints = [...result];
1022
+ let low = 0;
1023
+ let high = codePoints.length;
1024
+ while (low < high) {
1025
+ const mid = Math.ceil((low + high) / 2);
1026
+ const prefix = codePoints.slice(0, mid).join("");
1027
+ const candidate = prefix.length > 0 ? `${prefix} ${tailMarker}` : tailMarker;
1028
+ if (approximateTokenCount(candidate) <= maxTokens) low = mid;
1029
+ else high = mid - 1;
1030
+ }
1031
+ const prefix = codePoints.slice(0, low).join("");
1032
+ result = prefix.length > 0 ? `${prefix} ${tailMarker}` : tailMarker;
1038
1033
  }
1039
1034
  return result;
1040
1035
  }
@@ -1,10 +1,11 @@
1
1
  import type Database from "better-sqlite3";
2
2
  import { log } from "../logger.js";
3
3
  import {
4
- parseMessageParts,
5
4
  type LcmMessagePartInput,
6
5
  type MessagePartSourceFormat,
6
+ parseMessageParts,
7
7
  } from "../message-parts/index.js";
8
+ import { estimateTokenCount } from "../token-estimate.js";
8
9
 
9
10
  export interface LcmMessage {
10
11
  id: number;
@@ -48,10 +49,8 @@ export interface LcmStructuredRecallMatch {
48
49
  score: number;
49
50
  }
50
51
 
51
- /** Rough token count: ~4 chars per token. */
52
- export function estimateTokens(text: string): number {
53
- return Math.ceil(text.length / 4);
54
- }
52
+ /** Shared script-aware token estimate used by LCM accounting. */
53
+ export const estimateTokens = estimateTokenCount;
55
54
 
56
55
  const PUBLIC_LCM_SEARCH_LIMIT = 100;
57
56
 
package/src/lcm/engine.ts CHANGED
@@ -10,8 +10,8 @@ import {
10
10
  } from "./recall.js";
11
11
  import { LcmWorkQueue, type LcmObserveMessage } from "./queue.js";
12
12
  import type { PluginConfig } from "../types.js";
13
+ import { estimateTokenCount } from "../token-estimate.js";
13
14
  import { log } from "../logger.js";
14
-
15
15
  export interface LcmEngineConfig {
16
16
  enabled: boolean;
17
17
  leafBatchSize: number;
@@ -556,12 +556,14 @@ export class LcmEngine {
556
556
  const messages = this.archive!.getMessages(normalizedSessionId, fromTurn, toTurn);
557
557
  if (messages.length === 0) return [];
558
558
 
559
- // Enforce token budget keep first and last, truncate middle
560
- const maxChars = maxTokens * 4;
561
- let totalChars = 0;
562
- for (const m of messages) totalChars += m.content.length;
563
-
564
- if (totalChars <= maxChars) {
559
+ // Enforce the token budget while keeping the existing first/last shape.
560
+ const tokenBudget = Math.max(0, Math.floor(maxTokens));
561
+ if (tokenBudget === 0) return [];
562
+ const totalTokens = messages.reduce(
563
+ (sum, message) => sum + estimateTokenCount(message.content),
564
+ 0,
565
+ );
566
+ if (totalTokens <= tokenBudget) {
565
567
  return messages.map((m) => ({
566
568
  id: m.id,
567
569
  session_id: m.session_id,
@@ -571,45 +573,48 @@ export class LcmEngine {
571
573
  }));
572
574
  }
573
575
 
574
- // Keep first and last messages, truncate from middle
575
576
  const result: LcmExpandedMessage[] = [];
576
- let budget = maxChars;
577
-
578
- // Reserve space for the last message
579
577
  const lastMsg = messages[messages.length - 1];
580
- const lastMsgChars = Math.min(
581
- lastMsg.content.length,
582
- Math.floor(maxChars * 0.3),
578
+ const reservedLastTokens = Math.min(
579
+ estimateTokenCount(lastMsg.content),
580
+ Math.max(1, Math.floor(tokenBudget * 0.3)),
583
581
  );
584
- budget -= lastMsgChars;
582
+ let remainingTokens = tokenBudget - reservedLastTokens;
585
583
 
586
- // Add messages from the beginning
587
584
  for (let i = 0; i < messages.length - 1; i++) {
588
- if (budget <= 0) break;
589
- const m = messages[i];
590
- const truncated = m.content.slice(0, budget);
585
+ if (remainingTokens <= 0) break;
586
+ const content = truncateToTokenBudget(
587
+ messages[i].content,
588
+ remainingTokens,
589
+ );
590
+ if (!content) continue;
591
591
  result.push({
592
- id: m.id,
593
- session_id: m.session_id,
594
- turn_index: m.turn_index,
595
- role: m.role,
596
- content: truncated,
592
+ id: messages[i].id,
593
+ session_id: messages[i].session_id,
594
+ turn_index: messages[i].turn_index,
595
+ role: messages[i].role,
596
+ content,
597
597
  });
598
- budget -= truncated.length;
598
+ remainingTokens -= estimateTokenCount(content);
599
599
  }
600
600
 
601
- // Always append the last message
602
- result.push({
603
- id: lastMsg.id,
604
- session_id: lastMsg.session_id,
605
- turn_index: lastMsg.turn_index,
606
- role: lastMsg.role,
607
- content: lastMsg.content.slice(0, lastMsgChars + Math.max(0, budget)),
608
- });
609
-
601
+ const lastContent = truncateToTokenBudget(
602
+ lastMsg.content,
603
+ reservedLastTokens + remainingTokens,
604
+ );
605
+ if (lastContent) {
606
+ result.push({
607
+ id: lastMsg.id,
608
+ session_id: lastMsg.session_id,
609
+ turn_index: lastMsg.turn_index,
610
+ role: lastMsg.role,
611
+ content: lastContent,
612
+ });
613
+ }
610
614
  return result;
611
615
  }
612
616
 
617
+
613
618
  /** Get statistics about the LCM archive. */
614
619
  async getStats(sessionId?: string): Promise<{
615
620
  totalMessages: number;
@@ -691,3 +696,19 @@ export class LcmEngine {
691
696
  this.initPromise = null;
692
697
  }
693
698
  }
699
+ function truncateToTokenBudget(text: string, maxTokens: number): string {
700
+ if (maxTokens <= 0) return "";
701
+ if (estimateTokenCount(text) <= maxTokens) return text;
702
+ const codePoints = [...text];
703
+ let low = 0;
704
+ let high = codePoints.length;
705
+ while (low < high) {
706
+ const mid = Math.ceil((low + high) / 2);
707
+ if (estimateTokenCount(codePoints.slice(0, mid).join("")) <= maxTokens) {
708
+ low = mid;
709
+ } else {
710
+ high = mid - 1;
711
+ }
712
+ }
713
+ return codePoints.slice(0, low).join("");
714
+ }
@@ -1,8 +1,9 @@
1
1
  import { log } from "../logger.js";
2
+ import { looksLikeMechanicalTelemetryTranscript } from "../telemetry-transcript.js";
3
+ import { estimateTokenCount } from "../token-estimate.js";
2
4
  import type { LcmArchive, LcmMessage } from "./archive.js";
3
- import { LcmDag } from "./dag.js";
4
5
  import { estimateTokens } from "./archive.js";
5
- import { looksLikeMechanicalTelemetryTranscript } from "../telemetry-transcript.js";
6
+ import type { LcmDag } from "./dag.js";
6
7
 
7
8
  /** Generate a ULID-like ID (timestamp + random). */
8
9
  function generateNodeId(): string {
@@ -199,32 +200,42 @@ export class LcmSummarizer {
199
200
 
200
201
  /** Deterministic truncation: first and last sentence, plus middle truncation. */
201
202
  function deterministicTruncate(text: string, maxTokens: number): string {
202
- const maxChars = maxTokens * 4;
203
- if (text.length <= maxChars) return text;
203
+ if (maxTokens <= 0 || estimateTokenCount(text) <= maxTokens) return text;
204
204
 
205
205
  const sentences = text.split(/(?<=[.!?])\s+/).filter((s) => s.length > 0);
206
- if (sentences.length <= 2) {
207
- return text.slice(0, maxChars);
208
- }
206
+ if (sentences.length <= 2) return clampToTokenBudget(text, maxTokens);
209
207
 
210
208
  const first = sentences[0];
211
209
  const last = sentences[sentences.length - 1];
212
- const budget = maxChars - first.length - last.length - 20;
210
+ const render = (middle: string[], truncated: boolean): string =>
211
+ `${first} ${middle.join(" ")}${truncated ? " [...] " : " "}${last}`;
213
212
 
214
- if (budget <= 0) {
215
- return text.slice(0, maxChars);
216
- }
213
+ const base = render([], false);
214
+ if (estimateTokenCount(base) > maxTokens) return clampToTokenBudget(text, maxTokens);
215
+ if (estimateTokenCount(render([], true)) > maxTokens) return base;
217
216
 
218
- // Fill from the beginning until budget runs out
219
217
  const middle: string[] = [];
220
- let used = 0;
221
218
  for (let i = 1; i < sentences.length - 1; i++) {
222
- if (used + sentences[i].length > budget) break;
219
+ const candidate = [...middle, sentences[i]];
220
+ const truncated = i < sentences.length - 2;
221
+ if (estimateTokenCount(render(candidate, truncated)) > maxTokens) break;
223
222
  middle.push(sentences[i]);
224
- used += sentences[i].length;
225
223
  }
226
224
 
227
- return `${first} ${middle.join(" ")}${middle.length < sentences.length - 2 ? " [...] " : " "}${last}`;
225
+ const truncated = middle.length < sentences.length - 2;
226
+ return clampToTokenBudget(render(middle, truncated), maxTokens);
227
+ }
228
+
229
+ function clampToTokenBudget(text: string, maxTokens: number): string {
230
+ const codePoints = [...text];
231
+ let low = 0;
232
+ let high = codePoints.length;
233
+ while (low < high) {
234
+ const mid = Math.ceil((low + high) / 2);
235
+ if (estimateTokenCount(codePoints.slice(0, mid).join("")) <= maxTokens) low = mid;
236
+ else high = mid - 1;
237
+ }
238
+ return codePoints.slice(0, low).join("");
228
239
  }
229
240
 
230
241
  function chunkArray<T>(arr: T[], size: number): T[][] {