@remnic/core 9.57.6 → 9.58.1

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 (474) 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-4DO8XMq6.d.ts → access-service-BogiG4Dy.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-P6WBAR27.js → chunk-2GBFKHBZ.js} +9 -9
  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-LMXUWAXW.js → chunk-6TBIWIAG.js} +9 -8
  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-DUHMYBVE.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-TEHPIMXD.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-REVMY7RA.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.js +2 -1
  159. package/dist/{cli-CQdYilcx.d.ts → cli-CTgFjmDs.d.ts} +5 -5
  160. package/dist/cli.d.ts +14 -13
  161. package/dist/cli.js +57 -56
  162. package/dist/coding/pre-action-gate.d.ts +1 -1
  163. package/dist/compounding/engine.d.ts +2 -2
  164. package/dist/compounding/engine.js +10 -10
  165. package/dist/compounding/preference-consolidator.d.ts +2 -1
  166. package/dist/compression-optimizer.d.ts +1 -1
  167. package/dist/config.d.ts +1 -1
  168. package/dist/config.js +1 -1
  169. package/dist/connectors/codex-materialize-runner.d.ts +1 -1
  170. package/dist/connectors/codex-materialize-runner.js +12 -11
  171. package/dist/connectors/codex-materialize.d.ts +7 -12
  172. package/dist/connectors/codex-materialize.js +2 -1
  173. package/dist/connectors/index.d.ts +1 -1
  174. package/dist/connectors/index.js +12 -11
  175. package/dist/consolidation-provenance-check.d.ts +2 -2
  176. package/dist/consolidation-undo.d.ts +2 -2
  177. package/dist/contradiction/index.d.ts +2 -2
  178. package/dist/contradiction/index.js +4 -4
  179. package/dist/converge-config.d.ts +1 -1
  180. package/dist/convergence-refresh.d.ts +2 -2
  181. package/dist/convergence-refresh.js +12 -12
  182. package/dist/conversation-index/backend.d.ts +1 -1
  183. package/dist/conversation-index/chunker.d.ts +1 -1
  184. package/dist/conversation-index/faiss-adapter.d.ts +1 -1
  185. package/dist/conversation-index/indexer.d.ts +1 -1
  186. package/dist/conversation-index/search.d.ts +1 -1
  187. package/dist/corpus-watermark.d.ts +1 -1
  188. package/dist/corpus-watermark.js +12 -12
  189. package/dist/day-summary.d.ts +1 -1
  190. package/dist/day-summary.js +3 -2
  191. package/dist/delinearize.d.ts +1 -1
  192. package/dist/dependency-propagation-config.d.ts +1 -1
  193. package/dist/{dependency-propagation-delivery-C_K_AHcv.d.ts → dependency-propagation-delivery-DdwSc1qh.d.ts} +1 -1
  194. package/dist/direct-answer-wiring.d.ts +1 -1
  195. package/dist/direct-answer.d.ts +1 -1
  196. package/dist/embedding-fallback.d.ts +1 -1
  197. package/dist/enrichment/index.d.ts +1 -1
  198. package/dist/entity-retrieval.d.ts +2 -2
  199. package/dist/entity-retrieval.js +10 -10
  200. package/dist/entity-schema.d.ts +1 -1
  201. package/dist/explicit-capture.d.ts +10 -9
  202. package/dist/external-wiki-access.d.ts +12 -11
  203. package/dist/external-wiki-access.js +26 -25
  204. package/dist/external-wiki-collection-registration.d.ts +1 -1
  205. package/dist/external-wiki-collection.d.ts +1 -1
  206. package/dist/external-wiki-mcp-tools.d.ts +12 -11
  207. package/dist/extraction-error-classification.d.ts +1 -1
  208. package/dist/extraction-faithfulness.d.ts +1 -1
  209. package/dist/extraction-judge-telemetry.d.ts +1 -1
  210. package/dist/extraction-judge-training.d.ts +1 -1
  211. package/dist/extraction-judge.d.ts +1 -1
  212. package/dist/extraction-liveness.d.ts +1 -1
  213. package/dist/extraction-normalization.d.ts +1 -1
  214. package/dist/extraction-prompt.d.ts +1 -1
  215. package/dist/extraction-source-grounding-rules.d.ts +1 -1
  216. package/dist/extraction-source-grounding.d.ts +1 -1
  217. package/dist/extraction.d.ts +1 -1
  218. package/dist/extraction.js +16 -15
  219. package/dist/fallback-llm.d.ts +1 -1
  220. package/dist/graph-dashboard-diff.d.ts +1 -1
  221. package/dist/graph-dashboard-key.d.ts +1 -1
  222. package/dist/graph-dashboard-parser.d.ts +1 -1
  223. package/dist/graph-edge-reinforcement.d.ts +1 -1
  224. package/dist/graph-path-reconstruction.d.ts +1 -1
  225. package/dist/graph-path-scoring.d.ts +1 -1
  226. package/dist/graph-snapshot.d.ts +1 -1
  227. package/dist/graph.d.ts +1 -1
  228. package/dist/harmonic-retrieval.js +1 -1
  229. package/dist/importance.d.ts +1 -1
  230. package/dist/importers/index.d.ts +1 -1
  231. package/dist/in-flight-reads.d.ts +1 -1
  232. package/dist/index.d.ts +698 -697
  233. package/dist/index.js +81 -80
  234. package/dist/intent.d.ts +1 -1
  235. package/dist/lcm/archive.d.ts +3 -2
  236. package/dist/lcm/archive.js +2 -1
  237. package/dist/lcm/engine.d.ts +2 -1
  238. package/dist/lcm/engine.js +5 -4
  239. package/dist/lcm/index.d.ts +2 -1
  240. package/dist/lcm/index.js +7 -6
  241. package/dist/lcm/recall.d.ts +1 -0
  242. package/dist/lcm/summarizer.d.ts +1 -0
  243. package/dist/lcm/summarizer.js +3 -2
  244. package/dist/lcm/tools.d.ts +2 -1
  245. package/dist/lifecycle.d.ts +1 -1
  246. package/dist/live-connectors-runner.d.ts +1 -1
  247. package/dist/local-llm.d.ts +2 -3
  248. package/dist/local-llm.js +2 -1
  249. package/dist/local-model-endpoint.d.ts +1 -1
  250. package/dist/maintenance/memory-governance.d.ts +1 -1
  251. package/dist/maintenance/memory-governance.js +10 -10
  252. package/dist/maintenance/rebuild-memory-lifecycle-ledger.d.ts +2 -2
  253. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +10 -10
  254. package/dist/maintenance/rebuild-memory-projection.d.ts +2 -2
  255. package/dist/maintenance/rebuild-memory-projection.js +11 -11
  256. package/dist/{maintenance-DdP931i9.d.ts → maintenance-DvkiF7fm.d.ts} +3 -3
  257. package/dist/mcp-memory-inspector-app.d.ts +12 -11
  258. package/dist/memory-action-policy.d.ts +1 -1
  259. package/dist/memory-cache.d.ts +1 -1
  260. package/dist/memory-lifecycle-ledger-utils.d.ts +1 -1
  261. package/dist/memory-projection-store.d.ts +1 -1
  262. package/dist/memory-provenance.d.ts +1 -1
  263. package/dist/memory-snapshot.d.ts +1 -1
  264. package/dist/memory-worth-outcomes.d.ts +2 -2
  265. package/dist/models-json.d.ts +1 -1
  266. package/dist/namespaces/migrate.d.ts +3 -3
  267. package/dist/namespaces/migrate.js +11 -11
  268. package/dist/namespaces/principal.d.ts +1 -1
  269. package/dist/namespaces/search.d.ts +1 -1
  270. package/dist/namespaces/search.js +7 -7
  271. package/dist/namespaces/storage.d.ts +3 -3
  272. package/dist/namespaces/storage.js +10 -10
  273. package/dist/native-knowledge.d.ts +1 -1
  274. package/dist/offline-sync-impression-drain.d.ts +1 -1
  275. package/dist/operator-doctor-corpus.d.ts +1 -1
  276. package/dist/operator-doctor-corpus.js +13 -13
  277. package/dist/operator-doctor-replica.d.ts +1 -1
  278. package/dist/operator-toolkit.d.ts +3 -3
  279. package/dist/operator-toolkit.js +18 -18
  280. package/dist/orchestration/compression-guideline-coordinator.d.ts +2 -2
  281. package/dist/orchestration/maintenance.d.ts +4 -4
  282. package/dist/orchestration/maintenance.js +15 -15
  283. package/dist/{orchestrator-DxUWX7gs.d.ts → orchestrator-CXPIzs3D.d.ts} +9 -9
  284. package/dist/orchestrator.d.ts +10 -9
  285. package/dist/orchestrator.js +77 -76
  286. package/dist/patterns-cli.d.ts +1 -1
  287. package/dist/{pipeline-BKUMU1vU.d.ts → pipeline-BuYWU9rt.d.ts} +1 -1
  288. package/dist/policy-runtime.d.ts +1 -1
  289. package/dist/proactive-contention.d.ts +1 -1
  290. package/dist/provenance.d.ts +1 -1
  291. package/dist/{public-http-Dw0bJhcC.d.ts → public-http-CEwDOGO7.d.ts} +1 -1
  292. package/dist/{qmd-DJP-GLFe.d.ts → qmd-DYnrPw-m.d.ts} +1 -1
  293. package/dist/qmd-preflight.d.ts +2 -2
  294. package/dist/qmd-recall-cache.d.ts +1 -1
  295. package/dist/qmd.d.ts +2 -2
  296. package/dist/recall-concurrency-config.d.ts +1 -1
  297. package/dist/recall-disclosure-escalation.d.ts +1 -1
  298. package/dist/recall-explain-renderer.d.ts +1 -1
  299. package/dist/recall-explain-renderer.js +4 -3
  300. package/dist/recall-memory-map.d.ts +1 -1
  301. package/dist/recall-planner-llm.d.ts +1 -1
  302. package/dist/recall-state.d.ts +1 -1
  303. package/dist/recall-tag-filter.d.ts +1 -1
  304. package/dist/recall-timings.d.ts +1 -1
  305. package/dist/recall-xray-cli.d.ts +1 -1
  306. package/dist/recall-xray-cli.js +5 -4
  307. package/dist/recall-xray-renderer.d.ts +1 -1
  308. package/dist/recall-xray-renderer.js +4 -3
  309. package/dist/recall-xray.d.ts +2 -7
  310. package/dist/recall-xray.js +3 -2
  311. package/dist/reconcile/cursor.d.ts +1 -1
  312. package/dist/reconcile/manifest.d.ts +1 -1
  313. package/dist/reconcile/plan.d.ts +1 -1
  314. package/dist/replay/normalizers/chatgpt.d.ts +1 -1
  315. package/dist/replay/normalizers/claude.d.ts +1 -1
  316. package/dist/replay/normalizers/openclaw.d.ts +1 -1
  317. package/dist/replay/normalizers/shared.d.ts +1 -1
  318. package/dist/replay/runner.d.ts +1 -1
  319. package/dist/replay/types.d.ts +1 -1
  320. package/dist/replica-divergence.d.ts +1 -1
  321. package/dist/replica-peers-config.d.ts +1 -1
  322. package/dist/resolve-auth-token.d.ts +1 -1
  323. package/dist/resume-bundles.js +4 -3
  324. package/dist/retrieval-agents.d.ts +2 -2
  325. package/dist/retrieval-tiers.d.ts +1 -1
  326. package/dist/routing/engine.d.ts +1 -1
  327. package/dist/routing/store.d.ts +1 -1
  328. package/dist/salvage-envelope.d.ts +1 -1
  329. package/dist/schemas.d.ts +138 -138
  330. package/dist/{scope-profiles-CaKcnB-6.d.ts → scope-profiles-C1QG4Cmv.d.ts} +1 -1
  331. package/dist/search/embed-helper.d.ts +1 -1
  332. package/dist/search/factory.d.ts +1 -1
  333. package/dist/search/factory.js +6 -6
  334. package/dist/search/index.d.ts +1 -1
  335. package/dist/search/index.js +11 -11
  336. package/dist/search/lancedb-backend.d.ts +1 -1
  337. package/dist/search/lancedb-backend.js +2 -2
  338. package/dist/search/meilisearch-backend.d.ts +1 -1
  339. package/dist/search/meilisearch-backend.js +2 -2
  340. package/dist/search/noop-backend.d.ts +1 -1
  341. package/dist/search/orama-backend.d.ts +1 -1
  342. package/dist/search/orama-backend.js +2 -2
  343. package/dist/search/port.d.ts +1 -1
  344. package/dist/search/remote-backend.d.ts +1 -1
  345. package/dist/semantic-chunking.js +3 -2
  346. package/dist/{semantic-consolidation-P0eMWqh-.d.ts → semantic-consolidation-DMeX8XS0.d.ts} +1 -1
  347. package/dist/semantic-consolidation.d.ts +2 -2
  348. package/dist/semantic-consolidation.js +14 -13
  349. package/dist/semantic-rule-promotion.js +10 -10
  350. package/dist/semantic-rule-verifier.d.ts +1 -1
  351. package/dist/semantic-rule-verifier.js +10 -10
  352. package/dist/{service-D5GQLxq_.d.ts → service-CT3ENiTO.d.ts} +2 -2
  353. package/dist/session-observer-bands.d.ts +1 -1
  354. package/dist/session-observer-state.d.ts +1 -1
  355. package/dist/session-observer-state.js +1 -1
  356. package/dist/shared-context/manager.d.ts +9 -9
  357. package/dist/signal.d.ts +1 -1
  358. package/dist/source-agent-qualifier.d.ts +1 -1
  359. package/dist/source-agent-qualifier.js +12 -11
  360. package/dist/{storage-DIyil2ll.d.ts → storage-B0L76y7N.d.ts} +1 -1
  361. package/dist/storage.d.ts +2 -2
  362. package/dist/storage.js +9 -9
  363. package/dist/summarizer.d.ts +1 -1
  364. package/dist/summarizer.js +3 -2
  365. package/dist/summary-snapshot.d.ts +1 -1
  366. package/dist/support-passport/index.d.ts +15 -14
  367. package/dist/support-passport/index.js +28 -27
  368. package/dist/temporal-supersession.d.ts +2 -2
  369. package/dist/temporal-timeline-recall.d.ts +1 -1
  370. package/dist/temporal-validity.d.ts +1 -1
  371. package/dist/threading.d.ts +1 -1
  372. package/dist/tier-migration.d.ts +2 -2
  373. package/dist/tier-routing.d.ts +1 -1
  374. package/dist/token-estimate.d.ts +10 -0
  375. package/dist/token-estimate.js +8 -0
  376. package/dist/token-estimate.js.map +1 -0
  377. package/dist/topics.d.ts +1 -1
  378. package/dist/transcript.d.ts +1 -6
  379. package/dist/transcript.js +2 -1
  380. package/dist/transfer/backup.js +2 -2
  381. package/dist/transfer/capsule-export.js +2 -2
  382. package/dist/transfer/capsule-import.js +2 -2
  383. package/dist/transfer/types.d.ts +66 -66
  384. package/dist/trust-score-stage.d.ts +1 -1
  385. package/dist/trust-score.d.ts +1 -1
  386. package/dist/{types-CbXieHKr.d.ts → types-D7pB0QwO.d.ts} +1 -1
  387. package/dist/types.d.ts +1 -1
  388. package/dist/types.js +1 -1
  389. package/dist/utility-runtime.d.ts +1 -1
  390. package/dist/verified-recall.js +10 -10
  391. package/dist/write-envelope.d.ts +1 -1
  392. package/package.json +12 -2
  393. package/src/chunking.ts +6 -12
  394. package/src/config.ts +2 -0
  395. package/src/connectors/codex-materialize.ts +21 -26
  396. package/src/lcm/archive.ts +4 -5
  397. package/src/lcm/engine.ts +55 -34
  398. package/src/lcm/summarizer.ts +27 -16
  399. package/src/lcm-engine.test.ts +82 -2
  400. package/src/local-llm.ts +4 -6
  401. package/src/memory-extension-host/render-extensions-block.ts +3 -9
  402. package/src/orchestration/recall-section-coordinator.ts +93 -27
  403. package/src/recall-token-budget.test.ts +51 -0
  404. package/src/recall-xray.ts +4 -11
  405. package/src/semantic-chunking.ts +5 -12
  406. package/src/session-observer-state.ts +3 -3
  407. package/src/testing/subjects/recall-budget.test.ts +21 -5
  408. package/src/token-estimate.test.ts +38 -0
  409. package/src/token-estimate.ts +22 -0
  410. package/src/transcript.test.ts +38 -3
  411. package/src/transcript.ts +58 -59
  412. package/src/types.ts +1 -1
  413. package/dist/chunk-5ZIWRMZM.js.map +0 -1
  414. package/dist/chunk-HYQJQGD3.js.map +0 -1
  415. package/dist/chunk-KSXJOC4F.js.map +0 -1
  416. package/dist/chunk-LMXUWAXW.js.map +0 -1
  417. package/dist/chunk-LTAVSOZW.js.map +0 -1
  418. package/dist/chunk-P6WBAR27.js.map +0 -1
  419. package/dist/chunk-PBGWYM3H.js.map +0 -1
  420. package/dist/chunk-QKPDN325.js.map +0 -1
  421. package/dist/chunk-REVMY7RA.js.map +0 -1
  422. package/dist/chunk-Z54EM5YZ.js.map +0 -1
  423. /package/dist/{capsule-crypto-YO5QJ6L3.js.map → capsule-crypto-GWVG7LGC.js.map} +0 -0
  424. /package/dist/{chunk-YTZA7U6Q.js.map → chunk-2VHCLHZ5.js.map} +0 -0
  425. /package/dist/{chunk-NEJQ65NV.js.map → chunk-4CLE2LXB.js.map} +0 -0
  426. /package/dist/{chunk-VDX2J7OX.js.map → chunk-5W7CTZ2E.js.map} +0 -0
  427. /package/dist/{chunk-CZUHNJHX.js.map → chunk-6C7Q6NG3.js.map} +0 -0
  428. /package/dist/{chunk-4O6R3GPK.js.map → chunk-6W2ISYUV.js.map} +0 -0
  429. /package/dist/{chunk-3AJOJOFP.js.map → chunk-77DAFTUU.js.map} +0 -0
  430. /package/dist/{chunk-IKT6ZVO7.js.map → chunk-7DHFB5SG.js.map} +0 -0
  431. /package/dist/{chunk-SFR7J2G6.js.map → chunk-7G34CBKS.js.map} +0 -0
  432. /package/dist/{chunk-IT57AOMC.js.map → chunk-AH2NX6S7.js.map} +0 -0
  433. /package/dist/{chunk-FI3PIOOI.js.map → chunk-B4CSPFYN.js.map} +0 -0
  434. /package/dist/{chunk-7PJARJS4.js.map → chunk-BPM6UHUL.js.map} +0 -0
  435. /package/dist/{chunk-PPUTUOC3.js.map → chunk-BTHDQOF4.js.map} +0 -0
  436. /package/dist/{chunk-DUHMYBVE.js.map → chunk-CZGOLAEW.js.map} +0 -0
  437. /package/dist/{chunk-5PGYCWA7.js.map → chunk-DE6YCZEE.js.map} +0 -0
  438. /package/dist/{chunk-JOA4Q765.js.map → chunk-EUYWQ7TS.js.map} +0 -0
  439. /package/dist/{chunk-O5AFC5KM.js.map → chunk-EWL5DICB.js.map} +0 -0
  440. /package/dist/{chunk-4YZCIK5E.js.map → chunk-EZXGJJXM.js.map} +0 -0
  441. /package/dist/{chunk-GXIY65UH.js.map → chunk-FI5EAP4N.js.map} +0 -0
  442. /package/dist/{chunk-YOKSRIFI.js.map → chunk-FUWS2MNJ.js.map} +0 -0
  443. /package/dist/{chunk-6RK2X7BR.js.map → chunk-G3JFFUKR.js.map} +0 -0
  444. /package/dist/{chunk-VANZKHTN.js.map → chunk-G5OB4XZX.js.map} +0 -0
  445. /package/dist/{chunk-TEHPIMXD.js.map → chunk-HK6KYIF6.js.map} +0 -0
  446. /package/dist/{chunk-XBGLEOID.js.map → chunk-I4BLRM7X.js.map} +0 -0
  447. /package/dist/{chunk-6SX4MY5Q.js.map → chunk-J65AKOD6.js.map} +0 -0
  448. /package/dist/{chunk-BPWZDHFM.js.map → chunk-J6QIAYNR.js.map} +0 -0
  449. /package/dist/{chunk-XL3TATMJ.js.map → chunk-JHMAYABJ.js.map} +0 -0
  450. /package/dist/{chunk-F7M36OG2.js.map → chunk-JNWISMYG.js.map} +0 -0
  451. /package/dist/{chunk-VYIXCMUP.js.map → chunk-KBIIE57I.js.map} +0 -0
  452. /package/dist/{chunk-24PNX2GF.js.map → chunk-KXYMU4IN.js.map} +0 -0
  453. /package/dist/{chunk-FKTD7IVS.js.map → chunk-L3JVAM5Q.js.map} +0 -0
  454. /package/dist/{chunk-LIF6DO6U.js.map → chunk-MG7GI4TT.js.map} +0 -0
  455. /package/dist/{chunk-RUZM6Z52.js.map → chunk-MMZK2DWN.js.map} +0 -0
  456. /package/dist/{chunk-NTJ6TY44.js.map → chunk-ODAE36PG.js.map} +0 -0
  457. /package/dist/{chunk-YLHM5BQQ.js.map → chunk-OLX2HJFW.js.map} +0 -0
  458. /package/dist/{chunk-DNC4A7RS.js.map → chunk-OU7RWWW6.js.map} +0 -0
  459. /package/dist/{chunk-L3LRWDJW.js.map → chunk-OV36CROW.js.map} +0 -0
  460. /package/dist/{chunk-BXLOS5AJ.js.map → chunk-OWHERGF2.js.map} +0 -0
  461. /package/dist/{chunk-CSV34HIW.js.map → chunk-P5OYNV7B.js.map} +0 -0
  462. /package/dist/{chunk-SNW3YRO6.js.map → chunk-P6GOYLHN.js.map} +0 -0
  463. /package/dist/{chunk-USGKHF36.js.map → chunk-QUJC22CT.js.map} +0 -0
  464. /package/dist/{chunk-BVRREV3N.js.map → chunk-R7IV3EUJ.js.map} +0 -0
  465. /package/dist/{chunk-D4LVHPOA.js.map → chunk-RXN7Z2V5.js.map} +0 -0
  466. /package/dist/{chunk-7VSHDLJQ.js.map → chunk-T2VJ2EO5.js.map} +0 -0
  467. /package/dist/{chunk-U7BMN33M.js.map → chunk-UBSM6TZP.js.map} +0 -0
  468. /package/dist/{chunk-DQEMWVMT.js.map → chunk-UVYI6VIX.js.map} +0 -0
  469. /package/dist/{chunk-U7XLOSG2.js.map → chunk-VIM6QH4D.js.map} +0 -0
  470. /package/dist/{chunk-LJJWFV2Q.js.map → chunk-VJXSAJFW.js.map} +0 -0
  471. /package/dist/{chunk-HZC3OECF.js.map → chunk-VOKK4WCG.js.map} +0 -0
  472. /package/dist/{chunk-LRDP2AC2.js.map → chunk-YBCZTBN2.js.map} +0 -0
  473. /package/dist/{chunk-USAUBGBC.js.map → chunk-YKZFX2HQ.js.map} +0 -0
  474. /package/dist/{chunk-I2GIYTRU.js.map → chunk-ZOGOOSB3.js.map} +0 -0
