@remnic/core 9.3.712 → 9.3.714

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 (328) hide show
  1. package/dist/access-boundary.d.ts +6 -6
  2. package/dist/access-boundary.js +17 -16
  3. package/dist/access-cli.js +40 -39
  4. package/dist/access-cli.js.map +1 -1
  5. package/dist/access-http.d.ts +5 -5
  6. package/dist/access-http.js +24 -23
  7. package/dist/access-mcp.d.ts +18 -5
  8. package/dist/access-mcp.js +21 -20
  9. package/dist/access-operations-batch.js +18 -17
  10. package/dist/access-operations.d.ts +6 -5
  11. package/dist/access-operations.js +20 -19
  12. package/dist/access-schema.d.ts +10 -0
  13. package/dist/access-schema.js +1 -1
  14. package/dist/{access-service-Dx2rSJjH.d.ts → access-service-lddjZoRh.d.ts} +20 -5
  15. package/dist/access-service.d.ts +5 -5
  16. package/dist/access-service.js +16 -15
  17. package/dist/access-surface-catalog.d.ts +5 -5
  18. package/dist/access-surface-catalog.js +6 -1
  19. package/dist/access-surface-catalog.js.map +1 -1
  20. package/dist/action-confidence.d.ts +1 -1
  21. package/dist/active-memory-bridge.d.ts +3 -1
  22. package/dist/active-memory-bridge.js +2 -1
  23. package/dist/active-recall.d.ts +1 -1
  24. package/dist/active-recall.js +4 -3
  25. package/dist/active-recall.js.map +1 -1
  26. package/dist/adapters/index.js +4 -4
  27. package/dist/adapters/registry.js +2 -2
  28. package/dist/behavior-learner.d.ts +1 -1
  29. package/dist/behavior-signals.d.ts +1 -1
  30. package/dist/bootstrap.d.ts +4 -4
  31. package/dist/briefing.d.ts +1 -1
  32. package/dist/briefing.js +8 -7
  33. package/dist/buffer-surprise-report.d.ts +1 -1
  34. package/dist/buffer.d.ts +1 -1
  35. package/dist/calibration.d.ts +1 -1
  36. package/dist/capabilities.d.ts +1 -1
  37. package/dist/{catalog-CadPvH29.d.ts → catalog-CbR0CsUw.d.ts} +1 -1
  38. package/dist/causal-behavior.d.ts +1 -1
  39. package/dist/causal-consolidation.d.ts +1 -1
  40. package/dist/causal-consolidation.js +9 -8
  41. package/dist/causal-consolidation.js.map +1 -1
  42. package/dist/{chunk-OMSTU233.js → chunk-2KAYTPPT.js} +4 -4
  43. package/dist/{chunk-VYZNWH4J.js → chunk-2SXMCQEQ.js} +3 -3
  44. package/dist/{chunk-NXL5CVE7.js → chunk-2Y6LMOQQ.js} +2 -2
  45. package/dist/{chunk-AMXOCZHE.js → chunk-3VUPWDSE.js} +15 -3
  46. package/dist/{chunk-AMXOCZHE.js.map → chunk-3VUPWDSE.js.map} +1 -1
  47. package/dist/{chunk-FVQJYWH7.js → chunk-5GKPFL3S.js} +1 -1
  48. package/dist/{chunk-FVQJYWH7.js.map → chunk-5GKPFL3S.js.map} +1 -1
  49. package/dist/{chunk-LQ4J7ELC.js → chunk-67HG7KFE.js} +2 -2
  50. package/dist/{chunk-ZBG4K36I.js → chunk-6FUJ2VYY.js} +3 -3
  51. package/dist/{chunk-GMRNKPWO.js → chunk-6QUPUTT7.js} +2 -2
  52. package/dist/{chunk-Z56IHRVV.js → chunk-6UU43ZGD.js} +2 -2
  53. package/dist/{chunk-APJQ6UEA.js → chunk-AGNBY3VG.js} +4 -4
  54. package/dist/{chunk-OWOKOFXK.js → chunk-AJOIM6UR.js} +2 -2
  55. package/dist/{chunk-JBPKEARU.js → chunk-AU7Q3LSC.js} +4 -4
  56. package/dist/{chunk-YVWZW37D.js → chunk-BNOAB5HH.js} +62 -12
  57. package/dist/{chunk-YVWZW37D.js.map → chunk-BNOAB5HH.js.map} +1 -1
  58. package/dist/{chunk-A2R5NV6O.js → chunk-CTAHKIG6.js} +205 -29
  59. package/dist/chunk-CTAHKIG6.js.map +1 -0
  60. package/dist/{chunk-K22IJO4R.js → chunk-CTOQEZSN.js} +2 -2
  61. package/dist/{chunk-SINGJCUR.js → chunk-CX77GNMB.js} +14 -3
  62. package/dist/chunk-CX77GNMB.js.map +1 -0
  63. package/dist/{chunk-HSDJCT3V.js → chunk-DXGS5YOQ.js} +3 -3
  64. package/dist/{chunk-HV57RHMD.js → chunk-EH5ED3C6.js} +2 -2
  65. package/dist/{chunk-NUWZFSKC.js → chunk-FVZ2ISIX.js} +9 -1
  66. package/dist/chunk-FVZ2ISIX.js.map +1 -0
  67. package/dist/{chunk-GKPIY7EQ.js → chunk-FXZKXW7C.js} +117 -46
  68. package/dist/chunk-FXZKXW7C.js.map +1 -0
  69. package/dist/{chunk-7NDYFAJS.js → chunk-GM3BMWKR.js} +2 -2
  70. package/dist/{chunk-ZVDRL6SK.js → chunk-HHLWSWWF.js} +2 -2
  71. package/dist/chunk-HHLWSWWF.js.map +1 -0
  72. package/dist/{chunk-5QUS24F7.js → chunk-I56LKMF7.js} +2 -2
  73. package/dist/{chunk-Z3B2YW56.js → chunk-IHMT6XTY.js} +9 -5
  74. package/dist/chunk-IHMT6XTY.js.map +1 -0
  75. package/dist/chunk-JAKIDQB2.js +173 -0
  76. package/dist/chunk-JAKIDQB2.js.map +1 -0
  77. package/dist/{chunk-35YJ6KCV.js → chunk-JEIROMLX.js} +2 -2
  78. package/dist/{chunk-SKQCFAYU.js → chunk-JVEMUX6L.js} +2 -2
  79. package/dist/{chunk-CXMXAC5R.js → chunk-K44MIN6D.js} +3 -3
  80. package/dist/{chunk-5KQCOIPW.js → chunk-MECU6WSH.js} +188 -31
  81. package/dist/chunk-MECU6WSH.js.map +1 -0
  82. package/dist/{chunk-BQCFXAMN.js → chunk-MPHDVHIB.js} +2 -2
  83. package/dist/{chunk-BOGENF7P.js → chunk-MSVSP5VO.js} +1 -1
  84. package/dist/chunk-MSVSP5VO.js.map +1 -0
  85. package/dist/{chunk-QXNFQKWU.js → chunk-MTHNJCOJ.js} +2 -2
  86. package/dist/{chunk-3TCRU4JA.js → chunk-O62P2LFL.js} +2 -2
  87. package/dist/{chunk-JSDZMOT7.js → chunk-OC56UWV5.js} +11 -11
  88. package/dist/{chunk-7IBEWQLG.js → chunk-QOIONSOI.js} +2 -2
  89. package/dist/{chunk-4P5UAL3C.js → chunk-RBHANGVX.js} +6 -6
  90. package/dist/{chunk-SK2CR6MW.js → chunk-RVLB4E6N.js} +2 -2
  91. package/dist/{chunk-I3BT2IDW.js → chunk-SYDHFFAW.js} +1110 -28
  92. package/dist/chunk-SYDHFFAW.js.map +1 -0
  93. package/dist/{chunk-VILEUJXC.js → chunk-UC4YOAC2.js} +74 -2
  94. package/dist/{chunk-VILEUJXC.js.map → chunk-UC4YOAC2.js.map} +1 -1
  95. package/dist/{chunk-CNVIWMQI.js → chunk-UY4JMEAK.js} +2 -2
  96. package/dist/{chunk-S6FQLQGH.js → chunk-V7GDTQ6I.js} +2 -2
  97. package/dist/{chunk-JNOYYWCA.js → chunk-VO4UICII.js} +3 -3
  98. package/dist/{chunk-54TI5GLV.js → chunk-WI4QX7GH.js} +3 -3
  99. package/dist/{chunk-3XHD3XGK.js → chunk-XCTDHF7U.js} +30 -2
  100. package/dist/chunk-XCTDHF7U.js.map +1 -0
  101. package/dist/{chunk-XKU4YE6Z.js → chunk-XZTUMWKQ.js} +2 -2
  102. package/dist/{cli-DTWRoJEI.d.ts → cli-iAiVkbYJ.d.ts} +3 -3
  103. package/dist/cli.d.ts +6 -6
  104. package/dist/cli.js +43 -42
  105. package/dist/compounding/engine.d.ts +1 -1
  106. package/dist/compounding/engine.js +8 -7
  107. package/dist/compounding/preference-consolidator.d.ts +1 -1
  108. package/dist/compression-optimizer.d.ts +1 -1
  109. package/dist/config.d.ts +1 -1
  110. package/dist/config.js +4 -3
  111. package/dist/connectors/codex-materialize-runner.d.ts +1 -1
  112. package/dist/connectors/codex-materialize-runner.js +8 -7
  113. package/dist/connectors/codex-materialize.d.ts +1 -1
  114. package/dist/connectors/index.d.ts +1 -1
  115. package/dist/connectors/index.js +9 -8
  116. package/dist/consolidation-provenance-check.d.ts +1 -1
  117. package/dist/consolidation-undo.d.ts +1 -1
  118. package/dist/contradiction/index.d.ts +1 -1
  119. package/dist/conversation-index/backend.d.ts +1 -1
  120. package/dist/conversation-index/chunker.d.ts +1 -1
  121. package/dist/conversation-index/faiss-adapter.d.ts +1 -1
  122. package/dist/conversation-index/indexer.d.ts +1 -1
  123. package/dist/conversation-index/search.d.ts +1 -1
  124. package/dist/day-summary.d.ts +1 -1
  125. package/dist/delinearize.d.ts +1 -1
  126. package/dist/direct-answer-wiring.d.ts +1 -1
  127. package/dist/direct-answer.d.ts +1 -1
  128. package/dist/embedding-fallback.d.ts +1 -1
  129. package/dist/enrichment/index.d.ts +1 -1
  130. package/dist/entity-retrieval.d.ts +1 -1
  131. package/dist/entity-retrieval.js +8 -7
  132. package/dist/entity-schema.d.ts +1 -1
  133. package/dist/explicit-capture.d.ts +4 -4
  134. package/dist/explicit-capture.js +3 -2
  135. package/dist/extraction-faithfulness.d.ts +1 -1
  136. package/dist/extraction-judge-telemetry.d.ts +1 -1
  137. package/dist/extraction-judge-training.d.ts +1 -1
  138. package/dist/extraction-judge.d.ts +1 -1
  139. package/dist/extraction.d.ts +1 -1
  140. package/dist/extraction.js +4 -3
  141. package/dist/fallback-llm.d.ts +1 -1
  142. package/dist/identity-continuity.d.ts +1 -1
  143. package/dist/importance.d.ts +1 -1
  144. package/dist/index.d.ts +9 -9
  145. package/dist/index.js +62 -61
  146. package/dist/index.js.map +1 -1
  147. package/dist/intent.d.ts +1 -1
  148. package/dist/lcm/engine.d.ts +1 -1
  149. package/dist/lcm/index.d.ts +1 -1
  150. package/dist/lcm/index.js +3 -3
  151. package/dist/lcm/tools.d.ts +1 -1
  152. package/dist/lifecycle.d.ts +1 -1
  153. package/dist/live-connectors-runner.d.ts +1 -1
  154. package/dist/local-llm.d.ts +1 -1
  155. package/dist/maintenance/memory-governance.d.ts +1 -1
  156. package/dist/maintenance/memory-governance.js +8 -7
  157. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +8 -7
  158. package/dist/maintenance/rebuild-memory-projection.js +9 -8
  159. package/dist/mcp-memory-inspector-app.d.ts +5 -5
  160. package/dist/memory-action-policy.d.ts +1 -1
  161. package/dist/memory-cache.d.ts +1 -1
  162. package/dist/memory-lifecycle-ledger-utils.d.ts +1 -1
  163. package/dist/memory-projection-store.d.ts +1 -1
  164. package/dist/memory-provenance.d.ts +1 -1
  165. package/dist/memory-worth-outcomes.d.ts +1 -1
  166. package/dist/models-json.d.ts +1 -1
  167. package/dist/namespaces/migrate.d.ts +2 -2
  168. package/dist/namespaces/migrate.js +12 -11
  169. package/dist/namespaces/principal.d.ts +1 -1
  170. package/dist/namespaces/search.d.ts +1 -1
  171. package/dist/namespaces/search.js +3 -3
  172. package/dist/namespaces/storage.d.ts +2 -2
  173. package/dist/namespaces/storage.js +8 -7
  174. package/dist/native-knowledge.d.ts +1 -1
  175. package/dist/operator-toolkit.d.ts +1 -1
  176. package/dist/operator-toolkit.js +17 -16
  177. package/dist/orchestration/maintenance.d.ts +2 -2
  178. package/dist/orchestration/maintenance.js +10 -9
  179. package/dist/{orchestrator-B1iVjEyc.d.ts → orchestrator-iVZWi8aW.d.ts} +21 -4
  180. package/dist/orchestrator.d.ts +4 -4
  181. package/dist/orchestrator.js +32 -31
  182. package/dist/patterns-cli.d.ts +1 -1
  183. package/dist/policy-runtime.d.ts +1 -1
  184. package/dist/provenance.d.ts +1 -1
  185. package/dist/provenance.js +3 -2
  186. package/dist/qmd-recall-cache.d.ts +1 -1
  187. package/dist/qmd.d.ts +1 -1
  188. package/dist/recall-disclosure-escalation.d.ts +1 -1
  189. package/dist/recall-explain-renderer.d.ts +1 -1
  190. package/dist/recall-explain-renderer.js +3 -3
  191. package/dist/recall-handles.d.ts +228 -0
  192. package/dist/recall-handles.js +42 -0
  193. package/dist/recall-handles.js.map +1 -0
  194. package/dist/recall-planner-llm.d.ts +1 -1
  195. package/dist/recall-state.d.ts +40 -2
  196. package/dist/recall-state.js +3 -1
  197. package/dist/recall-tag-filter.d.ts +1 -1
  198. package/dist/recall-xray-cli.d.ts +1 -1
  199. package/dist/recall-xray-cli.js +4 -4
  200. package/dist/recall-xray-renderer.d.ts +1 -1
  201. package/dist/recall-xray-renderer.js +3 -3
  202. package/dist/recall-xray.d.ts +1 -1
  203. package/dist/recall-xray.js +2 -2
  204. package/dist/resolve-auth-token.d.ts +1 -1
  205. package/dist/resume-bundles.js +5 -4
  206. package/dist/retrieval-agents.d.ts +1 -1
  207. package/dist/retrieval-tiers.d.ts +1 -1
  208. package/dist/routing/engine.d.ts +1 -1
  209. package/dist/routing/store.d.ts +1 -1
  210. package/dist/sanitize.d.ts +2 -0
  211. package/dist/sanitize.js +6 -2
  212. package/dist/search/embed-helper.d.ts +1 -1
  213. package/dist/search/factory.d.ts +1 -1
  214. package/dist/search/factory.js +2 -2
  215. package/dist/search/index.d.ts +1 -1
  216. package/dist/search/index.js +4 -4
  217. package/dist/search/lancedb-backend.d.ts +1 -1
  218. package/dist/search/meilisearch-backend.d.ts +1 -1
  219. package/dist/search/noop-backend.d.ts +1 -1
  220. package/dist/search/orama-backend.d.ts +1 -1
  221. package/dist/search/port.d.ts +1 -1
  222. package/dist/search/remote-backend.d.ts +1 -1
  223. package/dist/{semantic-consolidation-CY8uVRNJ.d.ts → semantic-consolidation-DSjMDZ1u.d.ts} +1 -1
  224. package/dist/semantic-consolidation.d.ts +2 -2
  225. package/dist/semantic-consolidation.js +9 -8
  226. package/dist/semantic-rule-promotion.js +8 -7
  227. package/dist/semantic-rule-verifier.d.ts +1 -1
  228. package/dist/semantic-rule-verifier.js +8 -7
  229. package/dist/session-observer-bands.d.ts +1 -1
  230. package/dist/session-observer-state.d.ts +1 -1
  231. package/dist/shared-context/manager.d.ts +3 -3
  232. package/dist/signal.d.ts +1 -1
  233. package/dist/storage.d.ts +1 -1
  234. package/dist/storage.js +7 -6
  235. package/dist/summarizer.d.ts +1 -1
  236. package/dist/summary-snapshot.d.ts +1 -1
  237. package/dist/temporal-supersession.d.ts +1 -1
  238. package/dist/temporal-validity.d.ts +1 -1
  239. package/dist/threading.d.ts +1 -1
  240. package/dist/tier-migration.d.ts +1 -1
  241. package/dist/tier-routing.d.ts +1 -1
  242. package/dist/topics.d.ts +1 -1
  243. package/dist/transcript.d.ts +1 -1
  244. package/dist/transfer/import-sqlite.js +2 -2
  245. package/dist/{types-Dr0Cw3L9.d.ts → types-D9dwzU_x.d.ts} +25 -1
  246. package/dist/types.d.ts +1 -1
  247. package/dist/types.js +1 -1
  248. package/dist/utility-runtime.d.ts +1 -1
  249. package/dist/verified-recall.js +8 -7
  250. package/package.json +2 -2
  251. package/src/access-boundary.ts +4 -1
  252. package/src/access-http.ts +78 -20
  253. package/src/access-mcp.test.ts +29 -0
  254. package/src/access-mcp.ts +99 -17
  255. package/src/access-operations-batch.ts +14 -0
  256. package/src/access-operations.ts +5 -0
  257. package/src/access-schema.ts +8 -0
  258. package/src/access-service-observe-idempotency.test.ts +308 -0
  259. package/src/access-service.ts +106 -5
  260. package/src/access-surface-catalog.test.ts +4 -2
  261. package/src/access-surface-catalog.ts +5 -0
  262. package/src/active-memory-bridge.test.ts +72 -0
  263. package/src/active-memory-bridge.ts +21 -2
  264. package/src/chat/chat-cli.test.ts +142 -0
  265. package/src/chat/chat-cli.ts +206 -0
  266. package/src/chat/chat-config.ts +57 -0
  267. package/src/chat/chat-engine.test.ts +272 -0
  268. package/src/chat/chat-engine.ts +518 -0
  269. package/src/chat/chat-executor.ts +133 -0
  270. package/src/chat/chat-factory.ts +161 -0
  271. package/src/chat/chat-http.test.ts +197 -0
  272. package/src/chat/chat-http.ts +222 -0
  273. package/src/chat/chat-llm.ts +229 -0
  274. package/src/chat/chat-mcp.test.ts +135 -0
  275. package/src/chat/chat-security.test.ts +491 -0
  276. package/src/chat/chat-session.ts +296 -0
  277. package/src/chat/chat-system-prompt.ts +76 -0
  278. package/src/chat/chat-tools.ts +255 -0
  279. package/src/chat/chat-types.ts +167 -0
  280. package/src/cli.ts +20 -0
  281. package/src/config.ts +9 -0
  282. package/src/orchestrator.ts +106 -5
  283. package/src/recall-handles-wiring.test.ts +477 -0
  284. package/src/recall-handles.test.ts +283 -0
  285. package/src/recall-handles.ts +414 -0
  286. package/src/recall-state.ts +99 -0
  287. package/src/sanitize.ts +6 -0
  288. package/src/types.ts +29 -0
  289. package/dist/chunk-3XHD3XGK.js.map +0 -1
  290. package/dist/chunk-5KQCOIPW.js.map +0 -1
  291. package/dist/chunk-A2R5NV6O.js.map +0 -1
  292. package/dist/chunk-BOGENF7P.js.map +0 -1
  293. package/dist/chunk-GKPIY7EQ.js.map +0 -1
  294. package/dist/chunk-I3BT2IDW.js.map +0 -1
  295. package/dist/chunk-NUWZFSKC.js.map +0 -1
  296. package/dist/chunk-SINGJCUR.js.map +0 -1
  297. package/dist/chunk-Z3B2YW56.js.map +0 -1
  298. package/dist/chunk-ZVDRL6SK.js.map +0 -1
  299. /package/dist/{chunk-OMSTU233.js.map → chunk-2KAYTPPT.js.map} +0 -0
  300. /package/dist/{chunk-VYZNWH4J.js.map → chunk-2SXMCQEQ.js.map} +0 -0
  301. /package/dist/{chunk-NXL5CVE7.js.map → chunk-2Y6LMOQQ.js.map} +0 -0
  302. /package/dist/{chunk-LQ4J7ELC.js.map → chunk-67HG7KFE.js.map} +0 -0
  303. /package/dist/{chunk-ZBG4K36I.js.map → chunk-6FUJ2VYY.js.map} +0 -0
  304. /package/dist/{chunk-GMRNKPWO.js.map → chunk-6QUPUTT7.js.map} +0 -0
  305. /package/dist/{chunk-Z56IHRVV.js.map → chunk-6UU43ZGD.js.map} +0 -0
  306. /package/dist/{chunk-APJQ6UEA.js.map → chunk-AGNBY3VG.js.map} +0 -0
  307. /package/dist/{chunk-OWOKOFXK.js.map → chunk-AJOIM6UR.js.map} +0 -0
  308. /package/dist/{chunk-JBPKEARU.js.map → chunk-AU7Q3LSC.js.map} +0 -0
  309. /package/dist/{chunk-K22IJO4R.js.map → chunk-CTOQEZSN.js.map} +0 -0
  310. /package/dist/{chunk-HSDJCT3V.js.map → chunk-DXGS5YOQ.js.map} +0 -0
  311. /package/dist/{chunk-HV57RHMD.js.map → chunk-EH5ED3C6.js.map} +0 -0
  312. /package/dist/{chunk-7NDYFAJS.js.map → chunk-GM3BMWKR.js.map} +0 -0
  313. /package/dist/{chunk-5QUS24F7.js.map → chunk-I56LKMF7.js.map} +0 -0
  314. /package/dist/{chunk-35YJ6KCV.js.map → chunk-JEIROMLX.js.map} +0 -0
  315. /package/dist/{chunk-SKQCFAYU.js.map → chunk-JVEMUX6L.js.map} +0 -0
  316. /package/dist/{chunk-CXMXAC5R.js.map → chunk-K44MIN6D.js.map} +0 -0
  317. /package/dist/{chunk-BQCFXAMN.js.map → chunk-MPHDVHIB.js.map} +0 -0
  318. /package/dist/{chunk-QXNFQKWU.js.map → chunk-MTHNJCOJ.js.map} +0 -0
  319. /package/dist/{chunk-3TCRU4JA.js.map → chunk-O62P2LFL.js.map} +0 -0
  320. /package/dist/{chunk-JSDZMOT7.js.map → chunk-OC56UWV5.js.map} +0 -0
  321. /package/dist/{chunk-7IBEWQLG.js.map → chunk-QOIONSOI.js.map} +0 -0
  322. /package/dist/{chunk-4P5UAL3C.js.map → chunk-RBHANGVX.js.map} +0 -0
  323. /package/dist/{chunk-SK2CR6MW.js.map → chunk-RVLB4E6N.js.map} +0 -0
  324. /package/dist/{chunk-CNVIWMQI.js.map → chunk-UY4JMEAK.js.map} +0 -0
  325. /package/dist/{chunk-S6FQLQGH.js.map → chunk-V7GDTQ6I.js.map} +0 -0
  326. /package/dist/{chunk-JNOYYWCA.js.map → chunk-VO4UICII.js.map} +0 -0
  327. /package/dist/{chunk-54TI5GLV.js.map → chunk-WI4QX7GH.js.map} +0 -0
  328. /package/dist/{chunk-XKU4YE6Z.js.map → chunk-XZTUMWKQ.js.map} +0 -0
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Config parsing for the chat feature (issue #1583).
3
+ *
4
+ * Kept in a dedicated module so config.ts gains only an import + one call
5
+ * site — the god-file ratchet (#1520) tracks config.ts LOC.
6
+ */
7
+
8
+ import type { ChatConfig } from "../types.js";
9
+ import { coerceNumber } from "../connectors/coerce.js";
10
+
11
+ export const DEFAULT_CHAT_CONFIG: ChatConfig = {
12
+ enabled: false,
13
+ model: "",
14
+ maxToolCallsPerTurn: 8,
15
+ sessionTtlHours: 72,
16
+ };
17
+
18
+ /**
19
+ * Coerce a positive-integer config value, accepting numeric strings written
20
+ * via CLI `key=value` paths (rule 28 — coerce at the boundary). Falls back to
21
+ * the default for missing/unparseable values, matching the existing
22
+ * silent-default semantics of this parser.
23
+ */
24
+ function positiveInt(value: unknown, fallback: number): number {
25
+ const n = coerceNumber(value);
26
+ return typeof n === "number" && Number.isInteger(n) && n > 0 ? n : fallback;
27
+ }
28
+
29
+ /**
30
+ * Parse the `chat` config section from the raw config object.
31
+ *
32
+ * ```
33
+ * "chat": {
34
+ * "enabled": true,
35
+ * "model": "ollama/llama3",
36
+ * "maxToolCallsPerTurn": 8,
37
+ * "sessionTtlHours": 72
38
+ * }
39
+ * ```
40
+ *
41
+ * Missing fields default to {@link DEFAULT_CHAT_CONFIG} (rule 48 — explicit
42
+ * opt-in; `enabled` defaults to `false`).
43
+ */
44
+ export function parseChatConfig(
45
+ raw: unknown,
46
+ ): ChatConfig {
47
+ if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
48
+ return { ...DEFAULT_CHAT_CONFIG };
49
+ }
50
+ const obj = raw as Record<string, unknown>;
51
+ return {
52
+ enabled: obj.enabled === true || obj.enabled === "true" || obj.enabled === "1",
53
+ model: typeof obj.model === "string" ? obj.model : "",
54
+ maxToolCallsPerTurn: positiveInt(obj.maxToolCallsPerTurn, DEFAULT_CHAT_CONFIG.maxToolCallsPerTurn),
55
+ sessionTtlHours: positiveInt(obj.sessionTtlHours, DEFAULT_CHAT_CONFIG.sessionTtlHours),
56
+ };
57
+ }
@@ -0,0 +1,272 @@
1
+ /**
2
+ * Chat engine tests (issue #1583 PR 1).
3
+ *
4
+ * Uses the StubChatLlmAdapter to drive scripted tool-call sequences,
5
+ * verifying: read-only Q&A, confirmation protocol, budget exhaustion,
6
+ * LLM outage, citation guard, and per-session isolation.
7
+ */
8
+
9
+ import { strict as assert } from "node:assert";
10
+ import { mkdir, rm, readFile } from "node:fs/promises";
11
+ import { join } from "node:path";
12
+ import { tmpdir } from "node:os";
13
+ import { test } from "node:test";
14
+
15
+ import { ChatEngine } from "./chat-engine.js";
16
+ import { StubChatLlmAdapter, NullChatLlmAdapter } from "./chat-llm.js";
17
+ import { isConfirmationMessage } from "./chat-engine.js";
18
+ import {
19
+ createChatSession,
20
+ loadChatSession,
21
+ appendTranscriptEntry,
22
+ sessionBelongsToPrincipal,
23
+ chatSessionFile,
24
+ cleanupExpiredChatSessions,
25
+ } from "./chat-session.js";
26
+ import type { ChatSessionState } from "./chat-types.js";
27
+ import type { ChatToolExecutor } from "./chat-engine.js";
28
+
29
+ // ---------------------------------------------------------------------------
30
+ // Test helpers
31
+ // ---------------------------------------------------------------------------
32
+
33
+ function makeStubExecutor(overrides: Partial<ChatToolExecutor> = {}): ChatToolExecutor {
34
+ const base: ChatToolExecutor = {
35
+ async memorySearch() { return JSON.stringify({ results: [{ path: "mem/abc.md", score: 0.9, snippet: "Database uses PostgreSQL" }], count: 1 }); },
36
+ async memoryGet() { return JSON.stringify({ id: "abc", content: "Database uses PostgreSQL", category: "fact", tags: ["database"] }); },
37
+ async memoryTimeline() { return JSON.stringify({ found: true, timeline: [] }); },
38
+ async recallExplain() { return JSON.stringify({ found: true, tiers: [{ id: "abc", score: 0.9 }] }); },
39
+ async entityGet() { return JSON.stringify({ found: true, entity: { name: "database", type: "tool" } }); },
40
+ async stats() { return JSON.stringify({ profile: "test", entities: { count: 5 }, questions: { count: 2 } }); },
41
+ async reviewList() { return JSON.stringify({ items: [] }); },
42
+ async scopeInspect() { return JSON.stringify({ namespace: "default" }); },
43
+ async correctionPlan(request: string) { return { planId: "plan-1", preview: `Plan for: ${request}` }; },
44
+ async correctionApply(planId: string) { return JSON.stringify({ applied: true, planId }); },
45
+ async memoryPromote(memoryId: string) { return JSON.stringify({ promoted: true, memoryId }); },
46
+ };
47
+ return { ...base, ...overrides };
48
+ }
49
+
50
+ function makeEngine(
51
+ llm: StubChatLlmAdapter | NullChatLlmAdapter,
52
+ executor: ChatToolExecutor,
53
+ opts: { correctionAvailable?: boolean; maxToolCallsPerTurn?: number } = {},
54
+ ): ChatEngine {
55
+ return new ChatEngine({
56
+ llm,
57
+ executor,
58
+ maxToolCallsPerTurn: opts.maxToolCallsPerTurn ?? 8,
59
+ correctionAvailable: opts.correctionAvailable ?? false,
60
+ scopeInspectAvailable: false,
61
+ });
62
+ }
63
+
64
+ async function makeTempDir(): Promise<string> {
65
+ const dir = join(tmpdir(), `chat-test-${Date.now()}-${Math.random().toString(36).slice(2)}`);
66
+ await mkdir(dir, { recursive: true });
67
+ return dir;
68
+ }
69
+
70
+ // ---------------------------------------------------------------------------
71
+ // Tests
72
+ // ---------------------------------------------------------------------------
73
+
74
+ test("read-only Q&A: LLM calls memory_search and returns a text reply", async () => {
75
+ const executor = makeStubExecutor();
76
+ const llm = new StubChatLlmAdapter([
77
+ { toolCalls: [{ name: "memory_search", arguments: { query: "database" } }] },
78
+ { content: "Based on my search, your database uses PostgreSQL [id: abc]." },
79
+ ]);
80
+ const engine = makeEngine(llm, executor);
81
+
82
+ const session = await createChatSession(await makeTempDir(), {});
83
+ const result = await engine.processMessage("What database do I use?", session);
84
+
85
+ assert.ok(result.reply.length > 0);
86
+ assert.equal(result.chatSessionId, session.id);
87
+ assert.equal(result.error, undefined);
88
+ });
89
+
90
+ test("LLM outage → tagged error reply (rule 13)", async () => {
91
+ const executor = makeStubExecutor();
92
+ const llm = new NullChatLlmAdapter();
93
+ const engine = makeEngine(llm, executor);
94
+
95
+ const session = await createChatSession(await makeTempDir(), {});
96
+ const result = await engine.processMessage("anything", session);
97
+
98
+ assert.ok(result.reply.includes("[error]"), `Expected error tag, got: ${result.reply}`);
99
+ assert.ok(result.error);
100
+ });
101
+
102
+ test("tool budget exhaustion → partial reply with skippedTools", async () => {
103
+ const executor = makeStubExecutor();
104
+ // Script: LLM keeps calling tools forever.
105
+ const llm = new StubChatLlmAdapter([
106
+ { toolCalls: [{ name: "memory_search", arguments: { query: "a" } }] },
107
+ { toolCalls: [{ name: "memory_search", arguments: { query: "b" } }] },
108
+ ]);
109
+ const engine = makeEngine(llm, executor, { maxToolCallsPerTurn: 1 });
110
+
111
+ const session = await createChatSession(await makeTempDir(), {});
112
+ const result = await engine.processMessage("search a b", session);
113
+
114
+ // With maxToolCallsPerTurn=1, the second tool call is skipped.
115
+ assert.ok(result.skippedTools !== undefined || result.reply.length > 0);
116
+ });
117
+
118
+ test("confirmation protocol: isConfirmationMessage recognizes exact-match keywords", () => {
119
+ assert.ok(isConfirmationMessage("yes"));
120
+ assert.ok(isConfirmationMessage("y"));
121
+ assert.ok(isConfirmationMessage("apply"));
122
+ assert.ok(isConfirmationMessage("confirm"));
123
+ assert.ok(isConfirmationMessage(" YES "));
124
+ assert.ok(!isConfirmationMessage("no"));
125
+ assert.ok(!isConfirmationMessage("yeah"));
126
+ assert.ok(!isConfirmationMessage("please apply"));
127
+ });
128
+
129
+ test("citation guard: uncited memory assertion gets warning", async () => {
130
+ const executor = makeStubExecutor();
131
+ // The LLM makes an assertion without calling any tools first.
132
+ const llm = new StubChatLlmAdapter([
133
+ { content: "You remember that the database is PostgreSQL." },
134
+ ]);
135
+ const engine = makeEngine(llm, executor);
136
+
137
+ const session = await createChatSession(await makeTempDir(), {});
138
+ const result = await engine.processMessage("what database?", session);
139
+
140
+ assert.ok(
141
+ result.reply.includes("⚠️") || result.reply.includes("not grounded"),
142
+ `Expected citation warning, got: ${result.reply}`,
143
+ );
144
+ });
145
+
146
+ test("citation guard: cited assertion does NOT get warning", async () => {
147
+ const executor = makeStubExecutor();
148
+ // LLM calls a tool first, then makes the assertion.
149
+ const llm = new StubChatLlmAdapter([
150
+ { toolCalls: [{ name: "memory_search", arguments: { query: "database" } }] },
151
+ { content: "Based on the search, the database uses PostgreSQL [id: abc]." },
152
+ ]);
153
+ const engine = makeEngine(llm, executor);
154
+
155
+ const session = await createChatSession(await makeTempDir(), {});
156
+ const result = await engine.processMessage("what database?", session);
157
+
158
+ assert.ok(
159
+ !result.reply.includes("⚠️"),
160
+ `Expected no citation warning, got: ${result.reply}`,
161
+ );
162
+ });
163
+
164
+ // ---------------------------------------------------------------------------
165
+ // Session persistence tests
166
+ // ---------------------------------------------------------------------------
167
+
168
+ test("transcript persisted to JSONL and contains exclusion tag", async () => {
169
+ const dir = await makeTempDir();
170
+ const session = await createChatSession(dir, { principal: "alice" });
171
+
172
+ await appendTranscriptEntry(dir, session.id, {
173
+ role: "user",
174
+ content: "hello",
175
+ });
176
+ await appendTranscriptEntry(dir, session.id, {
177
+ role: "assistant",
178
+ content: "hi there",
179
+ });
180
+
181
+ const raw = await readFile(chatSessionFile(dir, session.id), "utf8");
182
+ const lines = raw.split("\n").filter((l) => l.trim());
183
+
184
+ // First line is the system header with the exclusion tag.
185
+ const header = JSON.parse(lines[0]!);
186
+ assert.equal(header.role, "system");
187
+ assert.ok(
188
+ header.content.includes("Excluded from memory extraction"),
189
+ "Transcript header must carry the memory-extraction exclusion tag",
190
+ );
191
+ assert.equal(lines.length, 3); // header + user + assistant
192
+ });
193
+
194
+ test("per-session isolation: different principals have no cross-visibility", async () => {
195
+ const dir = await makeTempDir();
196
+ const aliceSession = await createChatSession(dir, { principal: "alice" });
197
+ const bobSession = await createChatSession(dir, { principal: "bob" });
198
+
199
+ assert.ok(sessionBelongsToPrincipal(aliceSession, "alice"));
200
+ assert.ok(!sessionBelongsToPrincipal(aliceSession, "bob"));
201
+ assert.ok(sessionBelongsToPrincipal(bobSession, "bob"));
202
+ assert.ok(!sessionBelongsToPrincipal(bobSession, "alice"));
203
+ });
204
+
205
+ test("session without principal is accessible to anyone (single-user case)", async () => {
206
+ const dir = await makeTempDir();
207
+ const session = await createChatSession(dir, {});
208
+ assert.ok(sessionBelongsToPrincipal(session, "alice"));
209
+ assert.ok(sessionBelongsToPrincipal(session, "bob"));
210
+ assert.ok(sessionBelongsToPrincipal(session, undefined));
211
+ });
212
+
213
+ test("loadChatSession returns null for non-existent session", async () => {
214
+ const dir = await makeTempDir();
215
+ const result = await loadChatSession(dir, "nonexistent-id");
216
+ assert.equal(result, null);
217
+ });
218
+
219
+ test("loadChatSession round-trips transcript entries", async () => {
220
+ const dir = await makeTempDir();
221
+ const session = await createChatSession(dir, { principal: "alice" });
222
+ await appendTranscriptEntry(dir, session.id, { role: "user", content: "hello" });
223
+ await appendTranscriptEntry(dir, session.id, { role: "assistant", content: "world" });
224
+
225
+ const loaded = await loadChatSession(dir, session.id);
226
+ assert.ok(loaded);
227
+ assert.equal(loaded!.principal, "alice");
228
+ // 3 entries: system header + user + assistant
229
+ assert.equal(loaded!.transcript.length, 3);
230
+ });
231
+
232
+ // ---------------------------------------------------------------------------
233
+ // Cleanup tests
234
+ // ---------------------------------------------------------------------------
235
+
236
+ test("cleanupExpiredChatSessions removes old sessions", async () => {
237
+ const dir = await makeTempDir();
238
+ await createChatSession(dir, { principal: "test" });
239
+
240
+ // TTL of 0 hours → everything is expired.
241
+ const removed = await cleanupExpiredChatSessions(dir, 0);
242
+ assert.ok(removed >= 1);
243
+ });
244
+
245
+ // ---------------------------------------------------------------------------
246
+ // Review-round fixes (Thread 16: budget summary error handling)
247
+ // ---------------------------------------------------------------------------
248
+
249
+ test("budget exhaustion summary LLM failure does not crash (Thread 16)", async () => {
250
+ const executor = makeStubExecutor();
251
+ // With maxToolCallsPerTurn=1: call 1 executes the tool, call 2's tool is
252
+ // skipped (budget exhausted), call 3 is the summary. Make the summary throw.
253
+ const llm = new StubChatLlmAdapter([
254
+ { toolCalls: [{ name: "memory_search", arguments: { query: "a" } }] },
255
+ ]);
256
+ let callCount = 0;
257
+ const originalComplete = llm.complete.bind(llm);
258
+ llm.complete = async (messages, options) => {
259
+ callCount++;
260
+ // 3rd call is the budget-exhaustion summary — throw to verify the try/catch.
261
+ if (callCount === 3) throw new Error("summary LLM transport failure");
262
+ return originalComplete(messages, options);
263
+ };
264
+ const engine = makeEngine(llm, executor, { maxToolCallsPerTurn: 1 });
265
+
266
+ const session = await createChatSession(await makeTempDir(), {});
267
+ const result = await engine.processMessage("search a", session);
268
+
269
+ // Should return a partial reply, not throw.
270
+ assert.ok(result.reply.length > 0, "Should return a fallback reply");
271
+ assert.ok(result.skippedTools !== undefined, "Should report skipped tools");
272
+ });