@remnic/core 9.7.14 → 9.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/access-admin-ops-surface.d.ts +13 -6
- package/dist/access-admin-ops-surface.js +88 -74
- package/dist/access-audit.js +2 -2
- package/dist/access-boundary.d.ts +35 -17
- package/dist/access-boundary.js +89 -77
- package/dist/access-citation.d.ts +28 -0
- package/dist/access-citation.js +9 -0
- package/dist/access-cli.js +197 -181
- package/dist/access-cli.js.map +1 -1
- package/dist/access-errors.d.ts +16 -1
- package/dist/access-errors.js +7 -3
- package/dist/access-http.d.ts +25 -19
- package/dist/access-http.js +103 -90
- package/dist/access-identity-continuity-surface.d.ts +13 -8
- package/dist/access-identity-continuity-surface.js +88 -74
- package/dist/access-lcm-surface.d.ts +16 -9
- package/dist/access-lcm-surface.js +88 -74
- package/dist/access-mcp.d.ts +23 -16
- package/dist/access-mcp.js +99 -87
- package/dist/access-memory-search-fanout.d.ts +93 -0
- package/dist/access-memory-search-fanout.js +18 -0
- package/dist/access-observe-write-surface.d.ts +26 -8
- package/dist/access-observe-write-surface.js +88 -74
- package/dist/access-operations-batch.js +95 -83
- package/dist/access-operations.d.ts +26 -19
- package/dist/access-operations.js +97 -85
- package/dist/access-recall-concurrency.d.ts +250 -0
- package/dist/access-recall-concurrency.js +157 -0
- package/dist/access-recall-response.d.ts +130 -0
- package/dist/access-recall-response.js +147 -0
- package/dist/access-recall-surface.d.ts +15 -7
- package/dist/access-recall-surface.js +88 -74
- package/dist/access-schema.d.ts +34 -34
- package/dist/access-schema.js +8 -6
- package/dist/{access-service-CPhSnLOH.d.ts → access-service-DEL4qCrc.d.ts} +25 -32
- package/dist/access-service.d.ts +13 -6
- package/dist/access-service.js +92 -75
- package/dist/access-surface-catalog.d.ts +23 -16
- package/dist/access-surface-catalog.js +1 -0
- package/dist/access-surface-catalog.js.map +1 -1
- package/dist/access-token-capabilities.d.ts +2 -1
- package/dist/access-token-capabilities.js +4 -2
- package/dist/action-confidence.d.ts +3 -1
- package/dist/active-memory-bridge.d.ts +3 -1
- package/dist/active-memory-bridge.js +4 -4
- package/dist/active-recall.d.ts +4 -1
- package/dist/active-recall.js +23 -13
- package/dist/active-recall.js.map +1 -1
- package/dist/adapters/index.js +4 -4
- package/dist/adapters/registry.js +2 -2
- package/dist/{auto-sync-AAMTVZMK.js → auto-sync-UZG3XQZ5.js} +14 -8
- package/dist/{auto-sync-AAMTVZMK.js.map → auto-sync-UZG3XQZ5.js.map} +1 -1
- package/dist/behavior-learner.d.ts +3 -1
- package/dist/behavior-signals.d.ts +3 -1
- package/dist/bootstrap.d.ts +12 -7
- package/dist/bootstrap.js +2 -2
- package/dist/bounded-jsonl-state.d.ts +54 -0
- package/dist/bounded-jsonl-state.js +10 -0
- package/dist/boxes.js +2 -2
- package/dist/briefing.d.ts +5 -2
- package/dist/briefing.js +37 -27
- package/dist/buffer-surprise-report.d.ts +3 -1
- package/dist/buffer.d.ts +60 -2
- package/dist/buffer.js +3 -3
- package/dist/bulk-import/index.d.ts +5 -3
- package/dist/calibration.d.ts +3 -1
- package/dist/calibration.js +6 -6
- package/dist/capabilities.d.ts +7 -7
- package/dist/capabilities.js +1 -1
- package/dist/{capsule-crypto-CZJSLEFG.js → capsule-crypto-YO5QJ6L3.js} +3 -3
- package/dist/capsule-crypto-YO5QJ6L3.js.map +1 -0
- package/dist/{capsule-merge-Z2HUO6IK.js → capsule-merge-5KKJQBWQ.js} +4 -4
- package/dist/{catalog-uX9sT9ws.d.ts → catalog-BWqHb5HT.d.ts} +1 -1
- package/dist/causal-behavior.d.ts +3 -1
- package/dist/causal-behavior.js +6 -6
- package/dist/causal-chain.js +4 -4
- package/dist/causal-consolidation.d.ts +3 -1
- package/dist/causal-consolidation.js +46 -37
- package/dist/causal-consolidation.js.map +1 -1
- package/dist/causal-retrieval.js +6 -6
- package/dist/causal-trajectory-graph.d.ts +3 -1
- package/dist/causal-trajectory-graph.js +1 -1
- package/dist/causal-trajectory.js +1 -1
- package/dist/{chunk-A3GOMRJG.js → chunk-23L3IXFD.js} +2 -2
- package/dist/chunk-2CGLBUW3.js +15 -0
- package/dist/chunk-2CGLBUW3.js.map +1 -0
- package/dist/{chunk-MRHFJV44.js → chunk-2DQ2XREU.js} +2 -2
- package/dist/{chunk-ASKEM4AR.js → chunk-2EJOEICD.js} +1823 -1483
- package/dist/chunk-2EJOEICD.js.map +1 -0
- package/dist/{chunk-L2B3EE7B.js → chunk-2QMKMVHH.js} +2 -2
- package/dist/{chunk-6CALL5VN.js → chunk-2UFO7X4Q.js} +7 -7
- package/dist/{chunk-2MR3MFQB.js → chunk-2VEOKUPP.js} +3 -3
- package/dist/chunk-2Z24E5W2.js +22 -0
- package/dist/chunk-2Z24E5W2.js.map +1 -0
- package/dist/{chunk-RHOZ5LLO.js → chunk-347WD2LE.js} +2 -2
- package/dist/{chunk-5S7Z5SHA.js → chunk-34RC5RT7.js} +2 -2
- package/dist/{chunk-KGWXHWAH.js → chunk-3QAH2S33.js} +3 -3
- package/dist/{chunk-37CTGERM.js → chunk-3WCASNQZ.js} +12 -1
- package/dist/chunk-3WCASNQZ.js.map +1 -0
- package/dist/{chunk-ZIDSD6VC.js → chunk-3Y224NYC.js} +3 -3
- package/dist/{chunk-RBQLJSJ7.js → chunk-44XZ75OQ.js} +46 -10
- package/dist/chunk-44XZ75OQ.js.map +1 -0
- package/dist/{chunk-VFSP73LP.js → chunk-45GF7Y3V.js} +2 -2
- package/dist/{chunk-7ICYQUOL.js → chunk-4BJQODZF.js} +2514 -1526
- package/dist/chunk-4BJQODZF.js.map +1 -0
- package/dist/{chunk-JMBVCAR4.js → chunk-57OWKFUQ.js} +17 -6
- package/dist/chunk-57OWKFUQ.js.map +1 -0
- package/dist/{chunk-OEHCMALU.js → chunk-5I4M26Q7.js} +5 -5
- package/dist/{chunk-2E6G7FCE.js → chunk-5Q7EVV4X.js} +7 -7
- package/dist/{chunk-LC7OODWZ.js → chunk-5SB7SC2G.js} +80 -49
- package/dist/chunk-5SB7SC2G.js.map +1 -0
- package/dist/{chunk-VZ5OJE4X.js → chunk-5VQJBH2G.js} +2 -2
- package/dist/{chunk-PUBTHW5O.js → chunk-6DRPPQEJ.js} +2 -2
- package/dist/{chunk-NWTGD4AN.js → chunk-6KIQT7RG.js} +10 -9
- package/dist/chunk-6KIQT7RG.js.map +1 -0
- package/dist/{chunk-4LEF6XWJ.js → chunk-6O22TZSB.js} +3 -3
- package/dist/{chunk-ZQ7UBUZ6.js → chunk-6QYST5XN.js} +6 -6
- package/dist/{chunk-W4PGTAS2.js → chunk-6WAFYNVT.js} +3 -3
- package/dist/chunk-6ZMAMSHS.js +20 -0
- package/dist/chunk-6ZMAMSHS.js.map +1 -0
- package/dist/{chunk-R5VAVVHP.js → chunk-72A4L5JJ.js} +2 -2
- package/dist/{chunk-X64YLC24.js → chunk-72VAXGEI.js} +5 -13
- package/dist/chunk-72VAXGEI.js.map +1 -0
- package/dist/{chunk-IKQTFQMI.js → chunk-755Q3DGF.js} +22 -3
- package/dist/chunk-755Q3DGF.js.map +1 -0
- package/dist/{chunk-F7AU34FC.js → chunk-76GLCM57.js} +160 -127
- package/dist/chunk-76GLCM57.js.map +1 -0
- package/dist/{chunk-D7FOKIY5.js → chunk-7AMFUATK.js} +12 -9
- package/dist/{chunk-D7FOKIY5.js.map → chunk-7AMFUATK.js.map} +1 -1
- package/dist/{chunk-BFVEEB7W.js → chunk-7ON2JW73.js} +57 -22
- package/dist/chunk-7ON2JW73.js.map +1 -0
- package/dist/chunk-7RNBVZZO.js +270 -0
- package/dist/chunk-7RNBVZZO.js.map +1 -0
- package/dist/{chunk-3FAMU5TX.js → chunk-A6USBZTO.js} +2 -2
- package/dist/{chunk-TXVD7PLT.js → chunk-A72SWTWW.js} +7 -7
- package/dist/{chunk-FC77VUX5.js → chunk-A7Q43EUZ.js} +2 -2
- package/dist/{chunk-UJBESW6X.js → chunk-AE7WMV4V.js} +7 -7
- package/dist/{chunk-J2HSAU72.js → chunk-AIK5W3IW.js} +29 -1
- package/dist/chunk-AIK5W3IW.js.map +1 -0
- package/dist/{chunk-OKUEZOGW.js → chunk-AITCS2RC.js} +2 -2
- package/dist/{chunk-OC35KQIN.js → chunk-AJ7QCSIS.js} +4 -4
- package/dist/{chunk-FNMFJWBY.js → chunk-ANEUPF6O.js} +5 -5
- package/dist/chunk-AREGMX2J.js +50 -0
- package/dist/chunk-AREGMX2J.js.map +1 -0
- package/dist/chunk-AVPXXV6J.js +448 -0
- package/dist/chunk-AVPXXV6J.js.map +1 -0
- package/dist/{chunk-2A3OXAP2.js → chunk-B7RWBZXW.js} +2 -2
- package/dist/{chunk-GWTFLF2P.js → chunk-BJMBJZ2Y.js} +59 -59
- package/dist/chunk-BJMBJZ2Y.js.map +1 -0
- package/dist/{chunk-N3JDIRBW.js → chunk-BLAKS3CP.js} +4 -4
- package/dist/{chunk-M7VGG3ID.js → chunk-BV5WMSIJ.js} +9 -9
- package/dist/{chunk-2BFLDH5F.js → chunk-BXLOS5AJ.js} +2 -2
- package/dist/{chunk-UXKMMNJD.js → chunk-C23BYTJV.js} +6 -6
- package/dist/{chunk-JZHHREBL.js → chunk-C3AKM55W.js} +2 -2
- package/dist/{chunk-RP7YXA67.js → chunk-C5JF6EZ7.js} +2 -2
- package/dist/{chunk-FBNWOMCE.js → chunk-C6XBRYVD.js} +16 -15
- package/dist/chunk-C6XBRYVD.js.map +1 -0
- package/dist/{chunk-FTYDWKO6.js → chunk-CEN6DW6X.js} +12 -2
- package/dist/chunk-CEN6DW6X.js.map +1 -0
- package/dist/chunk-CGASUJVP.js +421 -0
- package/dist/chunk-CGASUJVP.js.map +1 -0
- package/dist/{chunk-DDGV7NAX.js → chunk-CQMEM5CC.js} +2 -2
- package/dist/{chunk-A7B4RMQG.js → chunk-CXZQOVIE.js} +12 -12
- package/dist/chunk-D3V3UMET.js +44 -0
- package/dist/chunk-D3V3UMET.js.map +1 -0
- package/dist/{chunk-7N2RUBAP.js → chunk-DDA47TES.js} +2 -2
- package/dist/{chunk-64SE4X5G.js → chunk-DEBHXHJR.js} +3 -3
- package/dist/{chunk-EHK6KZHK.js → chunk-DNHVPZOD.js} +2 -2
- package/dist/{chunk-437S3G37.js → chunk-DQEMWVMT.js} +2 -2
- package/dist/{chunk-LZZNTPLR.js → chunk-DX3XRGLH.js} +2 -2
- package/dist/chunk-EA2H5LML.js +72 -0
- package/dist/chunk-EA2H5LML.js.map +1 -0
- package/dist/{chunk-IFJ23BHU.js → chunk-EEC7UJPX.js} +112 -106
- package/dist/chunk-EEC7UJPX.js.map +1 -0
- package/dist/{chunk-KSNVDPDF.js → chunk-EKBNDKDY.js} +21 -20
- package/dist/chunk-EKBNDKDY.js.map +1 -0
- package/dist/{chunk-YF6FMLBT.js → chunk-EQWEIIKQ.js} +64 -34
- package/dist/chunk-EQWEIIKQ.js.map +1 -0
- package/dist/{chunk-7MW3CVLD.js → chunk-ES36HTSC.js} +4 -4
- package/dist/{chunk-TN7THW5U.js → chunk-ET356KSB.js} +2 -2
- package/dist/{chunk-YNDLCWXS.js → chunk-EZ25VE3G.js} +4 -4
- package/dist/{chunk-7LFXDGLH.js → chunk-F7GAIWFA.js} +20 -20
- package/dist/{chunk-IKSELOIV.js → chunk-FFPJINJE.js} +7 -7
- package/dist/chunk-FMLKOL2Z.js +636 -0
- package/dist/chunk-FMLKOL2Z.js.map +1 -0
- package/dist/chunk-FQC3DHH2.js +20 -0
- package/dist/chunk-FQC3DHH2.js.map +1 -0
- package/dist/{chunk-V53GJUSL.js → chunk-FUTF72MJ.js} +15 -15
- package/dist/chunk-GCASGYIO.js +71 -0
- package/dist/chunk-GCASGYIO.js.map +1 -0
- package/dist/{chunk-5UNWK7KU.js → chunk-GFPZS3F5.js} +8 -8
- package/dist/{chunk-BCDKBDSY.js → chunk-GNVZQ5JN.js} +99 -41
- package/dist/chunk-GNVZQ5JN.js.map +1 -0
- package/dist/{chunk-G6NKALRN.js → chunk-H4WG3UQZ.js} +2 -2
- package/dist/{chunk-HLTAPHZZ.js → chunk-HNZKVLAN.js} +2 -2
- package/dist/{chunk-5EZXHU36.js → chunk-HYQJQGD3.js} +2 -2
- package/dist/{chunk-ANADNYWK.js → chunk-IDZFBSHN.js} +8 -6
- package/dist/{chunk-ANADNYWK.js.map → chunk-IDZFBSHN.js.map} +1 -1
- package/dist/{chunk-XVLEGMJI.js → chunk-IFH6LWTO.js} +50 -8
- package/dist/chunk-IFH6LWTO.js.map +1 -0
- package/dist/{chunk-RULE4VG5.js → chunk-ILGC6JH6.js} +21 -6
- package/dist/chunk-ILGC6JH6.js.map +1 -0
- package/dist/chunk-IUM3VSHI.js +113 -0
- package/dist/chunk-IUM3VSHI.js.map +1 -0
- package/dist/{chunk-ND5M64JJ.js → chunk-IYNGIMVG.js} +101 -91
- package/dist/chunk-IYNGIMVG.js.map +1 -0
- package/dist/{chunk-TRWPCR6P.js → chunk-JECBQBEX.js} +3 -3
- package/dist/{chunk-HG32URJJ.js → chunk-JYIFGGEM.js} +2 -2
- package/dist/{chunk-VA4WSW54.js → chunk-JYIJNNN3.js} +3 -3
- package/dist/{chunk-L5HJQC36.js → chunk-K2TDK7GG.js} +3 -3
- package/dist/{chunk-MKDRQAA3.js → chunk-KAVBJSBQ.js} +4 -4
- package/dist/{chunk-EEARRVZ2.js → chunk-KF5EUVKQ.js} +16 -6
- package/dist/chunk-KF5EUVKQ.js.map +1 -0
- package/dist/chunk-KFZY2OFH.js +42 -0
- package/dist/chunk-KFZY2OFH.js.map +1 -0
- package/dist/{chunk-4OKLXWDZ.js → chunk-KNB7IBBU.js} +2 -2
- package/dist/{chunk-QLULYKWZ.js → chunk-KSXJOC4F.js} +2 -2
- package/dist/{chunk-OBMR4VIX.js → chunk-L2DPF4DF.js} +186 -15
- package/dist/chunk-L2DPF4DF.js.map +1 -0
- package/dist/{chunk-JI5AVXYO.js → chunk-LEPOP46W.js} +6 -6
- package/dist/{chunk-O7BBFJLP.js → chunk-LTAVSOZW.js} +2 -2
- package/dist/{chunk-E3VODCC3.js → chunk-LUDXM7MD.js} +14 -2
- package/dist/{chunk-E3VODCC3.js.map → chunk-LUDXM7MD.js.map} +1 -1
- package/dist/{chunk-2IZAGAJR.js → chunk-LULR4IUW.js} +3 -3
- package/dist/{chunk-NUWYOKS6.js → chunk-MC5Q3J7S.js} +398 -7
- package/dist/chunk-MC5Q3J7S.js.map +1 -0
- package/dist/{chunk-WMWVO45V.js → chunk-MC6XO7T4.js} +2 -2
- package/dist/chunk-MKQBXQKU.js +107 -0
- package/dist/chunk-MKQBXQKU.js.map +1 -0
- package/dist/{chunk-7PND4PUC.js → chunk-MMGGVZ4Q.js} +29 -9
- package/dist/{chunk-7PND4PUC.js.map → chunk-MMGGVZ4Q.js.map} +1 -1
- package/dist/{chunk-D6RIFG4O.js → chunk-MNB5RMAY.js} +2 -2
- package/dist/{chunk-67ZCJLJY.js → chunk-MYFQ3ED4.js} +8 -8
- package/dist/{chunk-V3UE5I5F.js → chunk-MYVPLJOO.js} +2 -2
- package/dist/chunk-NINSR6LX.js +83 -0
- package/dist/chunk-NINSR6LX.js.map +1 -0
- package/dist/{chunk-MTWKDOVO.js → chunk-NLE6TSLG.js} +2 -2
- package/dist/{chunk-DBRYEIGY.js → chunk-OJP3N7UK.js} +2 -2
- package/dist/{chunk-AD6AMF2N.js → chunk-OQJIB32H.js} +28 -23
- package/dist/chunk-OQJIB32H.js.map +1 -0
- package/dist/{chunk-SMX5267P.js → chunk-OZKOPQCM.js} +2 -2
- package/dist/{chunk-JCK7V6SN.js → chunk-P65YJEEL.js} +2 -2
- package/dist/{chunk-MTR7BZQN.js → chunk-PBGWYM3H.js} +3 -3
- package/dist/chunk-PIG4WJFQ.js +87 -0
- package/dist/chunk-PIG4WJFQ.js.map +1 -0
- package/dist/chunk-PQVCH2AE.js +76 -0
- package/dist/chunk-PQVCH2AE.js.map +1 -0
- package/dist/{chunk-AXTXN4T6.js → chunk-PWFCHOB3.js} +2 -2
- package/dist/{chunk-FDPZVY6D.js → chunk-QFN4U7AG.js} +2 -2
- package/dist/{chunk-K2ZLHESQ.js → chunk-QIEUU4UK.js} +2 -2
- package/dist/{chunk-AHLPZ2SF.js → chunk-QJAQNXWI.js} +44 -6
- package/dist/chunk-QJAQNXWI.js.map +1 -0
- package/dist/{chunk-3D6AFGQR.js → chunk-QVT3E4CE.js} +7 -7
- package/dist/{chunk-VYOFVVNK.js → chunk-R26ZMXR2.js} +24 -24
- package/dist/{chunk-VYOFVVNK.js.map → chunk-R26ZMXR2.js.map} +1 -1
- package/dist/chunk-R3DJVOSM.js +34 -0
- package/dist/chunk-R3DJVOSM.js.map +1 -0
- package/dist/{chunk-SFQ6QNL7.js → chunk-R6YJI3HE.js} +2 -1
- package/dist/{chunk-5CJVLG3H.js → chunk-RFDD3B7W.js} +5 -5
- package/dist/{chunk-JBPKEARU.js → chunk-RKNJBZ55.js} +4 -4
- package/dist/chunk-ROI7BK7H.js +88 -0
- package/dist/chunk-ROI7BK7H.js.map +1 -0
- package/dist/{chunk-UTRJFDP2.js → chunk-S2BBISHU.js} +4 -4
- package/dist/{chunk-IALLX7EE.js → chunk-S2FSBU4H.js} +3 -3
- package/dist/{chunk-BFPETA3S.js → chunk-SCCO46KZ.js} +3 -2
- package/dist/chunk-SCCO46KZ.js.map +1 -0
- package/dist/chunk-SICXL4MK.js +165 -0
- package/dist/chunk-SICXL4MK.js.map +1 -0
- package/dist/chunk-SK7AMF2J.js +168 -0
- package/dist/chunk-SK7AMF2J.js.map +1 -0
- package/dist/{chunk-GMSDQUCR.js → chunk-TDTXKOAJ.js} +6475 -5748
- package/dist/chunk-TDTXKOAJ.js.map +1 -0
- package/dist/{chunk-KSBEEMQF.js → chunk-TG6IHVE2.js} +151 -6
- package/dist/chunk-TG6IHVE2.js.map +1 -0
- package/dist/{chunk-7K5Q6COX.js → chunk-TVVEYCNW.js} +4 -4
- package/dist/{chunk-PK5XBDA6.js → chunk-U3ZAVRP3.js} +2 -2
- package/dist/{chunk-ODPLEWB6.js → chunk-UFGFMWAD.js} +47 -17
- package/dist/chunk-UFGFMWAD.js.map +1 -0
- package/dist/{chunk-4KJLYTLS.js → chunk-V57NLO5P.js} +2 -2
- package/dist/{chunk-FLVL4L3L.js → chunk-VDX2J7OX.js} +2 -2
- package/dist/{chunk-7XGSIUP5.js → chunk-W4FBGYGK.js} +1 -1
- package/dist/{chunk-TXLGHDHS.js → chunk-W4XJFLAL.js} +53 -32
- package/dist/chunk-W4XJFLAL.js.map +1 -0
- package/dist/{chunk-6SXVCD7W.js → chunk-W7IGLX4G.js} +2 -2
- package/dist/{chunk-KQDNAXOM.js → chunk-WG5HSQ4P.js} +19 -6
- package/dist/chunk-WG5HSQ4P.js.map +1 -0
- package/dist/{chunk-LTJAMRGI.js → chunk-WL5EBRDH.js} +17 -3
- package/dist/chunk-WL5EBRDH.js.map +1 -0
- package/dist/{chunk-KAWTOYAW.js → chunk-WLZYGLJ4.js} +2 -2
- package/dist/{chunk-W5NKDU24.js → chunk-WON4APQY.js} +4 -4
- package/dist/{chunk-NKCPUHCI.js → chunk-WZFAPHIK.js} +6 -6
- package/dist/{chunk-KQPJLK3X.js → chunk-XL24QQ63.js} +2 -2
- package/dist/{chunk-X47LWMKA.js → chunk-XMMESCPD.js} +26 -1
- package/dist/chunk-XMMESCPD.js.map +1 -0
- package/dist/{chunk-4B4MUS5D.js → chunk-YROFNEG5.js} +7 -7
- package/dist/{chunk-WU46YPY6.js → chunk-YV32N67O.js} +1 -1
- package/dist/chunk-YV32N67O.js.map +1 -0
- package/dist/{chunk-B3F4CWQR.js → chunk-YVKF3HEW.js} +1 -1
- package/dist/chunk-YVKF3HEW.js.map +1 -0
- package/dist/{chunk-VRAD2FGO.js → chunk-Z54EM5YZ.js} +12 -12
- package/dist/{chunk-2VN4JAX7.js → chunk-ZOKZIR5K.js} +95 -33
- package/dist/chunk-ZOKZIR5K.js.map +1 -0
- package/dist/{cli-_Y2Bmt6A.d.ts → cli-C_pqqTCw.d.ts} +28 -12
- package/dist/cli.d.ts +16 -9
- package/dist/cli.js +145 -130
- package/dist/compounding/engine.d.ts +5 -2
- package/dist/compounding/engine.js +39 -29
- package/dist/compounding/preference-consolidator.d.ts +3 -1
- package/dist/compounding/preference-consolidator.js +1 -1
- package/dist/compression-optimizer.d.ts +3 -1
- package/dist/config-qmd.d.ts +8 -0
- package/dist/config-qmd.js +20 -0
- package/dist/config-qmd.js.map +1 -0
- package/dist/config.d.ts +3 -1
- package/dist/config.js +15 -10
- package/dist/connectors/codex-materialize-runner.d.ts +3 -1
- package/dist/connectors/codex-materialize-runner.js +39 -29
- package/dist/connectors/codex-materialize.d.ts +3 -1
- package/dist/connectors/codex-materialize.js +2 -2
- package/dist/connectors/index.d.ts +3 -1
- package/dist/connectors/index.js +44 -35
- package/dist/consolidation-provenance-check.d.ts +5 -2
- package/dist/consolidation-provenance-check.js +4 -4
- package/dist/consolidation-undo.d.ts +5 -2
- package/dist/consolidation-undo.js +3 -3
- package/dist/contradiction/index.d.ts +5 -2
- package/dist/contradiction/index.js +10 -4
- package/dist/{contradiction-scan-J57T5QV7.js → contradiction-scan-RJBABNRH.js} +4 -4
- package/dist/contradiction-scan-RJBABNRH.js.map +1 -0
- package/dist/conversation-index/backend.d.ts +3 -1
- package/dist/conversation-index/backend.js +5 -5
- package/dist/conversation-index/chunker.d.ts +3 -1
- package/dist/conversation-index/cleanup.js +2 -2
- package/dist/conversation-index/faiss-adapter.d.ts +3 -1
- package/dist/conversation-index/faiss-adapter.js +2 -2
- package/dist/conversation-index/indexer.d.ts +3 -1
- package/dist/conversation-index/indexer.js +2 -2
- package/dist/conversation-index/search.d.ts +3 -1
- package/dist/conversation-index/search.js +2 -2
- package/dist/cross-namespace-budget.d.ts +45 -1
- package/dist/cross-namespace-budget.js +5 -3
- package/dist/dashboard-runtime.js +2 -2
- package/dist/day-summary.d.ts +3 -1
- package/dist/day-summary.js +3 -3
- package/dist/delinearize.d.ts +3 -1
- package/dist/direct-answer-wiring.d.ts +3 -1
- package/dist/direct-answer.d.ts +3 -1
- package/dist/{dreams-ledger-6UODQOX6.js → dreams-ledger-567VKVOU.js} +4 -2
- package/dist/dreams-ledger-567VKVOU.js.map +1 -0
- package/dist/embedding-fallback.d.ts +3 -1
- package/dist/embedding-fallback.js +3 -3
- package/dist/enrichment/index.d.ts +3 -1
- package/dist/entity-retrieval.d.ts +5 -2
- package/dist/entity-retrieval.js +38 -29
- package/dist/entity-schema.d.ts +3 -1
- package/dist/explicit-capture.d.ts +13 -8
- package/dist/explicit-capture.js +7 -3
- package/dist/extraction-faithfulness.d.ts +3 -1
- package/dist/extraction-faithfulness.js +5 -5
- package/dist/extraction-judge-telemetry.d.ts +3 -1
- package/dist/extraction-judge-telemetry.js +2 -2
- package/dist/extraction-judge-training.d.ts +3 -1
- package/dist/extraction-judge-training.js +2 -2
- package/dist/extraction-judge.d.ts +3 -1
- package/dist/extraction-judge.js +5 -5
- package/dist/extraction.d.ts +3 -1
- package/dist/extraction.js +13 -12
- package/dist/faithfulness-config.js +3 -2
- package/dist/fallback-llm.d.ts +3 -1
- package/dist/fallback-llm.js +4 -4
- package/dist/{first-start-migration-TI67YMLG.js → first-start-migration-CSRHCJIQ.js} +2 -2
- package/dist/{forget-CQCLB5LB.js → forget-HRPLVB3W.js} +5 -5
- package/dist/graph-dashboard-diff.d.ts +3 -1
- package/dist/graph-dashboard-key.d.ts +3 -1
- package/dist/graph-dashboard-parser.d.ts +3 -1
- package/dist/{graph-edge-decay-FO7AM4UR.js → graph-edge-decay-Z6ENALU2.js} +3 -3
- package/dist/graph-edge-decay-Z6ENALU2.js.map +1 -0
- package/dist/graph-edge-reinforcement.d.ts +3 -1
- package/dist/graph-snapshot.d.ts +3 -1
- package/dist/graph-snapshot.js +2 -2
- package/dist/graph.d.ts +57 -10
- package/dist/graph.js +1 -1
- package/dist/identity-continuity.d.ts +1 -2
- package/dist/importance.d.ts +3 -1
- package/dist/importers/index.d.ts +3 -1
- package/dist/in-flight-reads.d.ts +3 -1
- package/dist/index.d.ts +101 -92
- package/dist/index.js +290 -242
- package/dist/intent.d.ts +3 -1
- package/dist/lcm/archive.js +2 -2
- package/dist/lcm/engine.d.ts +3 -1
- package/dist/lcm/engine.js +6 -6
- package/dist/lcm/index.d.ts +3 -1
- package/dist/lcm/index.js +13 -13
- package/dist/lcm/schema.js +2 -2
- package/dist/lcm/summarizer.js +3 -3
- package/dist/lcm/tools.d.ts +3 -1
- package/dist/lifecycle.d.ts +3 -1
- package/dist/live-connectors-runner.d.ts +3 -1
- package/dist/local-llm.d.ts +5 -2
- package/dist/local-llm.js +3 -3
- package/dist/local-model-endpoint.d.ts +3 -1
- package/dist/logger.d.ts +3 -1
- package/dist/logger.js +7 -3
- package/dist/maintenance/archive-observations.js +2 -2
- package/dist/maintenance/memory-governance.d.ts +3 -1
- package/dist/maintenance/memory-governance.js +38 -28
- package/dist/maintenance/migrate-observations.js +3 -3
- package/dist/maintenance/observation-ledger-utils.js +2 -2
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.d.ts +102 -1
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +40 -28
- package/dist/maintenance/rebuild-memory-projection.js +41 -31
- package/dist/maintenance/rebuild-observations.js +3 -3
- package/dist/mcp-memory-inspector-app.d.ts +22 -15
- package/dist/memory-action-policy.d.ts +3 -1
- package/dist/memory-cache.d.ts +46 -2
- package/dist/memory-cache.js +7 -1
- package/dist/memory-corpus-version.js +2 -2
- package/dist/memory-lifecycle-ledger-utils.d.ts +25 -2
- package/dist/memory-lifecycle-ledger-utils.js +11 -1
- package/dist/memory-projection-store.d.ts +3 -1
- package/dist/memory-projection-store.js +2 -2
- package/dist/memory-provenance.d.ts +3 -1
- package/dist/memory-worth-outcomes.d.ts +5 -2
- package/dist/model-registry.js +2 -2
- package/dist/models-json.d.ts +3 -1
- package/dist/models-json.js +2 -2
- package/dist/namespaces/migrate.d.ts +6 -3
- package/dist/namespaces/migrate.js +54 -45
- package/dist/namespaces/principal.d.ts +27 -2
- package/dist/namespaces/principal.js +6 -2
- package/dist/namespaces/search.d.ts +16 -2
- package/dist/namespaces/search.js +25 -21
- package/dist/namespaces/storage.d.ts +24 -3
- package/dist/namespaces/storage.js +38 -30
- package/dist/native-knowledge.d.ts +3 -1
- package/dist/native-knowledge.js +2 -2
- package/dist/negative.js +2 -2
- package/dist/offline-sync-exclude-globs.d.ts +13 -0
- package/dist/offline-sync-exclude-globs.js +10 -0
- package/dist/offline-sync-exclude-globs.js.map +1 -0
- package/dist/offline-sync-impression-drain.d.ts +78 -0
- package/dist/offline-sync-impression-drain.js +37 -0
- package/dist/offline-sync-impression-drain.js.map +1 -0
- package/dist/offline-sync.js +4 -2
- package/dist/operator-toolkit.d.ts +5 -2
- package/dist/operator-toolkit.js +73 -58
- package/dist/orchestration/compression-guideline-coordinator.d.ts +5 -2
- package/dist/orchestration/compression-guideline-coordinator.js +3 -3
- package/dist/orchestration/maintenance.d.ts +121 -2
- package/dist/orchestration/maintenance.js +47 -34
- package/dist/{orchestrator-BbZ_dQSU.d.ts → orchestrator-CQJ8U7En.d.ts} +85 -79
- package/dist/orchestrator.d.ts +13 -8
- package/dist/orchestrator.js +197 -180
- package/dist/page-versioning.js +3 -3
- package/dist/patterns-cli.d.ts +3 -1
- package/dist/{pipeline-FRMDb1fn.d.ts → pipeline-Cn-kvYeV.d.ts} +1 -1
- package/dist/policy-runtime.d.ts +3 -1
- package/dist/prioritized-embed.d.ts +19 -0
- package/dist/prioritized-embed.js +8 -0
- package/dist/prioritized-embed.js.map +1 -0
- package/dist/proactive-contention.d.ts +28 -0
- package/dist/proactive-contention.js +8 -0
- package/dist/proactive-contention.js.map +1 -0
- package/dist/profiling.js +2 -2
- package/dist/provenance.d.ts +3 -1
- package/dist/provenance.js +3 -2
- package/dist/{purge-NDWV6WYD.js → purge-IRF47ZTX.js} +3 -3
- package/dist/{qmd-xJRrHFbA.d.ts → qmd-oh9VXz1d.d.ts} +11 -1
- package/dist/qmd-preflight.d.ts +5 -2
- package/dist/qmd-recall-cache.d.ts +3 -1
- package/dist/qmd-status.d.ts +57 -0
- package/dist/qmd-status.js +18 -0
- package/dist/qmd-status.js.map +1 -0
- package/dist/qmd.d.ts +5 -2
- package/dist/qmd.js +8 -4
- package/dist/recall-concurrency-config.d.ts +16 -0
- package/dist/recall-concurrency-config.js +10 -0
- package/dist/recall-concurrency-config.js.map +1 -0
- package/dist/recall-disclosure-escalation.d.ts +3 -1
- package/dist/recall-explain-renderer.d.ts +4 -1
- package/dist/recall-explain-renderer.js +3 -3
- package/dist/recall-memory-map.d.ts +26 -0
- package/dist/recall-memory-map.js +18 -0
- package/dist/recall-memory-map.js.map +1 -0
- package/dist/recall-planner-llm.d.ts +3 -1
- package/dist/recall-planner-llm.js +4 -4
- package/dist/recall-qos.js +2 -2
- package/dist/recall-state.d.ts +163 -2
- package/dist/recall-state.js +6 -3
- package/dist/recall-tag-filter.d.ts +3 -1
- package/dist/recall-timings.d.ts +11 -2
- package/dist/recall-timings.js +3 -1
- package/dist/recall-xray-cli.d.ts +3 -1
- package/dist/recall-xray-cli.js +4 -4
- package/dist/recall-xray-renderer.d.ts +3 -1
- package/dist/recall-xray-renderer.js +3 -3
- package/dist/recall-xray.d.ts +3 -1
- package/dist/recall-xray.js +2 -2
- package/dist/relevance.js +3 -3
- package/dist/replay/normalizers/chatgpt.d.ts +3 -1
- package/dist/replay/normalizers/claude.d.ts +3 -1
- package/dist/replay/normalizers/openclaw.d.ts +3 -1
- package/dist/replay/normalizers/shared.d.ts +3 -1
- package/dist/replay/runner.d.ts +3 -1
- package/dist/replay/types.d.ts +3 -1
- package/dist/rerank.d.ts +8 -1
- package/dist/rerank.js +3 -1
- package/dist/resolution-3K27T4VO.js +18 -0
- package/dist/resolution-3K27T4VO.js.map +1 -0
- package/dist/resolve-auth-token.d.ts +3 -1
- package/dist/resolve-provider-secret.js +2 -2
- package/dist/resume-bundles.js +19 -14
- package/dist/retrieval-agents.d.ts +19 -16
- package/dist/retrieval-agents.js +3 -3
- package/dist/retrieval-tiers.d.ts +3 -1
- package/dist/routing/engine.d.ts +3 -1
- package/dist/routing/store.d.ts +3 -1
- package/dist/routing/store.js +2 -2
- package/dist/salvage-envelope.d.ts +18 -0
- package/dist/salvage-envelope.js +15 -0
- package/dist/salvage-envelope.js.map +1 -0
- package/dist/schemas.d.ts +24 -24
- package/dist/scope-profiles-EpJG7yKP.d.ts +30 -0
- package/dist/search/document-scanner.js +3 -2
- package/dist/search/embed-helper.d.ts +3 -1
- package/dist/search/embed-helper.js +3 -3
- package/dist/search/factory.d.ts +3 -1
- package/dist/search/factory.js +19 -17
- package/dist/search/index.d.ts +3 -1
- package/dist/search/index.js +23 -21
- package/dist/search/lancedb-backend.d.ts +3 -1
- package/dist/search/lancedb-backend.js +6 -5
- package/dist/search/meilisearch-backend.d.ts +3 -1
- package/dist/search/meilisearch-backend.js +6 -5
- package/dist/search/noop-backend.d.ts +3 -1
- package/dist/search/orama-backend.d.ts +3 -1
- package/dist/search/orama-backend.js +6 -5
- package/dist/search/port.d.ts +27 -2
- package/dist/search/port.js +1 -1
- package/dist/search/remote-backend.d.ts +3 -1
- package/dist/search/remote-backend.js +3 -3
- package/dist/secure-store/index.d.ts +12 -1
- package/dist/secure-store/index.js +6 -3
- package/dist/{semantic-consolidation-fvdZRa5Z.d.ts → semantic-consolidation-DILZEQq3.d.ts} +1 -1
- package/dist/semantic-consolidation.d.ts +4 -2
- package/dist/semantic-consolidation.js +41 -32
- package/dist/semantic-rule-promotion.js +37 -27
- package/dist/semantic-rule-verifier.d.ts +3 -1
- package/dist/semantic-rule-verifier.js +38 -28
- package/dist/session-observer-bands.d.ts +3 -1
- package/dist/session-observer-state.d.ts +3 -1
- package/dist/session-observer-state.js +2 -2
- package/dist/session-transcript-migration.js +2 -2
- package/dist/shared-context/manager.d.ts +3 -1
- package/dist/shared-context/manager.js +3 -3
- package/dist/signal.d.ts +3 -1
- package/dist/{storage-CrvzaM9C.d.ts → storage-NIyKYD8g.d.ts} +505 -110
- package/dist/storage-guards.js +2 -2
- package/dist/storage.d.ts +6 -3
- package/dist/storage.js +41 -29
- package/dist/structured-attributes.d.ts +23 -1
- package/dist/structured-attributes.js +7 -1
- package/dist/summarizer.d.ts +3 -1
- package/dist/summarizer.js +11 -11
- package/dist/summary-snapshot.d.ts +3 -1
- package/dist/summary-snapshot.js +2 -2
- package/dist/temporal-index-batch.d.ts +1 -0
- package/dist/temporal-index-batch.js +10 -0
- package/dist/temporal-index-batch.js.map +1 -0
- package/dist/temporal-index-compat.d.ts +279 -1
- package/dist/temporal-index-compat.js +1 -1
- package/dist/temporal-index.d.ts +1 -225
- package/dist/temporal-index.js +35 -3
- package/dist/temporal-supersession.d.ts +5 -2
- package/dist/temporal-supersession.js +2 -2
- package/dist/temporal-timeline-recall.d.ts +3 -1
- package/dist/temporal-validity.d.ts +3 -1
- package/dist/threading.d.ts +3 -1
- package/dist/threading.js +2 -2
- package/dist/tier-migration.d.ts +5 -2
- package/dist/tier-routing.d.ts +3 -1
- package/dist/{tier-stats-KV7IWCJR.js → tier-stats-QEJSPFV2.js} +2 -2
- package/dist/tokens.js +3 -3
- package/dist/topics.d.ts +3 -1
- package/dist/transcript.d.ts +3 -1
- package/dist/transcript.js +2 -2
- package/dist/transfer/backup.js +3 -3
- package/dist/transfer/capsule-export.js +3 -3
- package/dist/transfer/capsule-import.js +6 -6
- package/dist/transfer/types.d.ts +24 -24
- package/dist/trust-score-stage.d.ts +75 -38
- package/dist/trust-score-stage.js +11 -3
- package/dist/trust-score.d.ts +3 -1
- package/dist/{types-Bl4BWDM8.d.ts → types-D_fG2ix8.d.ts} +49 -63
- package/dist/types-continuity.d.ts +52 -0
- package/dist/types-continuity.js +1 -0
- package/dist/types-continuity.js.map +1 -0
- package/dist/types.d.ts +3 -1
- package/dist/types.js +1 -1
- package/dist/utility-runtime.d.ts +3 -1
- package/dist/utils/serialize-mutations.d.ts +31 -2
- package/dist/utils/serialize-mutations.js +1 -1
- package/dist/verified-recall.js +39 -29
- package/dist/write-envelope.d.ts +282 -0
- package/dist/write-envelope.js +35 -0
- package/dist/write-envelope.js.map +1 -0
- package/dist/write-quarantine.d.ts +79 -0
- package/dist/write-quarantine.js +11 -0
- package/dist/write-quarantine.js.map +1 -0
- package/dist/write-rate-limiter.d.ts +45 -0
- package/dist/write-rate-limiter.js +9 -0
- package/dist/write-rate-limiter.js.map +1 -0
- package/package.json +37 -2
- package/src/access-admin-ops-surface.ts +17 -7
- package/src/access-boundary.test.ts +28 -0
- package/src/access-boundary.ts +31 -2
- package/src/access-citation.test.ts +72 -0
- package/src/access-citation.ts +139 -0
- package/src/access-errors.ts +21 -0
- package/src/access-fingerprint-parity.test.ts +211 -0
- package/src/access-http-cancellation.test.ts +59 -21
- package/src/access-http.test.ts +90 -0
- package/src/access-http.ts +79 -79
- package/src/access-mcp-cancellation.test.ts +16 -7
- package/src/access-memory-search-fanout.test.ts +220 -0
- package/src/access-memory-search-fanout.ts +168 -0
- package/src/access-observe-quarantine.test.ts +120 -0
- package/src/access-observe-write-surface.ts +130 -11
- package/src/access-operations-batch.ts +2 -0
- package/src/access-recall-concurrency.ts +773 -0
- package/src/access-recall-response.ts +307 -0
- package/src/access-recall-surface.ts +100 -252
- package/src/access-service-coding-write.test.ts +22 -4
- package/src/access-service-health.test.ts +49 -0
- package/src/access-service-namespace.test.ts +486 -8
- package/src/access-service-observe-scope.test.ts +4 -0
- package/src/access-service-recall-concurrency.test.ts +1990 -0
- package/src/access-service.ts +182 -175
- package/src/access-surface-catalog.ts +1 -0
- package/src/access-token-capabilities.ts +16 -9
- package/src/active-recall.test.ts +35 -0
- package/src/active-recall.ts +7 -0
- package/src/bounded-jsonl-state.ts +93 -0
- package/src/buffer-session.test.ts +422 -5
- package/src/buffer-surprise-trigger.test.ts +41 -0
- package/src/buffer.ts +218 -3
- package/src/capabilities-cluster-b.test.ts +1 -0
- package/src/capabilities.ts +5 -6
- package/src/cli.ts +21 -22
- package/src/coding/architecture-surfaces.test.ts +6 -3
- package/src/coding/architecture-surfaces.ts +36 -25
- package/src/coding/decision-surfaces.ts +42 -34
- package/src/compounding/engine.test.ts +5 -1
- package/src/compounding/engine.ts +7 -7
- package/src/config-qmd.ts +89 -0
- package/src/config.test.ts +136 -0
- package/src/config.ts +122 -118
- package/src/connector-provenance-lifecycle.test.ts +4 -4
- package/src/connectors/coerce.test.ts +131 -0
- package/src/connectors/coerce.ts +80 -7
- package/src/contradiction/contradiction.test.ts +10 -6
- package/src/contradiction/resolution.ts +17 -4
- package/src/correction/correction-access-wiring.ts +62 -18
- package/src/cross-namespace-budget.test.ts +35 -0
- package/src/cross-namespace-budget.ts +86 -19
- package/src/explicit-capture.ts +54 -17
- package/src/extraction.ts +2 -3
- package/src/faithfulness-graph-guard.test.ts +16 -69
- package/src/graph-recall.test.ts +31 -0
- package/src/graph.test.ts +137 -1
- package/src/graph.ts +274 -17
- package/src/index.ts +22 -2
- package/src/local-llm.ts +4 -6
- package/src/logger.ts +16 -0
- package/src/maintenance/dreams-ledger.ts +100 -49
- package/src/maintenance/rebuild-memory-lifecycle-ledger-cli.ts +77 -0
- package/src/maintenance/rebuild-memory-lifecycle-ledger.ts +373 -9
- package/src/memory-cache.ts +70 -0
- package/src/memory-lifecycle-ledger-utils.ts +51 -5
- package/src/namespaces/principal.test.ts +37 -1
- package/src/namespaces/principal.ts +48 -0
- package/src/namespaces/search.test.ts +79 -5
- package/src/namespaces/search.ts +59 -6
- package/src/namespaces/storage.ts +45 -1
- package/src/offline-sync-exclude-globs.ts +58 -0
- package/src/offline-sync-impression-drain.test.ts +316 -0
- package/src/offline-sync-impression-drain.ts +260 -0
- package/src/offline-sync.test.ts +32 -5
- package/src/offline-sync.ts +1 -14
- package/src/operator-toolkit.ts +1 -1
- package/src/orchestration/batch-deindex-flush.test.ts +146 -0
- package/src/orchestration/consolidation-run.ts +96 -85
- package/src/orchestration/extraction-envelope.ts +94 -0
- package/src/orchestration/extraction-persist-promotion.ts +134 -0
- package/src/orchestration/extraction-persist.ts +325 -260
- package/src/orchestration/extraction-run.test.ts +1 -1
- package/src/orchestration/extraction-run.ts +2 -2
- package/src/orchestration/graph-recall-coordinator.ts +18 -7
- package/src/orchestration/lifecycle-policy-coordinator.ts +63 -48
- package/src/orchestration/maintenance.test.ts +2103 -3
- package/src/orchestration/maintenance.ts +581 -2
- package/src/orchestration/namespace-read-fanout.ts +14 -8
- package/src/orchestration/orchestrator-helpers.ts +8 -1
- package/src/orchestration/orchestrator-init.ts +22 -6
- package/src/orchestration/orchestrator-namespace-scope.ts +71 -0
- package/src/orchestration/persistence-index.ts +64 -9
- package/src/orchestration/qmd-result-resolver.ts +27 -0
- package/src/orchestration/recall-entry.ts +34 -19
- package/src/orchestration/recall-internal.ts +207 -79
- package/src/orchestration/recall-rerank-coordinator.ts +229 -82
- package/src/orchestration/recall-result-formatter.ts +76 -20
- package/src/orchestration/recall-search-pipeline.test.ts +70 -0
- package/src/orchestration/recall-search-pipeline.ts +102 -129
- package/src/orchestration/recall-section-coordinator.ts +315 -85
- package/src/orchestration/semantic-consolidation-coordinator.ts +69 -37
- package/src/orchestration/session-context.ts +21 -0
- package/src/orchestration/workspace-ops.test.ts +348 -0
- package/src/orchestration/workspace-ops.ts +172 -34
- package/src/orchestrator-chunked-hash-durability.test.ts +126 -0
- package/src/orchestrator-connector-dedup.test.ts +1061 -40
- package/src/orchestrator-flush.test.ts +155 -39
- package/src/orchestrator-nonchunked-hash-durability.test.ts +121 -0
- package/src/orchestrator-source-attribution.test.ts +9 -9
- package/src/orchestrator.ts +166 -150
- package/src/prioritized-embed.ts +123 -0
- package/src/proactive-contention.test.ts +80 -0
- package/src/proactive-contention.ts +39 -0
- package/src/procedural/procedure-miner.test.ts +19 -14
- package/src/procedural/procedure-miner.ts +44 -13
- package/src/qmd-client.test.ts +111 -1
- package/src/qmd-status.ts +186 -0
- package/src/qmd.test.ts +22 -0
- package/src/qmd.ts +60 -48
- package/src/recall-budget-config.test.ts +30 -0
- package/src/recall-cold-deadline.test.ts +149 -0
- package/src/recall-concurrency-config.ts +28 -0
- package/src/recall-memory-map.ts +74 -0
- package/src/recall-rerank-coordinator.test.ts +508 -0
- package/src/recall-state.test.ts +825 -1
- package/src/recall-state.ts +406 -7
- package/src/recall-timings-access.test.ts +41 -0
- package/src/recall-timings.ts +16 -0
- package/src/relay/mission-access.test.ts +5 -5
- package/src/rerank.ts +22 -0
- package/src/retrieval-agents.test.ts +79 -0
- package/src/retrieval-agents.ts +71 -19
- package/src/salvage-envelope.ts +28 -0
- package/src/scopes/scope-plan.test.ts +42 -0
- package/src/scopes/scope-plan.ts +45 -0
- package/src/search/port.ts +24 -0
- package/src/secure-store/index.ts +1 -0
- package/src/secure-store/secure-fs.ts +48 -0
- package/src/secure-store/secure-store.test.ts +40 -0
- package/src/semantic-rule-promotion.ts +13 -4
- package/src/storage/content-hash-index.ts +655 -0
- package/src/storage/entity-timeline.ts +473 -0
- package/src/storage/memory-lifecycle-ledger-access.ts +603 -0
- package/src/storage/secure-line-reader.test.ts +197 -0
- package/src/storage/secure-line-reader.ts +181 -3
- package/src/storage-append-header-sniff.test.ts +195 -0
- package/src/storage-behavior-signals-cache.test.ts +237 -0
- package/src/storage-contract/cache-coherence.test.ts +96 -1
- package/src/storage-contract/content-hash.test.ts +47 -2
- package/src/storage-fact-hash-index-defer.test.ts +1200 -0
- package/src/storage-memory-lifecycle-ledger.test.ts +1146 -3
- package/src/storage.ts +942 -903
- package/src/structured-attributes.ts +52 -0
- package/src/temporal-index-batch.ts +112 -0
- package/src/temporal-index.test.ts +193 -0
- package/src/temporal-index.ts +49 -53
- package/src/testing/lifecycle-matrix.test.ts +212 -0
- package/src/testing/lifecycle-matrix.ts +271 -0
- package/src/testing/orchestrator-lite.ts +236 -0
- package/src/testing/subjects/dreams-ledger.test.ts +67 -0
- package/src/testing/subjects/extraction-lifecycle.test.ts +442 -0
- package/src/testing/subjects/lifecycle-ledger.test.ts +93 -0
- package/src/testing/subjects/recall-budget.test.ts +461 -0
- package/src/testing/subjects/serialized-write-chain.test.ts +224 -0
- package/src/trust-score-stage.test.ts +154 -0
- package/src/trust-score-stage.ts +216 -53
- package/src/types-continuity.ts +57 -0
- package/src/types.ts +66 -77
- package/src/utils/path-containment.test.ts +75 -0
- package/src/utils/path-containment.ts +76 -0
- package/src/utils/serialize-mutations.test.ts +122 -0
- package/src/utils/serialize-mutations.ts +68 -3
- package/src/wearables/memory-gen.test.ts +31 -9
- package/src/wearables/memory-gen.ts +95 -55
- package/src/wearables/pipeline.test.ts +10 -7
- package/src/wearables/service.test.ts +6 -8
- package/src/wearables/service.ts +2 -2
- package/src/write-envelope.extension.test.ts +111 -0
- package/src/write-envelope.test.ts +683 -0
- package/src/write-envelope.ts +902 -0
- package/src/write-quarantine.test.ts +193 -0
- package/src/write-quarantine.ts +241 -0
- package/src/write-rate-limiter.test.ts +105 -0
- package/src/write-rate-limiter.ts +135 -0
- package/src/write-sealed-parity.test.ts +204 -0
- package/dist/chunk-2VN4JAX7.js.map +0 -1
- package/dist/chunk-37CTGERM.js.map +0 -1
- package/dist/chunk-7ICYQUOL.js.map +0 -1
- package/dist/chunk-AD6AMF2N.js.map +0 -1
- package/dist/chunk-AHLPZ2SF.js.map +0 -1
- package/dist/chunk-ASKEM4AR.js.map +0 -1
- package/dist/chunk-B3F4CWQR.js.map +0 -1
- package/dist/chunk-BCDKBDSY.js.map +0 -1
- package/dist/chunk-BFPETA3S.js.map +0 -1
- package/dist/chunk-BFVEEB7W.js.map +0 -1
- package/dist/chunk-E5BK6CGV.js +0 -335
- package/dist/chunk-E5BK6CGV.js.map +0 -1
- package/dist/chunk-EEARRVZ2.js.map +0 -1
- package/dist/chunk-F7AU34FC.js.map +0 -1
- package/dist/chunk-FBNWOMCE.js.map +0 -1
- package/dist/chunk-FTYDWKO6.js.map +0 -1
- package/dist/chunk-GMSDQUCR.js.map +0 -1
- package/dist/chunk-GWTFLF2P.js.map +0 -1
- package/dist/chunk-IFJ23BHU.js.map +0 -1
- package/dist/chunk-IKQTFQMI.js.map +0 -1
- package/dist/chunk-J2HSAU72.js.map +0 -1
- package/dist/chunk-JMBVCAR4.js.map +0 -1
- package/dist/chunk-KQDNAXOM.js.map +0 -1
- package/dist/chunk-KSBEEMQF.js.map +0 -1
- package/dist/chunk-KSNVDPDF.js.map +0 -1
- package/dist/chunk-LC7OODWZ.js.map +0 -1
- package/dist/chunk-LTJAMRGI.js.map +0 -1
- package/dist/chunk-ND5M64JJ.js.map +0 -1
- package/dist/chunk-NUWYOKS6.js.map +0 -1
- package/dist/chunk-NWTGD4AN.js.map +0 -1
- package/dist/chunk-OBMR4VIX.js.map +0 -1
- package/dist/chunk-ODPLEWB6.js.map +0 -1
- package/dist/chunk-PHK3HARR.js +0 -32
- package/dist/chunk-PHK3HARR.js.map +0 -1
- package/dist/chunk-RANHFI3P.js +0 -8
- package/dist/chunk-RANHFI3P.js.map +0 -1
- package/dist/chunk-RBQLJSJ7.js.map +0 -1
- package/dist/chunk-RULE4VG5.js.map +0 -1
- package/dist/chunk-SVOZFLIQ.js +0 -16
- package/dist/chunk-SVOZFLIQ.js.map +0 -1
- package/dist/chunk-TXLGHDHS.js.map +0 -1
- package/dist/chunk-UN3CH3CO.js +0 -118
- package/dist/chunk-UN3CH3CO.js.map +0 -1
- package/dist/chunk-VSMRDBFT.js +0 -17
- package/dist/chunk-VSMRDBFT.js.map +0 -1
- package/dist/chunk-WU46YPY6.js.map +0 -1
- package/dist/chunk-X47LWMKA.js.map +0 -1
- package/dist/chunk-X64YLC24.js.map +0 -1
- package/dist/chunk-XVLEGMJI.js.map +0 -1
- package/dist/chunk-YF6FMLBT.js.map +0 -1
- package/dist/resolution-VH4NGENZ.js +0 -12
- /package/dist/{capsule-crypto-CZJSLEFG.js.map → access-citation.js.map} +0 -0
- /package/dist/{contradiction-scan-J57T5QV7.js.map → access-memory-search-fanout.js.map} +0 -0
- /package/dist/{dreams-ledger-6UODQOX6.js.map → access-recall-concurrency.js.map} +0 -0
- /package/dist/{graph-edge-decay-FO7AM4UR.js.map → access-recall-response.js.map} +0 -0
- /package/dist/{resolution-VH4NGENZ.js.map → bounded-jsonl-state.js.map} +0 -0
- /package/dist/{capsule-merge-Z2HUO6IK.js.map → capsule-merge-5KKJQBWQ.js.map} +0 -0
- /package/dist/{chunk-A3GOMRJG.js.map → chunk-23L3IXFD.js.map} +0 -0
- /package/dist/{chunk-MRHFJV44.js.map → chunk-2DQ2XREU.js.map} +0 -0
- /package/dist/{chunk-L2B3EE7B.js.map → chunk-2QMKMVHH.js.map} +0 -0
- /package/dist/{chunk-6CALL5VN.js.map → chunk-2UFO7X4Q.js.map} +0 -0
- /package/dist/{chunk-2MR3MFQB.js.map → chunk-2VEOKUPP.js.map} +0 -0
- /package/dist/{chunk-RHOZ5LLO.js.map → chunk-347WD2LE.js.map} +0 -0
- /package/dist/{chunk-5S7Z5SHA.js.map → chunk-34RC5RT7.js.map} +0 -0
- /package/dist/{chunk-KGWXHWAH.js.map → chunk-3QAH2S33.js.map} +0 -0
- /package/dist/{chunk-ZIDSD6VC.js.map → chunk-3Y224NYC.js.map} +0 -0
- /package/dist/{chunk-VFSP73LP.js.map → chunk-45GF7Y3V.js.map} +0 -0
- /package/dist/{chunk-OEHCMALU.js.map → chunk-5I4M26Q7.js.map} +0 -0
- /package/dist/{chunk-2E6G7FCE.js.map → chunk-5Q7EVV4X.js.map} +0 -0
- /package/dist/{chunk-VZ5OJE4X.js.map → chunk-5VQJBH2G.js.map} +0 -0
- /package/dist/{chunk-PUBTHW5O.js.map → chunk-6DRPPQEJ.js.map} +0 -0
- /package/dist/{chunk-4LEF6XWJ.js.map → chunk-6O22TZSB.js.map} +0 -0
- /package/dist/{chunk-ZQ7UBUZ6.js.map → chunk-6QYST5XN.js.map} +0 -0
- /package/dist/{chunk-W4PGTAS2.js.map → chunk-6WAFYNVT.js.map} +0 -0
- /package/dist/{chunk-R5VAVVHP.js.map → chunk-72A4L5JJ.js.map} +0 -0
- /package/dist/{chunk-3FAMU5TX.js.map → chunk-A6USBZTO.js.map} +0 -0
- /package/dist/{chunk-TXVD7PLT.js.map → chunk-A72SWTWW.js.map} +0 -0
- /package/dist/{chunk-FC77VUX5.js.map → chunk-A7Q43EUZ.js.map} +0 -0
- /package/dist/{chunk-UJBESW6X.js.map → chunk-AE7WMV4V.js.map} +0 -0
- /package/dist/{chunk-OKUEZOGW.js.map → chunk-AITCS2RC.js.map} +0 -0
- /package/dist/{chunk-OC35KQIN.js.map → chunk-AJ7QCSIS.js.map} +0 -0
- /package/dist/{chunk-FNMFJWBY.js.map → chunk-ANEUPF6O.js.map} +0 -0
- /package/dist/{chunk-2A3OXAP2.js.map → chunk-B7RWBZXW.js.map} +0 -0
- /package/dist/{chunk-N3JDIRBW.js.map → chunk-BLAKS3CP.js.map} +0 -0
- /package/dist/{chunk-M7VGG3ID.js.map → chunk-BV5WMSIJ.js.map} +0 -0
- /package/dist/{chunk-2BFLDH5F.js.map → chunk-BXLOS5AJ.js.map} +0 -0
- /package/dist/{chunk-UXKMMNJD.js.map → chunk-C23BYTJV.js.map} +0 -0
- /package/dist/{chunk-JZHHREBL.js.map → chunk-C3AKM55W.js.map} +0 -0
- /package/dist/{chunk-RP7YXA67.js.map → chunk-C5JF6EZ7.js.map} +0 -0
- /package/dist/{chunk-DDGV7NAX.js.map → chunk-CQMEM5CC.js.map} +0 -0
- /package/dist/{chunk-A7B4RMQG.js.map → chunk-CXZQOVIE.js.map} +0 -0
- /package/dist/{chunk-7N2RUBAP.js.map → chunk-DDA47TES.js.map} +0 -0
- /package/dist/{chunk-64SE4X5G.js.map → chunk-DEBHXHJR.js.map} +0 -0
- /package/dist/{chunk-EHK6KZHK.js.map → chunk-DNHVPZOD.js.map} +0 -0
- /package/dist/{chunk-437S3G37.js.map → chunk-DQEMWVMT.js.map} +0 -0
- /package/dist/{chunk-LZZNTPLR.js.map → chunk-DX3XRGLH.js.map} +0 -0
- /package/dist/{chunk-7MW3CVLD.js.map → chunk-ES36HTSC.js.map} +0 -0
- /package/dist/{chunk-TN7THW5U.js.map → chunk-ET356KSB.js.map} +0 -0
- /package/dist/{chunk-YNDLCWXS.js.map → chunk-EZ25VE3G.js.map} +0 -0
- /package/dist/{chunk-7LFXDGLH.js.map → chunk-F7GAIWFA.js.map} +0 -0
- /package/dist/{chunk-IKSELOIV.js.map → chunk-FFPJINJE.js.map} +0 -0
- /package/dist/{chunk-V53GJUSL.js.map → chunk-FUTF72MJ.js.map} +0 -0
- /package/dist/{chunk-5UNWK7KU.js.map → chunk-GFPZS3F5.js.map} +0 -0
- /package/dist/{chunk-G6NKALRN.js.map → chunk-H4WG3UQZ.js.map} +0 -0
- /package/dist/{chunk-HLTAPHZZ.js.map → chunk-HNZKVLAN.js.map} +0 -0
- /package/dist/{chunk-5EZXHU36.js.map → chunk-HYQJQGD3.js.map} +0 -0
- /package/dist/{chunk-TRWPCR6P.js.map → chunk-JECBQBEX.js.map} +0 -0
- /package/dist/{chunk-HG32URJJ.js.map → chunk-JYIFGGEM.js.map} +0 -0
- /package/dist/{chunk-VA4WSW54.js.map → chunk-JYIJNNN3.js.map} +0 -0
- /package/dist/{chunk-L5HJQC36.js.map → chunk-K2TDK7GG.js.map} +0 -0
- /package/dist/{chunk-MKDRQAA3.js.map → chunk-KAVBJSBQ.js.map} +0 -0
- /package/dist/{chunk-4OKLXWDZ.js.map → chunk-KNB7IBBU.js.map} +0 -0
- /package/dist/{chunk-QLULYKWZ.js.map → chunk-KSXJOC4F.js.map} +0 -0
- /package/dist/{chunk-JI5AVXYO.js.map → chunk-LEPOP46W.js.map} +0 -0
- /package/dist/{chunk-O7BBFJLP.js.map → chunk-LTAVSOZW.js.map} +0 -0
- /package/dist/{chunk-2IZAGAJR.js.map → chunk-LULR4IUW.js.map} +0 -0
- /package/dist/{chunk-WMWVO45V.js.map → chunk-MC6XO7T4.js.map} +0 -0
- /package/dist/{chunk-D6RIFG4O.js.map → chunk-MNB5RMAY.js.map} +0 -0
- /package/dist/{chunk-67ZCJLJY.js.map → chunk-MYFQ3ED4.js.map} +0 -0
- /package/dist/{chunk-V3UE5I5F.js.map → chunk-MYVPLJOO.js.map} +0 -0
- /package/dist/{chunk-MTWKDOVO.js.map → chunk-NLE6TSLG.js.map} +0 -0
- /package/dist/{chunk-DBRYEIGY.js.map → chunk-OJP3N7UK.js.map} +0 -0
- /package/dist/{chunk-SMX5267P.js.map → chunk-OZKOPQCM.js.map} +0 -0
- /package/dist/{chunk-JCK7V6SN.js.map → chunk-P65YJEEL.js.map} +0 -0
- /package/dist/{chunk-MTR7BZQN.js.map → chunk-PBGWYM3H.js.map} +0 -0
- /package/dist/{chunk-AXTXN4T6.js.map → chunk-PWFCHOB3.js.map} +0 -0
- /package/dist/{chunk-FDPZVY6D.js.map → chunk-QFN4U7AG.js.map} +0 -0
- /package/dist/{chunk-K2ZLHESQ.js.map → chunk-QIEUU4UK.js.map} +0 -0
- /package/dist/{chunk-3D6AFGQR.js.map → chunk-QVT3E4CE.js.map} +0 -0
- /package/dist/{chunk-SFQ6QNL7.js.map → chunk-R6YJI3HE.js.map} +0 -0
- /package/dist/{chunk-5CJVLG3H.js.map → chunk-RFDD3B7W.js.map} +0 -0
- /package/dist/{chunk-JBPKEARU.js.map → chunk-RKNJBZ55.js.map} +0 -0
- /package/dist/{chunk-UTRJFDP2.js.map → chunk-S2BBISHU.js.map} +0 -0
- /package/dist/{chunk-IALLX7EE.js.map → chunk-S2FSBU4H.js.map} +0 -0
- /package/dist/{chunk-7K5Q6COX.js.map → chunk-TVVEYCNW.js.map} +0 -0
- /package/dist/{chunk-PK5XBDA6.js.map → chunk-U3ZAVRP3.js.map} +0 -0
- /package/dist/{chunk-4KJLYTLS.js.map → chunk-V57NLO5P.js.map} +0 -0
- /package/dist/{chunk-FLVL4L3L.js.map → chunk-VDX2J7OX.js.map} +0 -0
- /package/dist/{chunk-7XGSIUP5.js.map → chunk-W4FBGYGK.js.map} +0 -0
- /package/dist/{chunk-6SXVCD7W.js.map → chunk-W7IGLX4G.js.map} +0 -0
- /package/dist/{chunk-KAWTOYAW.js.map → chunk-WLZYGLJ4.js.map} +0 -0
- /package/dist/{chunk-W5NKDU24.js.map → chunk-WON4APQY.js.map} +0 -0
- /package/dist/{chunk-NKCPUHCI.js.map → chunk-WZFAPHIK.js.map} +0 -0
- /package/dist/{chunk-KQPJLK3X.js.map → chunk-XL24QQ63.js.map} +0 -0
- /package/dist/{chunk-4B4MUS5D.js.map → chunk-YROFNEG5.js.map} +0 -0
- /package/dist/{chunk-VRAD2FGO.js.map → chunk-Z54EM5YZ.js.map} +0 -0
- /package/dist/{first-start-migration-TI67YMLG.js.map → first-start-migration-CSRHCJIQ.js.map} +0 -0
- /package/dist/{forget-CQCLB5LB.js.map → forget-HRPLVB3W.js.map} +0 -0
- /package/dist/{purge-NDWV6WYD.js.map → purge-IRF47ZTX.js.map} +0 -0
- /package/dist/{tier-stats-KV7IWCJR.js.map → tier-stats-QEJSPFV2.js.map} +0 -0
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { mkdtemp, open, rm, symlink, truncate, writeFile } from "node:fs/promises";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import test from "node:test";
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
readMaybeEncryptedLines,
|
|
9
|
+
readMemoryLifecycleEventsFromLines,
|
|
10
|
+
STATE_FILE_MAX_DECRYPT_BYTES,
|
|
11
|
+
} from "./secure-line-reader.js";
|
|
12
|
+
import { MAGIC_BYTES, MAGIC_HEADER_SIZE } from "../secure-store/secure-fs.js";
|
|
13
|
+
import type { MemoryLifecycleEvent } from "../types.js";
|
|
14
|
+
|
|
15
|
+
function event(eventId: string, memoryId: string, timestamp: string): MemoryLifecycleEvent {
|
|
16
|
+
return { eventId, memoryId, eventType: "created", timestamp, actor: "test", ruleVersion: "1" };
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async function* fromArray(rows: string[]): AsyncGenerator<string> {
|
|
20
|
+
for (const row of rows) yield row;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
test("readMemoryLifecycleEventsFromLines keeps only the last `limit` appended rows", async () => {
|
|
24
|
+
const rows = [
|
|
25
|
+
JSON.stringify(event("a", "m1", "2026-01-01T00:00:00.000Z")),
|
|
26
|
+
JSON.stringify(event("b", "m2", "2026-01-02T00:00:00.000Z")),
|
|
27
|
+
JSON.stringify(event("c", "m3", "2026-01-03T00:00:00.000Z")),
|
|
28
|
+
];
|
|
29
|
+
const tail = await readMemoryLifecycleEventsFromLines(fromArray(rows), 2);
|
|
30
|
+
assert.deepEqual(
|
|
31
|
+
tail.map((e) => e.eventId),
|
|
32
|
+
["b", "c"],
|
|
33
|
+
);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test("readMemoryLifecycleEventsFromLines skips malformed/blank rows fail-open and honors limit 0", async () => {
|
|
37
|
+
const rows = [
|
|
38
|
+
" ",
|
|
39
|
+
"{not json",
|
|
40
|
+
JSON.stringify({ eventId: "x", memoryId: "m", eventType: "created", timestamp: "t", actor: 42, ruleVersion: "1" }),
|
|
41
|
+
JSON.stringify(event("keep", "m", "2026-01-01T00:00:00.000Z")),
|
|
42
|
+
];
|
|
43
|
+
assert.deepEqual(await readMemoryLifecycleEventsFromLines(fromArray(rows), 5), [
|
|
44
|
+
event("keep", "m", "2026-01-01T00:00:00.000Z"),
|
|
45
|
+
]);
|
|
46
|
+
assert.deepEqual(await readMemoryLifecycleEventsFromLines(fromArray(rows), 0), []);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test("readMemoryLifecycleEventsFromLines admits any structurally valid eventType (readAll parity, issue #1910)", async () => {
|
|
50
|
+
const unknown = { eventId: "bad", memoryId: "m", eventType: "exploded", timestamp: "2026-01-01T00:00:00.000Z", actor: "test", ruleVersion: "1" };
|
|
51
|
+
const empty = { eventId: "empty", memoryId: "m", eventType: "", timestamp: "2026-01-02T00:00:00.000Z", actor: "test", ruleVersion: "1" };
|
|
52
|
+
const rows = [
|
|
53
|
+
// Unknown/typoed and empty-string eventTypes are structurally valid, so the
|
|
54
|
+
// bounded governance read MUST keep them — it has to match readAll, which
|
|
55
|
+
// admits any string eventType. Only genuinely malformed rows are dropped.
|
|
56
|
+
JSON.stringify(unknown),
|
|
57
|
+
JSON.stringify(empty),
|
|
58
|
+
JSON.stringify(event("keep", "m", "2026-01-03T00:00:00.000Z")),
|
|
59
|
+
];
|
|
60
|
+
const kept = await readMemoryLifecycleEventsFromLines(fromArray(rows), 5);
|
|
61
|
+
assert.deepEqual(
|
|
62
|
+
kept.map((e) => e.eventId).sort(),
|
|
63
|
+
["bad", "empty", "keep"],
|
|
64
|
+
);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
test("readMemoryLifecycleEventsFromLines filters by memoryId when provided", async () => {
|
|
68
|
+
const rows = [
|
|
69
|
+
JSON.stringify(event("a", "target", "2026-01-01T00:00:00.000Z")),
|
|
70
|
+
JSON.stringify(event("b", "other", "2026-01-02T00:00:00.000Z")),
|
|
71
|
+
JSON.stringify(event("c", "target", "2026-01-03T00:00:00.000Z")),
|
|
72
|
+
];
|
|
73
|
+
const tail = await readMemoryLifecycleEventsFromLines(fromArray(rows), 10, "target");
|
|
74
|
+
assert.deepEqual(
|
|
75
|
+
tail.map((e) => e.eventId),
|
|
76
|
+
["a", "c"],
|
|
77
|
+
);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test("readMaybeEncryptedLines streams plaintext lines without the encrypted fallback", async () => {
|
|
81
|
+
const dir = await mkdtemp(path.join(os.tmpdir(), "remnic-slr-plain-"));
|
|
82
|
+
const filePath = path.join(dir, "state.jsonl");
|
|
83
|
+
await writeFile(filePath, "one\ntwo\nthree\n", "utf8");
|
|
84
|
+
try {
|
|
85
|
+
const out: string[] = [];
|
|
86
|
+
for await (const line of readMaybeEncryptedLines(filePath, async () => {
|
|
87
|
+
throw new Error("plaintext must not use the encrypted whole-file reader");
|
|
88
|
+
})) {
|
|
89
|
+
if (line.trim()) out.push(line);
|
|
90
|
+
}
|
|
91
|
+
assert.deepEqual(out, ["one", "two", "three"]);
|
|
92
|
+
} finally {
|
|
93
|
+
await rm(dir, { recursive: true, force: true });
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test("readMaybeEncryptedLines refuses an oversized encrypted file before decrypting", async () => {
|
|
98
|
+
const dir = await mkdtemp(path.join(os.tmpdir(), "remnic-slr-oversize-"));
|
|
99
|
+
const filePath = path.join(dir, "state.jsonl");
|
|
100
|
+
// Write a valid encrypted magic header, then sparsely grow the file past the
|
|
101
|
+
// decrypt ceiling. `truncate` produces a sparse file, so this costs ~no disk.
|
|
102
|
+
const header = Buffer.alloc(MAGIC_HEADER_SIZE);
|
|
103
|
+
MAGIC_BYTES.copy(header, 0);
|
|
104
|
+
const handle = await open(filePath, "w");
|
|
105
|
+
await handle.write(header, 0, header.length, 0);
|
|
106
|
+
await handle.close();
|
|
107
|
+
await truncate(filePath, STATE_FILE_MAX_DECRYPT_BYTES + 1);
|
|
108
|
+
try {
|
|
109
|
+
let decryptCalled = false;
|
|
110
|
+
await assert.rejects(
|
|
111
|
+
async () => {
|
|
112
|
+
for await (const _line of readMaybeEncryptedLines(
|
|
113
|
+
filePath,
|
|
114
|
+
async () => {
|
|
115
|
+
decryptCalled = true;
|
|
116
|
+
return "";
|
|
117
|
+
},
|
|
118
|
+
STATE_FILE_MAX_DECRYPT_BYTES,
|
|
119
|
+
)) {
|
|
120
|
+
// no-op
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
(err: unknown) => {
|
|
124
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
125
|
+
assert.match(message, /whole-file decrypt limit/);
|
|
126
|
+
assert.match(message, /rebuild-memory-lifecycle-ledger|doctor/);
|
|
127
|
+
// The remedy is namespace-aware (#2033): an oversized encrypted ledger
|
|
128
|
+
// under namespaces/<ns>/state must not be pointed at a root-only command.
|
|
129
|
+
assert.match(message, /--namespace/);
|
|
130
|
+
assert.ok(message.includes(filePath));
|
|
131
|
+
return true;
|
|
132
|
+
},
|
|
133
|
+
);
|
|
134
|
+
assert.equal(decryptCalled, false);
|
|
135
|
+
} finally {
|
|
136
|
+
await rm(dir, { recursive: true, force: true });
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
test("readMaybeEncryptedLines does not refuse an oversized encrypted file without a decrypt ceiling (memory-actions parity, issue #1910)", async () => {
|
|
141
|
+
const dir = await mkdtemp(path.join(os.tmpdir(), "remnic-slr-nolimit-"));
|
|
142
|
+
const filePath = path.join(dir, "state.jsonl");
|
|
143
|
+
const header = Buffer.alloc(MAGIC_HEADER_SIZE);
|
|
144
|
+
MAGIC_BYTES.copy(header, 0);
|
|
145
|
+
const handle = await open(filePath, "w");
|
|
146
|
+
await handle.write(header, 0, header.length, 0);
|
|
147
|
+
await handle.close();
|
|
148
|
+
await truncate(filePath, STATE_FILE_MAX_DECRYPT_BYTES + 1);
|
|
149
|
+
try {
|
|
150
|
+
// No maxDecryptBytes argument: the caller (e.g. the memory-actions ledger)
|
|
151
|
+
// has no compaction remedy, so the size guard must NOT fire — decryption is
|
|
152
|
+
// attempted exactly as before the lifecycle-only ceiling was added.
|
|
153
|
+
let decryptCalled = false;
|
|
154
|
+
const lines: string[] = [];
|
|
155
|
+
for await (const line of readMaybeEncryptedLines(filePath, async () => {
|
|
156
|
+
decryptCalled = true;
|
|
157
|
+
return "row-1\nrow-2";
|
|
158
|
+
})) {
|
|
159
|
+
lines.push(line);
|
|
160
|
+
}
|
|
161
|
+
assert.equal(decryptCalled, true, "decrypt runs when no ceiling is supplied");
|
|
162
|
+
assert.deepEqual(lines, ["row-1", "row-2"]);
|
|
163
|
+
} finally {
|
|
164
|
+
await rm(dir, { recursive: true, force: true });
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
test("readMaybeEncryptedLines refuses a symlinked state file before opening it (#2033)", async () => {
|
|
169
|
+
const dir = await mkdtemp(path.join(os.tmpdir(), "remnic-slr-symlink-"));
|
|
170
|
+
try {
|
|
171
|
+
const realTarget = path.join(dir, "real.jsonl");
|
|
172
|
+
await writeFile(realTarget, "one\ntwo\n", "utf8");
|
|
173
|
+
const linkPath = path.join(dir, "link.jsonl");
|
|
174
|
+
await symlink(realTarget, linkPath);
|
|
175
|
+
let decryptCalled = false;
|
|
176
|
+
await assert.rejects(
|
|
177
|
+
async () => {
|
|
178
|
+
for await (const _line of readMaybeEncryptedLines(linkPath, async () => {
|
|
179
|
+
decryptCalled = true;
|
|
180
|
+
return "";
|
|
181
|
+
})) {
|
|
182
|
+
// no-op — the symlink must be refused before any line is yielded.
|
|
183
|
+
}
|
|
184
|
+
},
|
|
185
|
+
(err: unknown) => {
|
|
186
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
187
|
+
assert.match(message, /non-regular state file/);
|
|
188
|
+
assert.match(message, /symlink\/FIFO\/device/);
|
|
189
|
+
assert.ok(message.includes(linkPath));
|
|
190
|
+
return true;
|
|
191
|
+
},
|
|
192
|
+
);
|
|
193
|
+
assert.equal(decryptCalled, false, "never opened or decrypted the symlink target");
|
|
194
|
+
} finally {
|
|
195
|
+
await rm(dir, { recursive: true, force: true });
|
|
196
|
+
}
|
|
197
|
+
});
|
|
@@ -1,23 +1,61 @@
|
|
|
1
1
|
import { createReadStream } from "node:fs";
|
|
2
|
-
import { open } from "node:fs/promises";
|
|
2
|
+
import { lstat, open } from "node:fs/promises";
|
|
3
3
|
import { createInterface } from "node:readline";
|
|
4
4
|
|
|
5
5
|
import { isEncryptedFile, MAGIC_HEADER_SIZE } from "../secure-store/secure-fs.js";
|
|
6
|
-
import type { MemoryActionEvent } from "../types.js";
|
|
6
|
+
import type { MemoryActionEvent, MemoryLifecycleEvent } from "../types.js";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Whole-file decrypt ceiling for encrypted state files. V8's max string length
|
|
10
|
+
* is ~512MB; a decrypted body that approaches it throws an opaque
|
|
11
|
+
* `Invalid string length` on every read (the 691MB lifecycle-ledger incident).
|
|
12
|
+
* We refuse at a size comfortably under the cap and point the operator at the
|
|
13
|
+
* compaction remedy instead of letting the runtime crash. Plaintext streaming
|
|
14
|
+
* is unaffected at any size.
|
|
15
|
+
*/
|
|
16
|
+
export const STATE_FILE_MAX_DECRYPT_BYTES = 400 * 1024 * 1024;
|
|
7
17
|
|
|
8
18
|
/**
|
|
9
19
|
* Stream plaintext files by line while preserving authenticated whole-file
|
|
10
|
-
* reads for encrypted files.
|
|
20
|
+
* reads for encrypted files. `maxDecryptBytes` bounds the whole-file decrypt:
|
|
21
|
+
* when set, an encrypted file at/over it is refused before decryption with an
|
|
22
|
+
* actionable error. The ceiling is OPT-IN so it applies ONLY to callers with a
|
|
23
|
+
* compaction-backed remedy (the lifecycle ledger). Callers with no bounded
|
|
24
|
+
* maintenance path — e.g. the memory-actions ledger — omit it and keep the
|
|
25
|
+
* prior unbounded read (issue #1910).
|
|
11
26
|
*/
|
|
12
27
|
export async function* readMaybeEncryptedLines(
|
|
13
28
|
filePath: string,
|
|
14
29
|
readEncryptedFile: () => Promise<string>,
|
|
30
|
+
maxDecryptBytes?: number,
|
|
15
31
|
): AsyncGenerator<string> {
|
|
32
|
+
// Reject a symlink/FIFO/device BEFORE opening, matching the maintenance ledger
|
|
33
|
+
// probe (probeEncryptedRegularFileHeader, #2033): opening a FIFO blocks until a
|
|
34
|
+
// writer appears and a symlink would redirect the read outside our tree. An
|
|
35
|
+
// ENOENT from the missing regular file propagates so callers keep their
|
|
36
|
+
// absent-file → [] behavior (they catch ENOENT), exactly as a missing open did.
|
|
37
|
+
const entryStat = await lstat(filePath);
|
|
38
|
+
if (!entryStat.isFile()) {
|
|
39
|
+
throw new Error(
|
|
40
|
+
`refusing to read non-regular state file ${filePath} `
|
|
41
|
+
+ `(symlink/FIFO/device); opening it could block or escape containment (#2033)`,
|
|
42
|
+
);
|
|
43
|
+
}
|
|
16
44
|
const file = await open(filePath, "r");
|
|
17
45
|
const header = Buffer.alloc(MAGIC_HEADER_SIZE);
|
|
18
46
|
try {
|
|
19
47
|
const { bytesRead } = await file.read(header, 0, header.length, 0);
|
|
20
48
|
if (isEncryptedFile(header.subarray(0, bytesRead))) {
|
|
49
|
+
if (maxDecryptBytes !== undefined) {
|
|
50
|
+
const { size } = await file.stat();
|
|
51
|
+
if (size >= maxDecryptBytes) {
|
|
52
|
+
throw new Error(
|
|
53
|
+
`encrypted state file ${filePath} is ${size} bytes, at or over the ${maxDecryptBytes}-byte `
|
|
54
|
+
+ `whole-file decrypt limit. Run 'remnic rebuild-memory-lifecycle-ledger --write' (add `
|
|
55
|
+
+ `'--namespace <ns>' when the path is under namespaces/<ns>/state) or 'remnic doctor' to compact it.`,
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
21
59
|
yield* (await readEncryptedFile()).split("\n");
|
|
22
60
|
return;
|
|
23
61
|
}
|
|
@@ -77,3 +115,143 @@ export async function readMemoryActionEventRowsFromLines(
|
|
|
77
115
|
}
|
|
78
116
|
return writeIndex === 0 ? out : [...out.slice(writeIndex), ...out.slice(0, writeIndex)];
|
|
79
117
|
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Validate that a parsed row carries every required lifecycle-event field.
|
|
121
|
+
* Shared by the tail reader and the per-memory reader so both apply the EXACT
|
|
122
|
+
* same fail-open guard as `readAllLifecycleEventsFromLedger` (`eventType` is any
|
|
123
|
+
* string). Governance reads via `readMemoryLifecycleEvents(MAX_SAFE_INTEGER)`
|
|
124
|
+
* must be byte-for-byte identical to `readAllMemoryLifecycleEvents`, so this
|
|
125
|
+
* MUST NOT restrict `eventType` to a handled allow-list — an unknown type is
|
|
126
|
+
* still admitted here and sorted deterministically by `lifecycleEventSortRank`
|
|
127
|
+
* (issue #1910).
|
|
128
|
+
*/
|
|
129
|
+
function isValidLifecycleEventRow(
|
|
130
|
+
parsed: Partial<MemoryLifecycleEvent>,
|
|
131
|
+
): parsed is MemoryLifecycleEvent {
|
|
132
|
+
return (
|
|
133
|
+
typeof parsed.eventId === "string" &&
|
|
134
|
+
typeof parsed.memoryId === "string" &&
|
|
135
|
+
typeof parsed.eventType === "string" &&
|
|
136
|
+
typeof parsed.timestamp === "string" &&
|
|
137
|
+
typeof parsed.actor === "string" &&
|
|
138
|
+
typeof parsed.ruleVersion === "string"
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Bounded min-heap that retains the `limit` rows that rank highest under
|
|
144
|
+
* `compare`, breaking ties by append order (later rows win). Heap use is
|
|
145
|
+
* O(limit); each row costs O(log limit). Used for the per-memory timeline
|
|
146
|
+
* fallback, which must keep the last `limit` events in canonical
|
|
147
|
+
* timestamp/event order — not merely the last `limit` appended rows.
|
|
148
|
+
*/
|
|
149
|
+
class BoundedLifecycleTopN {
|
|
150
|
+
private readonly heap: Array<{ event: MemoryLifecycleEvent; seq: number }> = [];
|
|
151
|
+
|
|
152
|
+
constructor(
|
|
153
|
+
private readonly limit: number,
|
|
154
|
+
private readonly compare: (a: MemoryLifecycleEvent, b: MemoryLifecycleEvent) => number,
|
|
155
|
+
) {}
|
|
156
|
+
|
|
157
|
+
private less(
|
|
158
|
+
a: { event: MemoryLifecycleEvent; seq: number },
|
|
159
|
+
b: { event: MemoryLifecycleEvent; seq: number },
|
|
160
|
+
): boolean {
|
|
161
|
+
const c = this.compare(a.event, b.event);
|
|
162
|
+
return c !== 0 ? c < 0 : a.seq < b.seq;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
add(event: MemoryLifecycleEvent, seq: number): void {
|
|
166
|
+
const item = { event, seq };
|
|
167
|
+
if (this.heap.length < this.limit) {
|
|
168
|
+
this.heap.push(item);
|
|
169
|
+
this.siftUp(this.heap.length - 1);
|
|
170
|
+
} else if (this.heap.length > 0 && this.less(this.heap[0], item)) {
|
|
171
|
+
this.heap[0] = item;
|
|
172
|
+
this.siftDown(0);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/** Kept rows in ascending canonical order (append order breaks ties). */
|
|
177
|
+
drainSorted(): MemoryLifecycleEvent[] {
|
|
178
|
+
return this.heap
|
|
179
|
+
.slice()
|
|
180
|
+
.sort((a, b) => (this.less(a, b) ? -1 : this.less(b, a) ? 1 : 0))
|
|
181
|
+
.map((item) => item.event);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
private siftUp(index: number): void {
|
|
185
|
+
let i = index;
|
|
186
|
+
while (i > 0) {
|
|
187
|
+
const parent = (i - 1) >> 1;
|
|
188
|
+
if (!this.less(this.heap[i], this.heap[parent])) break;
|
|
189
|
+
[this.heap[i], this.heap[parent]] = [this.heap[parent], this.heap[i]];
|
|
190
|
+
i = parent;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
private siftDown(index: number): void {
|
|
195
|
+
let i = index;
|
|
196
|
+
const n = this.heap.length;
|
|
197
|
+
for (;;) {
|
|
198
|
+
const left = 2 * i + 1;
|
|
199
|
+
const right = 2 * i + 2;
|
|
200
|
+
let smallest = i;
|
|
201
|
+
if (left < n && this.less(this.heap[left], this.heap[smallest])) smallest = left;
|
|
202
|
+
if (right < n && this.less(this.heap[right], this.heap[smallest])) smallest = right;
|
|
203
|
+
if (smallest === i) break;
|
|
204
|
+
[this.heap[i], this.heap[smallest]] = [this.heap[smallest], this.heap[i]];
|
|
205
|
+
i = smallest;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Bounded tail read for lifecycle-event rows, mirroring
|
|
212
|
+
* `readMemoryActionEventRowsFromLines`: heap use is O(limit) rather than
|
|
213
|
+
* O(rows), no matter how large the ledger is. When `memoryId` is supplied,
|
|
214
|
+
* only rows for that memory are considered, so the per-memory timeline
|
|
215
|
+
* fallback never materializes the whole ledger either.
|
|
216
|
+
*
|
|
217
|
+
* Without `compare`, the retained rows are the last `limit` *appended* rows
|
|
218
|
+
* (returned oldest→newest of that tail); callers apply
|
|
219
|
+
* `sortMemoryLifecycleEvents` for canonical ordering. With `compare`, the
|
|
220
|
+
* retained rows are the `limit` rows that rank highest under `compare` (ties
|
|
221
|
+
* broken by append order), returned in ascending `compare` order — preserving
|
|
222
|
+
* the prior "filter → canonical sort → last N" semantics without loading the
|
|
223
|
+
* whole file.
|
|
224
|
+
*/
|
|
225
|
+
export async function readMemoryLifecycleEventsFromLines(
|
|
226
|
+
lines: AsyncIterable<string>,
|
|
227
|
+
limit: number,
|
|
228
|
+
memoryId?: string,
|
|
229
|
+
compare?: (a: MemoryLifecycleEvent, b: MemoryLifecycleEvent) => number,
|
|
230
|
+
): Promise<MemoryLifecycleEvent[]> {
|
|
231
|
+
if (limit <= 0) return [];
|
|
232
|
+
const top = compare ? new BoundedLifecycleTopN(limit, compare) : null;
|
|
233
|
+
const ring: MemoryLifecycleEvent[] = [];
|
|
234
|
+
let writeIndex = 0;
|
|
235
|
+
let seq = 0;
|
|
236
|
+
for await (const row of lines) {
|
|
237
|
+
const line = row.trim();
|
|
238
|
+
if (!line) continue;
|
|
239
|
+
try {
|
|
240
|
+
const parsed = JSON.parse(line) as Partial<MemoryLifecycleEvent>;
|
|
241
|
+
if (memoryId !== undefined && parsed.memoryId !== memoryId) continue;
|
|
242
|
+
if (!isValidLifecycleEventRow(parsed)) continue;
|
|
243
|
+
if (top) {
|
|
244
|
+
top.add(parsed, seq++);
|
|
245
|
+
} else if (ring.length < limit) {
|
|
246
|
+
ring.push(parsed);
|
|
247
|
+
} else {
|
|
248
|
+
ring[writeIndex] = parsed;
|
|
249
|
+
writeIndex = (writeIndex + 1) % limit;
|
|
250
|
+
}
|
|
251
|
+
} catch {
|
|
252
|
+
// Ignore malformed rows (fail-open).
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
if (top) return top.drainSorted();
|
|
256
|
+
return writeIndex === 0 ? ring : [...ring.slice(writeIndex), ...ring.slice(0, writeIndex)];
|
|
257
|
+
}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { mkdtemp, mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import test from "node:test";
|
|
6
|
+
|
|
7
|
+
import { StorageManager } from "./storage.js";
|
|
8
|
+
import type { MemoryLifecycleEvent } from "./types.js";
|
|
9
|
+
|
|
10
|
+
// Issue #1909 (Part A): the plaintext secure-append path must classify a file
|
|
11
|
+
// as encrypted by reading ONLY the fixed-size magic header, never the whole
|
|
12
|
+
// file (a lifecycle ledger can grow to hundreds of MB on a large corpus), and
|
|
13
|
+
// it must cache the classification so subsequent appends do NO reclassification
|
|
14
|
+
// read at all.
|
|
15
|
+
|
|
16
|
+
function lifecycleEvent(eventId: string): MemoryLifecycleEvent {
|
|
17
|
+
return {
|
|
18
|
+
eventId,
|
|
19
|
+
memoryId: `m-${eventId}`,
|
|
20
|
+
eventType: "created",
|
|
21
|
+
timestamp: "2026-01-01T00:00:00.000Z",
|
|
22
|
+
actor: "test",
|
|
23
|
+
ruleVersion: "1",
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Spy the private whole-file read used by the encrypted-append branch so a test
|
|
29
|
+
* can assert the plaintext append path NEVER reads the whole file to classify —
|
|
30
|
+
* the #1909 Part A invariant. The classification uses a header-only sniff
|
|
31
|
+
* (open + read MAGIC_HEADER_SIZE bytes), now (size, mtime)-identity-validated so
|
|
32
|
+
* a peer rewrite is detected (round 10); it never falls back to a whole-file read.
|
|
33
|
+
*/
|
|
34
|
+
function instrumentWholeFileReads(storage: StorageManager): { count: () => number } {
|
|
35
|
+
const priv = storage as unknown as {
|
|
36
|
+
readStorageSecureFile: (filePath: string) => Promise<string>;
|
|
37
|
+
};
|
|
38
|
+
const real = priv.readStorageSecureFile.bind(storage);
|
|
39
|
+
let reads = 0;
|
|
40
|
+
priv.readStorageSecureFile = async (filePath: string) => {
|
|
41
|
+
reads += 1;
|
|
42
|
+
return real(filePath);
|
|
43
|
+
};
|
|
44
|
+
return { count: () => reads };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async function withMemoryDir(run: (dir: string) => Promise<void>): Promise<void> {
|
|
48
|
+
const dir = await mkdtemp(path.join(os.tmpdir(), "remnic-header-sniff-"));
|
|
49
|
+
try {
|
|
50
|
+
await run(dir);
|
|
51
|
+
} finally {
|
|
52
|
+
await rm(dir, { recursive: true, force: true });
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
test("plaintext appends classify via a header sniff only — never a whole-file read", async () => {
|
|
57
|
+
await withMemoryDir(async (dir) => {
|
|
58
|
+
const ledgerPath = path.join(dir, "state", "memory-lifecycle-ledger.jsonl");
|
|
59
|
+
await mkdir(path.dirname(ledgerPath), { recursive: true });
|
|
60
|
+
// A large plaintext ledger — a whole-file sniff would scale with this.
|
|
61
|
+
await writeFile(ledgerPath, `${"x".repeat(2_000_000)}\n`, "utf8");
|
|
62
|
+
|
|
63
|
+
const storage = new StorageManager(dir);
|
|
64
|
+
const wholeReads = instrumentWholeFileReads(storage);
|
|
65
|
+
|
|
66
|
+
await storage.appendMemoryLifecycleEvents([lifecycleEvent("a")]);
|
|
67
|
+
await storage.appendMemoryLifecycleEvents([lifecycleEvent("b")]);
|
|
68
|
+
await storage.appendMemoryLifecycleEvents([lifecycleEvent("c")]);
|
|
69
|
+
assert.equal(
|
|
70
|
+
wholeReads.count(),
|
|
71
|
+
0,
|
|
72
|
+
"plaintext appends never read the whole file to classify (header sniff only)",
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
// The appends actually landed on top of the pre-existing large body.
|
|
76
|
+
const body = await readFile(ledgerPath, "utf8");
|
|
77
|
+
const rows = body.split("\n").filter((line) => line.startsWith("{"));
|
|
78
|
+
assert.equal(rows.length, 3, "all three appended events are present");
|
|
79
|
+
assert.ok(body.startsWith("x".repeat(100)), "the original plaintext body is preserved");
|
|
80
|
+
|
|
81
|
+
// White-box: the identity-validated cache entry records plaintext.
|
|
82
|
+
const cacheOwner = storage as unknown as {
|
|
83
|
+
secureFileEncryptionSniffCache: Map<
|
|
84
|
+
string,
|
|
85
|
+
{ identity: { size: number; mtimeMs: number }; encrypted: boolean }
|
|
86
|
+
>;
|
|
87
|
+
};
|
|
88
|
+
assert.equal(
|
|
89
|
+
cacheOwner.secureFileEncryptionSniffCache.get(ledgerPath)?.encrypted,
|
|
90
|
+
false,
|
|
91
|
+
"the ledger is classified as plaintext",
|
|
92
|
+
);
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
test("isEncryptedFileHeader classifies large files correctly from the header alone", async () => {
|
|
96
|
+
await withMemoryDir(async (dir) => {
|
|
97
|
+
const storage = new StorageManager(dir);
|
|
98
|
+
const priv = storage as unknown as {
|
|
99
|
+
isEncryptedFileHeader: (filePath: string) => Promise<boolean>;
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
// Large plaintext file → not encrypted, regardless of its size.
|
|
103
|
+
const plainPath = path.join(dir, "big-plain.jsonl");
|
|
104
|
+
await writeFile(plainPath, `${"y".repeat(1_500_000)}\n`, "utf8");
|
|
105
|
+
assert.equal(await priv.isEncryptedFileHeader.call(storage, plainPath), false);
|
|
106
|
+
|
|
107
|
+
// Encrypted file produced by the secure write path → encrypted, detected by
|
|
108
|
+
// the 12-byte magic header without decrypting the whole body.
|
|
109
|
+
const encDir = await mkdtemp(path.join(os.tmpdir(), "remnic-header-enc-"));
|
|
110
|
+
try {
|
|
111
|
+
const encStorage = new StorageManager(encDir);
|
|
112
|
+
encStorage.setSecureStoreKey(Buffer.alloc(32, 7));
|
|
113
|
+
await encStorage.appendMemoryLifecycleEvents([lifecycleEvent("e")]);
|
|
114
|
+
const encLedger = path.join(encDir, "state", "memory-lifecycle-ledger.jsonl");
|
|
115
|
+
const encPriv = encStorage as unknown as {
|
|
116
|
+
isEncryptedFileHeader: (filePath: string) => Promise<boolean>;
|
|
117
|
+
};
|
|
118
|
+
assert.equal(await encPriv.isEncryptedFileHeader.call(encStorage, encLedger), true);
|
|
119
|
+
} finally {
|
|
120
|
+
await rm(encDir, { recursive: true, force: true });
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Missing file → not encrypted (falls through to appendFile).
|
|
124
|
+
assert.equal(
|
|
125
|
+
await priv.isEncryptedFileHeader.call(storage, path.join(dir, "does-not-exist.jsonl")),
|
|
126
|
+
false,
|
|
127
|
+
);
|
|
128
|
+
// Short file (< MAGIC_HEADER_SIZE) → not encrypted.
|
|
129
|
+
const shortPath = path.join(dir, "short.jsonl");
|
|
130
|
+
await writeFile(shortPath, "hi", "utf8");
|
|
131
|
+
assert.equal(await priv.isEncryptedFileHeader.call(storage, shortPath), false);
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
test("encrypted files still round-trip through the append path unchanged", async () => {
|
|
136
|
+
await withMemoryDir(async (dir) => {
|
|
137
|
+
const storage = new StorageManager(dir);
|
|
138
|
+
storage.setSecureStoreKey(Buffer.alloc(32, 9));
|
|
139
|
+
|
|
140
|
+
await storage.appendMemoryLifecycleEvents([lifecycleEvent("one")]);
|
|
141
|
+
await storage.appendMemoryLifecycleEvents([lifecycleEvent("two")]);
|
|
142
|
+
|
|
143
|
+
// The decrypt-append-rewrite branch keeps the ledger readable + intact.
|
|
144
|
+
const events = await storage.readAllMemoryLifecycleEvents();
|
|
145
|
+
const ids = events.map((e) => e.eventId).sort();
|
|
146
|
+
assert.deepEqual(ids, ["one", "two"]);
|
|
147
|
+
|
|
148
|
+
// On disk the file is genuinely encrypted (header magic present).
|
|
149
|
+
const ledgerPath = path.join(dir, "state", "memory-lifecycle-ledger.jsonl");
|
|
150
|
+
const raw = await readFile(ledgerPath);
|
|
151
|
+
assert.ok(raw.length > 0);
|
|
152
|
+
assert.notEqual(raw.toString("utf8").includes("\"eventId\":\"one\""), true, "body is not plaintext");
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
test("append to a missing path creates the file via the plaintext append path", async () => {
|
|
157
|
+
await withMemoryDir(async (dir) => {
|
|
158
|
+
const storage = new StorageManager(dir);
|
|
159
|
+
const wholeReads = instrumentWholeFileReads(storage);
|
|
160
|
+
// Fresh (ENOENT) ledger: the header sniff stats → ENOENT → not-encrypted, and
|
|
161
|
+
// the append creates the file — never a whole-file read.
|
|
162
|
+
const n = await storage.appendMemoryLifecycleEvents([lifecycleEvent("fresh")]);
|
|
163
|
+
assert.equal(n, 1);
|
|
164
|
+
assert.equal(wholeReads.count(), 0, "creating the ledger does no whole-file read");
|
|
165
|
+
|
|
166
|
+
const ledgerPath = path.join(dir, "state", "memory-lifecycle-ledger.jsonl");
|
|
167
|
+
const body = await readFile(ledgerPath, "utf8");
|
|
168
|
+
assert.ok(body.includes("\"eventId\":\"fresh\""));
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
test("#1909 round 10: a peer encrypting the file flips the sniff classification (no raw append into ciphertext)", async () => {
|
|
173
|
+
// A long-lived manager caches plaintext for an append target; a SECOND manager
|
|
174
|
+
// then encrypts that file (whole-file rewrite). The first manager's next append
|
|
175
|
+
// must detect the flip via (size, mtime) identity re-validation and NOT append
|
|
176
|
+
// raw bytes into the encrypted body (data corruption).
|
|
177
|
+
await withMemoryDir(async (dir) => {
|
|
178
|
+
const a = new StorageManager(dir); // no key → plaintext writer
|
|
179
|
+
await a.appendMemoryLifecycleEvents([lifecycleEvent("a1")]); // creates the ledger
|
|
180
|
+
await a.appendMemoryLifecycleEvents([lifecycleEvent("a2")]); // now A caches plaintext=false
|
|
181
|
+
|
|
182
|
+
// Peer B encrypts the whole file by appending under a key.
|
|
183
|
+
const b = new StorageManager(dir);
|
|
184
|
+
b.setSecureStoreKey(Buffer.alloc(32, 7));
|
|
185
|
+
await b.appendMemoryLifecycleEvents([lifecycleEvent("b1")]); // whole-file rewrite → encrypted
|
|
186
|
+
|
|
187
|
+
// A's next append must detect the flip and refuse (A has no key) rather than
|
|
188
|
+
// corrupt the ciphertext with a raw plaintext append.
|
|
189
|
+
await assert.rejects(() => a.appendMemoryLifecycleEvents([lifecycleEvent("a3")]));
|
|
190
|
+
|
|
191
|
+
// The encrypted ledger is intact and fully decryptable by B — a3 never landed.
|
|
192
|
+
const ids = (await b.readAllMemoryLifecycleEvents()).map((e) => e.eventId).sort();
|
|
193
|
+
assert.deepEqual(ids, ["a1", "a2", "b1"]);
|
|
194
|
+
});
|
|
195
|
+
});
|