@@ -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[][] {
@@ -7,6 +7,7 @@ import test from "node:test";
7
7
  import { LcmArchive } from "./lcm/archive.js";
8
8
  import { LcmEngine, extractLcmConfig } from "./lcm/engine.js";
9
9
  import { openLcmDatabase } from "./lcm/schema.js";
10
+ import { estimateTokenCount } from "./token-estimate.js";
10
11
  import type { PluginConfig } from "./types.js";
11
12
 
12
13
  function createPluginConfig(memoryDir: string): PluginConfig {
@@ -661,20 +662,99 @@ test("expandContext preserves first, middle, and last row identity when truncate
661
662
  session_id: "session-truncated",
662
663
  turn_index: 2,
663
664
  role: "assistant",
664
- content: "BBBBBB",
665
+ content: "BBBBBBBB",
665
666
  },
666
667
  {
667
668
  id: last.id,
668
669
  session_id: "session-truncated",
669
670
  turn_index: 4,
670
671
  role: "assistant",
671
- content: "DDDDDD",
672
+ content: "DDDD",
672
673
  },
673
674
  ]);
674
675
  } finally {
675
676
  await rm(memoryDir, { recursive: true, force: true });
676
677
  }
677
678
  });
679
+ test("expandContext applies token budgets to wide-script messages", async () => {
680
+ const memoryDir = await mkdtemp(
681
+ path.join(os.tmpdir(), "engram-lcm-expand-wide-script-"),
682
+ );
683
+
684
+ try {
685
+ const engine = new LcmEngine(
686
+ createPluginConfig(memoryDir),
687
+ async () => "summary",
688
+ );
689
+ await engine.ensureInitialized();
690
+
691
+ const db = openLcmDatabase(memoryDir);
692
+ try {
693
+ const archive = new LcmArchive(db);
694
+ archive.appendMessages("session-wide-script", [
695
+ { turnIndex: 1, role: "user", content: "日本語".repeat(40) },
696
+ { turnIndex: 2, role: "assistant", content: "日本語".repeat(40) },
697
+ { turnIndex: 3, role: "user", content: "日本語".repeat(40) },
698
+ ]);
699
+ } finally {
700
+ db.close();
701
+ }
702
+
703
+ const expanded = await engine.expandContext(
704
+ "session-wide-script",
705
+ 1,
706
+ 3,
707
+ 10,
708
+ );
709
+
710
+ assert.ok(
711
+ expanded.reduce((sum, message) => sum + estimateTokenCount(message.content), 0) <= 10,
712
+ );
713
+ assert.ok(expanded.every((message) => message.content.length < 40));
714
+ } finally {
715
+ await rm(memoryDir, { recursive: true, force: true });
716
+ }
717
+ });
718
+ test("expandContext preserves the final message at the smallest positive budget", async () => {
719
+ const memoryDir = await mkdtemp(
720
+ path.join(os.tmpdir(), "engram-lcm-expand-minimum-budget-"),
721
+ );
722
+
723
+ let engine: LcmEngine | undefined;
724
+ try {
725
+ engine = new LcmEngine(
726
+ createPluginConfig(memoryDir),
727
+ async () => "summary",
728
+ );
729
+ await engine.ensureInitialized();
730
+
731
+ const db = openLcmDatabase(memoryDir);
732
+ try {
733
+ const archive = new LcmArchive(db);
734
+ archive.appendMessages("session-minimum-budget", [
735
+ { turnIndex: 1, role: "user", content: "first message" },
736
+ { turnIndex: 2, role: "assistant", content: "最後のメッセージ" },
737
+ ]);
738
+ } finally {
739
+ db.close();
740
+ }
741
+
742
+ const expanded = await engine.expandContext(
743
+ "session-minimum-budget",
744
+ 1,
745
+ 2,
746
+ 1,
747
+ );
748
+
749
+ assert.equal(expanded.at(-1)?.turn_index, 2);
750
+ assert.ok(
751
+ expanded.reduce((sum, message) => sum + estimateTokenCount(message.content), 0) <= 1,
752
+ );
753
+ } finally {
754
+ engine?.close();
755
+ await rm(memoryDir, { recursive: true, force: true });
756
+ }
757
+ });
678
758
 
