@remnic/core 9.69.46 → 9.69.48

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 (601) hide show
  1. package/dist/access-admin-ops-surface.d.ts +15 -14
  2. package/dist/access-admin-ops-surface.js +43 -42
  3. package/dist/access-authorization-probe.d.ts +15 -14
  4. package/dist/access-authorization-probe.js +44 -43
  5. package/dist/access-boundary.d.ts +15 -14
  6. package/dist/access-boundary.js +43 -42
  7. package/dist/access-cli.js +120 -115
  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 +15 -14
  11. package/dist/access-extraction-force-flush.js +43 -42
  12. package/dist/access-health-types.d.ts +1 -1
  13. package/dist/access-http-briefing.d.ts +15 -14
  14. package/dist/access-http-briefing.js +44 -43
  15. package/dist/access-http-lcm-compaction.d.ts +15 -14
  16. package/dist/access-http-lifecycle-flush.d.ts +15 -14
  17. package/dist/access-http-offline-stream.d.ts +15 -14
  18. package/dist/access-http-query.d.ts +2 -2
  19. package/dist/access-http-query.js +45 -44
  20. package/dist/access-http.d.ts +16 -15
  21. package/dist/access-http.js +68 -64
  22. package/dist/access-identity-continuity-surface.d.ts +11 -11
  23. package/dist/access-identity-continuity-surface.js +43 -42
  24. package/dist/access-lcm-surface.d.ts +15 -14
  25. package/dist/access-lcm-surface.js +43 -42
  26. package/dist/access-mcp-output-schemas.js +1 -1
  27. package/dist/access-mcp-shared-context-tools.d.ts +15 -14
  28. package/dist/access-mcp.d.ts +15 -14
  29. package/dist/access-mcp.js +56 -53
  30. package/dist/access-memory-search-fanout.d.ts +2 -2
  31. package/dist/{access-namespace-preflight-DDnsN22V.d.ts → access-namespace-preflight-CetF9ziA.d.ts} +1 -1
  32. package/dist/access-namespace-preflight.d.ts +3 -3
  33. package/dist/access-namespace-preflight.js +43 -42
  34. package/dist/access-observe-transcript.d.ts +11 -11
  35. package/dist/access-observe-write-category.d.ts +3 -3
  36. package/dist/access-observe-write-category.js +4 -4
  37. package/dist/access-observe-write-surface.d.ts +15 -14
  38. package/dist/access-observe-write-surface.js +43 -42
  39. package/dist/access-offline-manifest.d.ts +11 -11
  40. package/dist/access-offline-manifest.js +2 -2
  41. package/dist/access-operations-batch.js +45 -44
  42. package/dist/access-operations.d.ts +16 -15
  43. package/dist/access-operations.js +52 -50
  44. package/dist/access-recall-concurrency.d.ts +15 -14
  45. package/dist/access-recall-concurrency.js +43 -42
  46. package/dist/access-recall-response.d.ts +15 -14
  47. package/dist/access-recall-response.js +43 -42
  48. package/dist/access-recall-surface.d.ts +15 -14
  49. package/dist/access-recall-surface.js +43 -42
  50. package/dist/access-schema.d.ts +114 -114
  51. package/dist/access-schema.js +3 -3
  52. package/dist/{access-service-DGynYlbE.d.ts → access-service-DUkTWyZS.d.ts} +77 -74
  53. package/dist/access-service-helpers.d.ts +15 -14
  54. package/dist/access-service.d.ts +15 -14
  55. package/dist/access-service.js +43 -42
  56. package/dist/access-surface-catalog.d.ts +15 -14
  57. package/dist/access-surface-catalog.js +51 -44
  58. package/dist/access-surface-catalog.js.map +1 -1
  59. package/dist/access-wearables-meetings-surface.d.ts +4 -4
  60. package/dist/action-confidence.d.ts +1 -1
  61. package/dist/active-memory-bridge.d.ts +1 -1
  62. package/dist/active-recall.d.ts +1 -1
  63. package/dist/active-recall.js +3 -2
  64. package/dist/active-recall.js.map +1 -1
  65. package/dist/activity/digest.d.ts +1 -1
  66. package/dist/adapters/index.js +4 -4
  67. package/dist/adapters/registry.js +2 -2
  68. package/dist/ambient-provenance.d.ts +1 -1
  69. package/dist/artifact-search.d.ts +1 -1
  70. package/dist/background-generation.d.ts +1 -1
  71. package/dist/behavior-learner.d.ts +1 -1
  72. package/dist/behavior-signals.d.ts +1 -1
  73. package/dist/bootstrap.d.ts +11 -11
  74. package/dist/briefing-window.d.ts +2 -2
  75. package/dist/briefing.d.ts +2 -2
  76. package/dist/briefing.js +13 -13
  77. package/dist/buffer-surprise-report.d.ts +1 -1
  78. package/dist/buffer-turn-helpers.d.ts +1 -1
  79. package/dist/buffer.d.ts +2 -2
  80. package/dist/buffer.js +5 -5
  81. package/dist/bulk-import/index.d.ts +3 -3
  82. package/dist/calibration.d.ts +1 -1
  83. package/dist/capabilities.d.ts +1 -1
  84. package/dist/{catalog-Dl2hYqpS.d.ts → catalog-Bns9zpl3.d.ts} +1 -1
  85. package/dist/causal-behavior.d.ts +1 -1
  86. package/dist/causal-consolidation.d.ts +1 -1
  87. package/dist/causal-consolidation.js +14 -14
  88. package/dist/causal-trajectory-graph.d.ts +1 -1
  89. package/dist/{chunk-N46SNYMC.js → chunk-2B2RHLHI.js} +2 -2
  90. package/dist/{chunk-F5V7KRQS.js → chunk-2WMXZYMS.js} +2 -2
  91. package/dist/{chunk-KF57IQFX.js → chunk-32ZBIGID.js} +19 -15
  92. package/dist/chunk-32ZBIGID.js.map +1 -0
  93. package/dist/{chunk-ORQ7AZMU.js → chunk-3RKVSECD.js} +8 -8
  94. package/dist/{chunk-3NYM6JA4.js → chunk-4LPIVP77.js} +2 -2
  95. package/dist/{chunk-PW4HTWXL.js → chunk-55XWRL6F.js} +2 -2
  96. package/dist/{chunk-GGY7WKMB.js → chunk-5KBI2MOC.js} +1 -1
  97. package/dist/{chunk-IXMWTNNY.js → chunk-6A6HSXWV.js} +2 -2
  98. package/dist/{chunk-P7S7BVBJ.js → chunk-7DU2F7LM.js} +3 -3
  99. package/dist/{chunk-4HTQ76O4.js → chunk-7SJCSDQ3.js} +2 -2
  100. package/dist/{chunk-U4ESGVZ2.js → chunk-7YUX52XZ.js} +3 -3
  101. package/dist/{chunk-FMEP2RB5.js → chunk-B4WI7OF2.js} +2 -2
  102. package/dist/{chunk-EUP4AXIW.js → chunk-B6VDWDFN.js} +2 -2
  103. package/dist/{chunk-UOREPQ3C.js → chunk-BIXELUIR.js} +2 -2
  104. package/dist/{chunk-GYYLZOSO.js → chunk-BMBAA2M5.js} +13 -6
  105. package/dist/chunk-BMBAA2M5.js.map +1 -0
  106. package/dist/{chunk-AYZHHMTK.js → chunk-BNX2TUSF.js} +2 -2
  107. package/dist/{chunk-5ZXGH2ZL.js → chunk-BXOPUJ3W.js} +2 -2
  108. package/dist/{chunk-TE6B2UU6.js → chunk-CD7VTZIG.js} +2 -2
  109. package/dist/{chunk-EC5SVM6H.js → chunk-DE4ZGH2N.js} +2 -2
  110. package/dist/{chunk-YFVIA5A6.js → chunk-DRLZB3CN.js} +2 -2
  111. package/dist/{chunk-SRRZZHA5.js → chunk-EGOWY342.js} +2 -2
  112. package/dist/chunk-EIB4FJVR.js +46 -0
  113. package/dist/chunk-EIB4FJVR.js.map +1 -0
  114. package/dist/{chunk-W44T2KHG.js → chunk-EWBDJQ5Y.js} +3 -3
  115. package/dist/{chunk-JMBTNVMW.js → chunk-FGRJJBFO.js} +2 -2
  116. package/dist/{chunk-F3TURDFG.js → chunk-FPB5LQWW.js} +4 -1
  117. package/dist/{chunk-F3TURDFG.js.map → chunk-FPB5LQWW.js.map} +1 -1
  118. package/dist/{chunk-WGZHSQDV.js → chunk-G2XZFGYN.js} +4 -4
  119. package/dist/{chunk-CFIHVWYZ.js → chunk-GGROFIYX.js} +2 -2
  120. package/dist/{chunk-XLZZLUWL.js → chunk-HG74GEVY.js} +2 -2
  121. package/dist/chunk-HMDYSGY6.js +54 -0
  122. package/dist/chunk-HMDYSGY6.js.map +1 -0
  123. package/dist/{chunk-WUUGE7QC.js → chunk-HS75MCQF.js} +4 -4
  124. package/dist/{chunk-DO6R3LIE.js → chunk-I6IJDTRP.js} +2 -2
  125. package/dist/{chunk-7ZNJN5WZ.js → chunk-IHOXK2I6.js} +55 -6
  126. package/dist/chunk-IHOXK2I6.js.map +1 -0
  127. package/dist/{chunk-2NKY7OC6.js → chunk-IMJPJY7V.js} +2 -2
  128. package/dist/{chunk-TZ44E732.js → chunk-IO7K7GES.js} +2 -2
  129. package/dist/{chunk-7BNB7KHB.js → chunk-JIURESYF.js} +7 -7
  130. package/dist/{chunk-AYP2FZFA.js → chunk-JUOJGZPV.js} +2 -2
  131. package/dist/{chunk-4XTSKGLX.js → chunk-JYXLW22X.js} +3 -3
  132. package/dist/{chunk-HXDXR2FH.js → chunk-KPYMORPQ.js} +41 -31
  133. package/dist/chunk-KPYMORPQ.js.map +1 -0
  134. package/dist/{chunk-IPLYGWQF.js → chunk-KQAFEZQX.js} +5 -5
  135. package/dist/{chunk-AN2O4KZY.js → chunk-KVIMQAER.js} +2 -2
  136. package/dist/chunk-LI4APB6M.js +218 -0
  137. package/dist/chunk-LI4APB6M.js.map +1 -0
  138. package/dist/{chunk-VGRVJ6Z6.js → chunk-LIAM7U2J.js} +2 -2
  139. package/dist/{chunk-S3SND7SO.js → chunk-LIDJWAQV.js} +2 -2
  140. package/dist/{chunk-T5NI5K46.js → chunk-LYMJ36UI.js} +2 -2
  141. package/dist/{chunk-UGGOQKO6.js → chunk-MKJFPIVG.js} +93 -51
  142. package/dist/chunk-MKJFPIVG.js.map +1 -0
  143. package/dist/{chunk-UEJY6ZTQ.js → chunk-MKLDZNBD.js} +2 -2
  144. package/dist/{chunk-QUFJ3NJE.js → chunk-MNG536IS.js} +20 -20
  145. package/dist/{chunk-SW42MMAH.js → chunk-MQT3WP3F.js} +2 -2
  146. package/dist/{chunk-5EHPB6RL.js → chunk-MUNR43V3.js} +2 -2
  147. package/dist/{chunk-OV7GJ4KW.js → chunk-NNFB4CU4.js} +2 -2
  148. package/dist/{chunk-47C6DF43.js → chunk-OS4JVTHR.js} +3 -3
  149. package/dist/{chunk-W6YDAIVB.js → chunk-OSTXIFZW.js} +13 -2
  150. package/dist/chunk-OSTXIFZW.js.map +1 -0
  151. package/dist/chunk-P3YNEMYY.js +212 -0
  152. package/dist/chunk-P3YNEMYY.js.map +1 -0
  153. package/dist/{chunk-JOP7W24J.js → chunk-PA67GXYJ.js} +8 -8
  154. package/dist/{chunk-FSH6KRKO.js → chunk-PPJLJD2D.js} +2 -2
  155. package/dist/{chunk-AG6PANBG.js → chunk-PXJ6HE2W.js} +11 -11
  156. package/dist/{chunk-DDBLZJRV.js → chunk-QB4VXCFN.js} +2 -2
  157. package/dist/{chunk-L63M2PE4.js → chunk-QGAKUOKU.js} +2 -2
  158. package/dist/{chunk-YUQPUK6H.js → chunk-QLCMOHRE.js} +2 -2
  159. package/dist/{chunk-Q4AUIFUA.js → chunk-QMWKENNK.js} +4 -4
  160. package/dist/{chunk-66QOUYNS.js → chunk-R32AEEU3.js} +174 -57
  161. package/dist/chunk-R32AEEU3.js.map +1 -0
  162. package/dist/{chunk-TNQR7N52.js → chunk-S3YM4SZQ.js} +2 -1
  163. package/dist/chunk-S3YM4SZQ.js.map +1 -0
  164. package/dist/{chunk-RW4QNB4J.js → chunk-S45INSV3.js} +3 -1
  165. package/dist/{chunk-RW4QNB4J.js.map → chunk-S45INSV3.js.map} +1 -1
  166. package/dist/{chunk-EEHQY7ER.js → chunk-SK6HEQTW.js} +2 -2
  167. package/dist/{chunk-PGAB2V53.js → chunk-SMBWBEE7.js} +2 -2
  168. package/dist/{chunk-62NYUFNH.js → chunk-SS33RR4F.js} +8 -8
  169. package/dist/{chunk-4ECS33D4.js → chunk-T3T5HTZZ.js} +2 -2
  170. package/dist/{chunk-KG2OOAFF.js → chunk-TQSCR5DU.js} +6 -6
  171. package/dist/{chunk-D5DTZBTV.js → chunk-TW4MXM7H.js} +6 -6
  172. package/dist/chunk-TW4MXM7H.js.map +1 -0
  173. package/dist/{chunk-AS2NOPDM.js → chunk-V7IONNTJ.js} +2 -2
  174. package/dist/{chunk-HYWPUYLO.js → chunk-VV5GCNKG.js} +2 -2
  175. package/dist/{chunk-Y3XHIWVP.js → chunk-WL3PGPGU.js} +2 -2
  176. package/dist/{chunk-3UQIS2GC.js → chunk-WNMVUWI7.js} +2 -2
  177. package/dist/{chunk-66YUOI53.js → chunk-WQW6KPVC.js} +1 -1
  178. package/dist/chunk-WQW6KPVC.js.map +1 -0
  179. package/dist/{chunk-2AV75BJW.js → chunk-WVI43D54.js} +24 -1
  180. package/dist/chunk-WVI43D54.js.map +1 -0
  181. package/dist/{chunk-WOBMSMUK.js → chunk-WYU66IVZ.js} +3 -3
  182. package/dist/{chunk-A7GQBJP5.js → chunk-X2NFFBTO.js} +4 -4
  183. package/dist/{chunk-CWMMKFO7.js → chunk-XEHLYA5Z.js} +3 -3
  184. package/dist/{chunk-JIWZVOQM.js → chunk-XKHRNAK7.js} +2 -2
  185. package/dist/{chunk-VW5TGNJN.js → chunk-XXLH6KFO.js} +2 -2
  186. package/dist/chunk-XYGINDIJ.js +74 -0
  187. package/dist/chunk-XYGINDIJ.js.map +1 -0
  188. package/dist/{chunk-J5OXWG66.js → chunk-XZUHXKM3.js} +2 -2
  189. package/dist/{chunk-K4KE5MYE.js → chunk-Y5VT7BIT.js} +2 -2
  190. package/dist/{chunk-S7KDHFF2.js → chunk-YXWMZAAN.js} +2 -2
  191. package/dist/{chunk-EAZ3YZKN.js → chunk-ZEIJQ4I3.js} +2 -2
  192. package/dist/{cli-Cik3H7q1.d.ts → cli-Bbndoc6k.d.ts} +5 -5
  193. package/dist/cli.d.ts +17 -16
  194. package/dist/cli.js +90 -85
  195. package/dist/coding/export-okf-codegraph.d.ts +1 -1
  196. package/dist/coding/export-okf-codegraph.js +12 -12
  197. package/dist/coding/pre-action-gate.d.ts +1 -1
  198. package/dist/compounding/engine.d.ts +2 -2
  199. package/dist/compounding/engine.js +13 -13
  200. package/dist/compounding/preference-consolidator.d.ts +1 -1
  201. package/dist/compression-optimizer.d.ts +1 -1
  202. package/dist/config.d.ts +1 -1
  203. package/dist/config.js +3 -2
  204. package/dist/connectors/codex-materialize-runner.d.ts +1 -1
  205. package/dist/connectors/codex-materialize-runner.js +13 -13
  206. package/dist/connectors/codex-materialize.d.ts +1 -1
  207. package/dist/connectors/index.d.ts +1 -1
  208. package/dist/connectors/index.js +13 -13
  209. package/dist/consolidation-provenance-check.d.ts +2 -2
  210. package/dist/consolidation-undo.d.ts +2 -2
  211. package/dist/contradiction/index.d.ts +4 -4
  212. package/dist/converge-config.d.ts +1 -1
  213. package/dist/convergence-refresh.d.ts +2 -2
  214. package/dist/convergence-refresh.js +15 -15
  215. package/dist/conversation-index/backend.d.ts +1 -1
  216. package/dist/conversation-index/chunker.d.ts +1 -1
  217. package/dist/conversation-index/faiss-adapter.d.ts +1 -1
  218. package/dist/conversation-index/indexer.d.ts +1 -1
  219. package/dist/conversation-index/search.d.ts +1 -1
  220. package/dist/corpus-watermark.d.ts +1 -1
  221. package/dist/corpus-watermark.js +15 -15
  222. package/dist/cross-lingual-recall.d.ts +1 -1
  223. package/dist/day-summary.d.ts +1 -1
  224. package/dist/deep-recall-http-glue.d.ts +15 -14
  225. package/dist/deep-recall-http-glue.js +4 -4
  226. package/dist/deep-recall-mcp-tools.d.ts +15 -14
  227. package/dist/deep-recall-policy-llm.d.ts +2 -2
  228. package/dist/deep-recall-seeds.d.ts +1 -1
  229. package/dist/deep-recall.js +2 -2
  230. package/dist/delinearize.d.ts +1 -1
  231. package/dist/dependency-propagation-config.d.ts +1 -1
  232. package/dist/{dependency-propagation-delivery-BXaUoVUf.d.ts → dependency-propagation-delivery-B-LvcDaq.d.ts} +1 -1
  233. package/dist/direct-answer-wiring.d.ts +1 -1
  234. package/dist/direct-answer.d.ts +1 -1
  235. package/dist/embedding-fallback.d.ts +1 -1
  236. package/dist/enrichment/index.d.ts +1 -1
  237. package/dist/entity-origin-fields.d.ts +1 -1
  238. package/dist/entity-retrieval.d.ts +2 -2
  239. package/dist/entity-retrieval.js +14 -14
  240. package/dist/entity-schema.d.ts +1 -1
  241. package/dist/episodic-context.d.ts +1 -1
  242. package/dist/eval-trajectory-contracts.d.ts +1 -1
  243. package/dist/eval-trajectory-store.d.ts +1 -1
  244. package/dist/eval-trajectory.d.ts +1 -1
  245. package/dist/explicit-capture.d.ts +11 -11
  246. package/dist/external-wiki-access.d.ts +15 -14
  247. package/dist/external-wiki-access.js +44 -43
  248. package/dist/external-wiki-collection-registration.d.ts +1 -1
  249. package/dist/external-wiki-collection.d.ts +1 -1
  250. package/dist/external-wiki-mcp-tools.d.ts +15 -14
  251. package/dist/extraction-error-classification.d.ts +1 -1
  252. package/dist/extraction-evidence-class.d.ts +142 -0
  253. package/dist/extraction-evidence-class.js +113 -0
  254. package/dist/extraction-evidence-class.js.map +1 -0
  255. package/dist/extraction-faithfulness.d.ts +2 -2
  256. package/dist/extraction-judge-telemetry.d.ts +2 -2
  257. package/dist/extraction-judge-training.d.ts +2 -2
  258. package/dist/extraction-judge.d.ts +2 -2
  259. package/dist/extraction-liveness.d.ts +1 -1
  260. package/dist/extraction-normalization.d.ts +1 -1
  261. package/dist/extraction-prompt.d.ts +1 -1
  262. package/dist/extraction-source-grounding-rules.d.ts +1 -1
  263. package/dist/extraction-source-grounding.d.ts +1 -1
  264. package/dist/extraction-source-grounding.js +2 -2
  265. package/dist/extraction-span-config.d.ts +1 -1
  266. package/dist/extraction-span-materialize.d.ts +1 -1
  267. package/dist/extraction-span-wire.d.ts +1 -1
  268. package/dist/extraction.d.ts +2 -2
  269. package/dist/extraction.js +21 -21
  270. package/dist/fallback-llm-codex-subscription.d.ts +1 -1
  271. package/dist/fallback-llm.d.ts +1 -1
  272. package/dist/{generic-recall-paths-BkVVEoeO.d.ts → generic-recall-paths-Bfc7zigS.d.ts} +1 -1
  273. package/dist/graph-append-error.d.ts +1 -1
  274. package/dist/graph-dashboard-diff.d.ts +1 -1
  275. package/dist/graph-dashboard-key.d.ts +1 -1
  276. package/dist/graph-dashboard-parser.d.ts +1 -1
  277. package/dist/graph-edge-reinforcement.d.ts +1 -1
  278. package/dist/graph-jsonl.d.ts +1 -1
  279. package/dist/graph-path-reconstruction.d.ts +1 -1
  280. package/dist/graph-path-scoring.d.ts +1 -1
  281. package/dist/graph-snapshot.d.ts +1 -1
  282. package/dist/graph.d.ts +1 -1
  283. package/dist/harmonic-retrieval.js +1 -1
  284. package/dist/importance.d.ts +1 -1
  285. package/dist/importers/index.d.ts +1 -1
  286. package/dist/importers/test-utils.d.ts +1 -1
  287. package/dist/in-flight-reads.d.ts +1 -1
  288. package/dist/{index-Zb6SUV4R.d.ts → index-C7IA1DxB.d.ts} +3 -3
  289. package/dist/index.d.ts +798 -707
  290. package/dist/index.js +184 -134
  291. package/dist/intent.d.ts +1 -1
  292. package/dist/lcm/engine.d.ts +1 -1
  293. package/dist/lcm/engine.js +2 -2
  294. package/dist/lcm/index.d.ts +1 -1
  295. package/dist/lcm/index.js +7 -7
  296. package/dist/lcm/tools.d.ts +1 -1
  297. package/dist/lifecycle.d.ts +1 -1
  298. package/dist/live-connectors-runner.d.ts +1 -1
  299. package/dist/{local-llm-DdrhmecM.d.ts → local-llm-DH4tWtck.d.ts} +1 -1
  300. package/dist/local-llm-helpers.d.ts +2 -2
  301. package/dist/local-llm.d.ts +2 -2
  302. package/dist/local-llm.js +2 -2
  303. package/dist/local-model-endpoint.d.ts +1 -1
  304. package/dist/location/index.d.ts +5 -5
  305. package/dist/location/index.js +15 -15
  306. package/dist/maintenance/memory-governance.d.ts +1 -1
  307. package/dist/maintenance/memory-governance.js +13 -13
  308. package/dist/maintenance/rebuild-memory-lifecycle-ledger.d.ts +2 -2
  309. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +13 -13
  310. package/dist/maintenance/rebuild-memory-projection.d.ts +2 -2
  311. package/dist/maintenance/rebuild-memory-projection.js +14 -14
  312. package/dist/{maintenance-Du_VRvVg.d.ts → maintenance-n6yeTQhR.d.ts} +4 -4
  313. package/dist/mcp-memory-inspector-app.d.ts +15 -14
  314. package/dist/mcp-read-only-tools.js +1 -1
  315. package/dist/memory-action-policy.d.ts +1 -1
  316. package/dist/memory-browse-http-glue.d.ts +170 -0
  317. package/dist/memory-browse-http-glue.js +9 -0
  318. package/dist/memory-browse-http-glue.js.map +1 -0
  319. package/dist/memory-browse-mcp-tools.d.ts +160 -0
  320. package/dist/memory-browse-mcp-tools.js +8 -0
  321. package/dist/memory-browse-mcp-tools.js.map +1 -0
  322. package/dist/memory-browse-operations.d.ts +2 -0
  323. package/dist/memory-browse-operations.js +246 -0
  324. package/dist/memory-browse-operations.js.map +1 -0
  325. package/dist/memory-browse.d.ts +113 -0
  326. package/dist/memory-browse.js +49 -0
  327. package/dist/memory-browse.js.map +1 -0
  328. package/dist/memory-cache.d.ts +1 -1
  329. package/dist/memory-controller.d.ts +1 -1
  330. package/dist/memory-lifecycle-ledger-utils.d.ts +1 -1
  331. package/dist/memory-projection-store.d.ts +1 -1
  332. package/dist/memory-provenance.d.ts +1 -1
  333. package/dist/memory-snapshot.d.ts +1 -1
  334. package/dist/memory-subject-cli.d.ts +15 -14
  335. package/dist/memory-subject.d.ts +15 -14
  336. package/dist/memory-worth-outcomes.d.ts +2 -2
  337. package/dist/models-json.d.ts +1 -1
  338. package/dist/namespaces/migrate.d.ts +3 -3
  339. package/dist/namespaces/migrate.js +14 -14
  340. package/dist/namespaces/principal.d.ts +1 -1
  341. package/dist/namespaces/search.d.ts +1 -1
  342. package/dist/namespaces/search.js +6 -6
  343. package/dist/namespaces/storage.d.ts +3 -3
  344. package/dist/namespaces/storage.js +13 -13
  345. package/dist/native-knowledge.d.ts +1 -1
  346. package/dist/offline-sync-impression-drain.d.ts +1 -1
  347. package/dist/operator-doctor-corpus.d.ts +1 -1
  348. package/dist/operator-doctor-corpus.js +16 -16
  349. package/dist/operator-doctor-replica.d.ts +1 -1
  350. package/dist/operator-toolkit.d.ts +3 -3
  351. package/dist/operator-toolkit.js +23 -22
  352. package/dist/orchestration/compression-guideline-coordinator.d.ts +2 -2
  353. package/dist/orchestration/maintenance.d.ts +4 -4
  354. package/dist/orchestration/maintenance.js +23 -23
  355. package/dist/{orchestrator-D9U3Zgjp.d.ts → orchestrator-Cirjo3ZB.d.ts} +10 -10
  356. package/dist/orchestrator.d.ts +11 -11
  357. package/dist/orchestrator.js +122 -117
  358. package/dist/patterns-cli.d.ts +1 -1
  359. package/dist/{pipeline-4lqKJZ_0.d.ts → pipeline-DK79FrgH.d.ts} +1 -1
  360. package/dist/policy-runtime.d.ts +1 -1
  361. package/dist/proactive-contention.d.ts +2 -2
  362. package/dist/provenance.d.ts +77 -2
  363. package/dist/provenance.js +7 -3
  364. package/dist/{public-http-AabioFLh.d.ts → public-http-DaHHhWRR.d.ts} +1 -1
  365. package/dist/{qmd-DnoWaXKb.d.ts → qmd-BT0ErlRS.d.ts} +1 -1
  366. package/dist/qmd-preflight.d.ts +2 -2
  367. package/dist/qmd-recall-cache.d.ts +1 -1
  368. package/dist/qmd.d.ts +2 -2
  369. package/dist/qmd.js +4 -4
  370. package/dist/recall-concurrency-config.d.ts +1 -1
  371. package/dist/recall-context-composition.d.ts +43 -3
  372. package/dist/recall-context-composition.js +5 -1
  373. package/dist/recall-disclosure-escalation.d.ts +1 -1
  374. package/dist/recall-explain-renderer.d.ts +1 -1
  375. package/dist/recall-explain-renderer.js +3 -3
  376. package/dist/recall-memory-map.d.ts +1 -1
  377. package/dist/recall-navigate-access.d.ts +1 -1
  378. package/dist/recall-navigate-access.js +2 -2
  379. package/dist/recall-navigate-disclosure.d.ts +1 -1
  380. package/dist/recall-navigate-disclosure.js +2 -2
  381. package/dist/recall-navigate.d.ts +1 -1
  382. package/dist/recall-navigate.js +2 -2
  383. package/dist/recall-navigation-http-glue.d.ts +15 -14
  384. package/dist/recall-navigation-mcp-tools.d.ts +15 -14
  385. package/dist/recall-navigation-operations.js +44 -43
  386. package/dist/recall-navigation-renderer.d.ts +1 -1
  387. package/dist/recall-navigation.d.ts +1 -1
  388. package/dist/recall-navigation.js +43 -42
  389. package/dist/recall-planner-llm.d.ts +1 -1
  390. package/dist/recall-state-view-admission.d.ts +2 -2
  391. package/dist/recall-state-view-admission.js +5 -5
  392. package/dist/recall-state-view-anchors.d.ts +1 -1
  393. package/dist/recall-state.d.ts +1 -1
  394. package/dist/recall-tag-filter.d.ts +1 -1
  395. package/dist/recall-timings.d.ts +1 -1
  396. package/dist/recall-xray-cli.d.ts +1 -1
  397. package/dist/recall-xray-cli.js +4 -4
  398. package/dist/recall-xray-renderer.d.ts +1 -1
  399. package/dist/recall-xray-renderer.js +3 -3
  400. package/dist/recall-xray.d.ts +1 -1
  401. package/dist/recall-xray.js +2 -2
  402. package/dist/reconcile/cursor.d.ts +1 -1
  403. package/dist/reconcile/manifest.d.ts +4 -1
  404. package/dist/reconcile/manifest.js +1 -1
  405. package/dist/reconcile/plan.d.ts +1 -1
  406. package/dist/replay/normalizers/chatgpt.d.ts +1 -1
  407. package/dist/replay/normalizers/claude.d.ts +1 -1
  408. package/dist/replay/normalizers/openclaw.d.ts +1 -1
  409. package/dist/replay/normalizers/shared.d.ts +1 -1
  410. package/dist/replay/runner.d.ts +1 -1
  411. package/dist/replay/types.d.ts +1 -1
  412. package/dist/replica-divergence.d.ts +1 -1
  413. package/dist/replica-peers-config.d.ts +1 -1
  414. package/dist/resolve-auth-token.d.ts +1 -1
  415. package/dist/resume-bundles.js +4 -3
  416. package/dist/retrieval-agents.d.ts +2 -2
  417. package/dist/retrieval-tiers.d.ts +1 -1
  418. package/dist/routing/engine.d.ts +1 -1
  419. package/dist/routing/store.d.ts +1 -1
  420. package/dist/salvage-envelope.d.ts +1 -1
  421. package/dist/schemas.d.ts +160 -160
  422. package/dist/{scope-profiles-crazXegZ.d.ts → scope-profiles-BgPQx9iE.d.ts} +1 -1
  423. package/dist/search/embed-helper.d.ts +1 -1
  424. package/dist/search/factory.d.ts +1 -1
  425. package/dist/search/factory.js +5 -5
  426. package/dist/search/index.d.ts +1 -1
  427. package/dist/search/index.js +9 -9
  428. package/dist/search/lancedb-backend.d.ts +1 -1
  429. package/dist/search/meilisearch-backend.d.ts +1 -1
  430. package/dist/search/noop-backend.d.ts +1 -1
  431. package/dist/search/orama-backend.d.ts +1 -1
  432. package/dist/search/port.d.ts +1 -1
  433. package/dist/search/remote-backend.d.ts +1 -1
  434. package/dist/{semantic-consolidation-B09JI9WM.d.ts → semantic-consolidation-BrAWKVcm.d.ts} +1 -1
  435. package/dist/semantic-consolidation.d.ts +2 -2
  436. package/dist/semantic-consolidation.js +14 -14
  437. package/dist/semantic-rule-promotion.js +13 -13
  438. package/dist/semantic-rule-verifier.d.ts +1 -1
  439. package/dist/semantic-rule-verifier.js +13 -13
  440. package/dist/{service-ByjbCuj7.d.ts → service-CqHOTg87.d.ts} +2 -2
  441. package/dist/session-observer-bands.d.ts +1 -1
  442. package/dist/session-observer-state.d.ts +1 -1
  443. package/dist/shared-context/manager.d.ts +7 -7
  444. package/dist/signal.d.ts +1 -1
  445. package/dist/source-agent-qualifier.d.ts +1 -1
  446. package/dist/source-agent-qualifier.js +17 -17
  447. package/dist/standing-memory-block.d.ts +77 -0
  448. package/dist/standing-memory-block.js +40 -0
  449. package/dist/standing-memory-block.js.map +1 -0
  450. package/dist/standup/build.js +13 -13
  451. package/dist/{storage-CbdF077z.d.ts → storage-mD2YQ8_2.d.ts} +1 -1
  452. package/dist/storage.d.ts +2 -2
  453. package/dist/storage.js +12 -12
  454. package/dist/subject-config.d.ts +1 -1
  455. package/dist/summarizer.d.ts +1 -1
  456. package/dist/summarizer.js +3 -3
  457. package/dist/summary-snapshot.d.ts +1 -1
  458. package/dist/support-passport/index.d.ts +18 -17
  459. package/dist/support-passport/index.js +46 -45
  460. package/dist/{tagging-CF7DNV7_.d.ts → tagging-CiAVA-ID.d.ts} +2 -2
  461. package/dist/temporal-supersession.d.ts +2 -2
  462. package/dist/temporal-timeline-recall.d.ts +1 -1
  463. package/dist/temporal-timeline-recall.js +2 -2
  464. package/dist/temporal-validity.d.ts +1 -1
  465. package/dist/threading.d.ts +1 -1
  466. package/dist/tier-migration.d.ts +2 -2
  467. package/dist/tier-routing.d.ts +1 -1
  468. package/dist/topics.d.ts +1 -1
  469. package/dist/transcript.d.ts +1 -1
  470. package/dist/transfer/autodetect.js +1 -1
  471. package/dist/transfer/backup.js +1 -1
  472. package/dist/transfer/capsule-export.js +2 -2
  473. package/dist/transfer/export-okf.d.ts +1 -1
  474. package/dist/transfer/export-okf.js +13 -13
  475. package/dist/transfer/types.d.ts +78 -78
  476. package/dist/trust-score-stage.d.ts +1 -1
  477. package/dist/trust-score.d.ts +1 -1
  478. package/dist/{types-nmhnJpxx.d.ts → types-DgDaoY61.d.ts} +6 -0
  479. package/dist/types.d.ts +1 -1
  480. package/dist/types.js +1 -1
  481. package/dist/utility-runtime.d.ts +1 -1
  482. package/dist/verified-recall.js +13 -13
  483. package/dist/who-knows-cli.d.ts +15 -14
  484. package/dist/who-knows-cli.js +14 -14
  485. package/dist/who-knows.d.ts +15 -14
  486. package/dist/who-knows.js +13 -13
  487. package/dist/write-envelope.d.ts +1 -1
  488. package/package.json +2 -2
  489. package/src/access-boundary.ts +3 -0
  490. package/src/access-http.ts +2 -2
  491. package/src/access-mcp-output-schemas.ts +25 -0
  492. package/src/access-mcp.ts +3 -3
  493. package/src/access-operations.ts +2 -0
  494. package/src/access-service.ts +6 -1
  495. package/src/access-surface-catalog.test.ts +20 -0
  496. package/src/access-surface-catalog.ts +6 -0
  497. package/src/activity/timeline/index.ts +1 -0
  498. package/src/activity/timeline/journal-recap.test.ts +17 -0
  499. package/src/activity/timeline/journal-recap.ts +19 -6
  500. package/src/activity/timeline/recap-export-range.test.ts +289 -0
  501. package/src/activity/timeline/recap-export-range.ts +169 -0
  502. package/src/activity/timeline/recap-export.test.ts +73 -1
  503. package/src/activity/timeline/recap-export.ts +57 -4
  504. package/src/cli/memory-browse-commands.ts +77 -0
  505. package/src/cli.ts +2 -2
  506. package/src/config.ts +11 -0
  507. package/src/extraction-evidence-class.test.ts +355 -0
  508. package/src/extraction-evidence-class.ts +241 -0
  509. package/src/index.ts +1 -0
  510. package/src/mcp-read-only-tools.ts +3 -0
  511. package/src/memory-browse-http-glue.ts +93 -0
  512. package/src/memory-browse-mcp-tools.ts +57 -0
  513. package/src/memory-browse-operations.ts +88 -0
  514. package/src/memory-browse-surface.test.ts +500 -0
  515. package/src/memory-browse.ts +316 -0
  516. package/src/provenance.ts +3 -3
  517. package/src/recall-context-composition.test.ts +66 -0
  518. package/src/recall-context-composition.ts +102 -2
  519. package/src/reconcile/manifest.test.ts +27 -0
  520. package/src/reconcile/manifest.ts +7 -0
  521. package/src/standing-memory-block.test.ts +262 -0
  522. package/src/standing-memory-block.ts +331 -0
  523. package/src/types.ts +6 -0
  524. package/dist/chunk-2AV75BJW.js.map +0 -1
  525. package/dist/chunk-66QOUYNS.js.map +0 -1
  526. package/dist/chunk-66YUOI53.js.map +0 -1
  527. package/dist/chunk-7ZNJN5WZ.js.map +0 -1
  528. package/dist/chunk-D5DTZBTV.js.map +0 -1
  529. package/dist/chunk-GYYLZOSO.js.map +0 -1
  530. package/dist/chunk-HXDXR2FH.js.map +0 -1
  531. package/dist/chunk-KF57IQFX.js.map +0 -1
  532. package/dist/chunk-TNQR7N52.js.map +0 -1
  533. package/dist/chunk-UGGOQKO6.js.map +0 -1
  534. package/dist/chunk-W6YDAIVB.js.map +0 -1
  535. /package/dist/{chunk-N46SNYMC.js.map → chunk-2B2RHLHI.js.map} +0 -0
  536. /package/dist/{chunk-F5V7KRQS.js.map → chunk-2WMXZYMS.js.map} +0 -0
  537. /package/dist/{chunk-ORQ7AZMU.js.map → chunk-3RKVSECD.js.map} +0 -0
  538. /package/dist/{chunk-3NYM6JA4.js.map → chunk-4LPIVP77.js.map} +0 -0
  539. /package/dist/{chunk-PW4HTWXL.js.map → chunk-55XWRL6F.js.map} +0 -0
  540. /package/dist/{chunk-GGY7WKMB.js.map → chunk-5KBI2MOC.js.map} +0 -0
  541. /package/dist/{chunk-IXMWTNNY.js.map → chunk-6A6HSXWV.js.map} +0 -0
  542. /package/dist/{chunk-P7S7BVBJ.js.map → chunk-7DU2F7LM.js.map} +0 -0
  543. /package/dist/{chunk-4HTQ76O4.js.map → chunk-7SJCSDQ3.js.map} +0 -0
  544. /package/dist/{chunk-U4ESGVZ2.js.map → chunk-7YUX52XZ.js.map} +0 -0
  545. /package/dist/{chunk-FMEP2RB5.js.map → chunk-B4WI7OF2.js.map} +0 -0
  546. /package/dist/{chunk-EUP4AXIW.js.map → chunk-B6VDWDFN.js.map} +0 -0
  547. /package/dist/{chunk-UOREPQ3C.js.map → chunk-BIXELUIR.js.map} +0 -0
  548. /package/dist/{chunk-AYZHHMTK.js.map → chunk-BNX2TUSF.js.map} +0 -0
  549. /package/dist/{chunk-5ZXGH2ZL.js.map → chunk-BXOPUJ3W.js.map} +0 -0
  550. /package/dist/{chunk-TE6B2UU6.js.map → chunk-CD7VTZIG.js.map} +0 -0
  551. /package/dist/{chunk-EC5SVM6H.js.map → chunk-DE4ZGH2N.js.map} +0 -0
  552. /package/dist/{chunk-YFVIA5A6.js.map → chunk-DRLZB3CN.js.map} +0 -0
  553. /package/dist/{chunk-SRRZZHA5.js.map → chunk-EGOWY342.js.map} +0 -0
  554. /package/dist/{chunk-W44T2KHG.js.map → chunk-EWBDJQ5Y.js.map} +0 -0
  555. /package/dist/{chunk-JMBTNVMW.js.map → chunk-FGRJJBFO.js.map} +0 -0
  556. /package/dist/{chunk-WGZHSQDV.js.map → chunk-G2XZFGYN.js.map} +0 -0
  557. /package/dist/{chunk-CFIHVWYZ.js.map → chunk-GGROFIYX.js.map} +0 -0
  558. /package/dist/{chunk-XLZZLUWL.js.map → chunk-HG74GEVY.js.map} +0 -0
  559. /package/dist/{chunk-WUUGE7QC.js.map → chunk-HS75MCQF.js.map} +0 -0
  560. /package/dist/{chunk-DO6R3LIE.js.map → chunk-I6IJDTRP.js.map} +0 -0
  561. /package/dist/{chunk-2NKY7OC6.js.map → chunk-IMJPJY7V.js.map} +0 -0
  562. /package/dist/{chunk-TZ44E732.js.map → chunk-IO7K7GES.js.map} +0 -0
  563. /package/dist/{chunk-7BNB7KHB.js.map → chunk-JIURESYF.js.map} +0 -0
  564. /package/dist/{chunk-AYP2FZFA.js.map → chunk-JUOJGZPV.js.map} +0 -0
  565. /package/dist/{chunk-4XTSKGLX.js.map → chunk-JYXLW22X.js.map} +0 -0
  566. /package/dist/{chunk-IPLYGWQF.js.map → chunk-KQAFEZQX.js.map} +0 -0
  567. /package/dist/{chunk-AN2O4KZY.js.map → chunk-KVIMQAER.js.map} +0 -0
  568. /package/dist/{chunk-VGRVJ6Z6.js.map → chunk-LIAM7U2J.js.map} +0 -0
  569. /package/dist/{chunk-S3SND7SO.js.map → chunk-LIDJWAQV.js.map} +0 -0
  570. /package/dist/{chunk-T5NI5K46.js.map → chunk-LYMJ36UI.js.map} +0 -0
  571. /package/dist/{chunk-UEJY6ZTQ.js.map → chunk-MKLDZNBD.js.map} +0 -0
  572. /package/dist/{chunk-QUFJ3NJE.js.map → chunk-MNG536IS.js.map} +0 -0
  573. /package/dist/{chunk-SW42MMAH.js.map → chunk-MQT3WP3F.js.map} +0 -0
  574. /package/dist/{chunk-5EHPB6RL.js.map → chunk-MUNR43V3.js.map} +0 -0
  575. /package/dist/{chunk-OV7GJ4KW.js.map → chunk-NNFB4CU4.js.map} +0 -0
  576. /package/dist/{chunk-47C6DF43.js.map → chunk-OS4JVTHR.js.map} +0 -0
  577. /package/dist/{chunk-JOP7W24J.js.map → chunk-PA67GXYJ.js.map} +0 -0
  578. /package/dist/{chunk-FSH6KRKO.js.map → chunk-PPJLJD2D.js.map} +0 -0
  579. /package/dist/{chunk-AG6PANBG.js.map → chunk-PXJ6HE2W.js.map} +0 -0
  580. /package/dist/{chunk-DDBLZJRV.js.map → chunk-QB4VXCFN.js.map} +0 -0
  581. /package/dist/{chunk-L63M2PE4.js.map → chunk-QGAKUOKU.js.map} +0 -0
  582. /package/dist/{chunk-YUQPUK6H.js.map → chunk-QLCMOHRE.js.map} +0 -0
  583. /package/dist/{chunk-Q4AUIFUA.js.map → chunk-QMWKENNK.js.map} +0 -0
  584. /package/dist/{chunk-EEHQY7ER.js.map → chunk-SK6HEQTW.js.map} +0 -0
  585. /package/dist/{chunk-PGAB2V53.js.map → chunk-SMBWBEE7.js.map} +0 -0
  586. /package/dist/{chunk-62NYUFNH.js.map → chunk-SS33RR4F.js.map} +0 -0
  587. /package/dist/{chunk-4ECS33D4.js.map → chunk-T3T5HTZZ.js.map} +0 -0
  588. /package/dist/{chunk-KG2OOAFF.js.map → chunk-TQSCR5DU.js.map} +0 -0
  589. /package/dist/{chunk-AS2NOPDM.js.map → chunk-V7IONNTJ.js.map} +0 -0
  590. /package/dist/{chunk-HYWPUYLO.js.map → chunk-VV5GCNKG.js.map} +0 -0
  591. /package/dist/{chunk-Y3XHIWVP.js.map → chunk-WL3PGPGU.js.map} +0 -0
  592. /package/dist/{chunk-3UQIS2GC.js.map → chunk-WNMVUWI7.js.map} +0 -0
  593. /package/dist/{chunk-WOBMSMUK.js.map → chunk-WYU66IVZ.js.map} +0 -0
  594. /package/dist/{chunk-A7GQBJP5.js.map → chunk-X2NFFBTO.js.map} +0 -0
  595. /package/dist/{chunk-CWMMKFO7.js.map → chunk-XEHLYA5Z.js.map} +0 -0
  596. /package/dist/{chunk-JIWZVOQM.js.map → chunk-XKHRNAK7.js.map} +0 -0
  597. /package/dist/{chunk-VW5TGNJN.js.map → chunk-XXLH6KFO.js.map} +0 -0
  598. /package/dist/{chunk-J5OXWG66.js.map → chunk-XZUHXKM3.js.map} +0 -0
  599. /package/dist/{chunk-K4KE5MYE.js.map → chunk-Y5VT7BIT.js.map} +0 -0
  600. /package/dist/{chunk-S7KDHFF2.js.map → chunk-YXWMZAAN.js.map} +0 -0
  601. /package/dist/{chunk-EAZ3YZKN.js.map → chunk-ZEIJQ4I3.js.map} +0 -0
