@remnic/core 10.0.4 → 10.0.5

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 (299) hide show
  1. package/dist/access-admin-ops-surface.js +46 -46
  2. package/dist/access-authorization-probe.js +47 -47
  3. package/dist/access-boundary.js +46 -46
  4. package/dist/access-cli.d.ts.map +1 -1
  5. package/dist/access-cli.js +117 -112
  6. package/dist/access-cli.js.map +1 -1
  7. package/dist/access-extraction-force-flush.js +46 -46
  8. package/dist/access-http-briefing.js +47 -47
  9. package/dist/access-http-query.js +48 -48
  10. package/dist/access-http.d.ts.map +1 -1
  11. package/dist/access-http.js +69 -68
  12. package/dist/access-identity-continuity-surface.js +46 -46
  13. package/dist/access-lcm-surface.js +46 -46
  14. package/dist/access-mcp-correction-tools.d.ts +10 -0
  15. package/dist/access-mcp-correction-tools.d.ts.map +1 -0
  16. package/dist/access-mcp-correction-tools.js +8 -0
  17. package/dist/access-mcp.d.ts.map +1 -1
  18. package/dist/access-mcp.js +60 -59
  19. package/dist/access-namespace-preflight.js +46 -46
  20. package/dist/access-observe-write-category.js +3 -3
  21. package/dist/access-observe-write-surface.js +46 -46
  22. package/dist/access-offline-manifest.js +5 -5
  23. package/dist/access-operations-batch.js +49 -49
  24. package/dist/access-operations.d.ts +1 -0
  25. package/dist/access-operations.d.ts.map +1 -1
  26. package/dist/access-operations.js +56 -56
  27. package/dist/access-recall-concurrency.js +46 -46
  28. package/dist/access-recall-diagnostics.js +2 -2
  29. package/dist/access-recall-response.js +46 -46
  30. package/dist/access-recall-surface.js +46 -46
  31. package/dist/access-service.d.ts.map +1 -1
  32. package/dist/access-service.js +46 -46
  33. package/dist/access-surface-catalog.js +48 -48
  34. package/dist/active-recall.js +22 -22
  35. package/dist/adapters/index.js +4 -4
  36. package/dist/adapters/registry.js +2 -2
  37. package/dist/auto-sync-FN7E3OTO.js +2 -2
  38. package/dist/briefing.js +18 -18
  39. package/dist/buffer.js +1 -1
  40. package/dist/causal-consolidation.js +18 -18
  41. package/dist/{chunk-5YDWSWBU.js → chunk-2CP577K6.js} +2 -2
  42. package/dist/{chunk-ABET5LA5.js → chunk-37HCPM56.js} +23 -23
  43. package/dist/{chunk-5QHTQMRL.js → chunk-4CWUZBPF.js} +8 -6
  44. package/dist/chunk-4CWUZBPF.js.map +1 -0
  45. package/dist/{chunk-H6FA36LC.js → chunk-4K2H4KTR.js} +5 -5
  46. package/dist/{chunk-TSEUVMJL.js → chunk-52CQKST6.js} +39 -39
  47. package/dist/{chunk-H7BZBSP6.js → chunk-52II7PB6.js} +4 -4
  48. package/dist/{chunk-RH6MQ5NG.js → chunk-5BPXMAUP.js} +17 -68
  49. package/dist/chunk-5BPXMAUP.js.map +1 -0
  50. package/dist/{chunk-WLC6XA6C.js → chunk-5BY3IA2U.js} +16 -53
  51. package/dist/chunk-5BY3IA2U.js.map +1 -0
  52. package/dist/{chunk-ABDTBGI2.js → chunk-5CQO6LVM.js} +2 -2
  53. package/dist/{chunk-HSZBALHV.js → chunk-5LNPW7NE.js} +2 -2
  54. package/dist/{chunk-U3LVARGV.js → chunk-5X4ZJESN.js} +2 -2
  55. package/dist/{chunk-SNGNGH53.js → chunk-63UNVGZW.js} +4 -4
  56. package/dist/{chunk-2KB7TTAP.js → chunk-64S6X4IU.js} +4 -4
  57. package/dist/{chunk-3QAIX7TV.js → chunk-6KHF6PAB.js} +2 -2
  58. package/dist/{chunk-MYBCB4AT.js → chunk-6T42L5FI.js} +2 -2
  59. package/dist/{chunk-R6EOFKZG.js → chunk-7LYRFI5A.js} +2 -2
  60. package/dist/{chunk-4NFRYOHT.js → chunk-ANZXWOYD.js} +2 -2
  61. package/dist/{chunk-QDKABMUE.js → chunk-AXC7UTLM.js} +2 -2
  62. package/dist/{chunk-5XXTZ4Y7.js → chunk-B657ZGDH.js} +2 -2
  63. package/dist/{chunk-NSQ3GUVO.js → chunk-CYEK6Z3P.js} +4 -4
  64. package/dist/{chunk-IRYKRBEX.js → chunk-DJMVSUPK.js} +3 -3
  65. package/dist/{chunk-GLS3A4SQ.js → chunk-DST7HRSG.js} +2 -2
  66. package/dist/{chunk-UCCB7RKF.js → chunk-EMVJU7WC.js} +2 -2
  67. package/dist/{chunk-IY7ZISXV.js → chunk-ENNC3RZ6.js} +2 -2
  68. package/dist/{chunk-QLK3SL7Q.js → chunk-ETSJD5IV.js} +131 -12
  69. package/dist/chunk-ETSJD5IV.js.map +1 -0
  70. package/dist/{chunk-PTFL3INS.js → chunk-FC6RCOIU.js} +2 -2
  71. package/dist/{chunk-CQQTEDHS.js → chunk-FLV7M7CT.js} +2 -2
  72. package/dist/{chunk-IXF7TRMQ.js → chunk-FRH2UC7Q.js} +2 -2
  73. package/dist/{chunk-3SL52JKD.js → chunk-FZVROZKC.js} +5 -5
  74. package/dist/{chunk-Z2EVKNZR.js → chunk-GEGUBU3D.js} +2 -2
  75. package/dist/{chunk-LIGP5M2C.js → chunk-GF4H3B3R.js} +8 -8
  76. package/dist/{chunk-7OOW72QA.js → chunk-GVUW5XK5.js} +305 -136
  77. package/dist/chunk-GVUW5XK5.js.map +1 -0
  78. package/dist/{chunk-XTWF5XR6.js → chunk-HPAOMYKM.js} +67 -67
  79. package/dist/{chunk-ZOGZMAGI.js → chunk-I25RINZK.js} +2 -2
  80. package/dist/{chunk-FMIVV5SV.js → chunk-I56MTWE7.js} +2 -2
  81. package/dist/{chunk-EUL3S3LE.js → chunk-J5WI4QRP.js} +4 -4
  82. package/dist/{chunk-436IRJUD.js → chunk-JA54KIYD.js} +2 -2
  83. package/dist/{chunk-QWNBQ2T5.js → chunk-JMF5Q4JY.js} +10 -10
  84. package/dist/{chunk-IDSSDL4W.js → chunk-JXSYWTFK.js} +21 -4
  85. package/dist/{chunk-IDSSDL4W.js.map → chunk-JXSYWTFK.js.map} +1 -1
  86. package/dist/chunk-KIUVEMBI.js +49 -0
  87. package/dist/chunk-KIUVEMBI.js.map +1 -0
  88. package/dist/{chunk-IINXHPCV.js → chunk-KQI2PKYP.js} +2 -2
  89. package/dist/{chunk-RGPPNNDJ.js → chunk-KSUOM3YU.js} +9 -9
  90. package/dist/{chunk-CXG2T7BJ.js → chunk-KZEMFD6E.js} +3 -3
  91. package/dist/{chunk-O57VUN4U.js → chunk-L2VS27PZ.js} +2 -2
  92. package/dist/{chunk-IVGJXS5F.js → chunk-L34W4W4I.js} +2 -2
  93. package/dist/{chunk-MEP3POVW.js → chunk-L3PWXD73.js} +2 -2
  94. package/dist/{chunk-NQHSXHJP.js → chunk-L6GNS5GB.js} +2 -2
  95. package/dist/{chunk-YMXCJ7SE.js → chunk-M47Q6566.js} +2 -2
  96. package/dist/{chunk-DQJCQHII.js → chunk-MQNOIH2C.js} +6 -6
  97. package/dist/{chunk-IDSZNHIP.js → chunk-MULNBSPP.js} +4 -4
  98. package/dist/{chunk-4DXVWBDB.js → chunk-NBQIFCJE.js} +2 -2
  99. package/dist/{chunk-H2XCCUOT.js → chunk-NHSVW7UT.js} +15 -15
  100. package/dist/{chunk-BDHWKFGM.js → chunk-OFEHITAZ.js} +2 -2
  101. package/dist/{chunk-RYN4AIRM.js → chunk-OZABSIOV.js} +4 -4
  102. package/dist/{chunk-Y7CXM7UM.js → chunk-QMFKW4OU.js} +3 -3
  103. package/dist/{chunk-Z63GU5NN.js → chunk-QWWMS4AE.js} +1 -1
  104. package/dist/chunk-QWWMS4AE.js.map +1 -0
  105. package/dist/{chunk-R4O2G6ML.js → chunk-RSMIRLKM.js} +2 -2
  106. package/dist/{chunk-6AH2DDHI.js → chunk-S5TGRZWY.js} +2 -2
  107. package/dist/{chunk-TYOBAFCY.js → chunk-SNXBXYUE.js} +3 -3
  108. package/dist/{chunk-MEMH5NKJ.js → chunk-SUAX5JWY.js} +2 -2
  109. package/dist/{chunk-IHIP7TJ3.js → chunk-SWRN6U7Q.js} +2 -2
  110. package/dist/{chunk-DBA2W6VV.js → chunk-SZXPKUMN.js} +6 -6
  111. package/dist/{chunk-WQUGCOKF.js → chunk-TFA6HPOA.js} +4 -4
  112. package/dist/{chunk-VWCFR4NR.js → chunk-U6OXJQEH.js} +5 -5
  113. package/dist/{chunk-DYRXRQHV.js → chunk-V32NHP2R.js} +2 -2
  114. package/dist/{chunk-ORAL4HJC.js → chunk-WG4TMZJ2.js} +2 -2
  115. package/dist/{chunk-QVBB2CLG.js → chunk-WRICYGYG.js} +27 -26
  116. package/dist/chunk-WRICYGYG.js.map +1 -0
  117. package/dist/{chunk-CV5AMCPX.js → chunk-XPRAOTJY.js} +2 -2
  118. package/dist/{chunk-RZCRXCLI.js → chunk-XQSJXNWC.js} +2 -2
  119. package/dist/{chunk-P22M3DX3.js → chunk-YJAOYEWI.js} +2 -2
  120. package/dist/{chunk-OAFRMLL4.js → chunk-YJEUHGZZ.js} +2 -2
  121. package/dist/cli.js +89 -88
  122. package/dist/coding/export-okf-codegraph.js +16 -16
  123. package/dist/compounding/engine.js +17 -17
  124. package/dist/config.d.ts.map +1 -1
  125. package/dist/config.js +22 -22
  126. package/dist/connectors/codex-materialize-runner.js +17 -17
  127. package/dist/connectors/index.js +17 -17
  128. package/dist/consolidation-provenance-check.js +2 -2
  129. package/dist/contradiction/index.js +7 -7
  130. package/dist/convergence-refresh.js +19 -19
  131. package/dist/conversation-index/backend.js +2 -2
  132. package/dist/corpus-watermark.js +19 -19
  133. package/dist/correction/correction-access-wiring.d.ts +6 -6
  134. package/dist/correction/correction-access-wiring.d.ts.map +1 -1
  135. package/dist/correction/correction-classify.d.ts +25 -0
  136. package/dist/correction/correction-classify.d.ts.map +1 -0
  137. package/dist/correction/correction-config.d.ts +11 -0
  138. package/dist/correction/correction-config.d.ts.map +1 -0
  139. package/dist/correction/correction-contract.d.ts +13 -1
  140. package/dist/correction/correction-contract.d.ts.map +1 -1
  141. package/dist/correction/correction-executor.d.ts +2 -0
  142. package/dist/correction/correction-executor.d.ts.map +1 -1
  143. package/dist/correction/correction-planner.d.ts +24 -3
  144. package/dist/correction/correction-planner.d.ts.map +1 -1
  145. package/dist/entity-retrieval.js +17 -17
  146. package/dist/event-order-recall.js +2 -2
  147. package/dist/explicit-capture.js +2 -2
  148. package/dist/external-wiki-access.js +47 -47
  149. package/dist/extraction-span-materialize.js +2 -2
  150. package/dist/extraction.js +22 -22
  151. package/dist/harmonic-retrieval.js +17 -17
  152. package/dist/index.js +189 -188
  153. package/dist/lcm/index.js +3 -3
  154. package/dist/local-llm.d.ts.map +1 -1
  155. package/dist/local-llm.js +1 -1
  156. package/dist/location/index.js +19 -19
  157. package/dist/maintenance/memory-governance.js +17 -17
  158. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +17 -17
  159. package/dist/maintenance/rebuild-memory-projection-runner.js +19 -19
  160. package/dist/maintenance/rebuild-memory-projection.js +18 -18
  161. package/dist/memory-browse-operations.js +47 -47
  162. package/dist/memory-browse.js +1 -1
  163. package/dist/memory-subject.js +1 -1
  164. package/dist/namespaces/migrate.js +18 -18
  165. package/dist/namespaces/search.js +6 -6
  166. package/dist/namespaces/storage.js +17 -17
  167. package/dist/operator-doctor-corpus.js +20 -20
  168. package/dist/operator-toolkit.js +32 -32
  169. package/dist/orchestration/maintenance.js +28 -28
  170. package/dist/orchestration/session-context.d.ts.map +1 -1
  171. package/dist/orchestrator.js +113 -112
  172. package/dist/recall-explain-renderer.js +3 -3
  173. package/dist/recall-navigate-access.js +2 -2
  174. package/dist/recall-navigate-disclosure.js +2 -2
  175. package/dist/recall-navigate.js +2 -2
  176. package/dist/recall-navigation-operations.js +47 -47
  177. package/dist/recall-navigation.js +46 -46
  178. package/dist/recall-why-service.js +2 -2
  179. package/dist/recall-xray-cli.js +4 -4
  180. package/dist/recall-xray-renderer.js +3 -3
  181. package/dist/recall-xray.js +2 -2
  182. package/dist/reconcile/manifest.js +3 -3
  183. package/dist/{resolution-RSG4HZTL.js → resolution-TV3F7ZTH.js} +7 -7
  184. package/dist/resolution-TV3F7ZTH.js.map +1 -0
  185. package/dist/resume-bundles.js +23 -23
  186. package/dist/salvage-envelope.js +1 -1
  187. package/dist/search/factory.js +5 -5
  188. package/dist/search/index.js +9 -9
  189. package/dist/semantic-consolidation.js +23 -23
  190. package/dist/semantic-rule-promotion.js +17 -17
  191. package/dist/semantic-rule-verifier.js +17 -17
  192. package/dist/source-agent-qualifier.js +17 -17
  193. package/dist/standup/build.js +17 -17
  194. package/dist/storage.js +16 -16
  195. package/dist/subject-config.js +1 -1
  196. package/dist/summarizer.js +3 -3
  197. package/dist/support-passport/index.js +63 -63
  198. package/dist/transfer/export-okf.js +17 -17
  199. package/dist/types.d.ts +6 -23
  200. package/dist/types.d.ts.map +1 -1
  201. package/dist/types.js +1 -1
  202. package/dist/verified-recall.js +17 -17
  203. package/dist/who-knows-cli.js +18 -18
  204. package/dist/who-knows.js +17 -17
  205. package/dist/write-envelope.js +1 -1
  206. package/package.json +2 -2
  207. package/src/access-cli.ts +4 -0
  208. package/src/access-http.ts +1 -0
  209. package/src/access-mcp-correction-tools.ts +56 -0
  210. package/src/access-mcp.ts +4 -49
  211. package/src/access-operations.ts +3 -0
  212. package/src/access-service.ts +12 -21
  213. package/src/config.test.ts +26 -0
  214. package/src/config.ts +4 -60
  215. package/src/correction/correction-access-wiring.ts +56 -16
  216. package/src/correction/correction-classify.test.ts +185 -0
  217. package/src/correction/correction-classify.ts +157 -0
  218. package/src/correction/correction-config.ts +93 -0
  219. package/src/correction/correction-contract.ts +61 -14
  220. package/src/correction/correction-executor.test.ts +102 -2
  221. package/src/correction/correction-executor.ts +19 -1
  222. package/src/correction/correction-planner.test.ts +141 -0
  223. package/src/correction/correction-planner.ts +117 -11
  224. package/src/correction/correction-surfaces.test.ts +22 -1
  225. package/src/local-llm-qos.test.ts +36 -0
  226. package/src/local-llm.ts +20 -3
  227. package/src/orchestration/session-context.ts +0 -15
  228. package/src/recall-handles-wiring.test.ts +24 -0
  229. package/src/types.ts +6 -23
  230. package/dist/chunk-5QHTQMRL.js.map +0 -1
  231. package/dist/chunk-7OOW72QA.js.map +0 -1
  232. package/dist/chunk-QLK3SL7Q.js.map +0 -1
  233. package/dist/chunk-QVBB2CLG.js.map +0 -1
  234. package/dist/chunk-RH6MQ5NG.js.map +0 -1
  235. package/dist/chunk-WLC6XA6C.js.map +0 -1
  236. package/dist/chunk-Z63GU5NN.js.map +0 -1
  237. /package/dist/{resolution-RSG4HZTL.js.map → access-mcp-correction-tools.js.map} +0 -0
  238. /package/dist/{chunk-5YDWSWBU.js.map → chunk-2CP577K6.js.map} +0 -0
  239. /package/dist/{chunk-ABET5LA5.js.map → chunk-37HCPM56.js.map} +0 -0
  240. /package/dist/{chunk-H6FA36LC.js.map → chunk-4K2H4KTR.js.map} +0 -0
  241. /package/dist/{chunk-TSEUVMJL.js.map → chunk-52CQKST6.js.map} +0 -0
  242. /package/dist/{chunk-H7BZBSP6.js.map → chunk-52II7PB6.js.map} +0 -0
  243. /package/dist/{chunk-ABDTBGI2.js.map → chunk-5CQO6LVM.js.map} +0 -0
  244. /package/dist/{chunk-HSZBALHV.js.map → chunk-5LNPW7NE.js.map} +0 -0
  245. /package/dist/{chunk-U3LVARGV.js.map → chunk-5X4ZJESN.js.map} +0 -0
  246. /package/dist/{chunk-SNGNGH53.js.map → chunk-63UNVGZW.js.map} +0 -0
  247. /package/dist/{chunk-2KB7TTAP.js.map → chunk-64S6X4IU.js.map} +0 -0
  248. /package/dist/{chunk-3QAIX7TV.js.map → chunk-6KHF6PAB.js.map} +0 -0
  249. /package/dist/{chunk-MYBCB4AT.js.map → chunk-6T42L5FI.js.map} +0 -0
  250. /package/dist/{chunk-R6EOFKZG.js.map → chunk-7LYRFI5A.js.map} +0 -0
  251. /package/dist/{chunk-4NFRYOHT.js.map → chunk-ANZXWOYD.js.map} +0 -0
  252. /package/dist/{chunk-QDKABMUE.js.map → chunk-AXC7UTLM.js.map} +0 -0
  253. /package/dist/{chunk-5XXTZ4Y7.js.map → chunk-B657ZGDH.js.map} +0 -0
  254. /package/dist/{chunk-NSQ3GUVO.js.map → chunk-CYEK6Z3P.js.map} +0 -0
  255. /package/dist/{chunk-IRYKRBEX.js.map → chunk-DJMVSUPK.js.map} +0 -0
  256. /package/dist/{chunk-GLS3A4SQ.js.map → chunk-DST7HRSG.js.map} +0 -0
  257. /package/dist/{chunk-UCCB7RKF.js.map → chunk-EMVJU7WC.js.map} +0 -0
  258. /package/dist/{chunk-IY7ZISXV.js.map → chunk-ENNC3RZ6.js.map} +0 -0
  259. /package/dist/{chunk-PTFL3INS.js.map → chunk-FC6RCOIU.js.map} +0 -0
  260. /package/dist/{chunk-CQQTEDHS.js.map → chunk-FLV7M7CT.js.map} +0 -0
  261. /package/dist/{chunk-IXF7TRMQ.js.map → chunk-FRH2UC7Q.js.map} +0 -0
  262. /package/dist/{chunk-3SL52JKD.js.map → chunk-FZVROZKC.js.map} +0 -0
  263. /package/dist/{chunk-Z2EVKNZR.js.map → chunk-GEGUBU3D.js.map} +0 -0
  264. /package/dist/{chunk-LIGP5M2C.js.map → chunk-GF4H3B3R.js.map} +0 -0
  265. /package/dist/{chunk-XTWF5XR6.js.map → chunk-HPAOMYKM.js.map} +0 -0
  266. /package/dist/{chunk-ZOGZMAGI.js.map → chunk-I25RINZK.js.map} +0 -0
  267. /package/dist/{chunk-FMIVV5SV.js.map → chunk-I56MTWE7.js.map} +0 -0
  268. /package/dist/{chunk-EUL3S3LE.js.map → chunk-J5WI4QRP.js.map} +0 -0
  269. /package/dist/{chunk-436IRJUD.js.map → chunk-JA54KIYD.js.map} +0 -0
  270. /package/dist/{chunk-QWNBQ2T5.js.map → chunk-JMF5Q4JY.js.map} +0 -0
  271. /package/dist/{chunk-IINXHPCV.js.map → chunk-KQI2PKYP.js.map} +0 -0
  272. /package/dist/{chunk-RGPPNNDJ.js.map → chunk-KSUOM3YU.js.map} +0 -0
  273. /package/dist/{chunk-CXG2T7BJ.js.map → chunk-KZEMFD6E.js.map} +0 -0
  274. /package/dist/{chunk-O57VUN4U.js.map → chunk-L2VS27PZ.js.map} +0 -0
  275. /package/dist/{chunk-IVGJXS5F.js.map → chunk-L34W4W4I.js.map} +0 -0
  276. /package/dist/{chunk-MEP3POVW.js.map → chunk-L3PWXD73.js.map} +0 -0
  277. /package/dist/{chunk-NQHSXHJP.js.map → chunk-L6GNS5GB.js.map} +0 -0
  278. /package/dist/{chunk-YMXCJ7SE.js.map → chunk-M47Q6566.js.map} +0 -0
  279. /package/dist/{chunk-DQJCQHII.js.map → chunk-MQNOIH2C.js.map} +0 -0
  280. /package/dist/{chunk-IDSZNHIP.js.map → chunk-MULNBSPP.js.map} +0 -0
  281. /package/dist/{chunk-4DXVWBDB.js.map → chunk-NBQIFCJE.js.map} +0 -0
  282. /package/dist/{chunk-H2XCCUOT.js.map → chunk-NHSVW7UT.js.map} +0 -0
  283. /package/dist/{chunk-BDHWKFGM.js.map → chunk-OFEHITAZ.js.map} +0 -0
  284. /package/dist/{chunk-RYN4AIRM.js.map → chunk-OZABSIOV.js.map} +0 -0
  285. /package/dist/{chunk-Y7CXM7UM.js.map → chunk-QMFKW4OU.js.map} +0 -0
  286. /package/dist/{chunk-R4O2G6ML.js.map → chunk-RSMIRLKM.js.map} +0 -0
  287. /package/dist/{chunk-6AH2DDHI.js.map → chunk-S5TGRZWY.js.map} +0 -0
  288. /package/dist/{chunk-TYOBAFCY.js.map → chunk-SNXBXYUE.js.map} +0 -0
  289. /package/dist/{chunk-MEMH5NKJ.js.map → chunk-SUAX5JWY.js.map} +0 -0
  290. /package/dist/{chunk-IHIP7TJ3.js.map → chunk-SWRN6U7Q.js.map} +0 -0
  291. /package/dist/{chunk-DBA2W6VV.js.map → chunk-SZXPKUMN.js.map} +0 -0
  292. /package/dist/{chunk-WQUGCOKF.js.map → chunk-TFA6HPOA.js.map} +0 -0
  293. /package/dist/{chunk-VWCFR4NR.js.map → chunk-U6OXJQEH.js.map} +0 -0
  294. /package/dist/{chunk-DYRXRQHV.js.map → chunk-V32NHP2R.js.map} +0 -0
  295. /package/dist/{chunk-ORAL4HJC.js.map → chunk-WG4TMZJ2.js.map} +0 -0
  296. /package/dist/{chunk-CV5AMCPX.js.map → chunk-XPRAOTJY.js.map} +0 -0
  297. /package/dist/{chunk-RZCRXCLI.js.map → chunk-XQSJXNWC.js.map} +0 -0
  298. /package/dist/{chunk-P22M3DX3.js.map → chunk-YJAOYEWI.js.map} +0 -0
  299. /package/dist/{chunk-OAFRMLL4.js.map → chunk-YJEUHGZZ.js.map} +0 -0