679
759
  test("assembleRecallWithTrace preserves text and reports selected summary metadata", async () => {
680
760
  const memoryDir = await mkdtemp(
package/src/local-llm.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { log } from "./logger.js";
2
+ import { estimateTokenCount } from "./token-estimate.js";
2
3
  import type { PluginConfig } from "./types.js";
3
4
  import fs from "node:fs";
4
5
  import os from "node:os";
@@ -1431,18 +1432,15 @@ export class LocalLlmClient {
1431
1432
 
1432
1433
  return await this.runChatCompletionRequest(messages, options);
1433
1434
  }
1434
-
1435
1435
  /**
1436
- * Estimate tokens when local LLM doesn't return usage stats
1437
- * Rough estimate: 1 token ≈ 4 characters
1436
+ * Estimate tokens when local LLM does not return usage stats.
1438
1437
  */
1439
1438
  private estimateTokens(
1440
1439
  messages: Array<{ role: string; content: string }>,
1441
1440
  response: string
1442
1441
  ): { promptTokens: number; completionTokens: number; totalTokens: number } {
1443
- const promptChars = messages.reduce((sum, m) => sum + m.content.length, 0);
1444
- const promptTokens = Math.ceil(promptChars / 4);
1445
- const completionTokens = Math.ceil(response.length / 4);
1442
+ const promptTokens = messages.reduce((sum, message) => sum + estimateTokenCount(message.content), 0);
1443
+ const completionTokens = estimateTokenCount(response);
1446
1444
 
1447
1445
  return {
1448
1446
  promptTokens,
@@ -6,16 +6,10 @@
6
6
  * truncates with a footer listing omitted extensions when over budget.
7
7
  */
8
8
 
9
+ import { estimateTokenCount } from "../token-estimate.js";
9
10
  import { REMNIC_EXTENSIONS_TOTAL_TOKEN_LIMIT } from "./host-discovery.js";
10
11
  import type { DiscoveredExtension } from "./types.js";
11
12
 
12
- /**
13
- * Approximate token count using the 4 chars per token heuristic.
14
- */
15
- function estimateTokens(text: string): number {
16
- return Math.ceil(text.length / 4);
17
- }
18
-
19
13
  /**
20
14
  * Render a markdown block containing extension instructions for injection
21
15
  * into consolidation prompts.
@@ -36,14 +30,14 @@ extension produces or curates.
36
30
  `;
37
31
 
38
32
  let budget = REMNIC_EXTENSIONS_TOTAL_TOKEN_LIMIT;
39
- budget -= estimateTokens(header);
33
+ budget -= estimateTokenCount(header);
40
34
 
41
35
  const inlined: string[] = [];
42
36
  const omitted: string[] = [];
43
37
 
44
38
  for (const ext of extensions) {
45
39
  const block = `### remnic-extension/${ext.name}\n\`\`\`\n${ext.instructions}\n\`\`\`\n\n`;
46
- const cost = estimateTokens(block);
40
+ const cost = estimateTokenCount(block);
47
41
  if (cost <= budget) {
48
42
  inlined.push(block);
49
43
  budget -= cost;