@@ -0,0 +1,262 @@
1
+ /**
2
+ * standing-memory-block.test.ts — issue #2971 foundation slice.
3
+ *
4
+ * Pins the prefix-cache contract:
5
+ * - the block is byte-identical across consecutive builds with an unchanged
6
+ * store (same entries), regardless of input order and of the evaluation
7
+ * instant (the clock classifies bands but never renders);
8
+ * - the volatility lint refuses clocks, dates, and counters at build time;
9
+ * - three-band layering (pinned full, fresh full, older compressed to a
10
+ * recognition hook) with a hook ledger that makes steady-state rebuilds
11
+ * free and lets a later model slice pre-seed hooks;
12
+ * - the mechanical trim fallback drops compressed lines from the tail and
13
+ * never a pinned line;
14
+ * - zero-diff: with the feature off (and even on, pre-wiring), the live
15
+ * recall output is byte-identical and contains no standing block.
16
+ */
17
+ import assert from "node:assert/strict";
18
+ import os from "node:os";
19
+ import path from "node:path";
20
+ import { mkdtemp, mkdir, writeFile, rm } from "node:fs/promises";
21
+ import test from "node:test";
22
+
23
+ import { parseConfig } from "./config.js";
24
+ import { Orchestrator } from "./orchestrator.js";
25
+ import type { PluginConfig } from "./types.js";
26
+ import {
27
+ buildStandingMemoryBlock,
28
+ lintStandingBlockVolatility,
29
+ parseRecallStandingBlock,
30
+ parseStandingBlockFreshDays,
31
+ parseStandingBlockMaxChars,
32
+ standingHookLedgerKey,
33
+ StandingBlockBudgetError,
34
+ StandingBlockVolatilityError,
35
+ type StandingMemoryEntry,
36
+ } from "./standing-memory-block.js";
37
+
38
+ const NOW = Date.parse("2026-08-25T12:00:00.000Z");
39
+
40
+ const PINNED: StandingMemoryEntry = {
41
+ id: "p-core",
42
+ description: "Deploys always go through the blue pipeline, never green.",
43
+ pinned: true,
44
+ };
45
+ const PINNED2: StandingMemoryEntry = {
46
+ id: "p-two",
47
+ description: "Every release tag is signed with the release key.",
48
+ pinned: true,
49
+ };
50
+ const FRESH: StandingMemoryEntry = {
51
+ id: "f-recent",
52
+ description: "The auth service owner is Priya.",
53
+ lastChangedAt: "2026-08-20T09:00:00.000Z",
54
+ };
55
+ const OLD_LONG: StandingMemoryEntry = {
56
+ id: "o-history",
57
+ description:
58
+ "The billing stack migrated off the legacy invoice service after the Q3 review " +
59
+ "because duplicate charges surfaced twice and the team rebuilt invoicing around " +
60
+ "the ledger event stream that the finance group already trusted.",
61
+ lastChangedAt: "2025-11-01T00:00:00.000Z",
62
+ };
63
+
64
+ test("parseConfig: standing block keys default off with byte-identical behavior", () => {
65
+ const base = { memoryDir: "/tmp/remnic-standing-config-test" };
66
+ const parsed = parseConfig(base);
67
+ assert.equal(parsed.recallStandingBlock, false, "absent → false");
68
+ assert.equal(parsed.standingBlockFreshDays, 14);
69
+ assert.equal(parsed.standingBlockMaxChars, 2048);
70
+ assert.equal(parseConfig({ ...base, recallStandingBlock: false }).recallStandingBlock, false);
71
+ assert.equal(parseConfig({ ...base, recallStandingBlock: 0 }).recallStandingBlock, false);
72
+ assert.equal(parseConfig({ ...base, recallStandingBlock: "false" }).recallStandingBlock, false);
73
+ assert.equal(parseConfig({ ...base, recallStandingBlock: true }).recallStandingBlock, true);
74
+ assert.equal(parseConfig({ ...base, recallStandingBlock: 1 }).recallStandingBlock, true);
75
+ assert.equal(parseConfig({ ...base, recallStandingBlock: "true" }).recallStandingBlock, true);
76
+ assert.equal(parseStandingBlockFreshDays("30"), 30);
77
+ assert.equal(parseStandingBlockMaxChars(1024), 1024);
78
+ assert.throws(() => parseStandingBlockFreshDays(-1), /Invalid standingBlockFreshDays/);
79
+ assert.throws(() => parseStandingBlockMaxChars(0), /Invalid standingBlockMaxChars/);
80
+ });
81
+
82
+ test("block is byte-identical across consecutive builds with an unchanged store", () => {
83
+ const entries = [PINNED, FRESH, OLD_LONG];
84
+ const first = buildStandingMemoryBlock({ entries, nowMs: NOW });
85
+ const second = buildStandingMemoryBlock({ entries, nowMs: NOW });
86
+ assert.equal(first.text, second.text);
87
+ assert.equal(first.text, buildStandingMemoryBlock({ entries: [...entries].reverse(), nowMs: NOW }).text);
88
+ assert.equal(
89
+ first.text,
90
+ buildStandingMemoryBlock({ entries, nowMs: NOW + 3_600_000, ledger: first.ledger }).text,
91
+ "the evaluation clock must never render into the block",
92
+ );
93
+ assert.ok(first.text.startsWith("## Standing Memory (Remnic)"));
94
+ });
95
+
96
+ test("volatility lint refuses dates, clocks, relative dates, and counters at build time", () => {
97
+ assert.deepEqual(lintStandingBlockVolatility("shipped 2026-08-01"), [
98
+ { kind: "iso-date", token: "2026-08-01" },
99
+ ]);
100
+ assert.deepEqual(lintStandingBlockVolatility("standup at 10:30 sharp"), [
101
+ { kind: "clock-time", token: "10:30" },
102
+ ]);
103
+ assert.deepEqual(lintStandingBlockVolatility("fixed yesterday"), [
104
+ { kind: "relative-date", token: "yesterday" },
105
+ ]);
106
+ assert.deepEqual(lintStandingBlockVolatility("index has memories: 42 rows"), [
107
+ { kind: "counter", token: "memories: 42" },
108
+ ]);
109
+ assert.deepEqual(lintStandingBlockVolatility("quiet, stable line"), []);
110
+
111
+ const refuses = (entry: StandingMemoryEntry, kind: string) => {
112
+ assert.throws(
113
+ () => buildStandingMemoryBlock({ entries: [entry], nowMs: NOW }),
114
+ (err: unknown) => {
115
+ assert.ok(err instanceof StandingBlockVolatilityError);
116
+ assert.equal(err.matches[0]?.kind, kind);
117
+ return true;
118
+ },
119
+ `expected refusal for ${kind}`,
120
+ );
121
+ };
122
+ refuses({ id: "d1", description: "Deployed on 2026-08-01 to prod." }, "iso-date");
123
+ refuses({ id: "d2", description: "The standup moved to 09:15." }, "clock-time");
124
+ refuses({ id: "d3", description: "The on-call rota changed yesterday." }, "relative-date");
125
+ });
126
+
127
+ test("three-band layering: pinned full first, fresh full, older compressed to a hook", () => {
128
+ const built = buildStandingMemoryBlock({ entries: [OLD_LONG, FRESH, PINNED], nowMs: NOW });
129
+ const body = built.lines.filter((line) => line.startsWith("- "));
130
+ assert.deepEqual(built.bandCounts, { pinned: 1, fresh: 1, compressed: 1, dropped: 0 });
131
+ // Band rank, then id order: pinned band has p-core alone.
132
+ assert.equal(body[0], `- ${PINNED.description}`);
133
+ assert.equal(body[1], `- ${FRESH.description}`);
134
+ const hookLine = body[2] ?? "";
135
+ assert.ok(hookLine.startsWith("- "), "compressed line must render");
136
+ assert.ok(hookLine.endsWith("..."), "mechanical hook marks truncation");
137
+ assert.ok(hookLine.length <= 2 + 96 + 3, "hook stays inside the char budget");
138
+ assert.ok(
139
+ built.ledger.get(standingHookLedgerKey(OLD_LONG.description, 96))?.endsWith("..."),
140
+ "generated hook lands in the ledger",
141
+ );
142
+ });
143
+
144
+ test("hook ledger: pre-seeded hooks are used verbatim; reuse keeps bytes identical", () => {
145
+ const seeded = new Map([[standingHookLedgerKey(OLD_LONG.description, 96), "Billing moved to the ledger stream."]]);
146
+ const built = buildStandingMemoryBlock({ entries: [OLD_LONG], nowMs: NOW, ledger: seeded });
147
+ assert.ok(built.text.includes("- Billing moved to the ledger stream."));
148
+ const rebuilt = buildStandingMemoryBlock({ entries: [OLD_LONG], nowMs: NOW, ledger: built.ledger });
149
+ assert.equal(built.text, rebuilt.text);
150
+ assert.equal(seeded.size, 1, "input ledger is never mutated");
151
+ });
152
+
153
+ test("mechanical trim: drops compressed lines from the tail, never pinned lines", () => {
154
+ const built = buildStandingMemoryBlock({ entries: [OLD_LONG, FRESH, PINNED], nowMs: NOW, maxChars: 220 });
155
+ assert.equal(built.bandCounts.pinned, 1, "pinned line survives");
156
+ assert.ok(built.bandCounts.dropped >= 1, "tail lines dropped");
157
+ assert.ok(built.text.includes("- ...(older lines trimmed)"));
158
+ assert.ok(!built.text.includes(OLD_LONG.description.slice(0, 40)), "the longest compressed line is gone");
159
+ assert.throws(
160
+ () => buildStandingMemoryBlock({ entries: [PINNED, PINNED2], nowMs: NOW, maxChars: 80 }),
161
+ StandingBlockBudgetError,
162
+ );
163
+ });
164
+
165
+ test("no entries render an empty block; bad input is refused loudly", () => {
166
+ assert.equal(buildStandingMemoryBlock({ entries: [], nowMs: NOW }).text, "");
167
+ assert.throws(
168
+ () => buildStandingMemoryBlock({ entries: [{ id: "x", description: "" }], nowMs: NOW }),
169
+ /requires a non-empty description/,
170
+ );
171
+ assert.throws(
172
+ () =>
173
+ buildStandingMemoryBlock({
174
+ entries: [
175
+ { id: "x", description: "One." },
176
+ { id: "x", description: "Two." },
177
+ ],
178
+ nowMs: NOW,
179
+ }),
180
+ /ids must be unique/,
181
+ );
182
+ assert.throws(
183
+ () => buildStandingMemoryBlock({ entries: [{ id: "x", description: "One.", lastChangedAt: "nope" }], nowMs: NOW }),
184
+ /unparseable lastChangedAt/,
185
+ );
186
+ });
187
+
188
+ /**
189
+ * Zero-diff proof for the foundation slice: nothing on the live recall path
190
+ * reads the new keys yet, so recall output must stay byte-identical whether
191
+ * the flag is absent or explicitly true, and must contain no standing block.
192
+ * When the server wiring slice lands, this test flips to assert the block IS
193
+ * prepended when on — that flip is the wiring PR's fail-before receipt.
194
+ */
195
+ async function makeOrchestrator(
196
+ overrides: Partial<PluginConfig> = {},
197
+ ): Promise<{ orchestrator: Orchestrator; memoryDir: string }> {
198
+ const memoryDir = await mkdtemp(path.join(os.tmpdir(), "remnic-standing-zero-diff-"));
199
+ const config = parseConfig({
200
+ openaiApiKey: "sk-test",
201
+ memoryDir,
202
+ workspaceDir: path.join(memoryDir, "workspace"),
203
+ qmdEnabled: false,
204
+ embeddingFallbackEnabled: false,
205
+ multiGraphMemoryEnabled: false,
206
+ entityGraphEnabled: false,
207
+ timeGraphEnabled: false,
208
+ causalGraphEnabled: false,
209
+ extractionJudgeEnabled: false,
210
+ contradictionDetectionEnabled: false,
211
+ chunkingEnabled: false,
212
+ extractionMinChars: 0,
213
+ extractionMinImportanceLevel: "trivial",
214
+ inlineSourceAttributionEnabled: false,
215
+ initGateTimeoutMs: 200,
216
+ ...overrides,
217
+ });
218
+ return { orchestrator: new Orchestrator(config), memoryDir };
219
+ }
220
+
221
+ async function writeFact(memoryDir: string): Promise<void> {
222
+ const today = new Date().toISOString().slice(0, 10);
223
+ const factsDir = path.join(memoryDir, "facts", today);
224
+ await mkdir(factsDir, { recursive: true });
225
+ const now = new Date().toISOString();
226
+ const fact = [
227
+ "---",
228
+ "id: standing-fact",
229
+ "category: fact",
230
+ `created: ${now}`,
231
+ `updated: ${now}`,
232
+ "source: extraction",
233
+ "confidence: 0.8",
234
+ "confidenceTier: high",
235
+ "tags: []",
236
+ "status: active",
237
+ "---",
238
+ "",
239
+ "The blue pipeline is the only deploy path.",
240
+ "",
241
+ ];
242
+ await writeFile(path.join(factsDir, "standing-fact.md"), `${fact.join("\n")}\n`, "utf-8");
243
+ }
244
+
245
+ test("zero-diff: recall output is byte-identical with the flag off vs on (pre-wiring)", async () => {
246
+ const off = await makeOrchestrator();
247
+ const on = await makeOrchestrator({ recallStandingBlock: true });
248
+ try {
249
+ await writeFact(off.memoryDir);
250
+ await writeFact(on.memoryDir);
251
+ const outOff = await off.orchestrator.recall("how do deploys go out", "sess-standing-off");
252
+ const outOn = await on.orchestrator.recall("how do deploys go out", "sess-standing-on");
253
+ assert.equal(outOn, outOff, "recall must not change until the wiring slice lands");
254
+ assert.ok(!outOn.includes("## Standing Memory (Remnic)"), "no standing block in recall output");
255
+ assert.ok(outOn.includes("blue pipeline"), "sanity: the fact is recalled");
256
+ } finally {
257
+ await off.orchestrator.destroy();
258
+ await on.orchestrator.destroy();
259
+ await rm(off.memoryDir, { recursive: true, force: true });
260
+ await rm(on.memoryDir, { recursive: true, force: true });
261
+ }
262
+ });
@@ -0,0 +1,331 @@
1
+ /**
2
+ * Prefix-cache-stable standing memory block (issue #2971, foundation slice).
3
+ *
4
+ * A standing block is a compact, byte-stable index over pinned/high-value
5
+ * memories that a host injects as the FIRST part of the system prompt, so
6
+ * the LLM prefix cache survives across turns. Measured motivation
7
+ * (distill-kura, local OpenAI-compatible server): an identical 4k-token
8
+ * prefix reprices at 0.14 s vs 0.68 s cold, and one changed word at the
9
+ * front costs the whole cache. So the block contains no clock, no date,
10
+ * and no counter — and the builder REFUSES, at build time, any block that
11
+ * carries one instead of discovering slow turns weeks later.
12
+ *
13
+ * Determinism rules (byte-identical between rebuilds):
14
+ * - line order is band rank, then id codepoint order — never wall clock,
15
+ * never insertion order, never locale collation;
16
+ * - `nowMs` classifies bands but never renders into the text;
17
+ * - the trimmed-tail marker is a constant string (no count of dropped
18
+ * lines, which would itself be a counter).
19
+ *
20
+ * Host contract for the full feature (later wiring slices):
21
+ * - rebuild only when the underlying store changes (store version), not
22
+ * per turn and not on a timer — band edges must not drift mid-session;
23
+ * - inject the standing block BEFORE per-turn dynamic recall and any
24
+ * ticking content (persona with a clock), see docs/config-reference.md;
25
+ * - selection of which memories qualify (pinned/high-value) happens
26
+ * before this builder — it only layers and renders what it is given;
27
+ * - on build failure (volatility, budget) skip injection and log — the
28
+ * dynamic recall surface must not go blank.
29
+ *
30
+ * Pure: no I/O, inputs never mutated, the returned ledger is a fresh Map.
31
+ */
32
+ import { coerceBooleanLike, coerceNumber } from "./connectors/coerce.js";
33
+
34
+ export const DEFAULT_RECALL_STANDING_BLOCK = false;
35
+ export const DEFAULT_STANDING_BLOCK_FRESH_DAYS = 14;
36
+ export const DEFAULT_STANDING_BLOCK_MAX_CHARS = 2048;
37
+ /** ~24-token recognition hook at the repo's 4-chars-per-token Latin headroom. */
38
+ export const DEFAULT_STANDING_BLOCK_HOOK_CHARS = 96;
39
+
40
+ export const STANDING_BLOCK_HEADER = "## Standing Memory (Remnic)";
41
+ export const STANDING_BLOCK_INSTRUCTION =
42
+ "Use this standing index for orientation. It changes only when the memory store changes.";
43
+ export const STANDING_BLOCK_TRIMMED_MARKER = "- ...(older lines trimmed)";
44
+
45
+ export type StandingBand = "pinned" | "fresh" | "compressed";
46
+
47
+ export interface StandingMemoryEntry {
48
+ /** Stable document id. Total order within a band is id codepoint order. */
49
+ id: string;
50
+ /** The one-line description to stand in the index. */
51
+ description: string;
52
+ /** Pinned entries render in full, first band, never trimmed away. */
53
+ pinned?: boolean;
54
+ /** ISO timestamp of the last content change; drives the fresh band. */
55
+ lastChangedAt?: string;
56
+ }
57
+
58
+ /** Cache of compressed hooks keyed on `${description}\u0000${hookMaxChars}`. */
59
+ export type StandingHookLedger = ReadonlyMap<string, string>;
60
+
61
+ export function standingHookLedgerKey(description: string, hookMaxChars: number): string {
62
+ return `${description}\u0000${hookMaxChars}`;
63
+ }
64
+
65
+ export interface StandingBlockBandCounts {
66
+ pinned: number;
67
+ fresh: number;
68
+ compressed: number;
69
+ dropped: number;
70
+ }
71
+
72
+ export interface StandingMemoryBlock {
73
+ /** Full block text (header + lines + instruction). Empty string for no entries. */
74
+ text: string;
75
+ /** Lines without a trailing newline; empty for no entries. */
76
+ lines: string[];
77
+ /** Ledger including hooks generated this build. Fresh Map; input never mutated. */
78
+ ledger: StandingHookLedger;
79
+ bandCounts: StandingBlockBandCounts;
80
+ }
81
+
82
+ export type StandingVolatilityKind = "iso-date" | "clock-time" | "relative-date" | "counter";
83
+
84
+ export interface StandingVolatilityMatch {
85
+ kind: StandingVolatilityKind;
86
+ token: string;
87
+ }
88
+
89
+ const ISO_DATE_RE = /\b\d{4}-\d{2}-\d{2}(?:[T ]\d{2}:\d{2})?/;
90
+ const CLOCK_TIME_RE = /\b\d{1,2}:\d{2}(?::\d{2})?\b/;
91
+ const RELATIVE_DATE_RE =
92
+ /\b(?:today|tonight|tomorrow|yesterday|this\s+(?:morning|afternoon|evening|week|month|year))\b/i;
93
+ const COUNTER_RE = /\b(?:count|total|entries|memories|items)\s*[:=]\s*\d+\b/i;
94
+
95
+ /**
96
+ * Build-time volatility lint. Returns every cache-busting token class found:
97
+ * ISO dates/datetimes, clock times, relative date words, and index counters.
98
+ * Refusal is the designed behavior (a memory surface skips the block and
99
+ * logs) — a silently repricing prefix is the failure this catches.
100
+ */
101
+ export function lintStandingBlockVolatility(text: string): StandingVolatilityMatch[] {
102
+ const matches: StandingVolatilityMatch[] = [];
103
+ for (const [kind, re] of [
104
+ ["iso-date", ISO_DATE_RE],
105
+ ["clock-time", CLOCK_TIME_RE],
106
+ ["relative-date", RELATIVE_DATE_RE],
107
+ ["counter", COUNTER_RE],
108
+ ] as const) {
109
+ const hit = text.match(re);
110
+ if (hit) matches.push({ kind, token: hit[0] });
111
+ }
112
+ return matches;
113
+ }
114
+
115
+ export class StandingBlockError extends Error {
116
+ constructor(message: string) {
117
+ super(message);
118
+ this.name = "StandingBlockError";
119
+ }
120
+ }
121
+
122
+ export class StandingBlockVolatilityError extends StandingBlockError {
123
+ readonly matches: readonly StandingVolatilityMatch[];
124
+ constructor(matches: readonly StandingVolatilityMatch[]) {
125
+ super(
126
+ `standing memory block refused: volatile content busts the prefix cache (${matches
127
+ .map((m) => `${m.kind}:${JSON.stringify(m.token)}`)
128
+ .join(", ")})`,
129
+ );
130
+ this.name = "StandingBlockVolatilityError";
131
+ this.matches = matches;
132
+ }
133
+ }
134
+
135
+ export class StandingBlockBudgetError extends StandingBlockError {
136
+ constructor(maxChars: number) {
137
+ super(
138
+ `standing memory block does not fit: pinned lines alone exceed maxChars=${maxChars}; raise standingBlockMaxChars or unpin entries`,
139
+ );
140
+ this.name = "StandingBlockBudgetError";
141
+ }
142
+ }
143
+
144
+ export function parseRecallStandingBlock(raw: unknown): boolean {
145
+ return coerceBooleanLike(raw, "recallStandingBlock") === true;
146
+ }
147
+
148
+ export function parseStandingBlockFreshDays(raw: unknown): number {
149
+ if (raw === undefined || raw === null) return DEFAULT_STANDING_BLOCK_FRESH_DAYS;
150
+ const n = coerceNumber(raw, "standingBlockFreshDays");
151
+ if (n === undefined || !Number.isFinite(n) || n < 0) {
152
+ throw new Error(`Invalid standingBlockFreshDays: expected a number >= 0, got ${JSON.stringify(raw)}`);
153
+ }
154
+ return n;
155
+ }
156
+
157
+ export function parseStandingBlockMaxChars(raw: unknown): number {
158
+ if (raw === undefined || raw === null) return DEFAULT_STANDING_BLOCK_MAX_CHARS;
159
+ const n = coerceNumber(raw, "standingBlockMaxChars");
160
+ if (n === undefined || !Number.isFinite(n) || n < 1) {
161
+ throw new Error(`Invalid standingBlockMaxChars: expected an integer >= 1, got ${JSON.stringify(raw)}`);
162
+ }
163
+ return Math.floor(n);
164
+ }
165
+
166
+ /**
167
+ * Mechanical hook: deterministic first-cut of the description to the char
168
+ * budget at a word boundary. This is also the fallback when no model is
169
+ * reachable for compression — the standing surface never goes blank because
170
+ * a model is down. Model-generated hooks arrive pre-seeded in the ledger.
171
+ */
172
+ function mechanicalHook(description: string, budget: number): string {
173
+ if (description.length <= budget) return description;
174
+ const cut = description.slice(0, budget);
175
+ const lastSpace = cut.lastIndexOf(" ");
176
+ const base = lastSpace > Math.floor(budget * 0.6) ? cut.slice(0, lastSpace) : cut;
177
+ return `${base.replace(/[\s,;:]+$/, "")}...`;
178
+ }
179
+
180
+ interface LayeredEntry {
181
+ id: string;
182
+ band: StandingBand;
183
+ line: string;
184
+ }
185
+
186
+ const BAND_RANK: Record<StandingBand, number> = { pinned: 0, fresh: 1, compressed: 2 };
187
+
188
+ function validateEntries(entries: readonly StandingMemoryEntry[]): Map<string, StandingMemoryEntry> {
189
+ if (!Array.isArray(entries)) {
190
+ throw new StandingBlockError("standing memory block entries must be an array");
191
+ }
192
+ const seen = new Set<string>();
193
+ const validated = new Map<string, StandingMemoryEntry>();
194
+ for (const entry of entries) {
195
+ if (typeof entry !== "object" || entry === null) {
196
+ throw new StandingBlockError(`standing memory block entry must be an object, got ${JSON.stringify(entry)}`);
197
+ }
198
+ const id = typeof entry.id === "string" ? entry.id.trim() : "";
199
+ const description = typeof entry.description === "string" ? entry.description.trim() : "";
200
+ if (id.length === 0) {
201
+ throw new StandingBlockError("standing memory block entry requires a non-empty id");
202
+ }
203
+ if (description.length === 0) {
204
+ throw new StandingBlockError(`standing memory block entry ${id} requires a non-empty description`);
205
+ }
206
+ if (seen.has(id)) {
207
+ throw new StandingBlockError(`standing memory block entry ids must be unique; duplicate: ${id}`);
208
+ }
209
+ if (entry.lastChangedAt !== undefined && Number.isNaN(Date.parse(entry.lastChangedAt))) {
210
+ throw new StandingBlockError(
211
+ `standing memory block entry ${id} has an unparseable lastChangedAt: ${JSON.stringify(entry.lastChangedAt)}`,
212
+ );
213
+ }
214
+ seen.add(id);
215
+ validated.set(id, { ...entry, id, description });
216
+ }
217
+ return validated;
218
+ }
219
+
220
+ export interface StandingMemoryBlockInput {
221
+ entries: readonly StandingMemoryEntry[];
222
+ /**
223
+ * Evaluation instant for the fresh band. Hosts must key rebuilds on the
224
+ * store version, not on this clock, or band edges drift per turn.
225
+ */
226
+ nowMs: number;
227
+ freshDays?: number;
228
+ maxChars?: number;
229
+ hookMaxChars?: number;
230
+ ledger?: StandingHookLedger;
231
+ }
232
+
233
+ export function buildStandingMemoryBlock(input: StandingMemoryBlockInput): StandingMemoryBlock {
234
+ const freshDays = input.freshDays ?? DEFAULT_STANDING_BLOCK_FRESH_DAYS;
235
+ const maxChars = input.maxChars ?? DEFAULT_STANDING_BLOCK_MAX_CHARS;
236
+ const hookMaxChars = input.hookMaxChars ?? DEFAULT_STANDING_BLOCK_HOOK_CHARS;
237
+ if (!Number.isFinite(input.nowMs)) {
238
+ throw new StandingBlockError("standing memory block requires a finite nowMs");
239
+ }
240
+ if (!(freshDays >= 0) || !Number.isFinite(freshDays)) {
241
+ throw new StandingBlockError("standing memory block requires freshDays >= 0");
242
+ }
243
+ if (!(maxChars >= 1) || !Number.isFinite(maxChars)) {
244
+ throw new StandingBlockError("standing memory block requires maxChars >= 1");
245
+ }
246
+ if (!(hookMaxChars >= 1) || !Number.isFinite(hookMaxChars)) {
247
+ throw new StandingBlockError("standing memory block requires hookMaxChars >= 1");
248
+ }
249
+
250
+ const entries = validateEntries(input.entries);
251
+ const ledger = new Map(input.ledger ?? []);
252
+ if (entries.size === 0) {
253
+ return {
254
+ text: "",
255
+ lines: [],
256
+ ledger,
257
+ bandCounts: { pinned: 0, fresh: 0, compressed: 0, dropped: 0 },
258
+ };
259
+ }
260
+
261
+ const freshCutoff = input.nowMs - freshDays * 86_400_000;
262
+ const layered: LayeredEntry[] = [];
263
+ for (const entry of entries.values()) {
264
+ let band: StandingBand;
265
+ if (entry.pinned === true) {
266
+ band = "pinned";
267
+ } else if (
268
+ typeof entry.lastChangedAt === "string" &&
269
+ Date.parse(entry.lastChangedAt) >= freshCutoff &&
270
+ Date.parse(entry.lastChangedAt) <= input.nowMs
271
+ ) {
272
+ band = "fresh";
273
+ } else {
274
+ band = "compressed";
275
+ }
276
+ if (band !== "compressed") {
277
+ layered.push({ id: entry.id, band, line: `- ${entry.description}` });
278
+ continue;
279
+ }
280
+ const key = standingHookLedgerKey(entry.description, hookMaxChars);
281
+ const cached = ledger.get(key);
282
+ const hook = cached ?? mechanicalHook(entry.description, hookMaxChars);
283
+ if (cached === undefined) ledger.set(key, hook);
284
+ layered.push({ id: entry.id, band, line: `- ${hook}` });
285
+ }
286
+
287
+ // Band rank, then id codepoint order. Never localeCompare: collation is
288
+ // ICU-dependent and this text must be byte-identical across rebuilds.
289
+ layered.sort(
290
+ (a, b) =>
291
+ BAND_RANK[a.band] - BAND_RANK[b.band] || (a.id < b.id ? -1 : a.id > b.id ? 1 : 0),
292
+ );
293
+
294
+ const render = (keep: number, withMarker: boolean): { text: string; lines: string[] } => {
295
+ const body = layered.slice(0, keep).map((item) => item.line);
296
+ if (withMarker) body.push(STANDING_BLOCK_TRIMMED_MARKER);
297
+ const lines = [STANDING_BLOCK_HEADER, "", ...body, "", STANDING_BLOCK_INSTRUCTION];
298
+ return { text: lines.join("\n"), lines };
299
+ };
300
+
301
+ const pinnedCount = layered.filter((item) => item.band === "pinned").length;
302
+ let keep = layered.length;
303
+ let rendered = render(keep, false);
304
+ // Mechanical trim: drop from the tail. The sort puts compressed lines
305
+ // last, so oldest-band hooks go first and pinned lines are never reached.
306
+ while (rendered.text.length > maxChars && keep > pinnedCount) {
307
+ keep -= 1;
308
+ rendered = render(keep, true);
309
+ }
310
+ if (rendered.text.length > maxChars) {
311
+ throw new StandingBlockBudgetError(maxChars);
312
+ }
313
+
314
+ const volatility = lintStandingBlockVolatility(rendered.text);
315
+ if (volatility.length > 0) {
316
+ throw new StandingBlockVolatilityError(volatility);
317
+ }
318
+
319
+ const kept = layered.slice(0, keep);
320
+ return {
321
+ text: rendered.text,
322
+ lines: rendered.lines,
323
+ ledger,
324
+ bandCounts: {
325
+ pinned: kept.filter((item) => item.band === "pinned").length,
326
+ fresh: kept.filter((item) => item.band === "fresh").length,
327
+ compressed: kept.filter((item) => item.band === "compressed").length,
328
+ dropped: layered.length - keep,
329
+ },
330
+ };
331
+ }
package/src/types.ts CHANGED
@@ -991,6 +991,12 @@ export interface PluginConfig
991
991
  recallDirectAnswerEnabled: boolean;
992
992
  /** Recall state views (issue #1952): label current/historical/transition on change-intent queries. Default false. */
993
993
  recallStateViews: boolean;
994
+ /** Standing memory block (issue #2971): byte-stable pinned-memory index injected before per-turn recall; parsed now, injection lands in a later slice. Default false. */
995
+ recallStandingBlock: boolean;
996
+ /** Days a memory stays in the standing block's full-text fresh band. Default 14. */
997
+ standingBlockFreshDays: number;
998
+ /** Hard character budget for the standing memory block. Default 2048. */
999
+ standingBlockMaxChars: number;
994
1000
  /**
995
1001
  * Disclosure auto-escalation policy (issue #677 PR 4/4). `"auto"`
996
1002
  * escalates chunk → section when top-K confidence falls below