@@ -68,6 +68,8 @@ export interface ExecutorMemory {
68
68
  entityRef?: string;
69
69
  /** Raw content used for the tombstone hash (rule 23). */
70
70
  rawContent: string;
71
+ /** Frontmatter status. Absent is treated as active. */
72
+ status?: string;
71
73
  }
72
74
 
73
75
  export interface ExecutorDeps {
@@ -486,13 +488,29 @@ export class CorrectionExecutor {
486
488
  if (action.replacement && !replacementResult) {
487
489
  continue;
488
490
  }
491
+ if (action.winnerId && !action.replacement) {
492
+ const winner = await this.deps.getMemory(namespace, action.winnerId, abortSignal);
493
+ const winnerStatus = winner ? (winner.status ?? "active") : undefined;
494
+ if (!winner || winnerStatus !== "active") {
495
+ results.push({
496
+ action,
497
+ status: "failed",
498
+ error: winner
499
+ ? `supersede winner is not active: ${action.winnerId} (${winnerStatus})`
500
+ : `supersede winner not found: ${action.winnerId}`,
501
+ });
502
+ continue;
503
+ }
504
+ }
489
505
  await this.retireAndTombstone(
490
506
  namespace,
491
507
  action,
492
508
  "supersession",
493
509
  results,
494
510
  appliedTouched,
495
- { supersededBy: replacementResult?.memoryId },
511
+ {
512
+ supersededBy: replacementResult?.memoryId ?? action.winnerId,
513
+ },
496
514
  abortSignal
497
515
  );
498
516
  if (results.at(-1)?.status === "applied") {
@@ -38,6 +38,8 @@ interface StubState {
38
38
  writeReplacementCalls: number;
39
39
  propagateCalls: number;
40
40
  retireCalls: number;
41
+ statusById?: Map<string, string>;
42
+ classifyCalls?: number;
41
43
  }
42
44
 
43
45
  function makeCandidate(over: Partial<PlannerCandidate> & { memoryId: string }): PlannerCandidate {
@@ -72,12 +74,18 @@ function makeDeps(stateDir: string, state: StubState, opts: { maxAffected?: numb
72
74
  }
73
75
  return out;
74
76
  },
77
+ inspectMemory: async ({ memoryId }) => {
78
+ const candidate = state.candidatesById.get(memoryId);
79
+ if (!candidate) return null;
80
+ return { candidate, status: state.statusById?.get(memoryId) ?? "active" };
81
+ },
75
82
  expandNeighbors: async ({ seedIds }) => {
76
83
  // No neighbors in the stub; return the seeds themselves so neighbor
77
84
  // expansion is exercised but produces no surprises.
78
85
  return seedIds.map((id) => state.candidatesById.get(id)).filter((c): c is PlannerCandidate => c !== undefined);
79
86
  },
80
87
  classifyAndDraft: async ({ text, candidates }) => {
88
+ state.classifyCalls = (state.classifyCalls ?? 0) + 1;
81
89
  return typeof state.llmResult === "function"
82
90
  ? state.llmResult(text, candidates)
83
91
  : state.llmResult;
@@ -787,3 +795,136 @@ test("#1713: recoverStaleApplyingPlans recovers pre-fix plans immediately at exp
787
795
  assert.equal(recovered.length, 1, "pre-fix plan at expiry must be recovered immediately");
788
796
  });
789
797
  });
798
+
799
+ test("duplicateOf skips the LLM and drafts one recoverable supersede linked to the winner", async () => {
800
+ await withTempDir(async (dir) => {
801
+ const state: StubState = {
802
+ candidatesById: new Map([
803
+ ["mem-loser", makeCandidate({ memoryId: "mem-loser", content: "duplicate fact" })],
804
+ ["mem-winner", makeCandidate({ memoryId: "mem-winner", content: "canonical fact" })],
805
+ ]),
806
+ llmResult: () => {
807
+ throw new Error("classifyAndDraft must not run in duplicate mode");
808
+ },
809
+ writeReplacementCalls: 0,
810
+ propagateCalls: 0,
811
+ retireCalls: 0,
812
+ };
813
+ const plan = await new CorrectionPlanner(makeDeps(dir, state)).plan(
814
+ { text: "mem-loser duplicates mem-winner", targetIds: ["mem-loser"], duplicateOf: "mem-winner" },
815
+ ["default"],
816
+ );
817
+ assert.equal(state.classifyCalls ?? 0, 0);
818
+ assert.equal(plan.actions.length, 1);
819
+ assert.deepEqual(plan.actions[0], { kind: "supersede", loserId: "mem-loser", winnerId: "mem-winner" });
820
+ assert.equal(plan.classification, "outdated");
821
+ assert.equal(plan.confidence, 1);
822
+ assert.equal(plan.affected.length, 1);
823
+ assert.equal(plan.affected[0]?.memoryId, "mem-loser");
824
+ assert.equal(plan.request.duplicateOf, "mem-winner");
825
+ });
826
+ });
827
+
828
+ test("a model-selected winnerId outside the located set is dropped", async () => {
829
+ await withTempDir(async (dir) => {
830
+ const state: StubState = {
831
+ candidatesById: new Map([
832
+ ["mem-loser", makeCandidate({ memoryId: "mem-loser", content: "duplicate fact" })],
833
+ ["mem-winner", makeCandidate({ memoryId: "mem-winner", content: "canonical fact" })],
834
+ ]),
835
+ llmResult: {
836
+ classification: "outdated",
837
+ confidence: 0.9,
838
+ actions: [{ kind: "supersede", loserId: "mem-loser", winnerId: "mem-not-located" }],
839
+ relevance: [{ memoryId: "mem-loser", why: "duplicate" }],
840
+ warnings: [],
841
+ },
842
+ writeReplacementCalls: 0,
843
+ propagateCalls: 0,
844
+ retireCalls: 0,
845
+ };
846
+ const plan = await new CorrectionPlanner(makeDeps(dir, state)).plan(
847
+ { text: "retire the duplicate", targetIds: ["mem-loser"] },
848
+ ["default"],
849
+ );
850
+ assert.equal(plan.actions.length, 0);
851
+ assert.match(plan.warnings.join(" "), /outside the located candidate set/);
852
+ });
853
+ });
854
+
855
+ test("a model-selected winnerId inside the located set is kept", async () => {
856
+ await withTempDir(async (dir) => {
857
+ const state: StubState = {
858
+ candidatesById: new Map([
859
+ ["mem-loser", makeCandidate({ memoryId: "mem-loser", content: "duplicate fact" })],
860
+ ["mem-winner", makeCandidate({ memoryId: "mem-winner", content: "canonical fact" })],
861
+ ]),
862
+ llmResult: {
863
+ classification: "outdated",
864
+ confidence: 0.9,
865
+ actions: [{ kind: "supersede", loserId: "mem-loser", winnerId: "mem-winner" }],
866
+ relevance: [{ memoryId: "mem-loser", why: "duplicate" }],
867
+ warnings: [],
868
+ },
869
+ writeReplacementCalls: 0,
870
+ propagateCalls: 0,
871
+ retireCalls: 0,
872
+ };
873
+ const plan = await new CorrectionPlanner(makeDeps(dir, state)).plan(
874
+ { text: "retire the duplicate", targetIds: ["mem-loser", "mem-winner"] },
875
+ ["default"],
876
+ );
877
+ assert.deepEqual(plan.actions, [{ kind: "supersede", loserId: "mem-loser", winnerId: "mem-winner" }]);
878
+ });
879
+ });
880
+
881
+ test("duplicateOf rejects same id, missing memories, inactive memories, and a target list that is not exactly one", async () => {
882
+ await withTempDir(async (dir) => {
883
+ const state: StubState = {
884
+ candidatesById: new Map([
885
+ ["mem-loser", makeCandidate({ memoryId: "mem-loser" })],
886
+ ["mem-winner", makeCandidate({ memoryId: "mem-winner" })],
887
+ ["mem-old", makeCandidate({ memoryId: "mem-old" })],
888
+ ]),
889
+ statusById: new Map([["mem-old", "superseded"]]),
890
+ llmResult: { classification: "outdated", confidence: 1, actions: [], relevance: [], warnings: [] },
891
+ writeReplacementCalls: 0,
892
+ propagateCalls: 0,
893
+ retireCalls: 0,
894
+ };
895
+ const planner = new CorrectionPlanner(makeDeps(dir, state));
896
+ await assert.rejects(
897
+ () => planner.plan({ text: "same", targetIds: ["mem-loser"], duplicateOf: "mem-loser" }, ["default"]),
898
+ /different memory from targetIds/,
899
+ );
900
+ await assert.rejects(
901
+ () => planner.plan({ text: "missing loser", targetIds: ["missing"], duplicateOf: "mem-winner" }, ["default"]),
902
+ /target memory not found: missing/,
903
+ );
904
+ await assert.rejects(
905
+ () => planner.plan({ text: "missing winner", targetIds: ["mem-loser"], duplicateOf: "missing-winner" }, ["default"]),
906
+ /duplicateOf memory not found: missing-winner/,
907
+ );
908
+ await assert.rejects(
909
+ () => planner.plan({ text: "inactive loser", targetIds: ["mem-old"], duplicateOf: "mem-winner" }, ["default"]),
910
+ /target memory is not active: mem-old \(superseded\)/,
911
+ );
912
+ state.statusById = new Map([["mem-winner", "forgotten"]]);
913
+ await assert.rejects(
914
+ () => planner.plan({ text: "inactive winner", targetIds: ["mem-loser"], duplicateOf: "mem-winner" }, ["default"]),
915
+ /duplicateOf memory is not active: mem-winner \(forgotten\)/,
916
+ );
917
+ await assert.rejects(
918
+ () => planner.plan({ text: "no target", duplicateOf: "mem-winner" }, ["default"]),
919
+ /exactly one targetIds entry/,
920
+ );
921
+ await assert.rejects(
922
+ () => planner.plan(
923
+ { text: "two targets", targetIds: ["mem-loser", "mem-winner"], duplicateOf: "mem-winner" },
924
+ ["default"],
925
+ ),
926
+ /exactly one targetIds entry/,
927
+ );
928
+ assert.equal(state.classifyCalls ?? 0, 0);
929
+ });
930
+ });
@@ -7,9 +7,11 @@
7
7
  * error, rule 34). Else: search via the injected search path scoped to
8
8
  * the caller's readable namespaces, plus 1-hop entity-graph neighbors of
9
9
  * top hits. Cap candidates at `maxAffected`.
10
- * 2. CLASSIFY + DRAFT via one LLM call (Responses API only, gotcha 1). On
11
- * LLM failure → deterministic fallback plan (rule 13): classification
12
- * `outdated`, `confidence: 0`, empty actions, warning set.
10
+ * 2. CLASSIFY + DRAFT via one LLM call (Responses API only, gotcha 1),
11
+ * unless `duplicateOf` is set — that path skips the LLM and drafts one
12
+ * recoverable supersede. On LLM failure → deterministic fallback plan
13
+ * (rule 13): classification `outdated`, `confidence: 0`, empty actions.
14
+ * A caller abort throws instead of falling back.
13
15
  * 3. RENDER DIFF by materializing what each action would do, using the
14
16
  * injected diff renderer (page-versioning — reuse, don't fork).
15
17
  * 4. PERSIST the plan atomically under
@@ -81,6 +83,16 @@ export interface PlannerDeps {
81
83
  targetIds: readonly string[];
82
84
  namespaces: readonly string[];
83
85
  }): Promise<PlannerCandidate[]>;
86
+ /**
87
+ * Inspect one memory for duplicate-mode validation. Absent means "not
88
+ * found" (including outside the readable namespaces). A missing method
89
+ * falls back to {@link resolveTargets}, which reports inactive memories
90
+ * as not found.
91
+ */
92
+ inspectMemory?(request: {
93
+ memoryId: string;
94
+ namespaces: readonly string[];
95
+ }): Promise<PlannerMemoryInspection | null>;
84
96
  /**
85
97
  * One-hop entity-graph neighbors of the supplied candidates. Returns the
86
98
  * union (deduped by memoryId); neighbors not already in `seedIds` only.
@@ -99,6 +111,7 @@ export interface PlannerDeps {
99
111
  classifyAndDraft(request: {
100
112
  text: string;
101
113
  candidates: PlannerCandidate[];
114
+ abortSignal?: AbortSignal;
102
115
  }): Promise<LlmClassificationResult>;
103
116
  /**
104
117
  * Render a human-readable diff preview for the planned actions, using
@@ -120,6 +133,13 @@ export interface PlannerDeps {
120
133
  now(): Date;
121
134
  }
122
135
 
136
+ /** One memory inspected for an explicit duplicate supersede. */
137
+ export interface PlannerMemoryInspection {
138
+ candidate: PlannerCandidate;
139
+ /** Omitted status is treated as active (older files leave it unset). */
140
+ status?: string;
141
+ }
142
+
123
143
  /** Result of the single LLM classify+draft call. */
124
144
  export interface LlmClassificationResult {
125
145
  classification: CorrectionClassification;
@@ -177,10 +197,15 @@ export class CorrectionPlanner {
177
197
  );
178
198
  }
179
199
 
180
- // 1. LOCATE
181
- const located = await this.locateCandidates(cleaned, namespaces, abortSignal);
200
+ // 1. LOCATE — duplicateOf skips search and the classifier.
201
+ const explicitDuplicate = cleaned.duplicateOf
202
+ ? await this.planExplicitDuplicate(cleaned, namespaces, abortSignal)
203
+ : null;
204
+ const located = explicitDuplicate
205
+ ? { candidates: [explicitDuplicate.loser] }
206
+ : await this.locateCandidates(cleaned, namespaces, abortSignal);
182
207
  throwIfAborted(abortSignal, "correction planning aborted");
183
- if (located.candidates.length === 0) {
208
+ if (!explicitDuplicate && located.candidates.length === 0) {
184
209
  // No candidates AND no explicit targets → empty plan is valid (the user
185
210
  // can still discard). But explicit-target-not-found was already raised
186
211
  // inside locateCandidates (rule 34).
@@ -191,10 +216,13 @@ export class CorrectionPlanner {
191
216
  }
192
217
 
193
218
  // 2. CLASSIFY + DRAFT
194
- const llm = await this.deps.classifyAndDraft({
195
- text: cleaned.text,
196
- candidates: located.candidates,
197
- });
219
+ const llm = explicitDuplicate
220
+ ? explicitDuplicate.llm
221
+ : await this.deps.classifyAndDraft({
222
+ text: cleaned.text,
223
+ candidates: located.candidates,
224
+ abortSignal,
225
+ });
198
226
  throwIfAborted(abortSignal, "correction planning aborted");
199
227
 
200
228
  // Validate every action shape before persisting — the adapter is trusted
@@ -235,6 +263,7 @@ export class CorrectionPlanner {
235
263
  // absent from the candidate set and warn.
236
264
  const candidateIds = new Set(located.candidates.map((c) => c.memoryId));
237
265
  if (llm.actions.length > 0 && candidateIds.size > 0) {
266
+ const explicitWinner = cleaned.duplicateOf;
238
267
  const filtered = llm.actions.filter((action) => {
239
268
  const id =
240
269
  action.kind === "supersede"
@@ -242,7 +271,19 @@ export class CorrectionPlanner {
242
271
  : action.kind === "edit" || action.kind === "retract" || action.kind === "rescope"
243
272
  ? action.memoryId
244
273
  : null;
245
- return id === null || candidateIds.has(id);
274
+ if (id !== null && !candidateIds.has(id)) return false;
275
+ // A model-picked winner must be a located candidate. duplicateOf is
276
+ // the exception: that winner was validated as an active memory and is
277
+ // intentionally not part of the mutated set.
278
+ if (
279
+ action.kind === "supersede" &&
280
+ action.winnerId &&
281
+ action.winnerId !== explicitWinner &&
282
+ !candidateIds.has(action.winnerId)
283
+ ) {
284
+ return false;
285
+ }
286
+ return true;
246
287
  });
247
288
  if (filtered.length < llm.actions.length) {
248
289
  const dropped = llm.actions.length - filtered.length;
@@ -290,6 +331,71 @@ export class CorrectionPlanner {
290
331
  return this.persist(plan, abortSignal);
291
332
  }
292
333
 
334
+ private async planExplicitDuplicate(
335
+ request: CorrectionRequest,
336
+ namespaces: readonly string[],
337
+ abortSignal?: AbortSignal,
338
+ ): Promise<{ loser: PlannerCandidate; llm: LlmClassificationResult }> {
339
+ const winnerId = request.duplicateOf;
340
+ if (!winnerId || !request.targetIds || request.targetIds.length !== 1) {
341
+ throw new CorrectionContractError(
342
+ "CorrectionRequest.duplicateOf requires exactly one targetIds entry (the duplicate memory).",
343
+ );
344
+ }
345
+ if (request.targetIds[0] === winnerId) {
346
+ throw new CorrectionContractError(
347
+ "CorrectionRequest.duplicateOf must be a different memory from targetIds.",
348
+ );
349
+ }
350
+ const loserId = request.targetIds[0];
351
+ const loser = await this.inspectDuplicateParty(loserId, namespaces, "target");
352
+ throwIfAborted(abortSignal, "correction planning aborted");
353
+ await this.inspectDuplicateParty(winnerId, namespaces, "duplicateOf");
354
+ throwIfAborted(abortSignal, "correction planning aborted");
355
+ return {
356
+ loser,
357
+ llm: {
358
+ classification: "outdated",
359
+ confidence: 1,
360
+ actions: [{ kind: "supersede", loserId, winnerId }],
361
+ relevance: [{
362
+ memoryId: loserId,
363
+ why: `duplicate of ${winnerId}; recoverable supersede`,
364
+ }],
365
+ warnings: [],
366
+ },
367
+ };
368
+ }
369
+
370
+ private async inspectDuplicateParty(
371
+ memoryId: string,
372
+ namespaces: readonly string[],
373
+ role: "target" | "duplicateOf",
374
+ ): Promise<PlannerCandidate> {
375
+ const notFound = role === "target"
376
+ ? `target memory not found: ${memoryId}`
377
+ : `duplicateOf memory not found: ${memoryId}`;
378
+ const inactive = (status: string) => role === "target"
379
+ ? `target memory is not active: ${memoryId} (${status})`
380
+ : `duplicateOf memory is not active: ${memoryId} (${status})`;
381
+ if (this.deps.inspectMemory) {
382
+ const found = await this.deps.inspectMemory({ memoryId, namespaces });
383
+ if (!found) throw new CorrectionContractError(notFound);
384
+ const status = found.status && found.status.length > 0 ? found.status : "active";
385
+ if (status !== "active") throw new CorrectionContractError(inactive(status));
386
+ return found.candidate;
387
+ }
388
+ try {
389
+ const resolved = await this.deps.resolveTargets({ targetIds: [memoryId], namespaces });
390
+ const candidate = resolved.find((item) => item.memoryId === memoryId) ?? resolved[0];
391
+ if (!candidate) throw new CorrectionContractError(notFound);
392
+ return candidate;
393
+ } catch (err) {
394
+ if (err instanceof CorrectionContractError) throw new CorrectionContractError(notFound);
395
+ throw err;
396
+ }
397
+ }
398
+
293
399
  private async locateCandidates(
294
400
  request: CorrectionRequest,
295
401
  namespaces: readonly string[],
@@ -39,7 +39,7 @@ function extractToolNames(response: unknown): Set<string> {
39
39
  }
40
40
 
41
41
  interface MockCalls {
42
- planRequests: Array<{ text: string; targetIds?: string[]; principal?: string; abortSignal?: AbortSignal }>;
42
+ planRequests: Array<{ text: string; targetIds?: string[]; duplicateOf?: string; principal?: string; abortSignal?: AbortSignal }>;
43
43
  applyRequests: Array<{ planId: string; confirm: boolean; principal?: string; abortSignal?: AbortSignal }>;
44
44
  }
45
45
 
@@ -54,6 +54,7 @@ function makeMockService(
54
54
  req: {
55
55
  text: string;
56
56
  targetIds?: string[];
57
+ duplicateOf?: string;
57
58
  principal?: string;
58
59
  },
59
60
  opts?: { abortSignal?: AbortSignal },
@@ -128,6 +129,26 @@ test("tools/list gate: correction tools present when correctionVisible on (defau
128
129
  assert.equal(names.has("engram.memory_correct_apply"), true, "apply tool must be present by default");
129
130
  assert.equal(names.has("remnic_memory_correct_plan"), true, "plan alias must be present");
130
131
  assert.equal(names.has("remnic_memory_correct_apply"), true, "apply alias must be present");
132
+ const tools = (response as { result?: { tools?: Array<{ name?: string; inputSchema?: { properties?: Record<string, unknown> } }> } }).result?.tools ?? [];
133
+ const planTool = tools.find((tool) => tool.name === "engram.memory_correct_plan");
134
+ assert.ok(planTool?.inputSchema?.properties?.duplicateOf, "plan schema must advertise duplicateOf");
135
+ });
136
+
137
+ test("MCP surface: memory_correct_plan forwards duplicateOf", async () => {
138
+ const { service, calls } = makeMockService(STUB_PLAN, STUB_OUTCOME);
139
+ const server = new EngramMcpServer(service, { emitLegacyTools: true, correctionVisible: true });
140
+ await server.handleRequest({
141
+ jsonrpc: "2.0",
142
+ id: 2,
143
+ method: "tools/call",
144
+ params: {
145
+ name: "engram.memory_correct_plan",
146
+ arguments: { text: "duplicate", targetIds: ["mem-loser"], duplicateOf: "mem-winner" },
147
+ },
148
+ });
149
+ assert.equal(calls.planRequests.length, 1);
150
+ assert.deepEqual(calls.planRequests[0]?.targetIds, ["mem-loser"]);
151
+ assert.equal(calls.planRequests[0]?.duplicateOf, "mem-winner");
131
152
  });
132
153
 
133
154
  // ---------------------------------------------------------------------------
@@ -188,6 +188,42 @@ test("LocalLlmClient allows recall-critical work to run while background work is
188
188
  }
189
189
  });
190
190
 
191
+ test("LocalLlmClient drops a queued background request when its signal aborts", async () => {
192
+ const client = new LocalLlmClient(createConfig());
193
+ let runs = 0;
194
+ let releaseHeld: (() => void) | undefined;
195
+ (client as any).runChatCompletionRequest = async () => {
196
+ runs += 1;
197
+ await new Promise<void>((resolve) => {
198
+ releaseHeld = resolve;
199
+ });
200
+ return {
201
+ content: "held",
202
+ usage: { promptTokens: 1, completionTokens: 1, totalTokens: 2 },
203
+ };
204
+ };
205
+
206
+ const held = client.chatCompletion(
207
+ [{ role: "user", content: "hold" }],
208
+ { operation: "hold", priority: "background" },
209
+ );
210
+ await tick();
211
+ assert.equal(runs, 1);
212
+
213
+ const controller = new AbortController();
214
+ const queued = client.chatCompletion(
215
+ [{ role: "user", content: "queued" }],
216
+ { operation: "queued", priority: "background", signal: controller.signal },
217
+ );
218
+ controller.abort();
219
+ assert.equal(await queued, null);
220
+ assert.equal(runs, 1);
221
+ assert.equal((client as any).requestQueues.background.length, 0);
222
+
223
+ releaseHeld?.();
224
+ assert.equal((await held)?.content, "held");
225
+ });
226
+
191
227
  test("LocalLlmClient skips queued work once a cooldown is activated", async () => {
192
228
  const client = new LocalLlmClient(createConfig());
193
229
  const executionOrder: string[] = [];
package/src/local-llm.ts CHANGED
@@ -1351,13 +1351,30 @@ export class LocalLlmClient {
1351
1351
  if (options.priority) {
1352
1352
  const priority = options.priority;
1353
1353
  return await new Promise<LocalLlmChatCompletionResult | null>((resolve) => {
1354
- this.requestQueues[priority].push({
1354
+ let settled = false;
1355
+ let onAbort: (() => void) | undefined;
1356
+ const finish = (value: LocalLlmChatCompletionResult | null): void => {
1357
+ if (settled) return;
1358
+ settled = true;
1359
+ if (onAbort) options.signal?.removeEventListener("abort", onAbort);
1360
+ resolve(value);
1361
+ };
1362
+ const queued: LocalLlmQueuedRequest = {
1355
1363
  messages,
1356
1364
  options,
1357
1365
  priority,
1358
1366
  enqueuedAtMs: Date.now(),
1359
- resolve,
1360
- });
1367
+ resolve: finish,
1368
+ };
1369
+ onAbort = () => {
1370
+ const queue = this.requestQueues[priority];
1371
+ const index = queue.indexOf(queued);
1372
+ if (index >= 0) queue.splice(index, 1);
1373
+ finish(null);
1374
+ };
1375
+ this.requestQueues[priority].push(queued);
1376
+ if (options.signal?.aborted) onAbort();
1377
+ else if (options.signal) options.signal.addEventListener("abort", onAbort, { once: true });
1361
1378
  this.scheduleQueueDrain();
1362
1379
  });
1363
1380
  }
@@ -632,21 +632,6 @@ export class SessionContextCoordinator {
632
632
  const principal = req.principal || resolvePrincipal(req.sessionKey, this.deps.config);
633
633
  return canWriteNamespace(principal, req.namespace, this.deps.config);
634
634
  },
635
- llmComplete: async ({ system, user }) => {
636
- const llmResult = await this.deps.localLlm.chatCompletion(
637
- [
638
- { role: "system", content: system },
639
- { role: "user", content: user },
640
- ],
641
- { operation: "correction-classify", priority: "background" },
642
- );
643
- if (!llmResult) {
644
- throw new Error(
645
- "passive correction classify+draft: local LLM unavailable (disabled or in cooldown)",
646
- );
647
- }
648
- return llmResult.content;
649
- },
650
635
  });
651
636
  return this.deps._passiveCorrectionService;
652
637
  }
@@ -397,6 +397,30 @@ test("correctionPlan: a handle in targetIds is resolved before planning; an unkn
397
397
  }
398
398
  });
399
399
 
400
+ test("correctionPlan: a handle in duplicateOf is resolved before planning", async () => {
401
+ const { service, memoryDir } = await makeHarness({
402
+ recallMemoryHandles: true,
403
+ correctionEnabled: true,
404
+ });
405
+ try {
406
+ await assert.rejects(
407
+ () =>
408
+ service.correctionPlan({
409
+ text: "that memory is a duplicate",
410
+ targetIds: ["fact-loser"],
411
+ duplicateOf: "[m:dead]",
412
+ sessionKey: SESSION,
413
+ }),
414
+ (err: unknown) =>
415
+ err instanceof EngramAccessInputError &&
416
+ /not found/i.test((err as Error).message) &&
417
+ (err as Error).message.includes("[m:dead]"),
418
+ );
419
+ } finally {
420
+ await rm(memoryDir, { recursive: true, force: true });
421
+ }
422
+ });
423
+
400
424
  test("correctionPlan: a resolved handle targetIds set reaches the planner as concrete ids (round-trip)", async () => {
401
425
  const { orchestrator, service, memoryDir } = await makeHarness({
402
426
  recallMemoryHandles: true,
package/src/types.ts CHANGED
@@ -916,33 +916,16 @@ export interface PluginConfig
916
916
  * deployments prefer the older (always-inject) behavior. Default `false`.
917
917
  */
918
918
  temporalExpiredInInjection: boolean;
919
- /**
920
- * Correction Contract (issue #1580) — one plan/apply pipeline for memory
921
- * corrections. Master visibility + enforcement gate for the
922
- * `memory_correct_plan` / `memory_correct_apply` tools. Default `true`:
923
- * planning is read-only (locates candidates + drafts a pending plan file)
924
- * and safe to ship on; `false` hides the tools and rejects runtime calls so
925
- * the surface cannot drift out of sync. Parsed from `correction.enabled`
926
- * (nested) or `correctionEnabled` (flat legacy).
927
- */
919
+ /** Correction Contract (#1580). `correction.enabled` / `correctionEnabled`. Default true. */
928
920
  correctionEnabled: boolean;
929
- /**
930
- * Whether `memory_correct_apply` requires an explicit `confirm: true` to
931
- * mutate memories. Default `true` (least-privileged). Parsed from
932
- * `correction.applyRequiresConfirm` / `correctionApplyRequiresConfirm`.
933
- */
921
+ /** Apply requires `confirm: true`. `correction.applyRequiresConfirm`. Default true. */
934
922
  correctionApplyRequiresConfirm: boolean;
935
- /**
936
- * Max memories a single correction plan may touch; over-limit plans are
937
- * rejected, never silently truncated (review thread). Default `10`. Parsed
938
- * from `correction.maxAffected` / `correctionMaxAffected`.
939
- */
923
+ /** Max memories one plan may touch. `correction.maxAffected`. Default 10. */
940
924
  correctionMaxAffected: number;
941
- /**
942
- * Plan time-to-live in hours; expired plans are rejected at apply. Default
943
- * `24`. Parsed from `correction.planTtlHours` / `correctionPlanTtlHours`.
944
- */
925
+ /** Plan TTL hours. `correction.planTtlHours`. Default 24. */
945
926
  correctionPlanTtlHours: number;
927
+ /** Per-attempt classify timeout (ms, 1..300000). `correction.classifyTimeoutMs`. Default 45000. */
928
+ correctionClassifyTimeoutMs: number;
946
929
  /**
947
930
  * Passive correction capture (issue #1581) — detects corrections expressed
948
931
  * passively in conversation turns during extraction and routes them to the