@remnic/core 10.0.3 → 10.0.4
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.js +37 -37
- package/dist/access-authorization-probe.js +38 -38
- package/dist/access-boundary.js +37 -37
- package/dist/access-cli.js +100 -98
- package/dist/access-cli.js.map +1 -1
- package/dist/access-extraction-force-flush.js +37 -37
- package/dist/access-http-briefing.js +38 -38
- package/dist/access-http-query.js +39 -39
- package/dist/access-http.js +56 -56
- package/dist/access-identity-continuity-surface.js +37 -37
- package/dist/access-lcm-surface.js +37 -37
- package/dist/access-mcp.js +47 -47
- package/dist/access-namespace-preflight.js +37 -37
- package/dist/access-observe-write-category.js +2 -2
- package/dist/access-observe-write-surface.js +37 -37
- package/dist/access-offline-manifest.js +4 -4
- package/dist/access-operations-batch.js +38 -38
- package/dist/access-operations.js +45 -45
- package/dist/access-recall-concurrency.js +37 -37
- package/dist/access-recall-diagnostics.js +2 -2
- package/dist/access-recall-response.js +37 -37
- package/dist/access-recall-surface.js +37 -37
- package/dist/access-service.js +37 -37
- package/dist/access-surface-catalog.js +39 -39
- package/dist/active-recall.js +17 -16
- 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-PAWBFQWL.js → auto-sync-FN7E3OTO.js} +4 -4
- package/dist/briefing.js +16 -16
- package/dist/buffer.js +7 -7
- package/dist/causal-consolidation.js +17 -17
- package/dist/{chunk-G3T6YMBO.js → chunk-2KB7TTAP.js} +4 -4
- package/dist/{chunk-345MEMRU.js → chunk-3OUGSOJP.js} +2 -2
- package/dist/{chunk-CFCEJNOE.js → chunk-3QAIX7TV.js} +2 -2
- package/dist/{chunk-LMMC55WM.js → chunk-436IRJUD.js} +4 -4
- package/dist/{chunk-G2STTRYB.js → chunk-4D2YH47Z.js} +2 -2
- package/dist/{chunk-RKSGUMVL.js → chunk-4DXVWBDB.js} +2 -2
- package/dist/{chunk-CGOKNQDK.js → chunk-4NFRYOHT.js} +2 -2
- package/dist/{chunk-CHI37EHP.js → chunk-5QHTQMRL.js} +5 -5
- package/dist/{chunk-RFIJRZ4K.js → chunk-5XXTZ4Y7.js} +2 -2
- package/dist/{chunk-YQSWNFZE.js → chunk-5YDWSWBU.js} +2 -2
- package/dist/{chunk-TNVZCLJU.js → chunk-6AH2DDHI.js} +2 -2
- package/dist/{chunk-5GINB5LC.js → chunk-6MKLMEBE.js} +1 -1
- package/dist/{chunk-5GINB5LC.js.map → chunk-6MKLMEBE.js.map} +1 -1
- package/dist/{chunk-JDOBIFBK.js → chunk-7CXL25AA.js} +2 -2
- package/dist/{chunk-YLBE6PLR.js → chunk-7IGMKQWK.js} +2 -2
- package/dist/{chunk-NGOZH7R5.js → chunk-7OOW72QA.js} +21 -21
- package/dist/{chunk-2QZFZNXO.js → chunk-ABDTBGI2.js} +2 -2
- package/dist/{chunk-6JIHC5AS.js → chunk-ABET5LA5.js} +59 -48
- package/dist/chunk-ABET5LA5.js.map +1 -0
- package/dist/{chunk-IB63OVUB.js → chunk-AJ6ORARY.js} +6 -6
- package/dist/{chunk-32HO62I4.js → chunk-BDHWKFGM.js} +2 -2
- package/dist/{chunk-AW4QUFQF.js → chunk-BTNNL7ZW.js} +3 -2
- package/dist/{chunk-AW4QUFQF.js.map → chunk-BTNNL7ZW.js.map} +1 -1
- package/dist/{chunk-MY7B3HYC.js → chunk-CLCMDKTB.js} +2 -2
- package/dist/{chunk-OZVHLBH6.js → chunk-CQQTEDHS.js} +2 -2
- package/dist/{chunk-7KICGR5W.js → chunk-CV5AMCPX.js} +2 -2
- package/dist/{chunk-BTOHGPHC.js → chunk-CXG2T7BJ.js} +3 -3
- package/dist/{chunk-X6X4UASX.js → chunk-DBA2W6VV.js} +230 -81
- package/dist/chunk-DBA2W6VV.js.map +1 -0
- package/dist/{chunk-E7FN5N5E.js → chunk-DQJCQHII.js} +2 -2
- package/dist/{chunk-OWHPA4JS.js → chunk-DYRXRQHV.js} +2 -2
- package/dist/{chunk-IIFFC3RL.js → chunk-EDR6CVWZ.js} +2 -2
- package/dist/{chunk-2JRB3Z5N.js → chunk-EP5WXE2G.js} +3 -3
- package/dist/chunk-EWMRFW3F.js +38 -0
- package/dist/chunk-EWMRFW3F.js.map +1 -0
- package/dist/chunk-FMIVV5SV.js +80 -0
- package/dist/chunk-FMIVV5SV.js.map +1 -0
- package/dist/{chunk-2EZB2DL5.js → chunk-GE6HETIF.js} +3 -3
- package/dist/{chunk-HP4CU2ZA.js → chunk-GLS3A4SQ.js} +2 -2
- package/dist/{chunk-I6WFBNA4.js → chunk-H2XCCUOT.js} +3 -3
- package/dist/{chunk-IXYIW3MZ.js → chunk-H6FA36LC.js} +2 -2
- package/dist/{chunk-NLDMTXWV.js → chunk-HSZBALHV.js} +2 -2
- package/dist/{chunk-XG4XXI66.js → chunk-I25RQA65.js} +2 -2
- package/dist/{chunk-PNFWIB4X.js → chunk-IHIP7TJ3.js} +2 -2
- package/dist/{chunk-4DDIWRT7.js → chunk-IINXHPCV.js} +2 -2
- package/dist/{chunk-EJJMDLVB.js → chunk-IJ4FEGZR.js} +2 -2
- package/dist/{chunk-IHCYCE6D.js → chunk-IRYKRBEX.js} +4 -4
- package/dist/{chunk-MYYHACOW.js → chunk-IVGJXS5F.js} +2 -2
- package/dist/{chunk-Q6GI7HEC.js → chunk-IXF7TRMQ.js} +2 -2
- package/dist/{chunk-O6AYMAPJ.js → chunk-IY7ZISXV.js} +2 -2
- package/dist/{chunk-5FLFPTDK.js → chunk-JA72X46J.js} +2 -2
- package/dist/{chunk-NLYOOGZ7.js → chunk-KE5PWLI6.js} +3 -3
- package/dist/{chunk-6AA7XFMO.js → chunk-LIGP5M2C.js} +2 -2
- package/dist/{chunk-KW77LGDX.js → chunk-MEMH5NKJ.js} +2 -2
- package/dist/{chunk-KRTIHXWC.js → chunk-MEP3POVW.js} +2 -2
- package/dist/{chunk-35A7GCHG.js → chunk-MYBCB4AT.js} +2 -2
- package/dist/{chunk-QPWUG2S3.js → chunk-NSQ3GUVO.js} +2 -2
- package/dist/{chunk-NXVD46HT.js → chunk-O57VUN4U.js} +4 -4
- package/dist/{chunk-L6DJJEFW.js → chunk-OAFRMLL4.js} +2 -2
- package/dist/{chunk-PCO3P7WJ.js → chunk-OGVJXIHX.js} +2 -2
- package/dist/{chunk-JSUA4YFE.js → chunk-ORAL4HJC.js} +2 -2
- package/dist/{chunk-RXT6UB25.js → chunk-P22M3DX3.js} +2 -2
- package/dist/{chunk-I2KEH2LS.js → chunk-PTFL3INS.js} +2 -2
- package/dist/{chunk-5QXMESS7.js → chunk-QDKABMUE.js} +2 -2
- package/dist/{chunk-34G4U6HS.js → chunk-QRZMJ2WN.js} +2 -2
- package/dist/{chunk-GBA4N2SG.js → chunk-QVBB2CLG.js} +13 -13
- package/dist/{chunk-6GU3TNJJ.js → chunk-QWNBQ2T5.js} +244 -47
- package/dist/chunk-QWNBQ2T5.js.map +1 -0
- package/dist/{chunk-GUD6FYQG.js → chunk-R4O2G6ML.js} +2 -2
- package/dist/{chunk-CSLK7BN6.js → chunk-R6EOFKZG.js} +2 -2
- package/dist/{chunk-Y2ADLCB3.js → chunk-RGPPNNDJ.js} +8 -8
- package/dist/{chunk-BLHPADUX.js → chunk-RH6MQ5NG.js} +6 -11
- package/dist/chunk-RH6MQ5NG.js.map +1 -0
- package/dist/{chunk-2NV7MMMM.js → chunk-RZCRXCLI.js} +2 -2
- package/dist/{chunk-WI37MWRZ.js → chunk-SNGNGH53.js} +2 -2
- package/dist/{chunk-R4NGLTUS.js → chunk-TDMNADPI.js} +2 -2
- package/dist/{chunk-UVVSOSHL.js → chunk-TSEUVMJL.js} +44 -44
- package/dist/{chunk-O24EWFPQ.js → chunk-TYOBAFCY.js} +3 -3
- package/dist/{chunk-LI76ESJL.js → chunk-TZQOCQXB.js} +2 -2
- package/dist/{chunk-WHTMVTU4.js → chunk-U3LVARGV.js} +2 -2
- package/dist/{chunk-AUWMKDKL.js → chunk-UCCB7RKF.js} +2 -2
- package/dist/{chunk-OV7EYM6M.js → chunk-VWCFR4NR.js} +9 -9
- package/dist/{chunk-DQTCGVFK.js → chunk-WHCD4Z3D.js} +2 -2
- package/dist/{chunk-ZS7RK6H6.js → chunk-WLC6XA6C.js} +4 -4
- package/dist/{chunk-QCP75JGO.js → chunk-WQUGCOKF.js} +5 -5
- package/dist/{chunk-KATEWTQJ.js → chunk-X7J56GPG.js} +2 -2
- package/dist/{chunk-ME2LMFHF.js → chunk-XTWF5XR6.js} +51 -51
- package/dist/{chunk-22DYJLFN.js → chunk-XVC7JU2N.js} +39 -18
- package/dist/chunk-XVC7JU2N.js.map +1 -0
- package/dist/{chunk-HO7IQP7G.js → chunk-Y7CXM7UM.js} +3 -3
- package/dist/{chunk-44PBRUXB.js → chunk-YMXCJ7SE.js} +2 -2
- package/dist/{chunk-AXFWMRKI.js → chunk-YQJCWR4R.js} +2 -2
- package/dist/{chunk-2DHJTJIY.js → chunk-Z2EVKNZR.js} +2 -2
- package/dist/{chunk-RKDSELA3.js → chunk-Z63GU5NN.js} +1 -1
- package/dist/chunk-Z63GU5NN.js.map +1 -0
- package/dist/{chunk-4NRZ7BRS.js → chunk-ZOGZMAGI.js} +2 -2
- package/dist/cli.js +78 -77
- package/dist/coding/export-okf-codegraph.js +16 -16
- package/dist/compounding/engine.js +16 -16
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +17 -16
- package/dist/connectors/codex-materialize-runner.js +16 -16
- package/dist/connectors/index.js +16 -16
- package/dist/convergence-refresh.js +18 -18
- package/dist/conversation-index/backend.js +2 -2
- package/dist/corpus-read-cancellation.d.ts +7 -0
- package/dist/corpus-read-cancellation.d.ts.map +1 -1
- package/dist/corpus-read-cancellation.js +1 -1
- package/dist/corpus-watermark.js +18 -18
- package/dist/directory-sidecars.js +2 -2
- package/dist/entity-retrieval.js +16 -16
- package/dist/external-wiki-access.js +38 -38
- package/dist/extraction.js +21 -21
- package/dist/harmonic-retrieval.js +16 -16
- package/dist/index.js +102 -100
- package/dist/location/index.js +19 -19
- package/dist/maintenance/archive-observations.js +2 -2
- package/dist/maintenance/atomic-file.d.ts +1 -1
- package/dist/maintenance/atomic-file.d.ts.map +1 -1
- package/dist/maintenance/atomic-file.js +1 -1
- package/dist/maintenance/memory-governance.js +16 -16
- package/dist/maintenance/migrate-observations.js +3 -3
- package/dist/maintenance/observation-ledger-utils.js +2 -2
- package/dist/maintenance/projection-rebuild-abort.d.ts +17 -0
- package/dist/maintenance/projection-rebuild-abort.d.ts.map +1 -0
- package/dist/maintenance/projection-timeline.d.ts +19 -0
- package/dist/maintenance/projection-timeline.d.ts.map +1 -0
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +16 -16
- package/dist/maintenance/rebuild-memory-projection-runner.d.ts +8 -0
- package/dist/maintenance/rebuild-memory-projection-runner.d.ts.map +1 -0
- package/dist/maintenance/rebuild-memory-projection-runner.js +113 -0
- package/dist/maintenance/rebuild-memory-projection-runner.js.map +1 -0
- package/dist/maintenance/rebuild-memory-projection.d.ts +8 -0
- package/dist/maintenance/rebuild-memory-projection.d.ts.map +1 -1
- package/dist/maintenance/rebuild-memory-projection.js +17 -17
- package/dist/maintenance/rebuild-observations.js +3 -3
- package/dist/memory-browse-operations.js +38 -38
- package/dist/memory-browse.js +7 -7
- package/dist/namespaces/migrate.js +17 -17
- package/dist/namespaces/search.js +8 -8
- package/dist/namespaces/storage.js +16 -16
- package/dist/offline-sync-impression-drain.js +3 -3
- package/dist/operator-doctor-corpus.js +19 -19
- package/dist/operator-toolkit.js +26 -25
- package/dist/orchestration/full-corpus-heap-guard.d.ts +25 -0
- package/dist/orchestration/full-corpus-heap-guard.d.ts.map +1 -0
- package/dist/orchestration/maintenance.d.ts.map +1 -1
- package/dist/orchestration/maintenance.js +27 -25
- package/dist/orchestration/projection-rebuild-schedule.d.ts +40 -0
- package/dist/orchestration/projection-rebuild-schedule.d.ts.map +1 -1
- package/dist/orchestrator.js +100 -98
- package/dist/projection-rebuild-config.d.ts +52 -0
- package/dist/projection-rebuild-config.d.ts.map +1 -0
- package/dist/projection-rebuild-config.js +14 -0
- package/dist/projection-rebuild-config.js.map +1 -0
- package/dist/qmd.js +4 -4
- package/dist/recall-explain-renderer.js +3 -3
- package/dist/recall-navigate-access.js +2 -2
- package/dist/recall-navigate-disclosure.js +2 -2
- package/dist/recall-navigate.js +2 -2
- package/dist/recall-navigation-operations.js +38 -38
- package/dist/recall-navigation.js +37 -37
- package/dist/recall-state-view-admission.js +7 -7
- package/dist/recall-state.js +2 -2
- package/dist/recall-why-service.js +7 -7
- package/dist/recall-xray-cli.js +4 -4
- package/dist/recall-xray-renderer.js +3 -3
- package/dist/recall-xray.js +2 -2
- package/dist/relevance.js +2 -2
- package/dist/resume-bundles.js +18 -17
- package/dist/search/factory.js +7 -7
- package/dist/search/index.js +11 -11
- package/dist/semantic-consolidation.js +17 -17
- package/dist/semantic-rule-promotion.js +16 -16
- package/dist/semantic-rule-verifier.js +16 -16
- package/dist/source-agent-qualifier.js +20 -20
- package/dist/standup/build.js +16 -16
- package/dist/storage/memory-lifecycle-ledger-access.d.ts +5 -3
- package/dist/storage/memory-lifecycle-ledger-access.d.ts.map +1 -1
- package/dist/storage/memory-read-store.d.ts +2 -2
- package/dist/storage/memory-read-store.d.ts.map +1 -1
- package/dist/storage.d.ts +4 -2
- package/dist/storage.d.ts.map +1 -1
- package/dist/storage.js +15 -15
- package/dist/support-passport/index.js +40 -40
- package/dist/transfer/export-okf.js +17 -17
- package/dist/types.d.ts +2 -17
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +1 -1
- package/dist/verified-recall.js +16 -16
- package/dist/who-knows-cli.js +17 -17
- package/dist/who-knows.js +16 -16
- package/package.json +2 -2
- package/src/config.test.ts +9 -0
- package/src/config.ts +2 -5
- package/src/corpus-read-cancellation.ts +7 -0
- package/src/maintenance/atomic-file.ts +2 -0
- package/src/maintenance/projection-rebuild-abort.ts +40 -0
- package/src/maintenance/projection-timeline.ts +209 -0
- package/src/maintenance/rebuild-memory-projection-runner.ts +94 -0
- package/src/maintenance/rebuild-memory-projection.memory.test.ts +159 -0
- package/src/maintenance/rebuild-memory-projection.retain-raw.test.ts +202 -0
- package/src/maintenance/rebuild-memory-projection.ts +63 -91
- package/src/orchestration/full-corpus-heap-guard.ts +38 -0
- package/src/orchestration/maintenance.test.ts +2 -2
- package/src/orchestration/maintenance.ts +2 -0
- package/src/orchestration/projection-rebuild-schedule.test.ts +498 -0
- package/src/orchestration/projection-rebuild-schedule.ts +280 -9
- package/src/projection-rebuild-config.ts +82 -0
- package/src/storage/memory-lifecycle-ledger-access.ts +61 -33
- package/src/storage/memory-read-store.ts +10 -4
- package/src/storage.ts +45 -33
- package/src/types.ts +2 -18
- package/dist/chunk-22DYJLFN.js.map +0 -1
- package/dist/chunk-6GU3TNJJ.js.map +0 -1
- package/dist/chunk-6JIHC5AS.js.map +0 -1
- package/dist/chunk-BLHPADUX.js.map +0 -1
- package/dist/chunk-RKDSELA3.js.map +0 -1
- package/dist/chunk-X6X4UASX.js.map +0 -1
- /package/dist/{auto-sync-PAWBFQWL.js.map → auto-sync-FN7E3OTO.js.map} +0 -0
- /package/dist/{chunk-G3T6YMBO.js.map → chunk-2KB7TTAP.js.map} +0 -0
- /package/dist/{chunk-345MEMRU.js.map → chunk-3OUGSOJP.js.map} +0 -0
- /package/dist/{chunk-CFCEJNOE.js.map → chunk-3QAIX7TV.js.map} +0 -0
- /package/dist/{chunk-LMMC55WM.js.map → chunk-436IRJUD.js.map} +0 -0
- /package/dist/{chunk-G2STTRYB.js.map → chunk-4D2YH47Z.js.map} +0 -0
- /package/dist/{chunk-RKSGUMVL.js.map → chunk-4DXVWBDB.js.map} +0 -0
- /package/dist/{chunk-CGOKNQDK.js.map → chunk-4NFRYOHT.js.map} +0 -0
- /package/dist/{chunk-CHI37EHP.js.map → chunk-5QHTQMRL.js.map} +0 -0
- /package/dist/{chunk-RFIJRZ4K.js.map → chunk-5XXTZ4Y7.js.map} +0 -0
- /package/dist/{chunk-YQSWNFZE.js.map → chunk-5YDWSWBU.js.map} +0 -0
- /package/dist/{chunk-TNVZCLJU.js.map → chunk-6AH2DDHI.js.map} +0 -0
- /package/dist/{chunk-JDOBIFBK.js.map → chunk-7CXL25AA.js.map} +0 -0
- /package/dist/{chunk-YLBE6PLR.js.map → chunk-7IGMKQWK.js.map} +0 -0
- /package/dist/{chunk-NGOZH7R5.js.map → chunk-7OOW72QA.js.map} +0 -0
- /package/dist/{chunk-2QZFZNXO.js.map → chunk-ABDTBGI2.js.map} +0 -0
- /package/dist/{chunk-IB63OVUB.js.map → chunk-AJ6ORARY.js.map} +0 -0
- /package/dist/{chunk-32HO62I4.js.map → chunk-BDHWKFGM.js.map} +0 -0
- /package/dist/{chunk-MY7B3HYC.js.map → chunk-CLCMDKTB.js.map} +0 -0
- /package/dist/{chunk-OZVHLBH6.js.map → chunk-CQQTEDHS.js.map} +0 -0
- /package/dist/{chunk-7KICGR5W.js.map → chunk-CV5AMCPX.js.map} +0 -0
- /package/dist/{chunk-BTOHGPHC.js.map → chunk-CXG2T7BJ.js.map} +0 -0
- /package/dist/{chunk-E7FN5N5E.js.map → chunk-DQJCQHII.js.map} +0 -0
- /package/dist/{chunk-OWHPA4JS.js.map → chunk-DYRXRQHV.js.map} +0 -0
- /package/dist/{chunk-IIFFC3RL.js.map → chunk-EDR6CVWZ.js.map} +0 -0
- /package/dist/{chunk-2JRB3Z5N.js.map → chunk-EP5WXE2G.js.map} +0 -0
- /package/dist/{chunk-2EZB2DL5.js.map → chunk-GE6HETIF.js.map} +0 -0
- /package/dist/{chunk-HP4CU2ZA.js.map → chunk-GLS3A4SQ.js.map} +0 -0
- /package/dist/{chunk-I6WFBNA4.js.map → chunk-H2XCCUOT.js.map} +0 -0
- /package/dist/{chunk-IXYIW3MZ.js.map → chunk-H6FA36LC.js.map} +0 -0
- /package/dist/{chunk-NLDMTXWV.js.map → chunk-HSZBALHV.js.map} +0 -0
- /package/dist/{chunk-XG4XXI66.js.map → chunk-I25RQA65.js.map} +0 -0
- /package/dist/{chunk-PNFWIB4X.js.map → chunk-IHIP7TJ3.js.map} +0 -0
- /package/dist/{chunk-4DDIWRT7.js.map → chunk-IINXHPCV.js.map} +0 -0
- /package/dist/{chunk-EJJMDLVB.js.map → chunk-IJ4FEGZR.js.map} +0 -0
- /package/dist/{chunk-IHCYCE6D.js.map → chunk-IRYKRBEX.js.map} +0 -0
- /package/dist/{chunk-MYYHACOW.js.map → chunk-IVGJXS5F.js.map} +0 -0
- /package/dist/{chunk-Q6GI7HEC.js.map → chunk-IXF7TRMQ.js.map} +0 -0
- /package/dist/{chunk-O6AYMAPJ.js.map → chunk-IY7ZISXV.js.map} +0 -0
- /package/dist/{chunk-5FLFPTDK.js.map → chunk-JA72X46J.js.map} +0 -0
- /package/dist/{chunk-NLYOOGZ7.js.map → chunk-KE5PWLI6.js.map} +0 -0
- /package/dist/{chunk-6AA7XFMO.js.map → chunk-LIGP5M2C.js.map} +0 -0
- /package/dist/{chunk-KW77LGDX.js.map → chunk-MEMH5NKJ.js.map} +0 -0
- /package/dist/{chunk-KRTIHXWC.js.map → chunk-MEP3POVW.js.map} +0 -0
- /package/dist/{chunk-35A7GCHG.js.map → chunk-MYBCB4AT.js.map} +0 -0
- /package/dist/{chunk-QPWUG2S3.js.map → chunk-NSQ3GUVO.js.map} +0 -0
- /package/dist/{chunk-NXVD46HT.js.map → chunk-O57VUN4U.js.map} +0 -0
- /package/dist/{chunk-L6DJJEFW.js.map → chunk-OAFRMLL4.js.map} +0 -0
- /package/dist/{chunk-PCO3P7WJ.js.map → chunk-OGVJXIHX.js.map} +0 -0
- /package/dist/{chunk-JSUA4YFE.js.map → chunk-ORAL4HJC.js.map} +0 -0
- /package/dist/{chunk-RXT6UB25.js.map → chunk-P22M3DX3.js.map} +0 -0
- /package/dist/{chunk-I2KEH2LS.js.map → chunk-PTFL3INS.js.map} +0 -0
- /package/dist/{chunk-5QXMESS7.js.map → chunk-QDKABMUE.js.map} +0 -0
- /package/dist/{chunk-34G4U6HS.js.map → chunk-QRZMJ2WN.js.map} +0 -0
- /package/dist/{chunk-GBA4N2SG.js.map → chunk-QVBB2CLG.js.map} +0 -0
- /package/dist/{chunk-GUD6FYQG.js.map → chunk-R4O2G6ML.js.map} +0 -0
- /package/dist/{chunk-CSLK7BN6.js.map → chunk-R6EOFKZG.js.map} +0 -0
- /package/dist/{chunk-Y2ADLCB3.js.map → chunk-RGPPNNDJ.js.map} +0 -0
- /package/dist/{chunk-2NV7MMMM.js.map → chunk-RZCRXCLI.js.map} +0 -0
- /package/dist/{chunk-WI37MWRZ.js.map → chunk-SNGNGH53.js.map} +0 -0
- /package/dist/{chunk-R4NGLTUS.js.map → chunk-TDMNADPI.js.map} +0 -0
- /package/dist/{chunk-UVVSOSHL.js.map → chunk-TSEUVMJL.js.map} +0 -0
- /package/dist/{chunk-O24EWFPQ.js.map → chunk-TYOBAFCY.js.map} +0 -0
- /package/dist/{chunk-LI76ESJL.js.map → chunk-TZQOCQXB.js.map} +0 -0
- /package/dist/{chunk-WHTMVTU4.js.map → chunk-U3LVARGV.js.map} +0 -0
- /package/dist/{chunk-AUWMKDKL.js.map → chunk-UCCB7RKF.js.map} +0 -0
- /package/dist/{chunk-OV7EYM6M.js.map → chunk-VWCFR4NR.js.map} +0 -0
- /package/dist/{chunk-DQTCGVFK.js.map → chunk-WHCD4Z3D.js.map} +0 -0
- /package/dist/{chunk-ZS7RK6H6.js.map → chunk-WLC6XA6C.js.map} +0 -0
- /package/dist/{chunk-QCP75JGO.js.map → chunk-WQUGCOKF.js.map} +0 -0
- /package/dist/{chunk-KATEWTQJ.js.map → chunk-X7J56GPG.js.map} +0 -0
- /package/dist/{chunk-ME2LMFHF.js.map → chunk-XTWF5XR6.js.map} +0 -0
- /package/dist/{chunk-HO7IQP7G.js.map → chunk-Y7CXM7UM.js.map} +0 -0
- /package/dist/{chunk-44PBRUXB.js.map → chunk-YMXCJ7SE.js.map} +0 -0
- /package/dist/{chunk-AXFWMRKI.js.map → chunk-YQJCWR4R.js.map} +0 -0
- /package/dist/{chunk-2DHJTJIY.js.map → chunk-Z2EVKNZR.js.map} +0 -0
- /package/dist/{chunk-4NRZ7BRS.js.map → chunk-ZOGZMAGI.js.map} +0 -0
|
@@ -34,6 +34,13 @@ export interface CorpusReadOptions {
|
|
|
34
34
|
* work, and on a tree large enough to matter there are thousands of them.
|
|
35
35
|
*/
|
|
36
36
|
abortSignal?: AbortSignal;
|
|
37
|
+
/**
|
|
38
|
+
* When `false`, parsed memories are returned without the raw file text kept
|
|
39
|
+
* beside them (`rememberRawFrontmatter`). Default (unset) retains that copy.
|
|
40
|
+
* A `false` read bypasses the shared corpus caches so it cannot replace a
|
|
41
|
+
* raw-retaining snapshot other callers still need.
|
|
42
|
+
*/
|
|
43
|
+
retainRaw?: boolean;
|
|
37
44
|
}
|
|
38
45
|
|
|
39
46
|
/** Stop a corpus read at a scan boundary when its caller has given up. */
|
|
@@ -72,6 +72,7 @@ export async function commitPreparedFileAtomically(
|
|
|
72
72
|
tempPath: string,
|
|
73
73
|
outputPath: string,
|
|
74
74
|
backupPath?: string,
|
|
75
|
+
beforeRename?: () => void | Promise<void>,
|
|
75
76
|
): Promise<string | undefined> {
|
|
76
77
|
const dir = path.dirname(outputPath);
|
|
77
78
|
let resolvedBackupPath: string | undefined;
|
|
@@ -79,6 +80,7 @@ export async function commitPreparedFileAtomically(
|
|
|
79
80
|
if (backupPath) {
|
|
80
81
|
resolvedBackupPath = await copyExistingFileToBackup(outputPath, backupPath);
|
|
81
82
|
}
|
|
83
|
+
if (beforeRename) await beforeRename();
|
|
82
84
|
await rename(tempPath, outputPath);
|
|
83
85
|
await syncDirectory(dir);
|
|
84
86
|
return resolvedBackupPath;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stop a projection-rebuild child when its parent is gone.
|
|
3
|
+
*
|
|
4
|
+
* A non-detached fork is reparented, not killed, when the daemon exits. The
|
|
5
|
+
* IPC channel closes first. The child must observe that and refuse to publish,
|
|
6
|
+
* or it can rename `memory-projection.sqlite.tmp` over a projection the
|
|
7
|
+
* replacement daemon has already started serving.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export function projectionRebuildParentDisconnected(): Error {
|
|
11
|
+
return new Error("projection rebuild runner parent disconnected");
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** Abort when this process loses its parent IPC channel. */
|
|
15
|
+
export function watchProjectionRebuildParent(): AbortSignal {
|
|
16
|
+
const abort = new AbortController();
|
|
17
|
+
const stop = () => {
|
|
18
|
+
if (abort.signal.aborted) return;
|
|
19
|
+
abort.abort(projectionRebuildParentDisconnected());
|
|
20
|
+
};
|
|
21
|
+
process.on("disconnect", stop);
|
|
22
|
+
if (process.connected === false) stop();
|
|
23
|
+
return abort.signal;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Yield so a disconnect that arrived during the previous await is applied,
|
|
28
|
+
* then refuse to continue. No-op when the caller did not pass a signal.
|
|
29
|
+
*/
|
|
30
|
+
export async function throwIfProjectionRebuildAborted(
|
|
31
|
+
signal: AbortSignal | undefined,
|
|
32
|
+
): Promise<void> {
|
|
33
|
+
if (!signal) return;
|
|
34
|
+
await new Promise<void>((resolve) => {
|
|
35
|
+
setImmediate(resolve);
|
|
36
|
+
});
|
|
37
|
+
if (!signal.aborted) return;
|
|
38
|
+
const reason = signal.reason;
|
|
39
|
+
throw reason instanceof Error ? reason : projectionRebuildParentDisconnected();
|
|
40
|
+
}
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Projection timeline assembly.
|
|
3
|
+
*
|
|
4
|
+
* The lifecycle ledger is streamed one event at a time. Dedup keeps the same
|
|
5
|
+
* winner as sort-then-first-wins (stable `compareMemoryLifecycleEvents`, file
|
|
6
|
+
* order on ties) without materializing the unsorted ledger beside the result.
|
|
7
|
+
*/
|
|
8
|
+
import path from "node:path";
|
|
9
|
+
import { throwIfAborted } from "../abort-error.js";
|
|
10
|
+
import { log } from "../logger.js";
|
|
11
|
+
import type { MemoryFile, MemoryLifecycleEvent } from "../types.js";
|
|
12
|
+
import {
|
|
13
|
+
buildLifecycleEventsForMemory,
|
|
14
|
+
compareMemoryLifecycleEvents,
|
|
15
|
+
memoryLifecycleEventProjectionIdentity,
|
|
16
|
+
sortMemoryLifecycleEvents,
|
|
17
|
+
} from "../memory-lifecycle-ledger-utils.js";
|
|
18
|
+
import type {
|
|
19
|
+
SkippedBlankIdTimelineEvent,
|
|
20
|
+
SkippedDuplicateTimelineEvent,
|
|
21
|
+
} from "./projection-support.js";
|
|
22
|
+
|
|
23
|
+
export interface ProjectionLedgerReader {
|
|
24
|
+
iterateMemoryLifecycleEvents(): AsyncIterable<MemoryLifecycleEvent>;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface ProjectionTimeline {
|
|
28
|
+
events: MemoryLifecycleEvent[];
|
|
29
|
+
usedLifecycleLedger: boolean;
|
|
30
|
+
sourceLifecycleLedgerEventCount: number;
|
|
31
|
+
skipped: SkippedDuplicateTimelineEvent[];
|
|
32
|
+
skippedBlankIds: SkippedBlankIdTimelineEvent[];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function loadTimelineEvents(
|
|
36
|
+
memories: MemoryFile[],
|
|
37
|
+
lifecycleEvents: MemoryLifecycleEvent[],
|
|
38
|
+
): { events: MemoryLifecycleEvent[]; usedLifecycleLedger: boolean } {
|
|
39
|
+
if (lifecycleEvents.length > 0) {
|
|
40
|
+
return {
|
|
41
|
+
events: sortMemoryLifecycleEvents(lifecycleEvents),
|
|
42
|
+
usedLifecycleLedger: true,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return {
|
|
47
|
+
events: sortMemoryLifecycleEvents(memories.flatMap((memory) => buildLifecycleEventsForMemory(memory))),
|
|
48
|
+
usedLifecycleLedger: false,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function deduplicateTimelineEvents(
|
|
53
|
+
events: MemoryLifecycleEvent[],
|
|
54
|
+
sourcePathFor: (event: MemoryLifecycleEvent) => string,
|
|
55
|
+
): {
|
|
56
|
+
events: MemoryLifecycleEvent[];
|
|
57
|
+
skipped: SkippedDuplicateTimelineEvent[];
|
|
58
|
+
skippedBlankIds: SkippedBlankIdTimelineEvent[];
|
|
59
|
+
} {
|
|
60
|
+
const keptById = new Map<string, { event: MemoryLifecycleEvent; path: string }>();
|
|
61
|
+
const unique: MemoryLifecycleEvent[] = [];
|
|
62
|
+
const skipped: SkippedDuplicateTimelineEvent[] = [];
|
|
63
|
+
const skippedBlankIds: SkippedBlankIdTimelineEvent[] = [];
|
|
64
|
+
for (const event of sortMemoryLifecycleEvents(events)) {
|
|
65
|
+
const sourcePath = sourcePathFor(event);
|
|
66
|
+
if (!event.memoryId.trim()) {
|
|
67
|
+
skippedBlankIds.push({ eventId: event.eventId, path: sourcePath });
|
|
68
|
+
log.warn(
|
|
69
|
+
`projection rebuild skipped timeline event with blank memory id: `
|
|
70
|
+
+ `event_id=${event.eventId} path=${sourcePath}`,
|
|
71
|
+
);
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
const kept = keptById.get(event.eventId);
|
|
75
|
+
if (kept) {
|
|
76
|
+
skipped.push({
|
|
77
|
+
eventId: event.eventId,
|
|
78
|
+
keptPath: kept.path,
|
|
79
|
+
skippedPath: sourcePath,
|
|
80
|
+
});
|
|
81
|
+
log.warn(
|
|
82
|
+
`projection rebuild skipped duplicate timeline event_id=${event.eventId}: `
|
|
83
|
+
+ `kept=${kept.path} skipped=${sourcePath}`,
|
|
84
|
+
);
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
keptById.set(event.eventId, { event, path: sourcePath });
|
|
88
|
+
unique.push(event);
|
|
89
|
+
}
|
|
90
|
+
return { events: unique, skipped, skippedBlankIds };
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function internPool(): <T extends string>(value: T) => T {
|
|
94
|
+
const pool = new Map<string, string>();
|
|
95
|
+
return <T extends string>(value: T): T => {
|
|
96
|
+
const existing = pool.get(value);
|
|
97
|
+
if (existing !== undefined) return existing as T;
|
|
98
|
+
pool.set(value, value);
|
|
99
|
+
return value;
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** Share repeated ledger scalars so a long ledger does not keep one string per row. */
|
|
104
|
+
function internLedgerEvent(event: MemoryLifecycleEvent, intern: <T extends string>(value: T) => T): void {
|
|
105
|
+
event.memoryId = intern(event.memoryId);
|
|
106
|
+
event.eventType = intern(event.eventType);
|
|
107
|
+
event.timestamp = intern(event.timestamp);
|
|
108
|
+
event.actor = intern(event.actor);
|
|
109
|
+
event.ruleVersion = intern(event.ruleVersion);
|
|
110
|
+
if (event.reasonCode) event.reasonCode = intern(event.reasonCode);
|
|
111
|
+
if (event.correlationId) event.correlationId = intern(event.correlationId);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Stream ledger events into the deduped timeline. Returns null when the ledger
|
|
116
|
+
* contributed no parsable row, so the caller can fall back to frontmatter.
|
|
117
|
+
*/
|
|
118
|
+
async function consumeLifecycleLedger(
|
|
119
|
+
events: AsyncIterable<MemoryLifecycleEvent>,
|
|
120
|
+
ledgerPath: string,
|
|
121
|
+
signal?: AbortSignal,
|
|
122
|
+
): Promise<ProjectionTimeline | null> {
|
|
123
|
+
const intern = internPool();
|
|
124
|
+
const best = new Map<string, MemoryLifecycleEvent>();
|
|
125
|
+
const losers: MemoryLifecycleEvent[] = [];
|
|
126
|
+
const blanks: MemoryLifecycleEvent[] = [];
|
|
127
|
+
const identities = new Set<string>();
|
|
128
|
+
let sawEvent = false;
|
|
129
|
+
for await (const event of events) {
|
|
130
|
+
throwIfAborted(signal, "projection rebuild aborted");
|
|
131
|
+
sawEvent = true;
|
|
132
|
+
internLedgerEvent(event, intern);
|
|
133
|
+
identities.add(memoryLifecycleEventProjectionIdentity(event));
|
|
134
|
+
if (!event.memoryId.trim()) {
|
|
135
|
+
blanks.push(event);
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
const prev = best.get(event.eventId);
|
|
139
|
+
if (!prev) {
|
|
140
|
+
best.set(event.eventId, event);
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
if (compareMemoryLifecycleEvents(event, prev) < 0) {
|
|
144
|
+
losers.push(prev);
|
|
145
|
+
best.set(event.eventId, event);
|
|
146
|
+
} else {
|
|
147
|
+
losers.push(event);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
if (!sawEvent) return null;
|
|
151
|
+
const sourceLifecycleLedgerEventCount = identities.size;
|
|
152
|
+
identities.clear();
|
|
153
|
+
const skippedBlankIds = sortMemoryLifecycleEvents(blanks).map((event) => {
|
|
154
|
+
log.warn(
|
|
155
|
+
`projection rebuild skipped timeline event with blank memory id: `
|
|
156
|
+
+ `event_id=${event.eventId} path=${ledgerPath}`,
|
|
157
|
+
);
|
|
158
|
+
return { eventId: event.eventId, path: ledgerPath };
|
|
159
|
+
});
|
|
160
|
+
const skipped = sortMemoryLifecycleEvents(losers).map((event) => {
|
|
161
|
+
log.warn(
|
|
162
|
+
`projection rebuild skipped duplicate timeline event_id=${event.eventId}: `
|
|
163
|
+
+ `kept=${ledgerPath} skipped=${ledgerPath}`,
|
|
164
|
+
);
|
|
165
|
+
return {
|
|
166
|
+
eventId: event.eventId,
|
|
167
|
+
keptPath: ledgerPath,
|
|
168
|
+
skippedPath: ledgerPath,
|
|
169
|
+
};
|
|
170
|
+
});
|
|
171
|
+
return {
|
|
172
|
+
events: sortMemoryLifecycleEvents([...best.values()]),
|
|
173
|
+
usedLifecycleLedger: true,
|
|
174
|
+
sourceLifecycleLedgerEventCount,
|
|
175
|
+
skipped,
|
|
176
|
+
skippedBlankIds,
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export async function loadProjectionTimeline(options: {
|
|
181
|
+
storage: ProjectionLedgerReader;
|
|
182
|
+
memoryDir: string;
|
|
183
|
+
memories: MemoryFile[];
|
|
184
|
+
signal?: AbortSignal;
|
|
185
|
+
}): Promise<ProjectionTimeline> {
|
|
186
|
+
const ledgerPath = path.join(options.memoryDir, "state", "memory-lifecycle-ledger.jsonl");
|
|
187
|
+
const streamed = await consumeLifecycleLedger(
|
|
188
|
+
options.storage.iterateMemoryLifecycleEvents(),
|
|
189
|
+
ledgerPath,
|
|
190
|
+
options.signal,
|
|
191
|
+
);
|
|
192
|
+
if (streamed) return streamed;
|
|
193
|
+
|
|
194
|
+
const timeline = loadTimelineEvents(options.memories, []);
|
|
195
|
+
const memoryPathById = new Map(
|
|
196
|
+
options.memories.map((memory) => [memory.frontmatter.id, memory.path]),
|
|
197
|
+
);
|
|
198
|
+
const deduplicated = deduplicateTimelineEvents(
|
|
199
|
+
timeline.events,
|
|
200
|
+
(event) => memoryPathById.get(event.memoryId) ?? ledgerPath,
|
|
201
|
+
);
|
|
202
|
+
return {
|
|
203
|
+
events: deduplicated.events,
|
|
204
|
+
usedLifecycleLedger: false,
|
|
205
|
+
sourceLifecycleLedgerEventCount: 0,
|
|
206
|
+
skipped: deduplicated.skipped,
|
|
207
|
+
skippedBlankIds: deduplicated.skippedBlankIds,
|
|
208
|
+
};
|
|
209
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Child-process entry for the scheduled memory-projection rebuild.
|
|
3
|
+
*
|
|
4
|
+
* The daemon forks this file so the corpus and lifecycle ledger are allocated
|
|
5
|
+
* in a fresh heap. Invoke only with `--remnic-projection-rebuild-runner`.
|
|
6
|
+
* Arguments are the memory directory, default namespace, and an ISO timestamp.
|
|
7
|
+
* Do not put secrets, passphrases, or secure-store unlock material on argv
|
|
8
|
+
* or in the environment — an encrypted store rebuilds in the parent instead.
|
|
9
|
+
*/
|
|
10
|
+
import { initLogger, log } from "../logger.js";
|
|
11
|
+
import { watchProjectionRebuildParent } from "./projection-rebuild-abort.js";
|
|
12
|
+
import { rebuildMemoryProjection } from "./rebuild-memory-projection.js";
|
|
13
|
+
|
|
14
|
+
export const PROJECTION_REBUILD_RUNNER_FLAG = "--remnic-projection-rebuild-runner";
|
|
15
|
+
|
|
16
|
+
export interface ProjectionRebuildRunnerSuccess {
|
|
17
|
+
ok: true;
|
|
18
|
+
currentRows: number;
|
|
19
|
+
timelineRows: number;
|
|
20
|
+
entityMentionRows: number;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
interface ProjectionRebuildRunnerFailure {
|
|
24
|
+
ok: false;
|
|
25
|
+
error: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function runnerArgs(): { memoryDir: string; defaultNamespace: string; now: Date } {
|
|
29
|
+
const index = process.argv.indexOf(PROJECTION_REBUILD_RUNNER_FLAG);
|
|
30
|
+
const memoryDir = index >= 0 ? process.argv[index + 1] : undefined;
|
|
31
|
+
const defaultNamespace = index >= 0 ? process.argv[index + 2] : undefined;
|
|
32
|
+
const nowIso = index >= 0 ? process.argv[index + 3] : undefined;
|
|
33
|
+
if (!memoryDir || !defaultNamespace || !nowIso) {
|
|
34
|
+
throw new Error(
|
|
35
|
+
"projection rebuild runner requires memoryDir, defaultNamespace, and now",
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
const now = new Date(nowIso);
|
|
39
|
+
if (Number.isNaN(now.getTime())) {
|
|
40
|
+
throw new Error(`projection rebuild runner now is not a timestamp: ${nowIso}`);
|
|
41
|
+
}
|
|
42
|
+
return { memoryDir, defaultNamespace, now };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function send(message: ProjectionRebuildRunnerSuccess | ProjectionRebuildRunnerFailure): Promise<void> {
|
|
46
|
+
if (typeof process.send !== "function") {
|
|
47
|
+
throw new Error("projection rebuild runner requires an IPC channel");
|
|
48
|
+
}
|
|
49
|
+
const sendMessage = process.send.bind(process);
|
|
50
|
+
return new Promise((resolve, reject) => {
|
|
51
|
+
sendMessage(message, (err) => (err ? reject(err) : resolve()));
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async function main(): Promise<void> {
|
|
56
|
+
initLogger();
|
|
57
|
+
const { memoryDir, defaultNamespace, now } = runnerArgs();
|
|
58
|
+
const signal = watchProjectionRebuildParent();
|
|
59
|
+
const result = await rebuildMemoryProjection({
|
|
60
|
+
memoryDir,
|
|
61
|
+
defaultNamespace,
|
|
62
|
+
dryRun: false,
|
|
63
|
+
now,
|
|
64
|
+
signal,
|
|
65
|
+
});
|
|
66
|
+
if (signal.aborted || process.connected === false) {
|
|
67
|
+
throw new Error("projection rebuild runner parent disconnected");
|
|
68
|
+
}
|
|
69
|
+
await send({
|
|
70
|
+
ok: true,
|
|
71
|
+
currentRows: result.currentRows,
|
|
72
|
+
timelineRows: result.timelineRows,
|
|
73
|
+
entityMentionRows: result.entityMentionRows,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const invokedAsRunner = process.argv.includes(PROJECTION_REBUILD_RUNNER_FLAG);
|
|
78
|
+
if (invokedAsRunner) {
|
|
79
|
+
main().then(
|
|
80
|
+
() => process.exit(0),
|
|
81
|
+
(err: unknown) => {
|
|
82
|
+
const error = err instanceof Error ? err.message : String(err);
|
|
83
|
+
if (process.connected === false) {
|
|
84
|
+
process.exit(1);
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
log.warn(`projection rebuild runner failed: ${error}`);
|
|
88
|
+
send({ ok: false, error }).then(
|
|
89
|
+
() => process.exit(1),
|
|
90
|
+
() => process.exit(1),
|
|
91
|
+
);
|
|
92
|
+
},
|
|
93
|
+
);
|
|
94
|
+
}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Heap regression for the scheduled projection rebuild.
|
|
3
|
+
*
|
|
4
|
+
* About 20k synthetic facts (~2 KB each) and a 100k-line lifecycle ledger are
|
|
5
|
+
* rebuilt once under `--max-old-space-size=192` and once with the default
|
|
6
|
+
* heap. Row counts and the SHA-256 of the projection rows must match. The
|
|
7
|
+
* child is a fresh process so the fixture writer does not share its heap
|
|
8
|
+
* with the rebuild.
|
|
9
|
+
*/
|
|
10
|
+
import assert from "node:assert/strict";
|
|
11
|
+
import { spawn } from "node:child_process";
|
|
12
|
+
import { createHash } from "node:crypto";
|
|
13
|
+
import { createWriteStream } from "node:fs";
|
|
14
|
+
import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises";
|
|
15
|
+
import { once } from "node:events";
|
|
16
|
+
import os from "node:os";
|
|
17
|
+
import path from "node:path";
|
|
18
|
+
import { fileURLToPath } from "node:url";
|
|
19
|
+
import v8 from "node:v8";
|
|
20
|
+
|
|
21
|
+
const FACT_COUNT = 20_000;
|
|
22
|
+
const LEDGER_LINES = 100_000;
|
|
23
|
+
const BODY_BYTES = 2048;
|
|
24
|
+
const FIXED_NOW = "2026-06-01T00:00:00.000Z";
|
|
25
|
+
const CHILD_FLAG = "--projection-memory-child";
|
|
26
|
+
|
|
27
|
+
function factId(index: number): string {
|
|
28
|
+
return `fact-${String(index).padStart(5, "0")}`;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async function writeCorpus(memoryDir: string): Promise<void> {
|
|
32
|
+
const factDir = path.join(memoryDir, "facts", "2026-01-01");
|
|
33
|
+
await mkdir(factDir, { recursive: true });
|
|
34
|
+
await mkdir(path.join(memoryDir, "state"), { recursive: true });
|
|
35
|
+
let next = 0;
|
|
36
|
+
const workers = Array.from({ length: 32 }, async () => {
|
|
37
|
+
for (;;) {
|
|
38
|
+
const index = next;
|
|
39
|
+
next += 1;
|
|
40
|
+
if (index >= FACT_COUNT) return;
|
|
41
|
+
const id = factId(index);
|
|
42
|
+
const body = Buffer.alloc(BODY_BYTES, 97);
|
|
43
|
+
body.write(`fact ${id} `);
|
|
44
|
+
for (let offset = 16; offset < body.length; offset += 1) {
|
|
45
|
+
body[offset] = 97 + ((index + offset) % 26);
|
|
46
|
+
}
|
|
47
|
+
const markdown = `---\nid: ${id}\ncategory: fact\nconfidence: 0.5\ncreated: 2026-01-01T00:00:00.000Z\nupdated: 2026-01-01T00:00:00.000Z\nstatus: active\nsource: synthetic\n---\n\n${body.toString("utf8")}\n`;
|
|
48
|
+
await writeFile(path.join(factDir, `${id}.md`), markdown);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
await Promise.all(workers);
|
|
52
|
+
|
|
53
|
+
const ledger = createWriteStream(path.join(memoryDir, "state", "memory-lifecycle-ledger.jsonl"));
|
|
54
|
+
for (let index = 0; index < LEDGER_LINES; index += 1) {
|
|
55
|
+
const line = `{"eventId":"evt-${String(index).padStart(6, "0")}","memoryId":"${factId(index % FACT_COUNT)}","eventType":"created","timestamp":"${new Date(Date.UTC(2026, 0, 1) + index * 1000).toISOString()}","actor":"synthetic","ruleVersion":"1"}\n`;
|
|
56
|
+
if (!ledger.write(line)) await once(ledger, "drain");
|
|
57
|
+
}
|
|
58
|
+
ledger.end();
|
|
59
|
+
await once(ledger, "finish");
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async function runChild(memoryDir: string, maxOldSpace?: number): Promise<void> {
|
|
63
|
+
const args: string[] = [];
|
|
64
|
+
if (maxOldSpace !== undefined) args.push(`--max-old-space-size=${maxOldSpace}`);
|
|
65
|
+
args.push("--import", "tsx", fileURLToPath(import.meta.url), CHILD_FLAG, memoryDir);
|
|
66
|
+
const nodeOptions = (process.env.NODE_OPTIONS ?? "")
|
|
67
|
+
.split(" ")
|
|
68
|
+
.filter((part) => part.length > 0 && !part.startsWith("--max-old-space-size"))
|
|
69
|
+
.join(" ");
|
|
70
|
+
await new Promise<void>((resolve, reject) => {
|
|
71
|
+
const child = spawn(process.execPath, args, {
|
|
72
|
+
env: { ...process.env, NODE_OPTIONS: nodeOptions },
|
|
73
|
+
stdio: "inherit",
|
|
74
|
+
});
|
|
75
|
+
child.once("error", reject);
|
|
76
|
+
child.once("exit", (code) => {
|
|
77
|
+
if (code === 0) resolve();
|
|
78
|
+
else reject(new Error(`projection memory child exited ${code ?? "null"}`));
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async function projectionDigest(memoryDir: string): Promise<{ hash: string; counts: Record<string, number> }> {
|
|
84
|
+
const { openProjectionReadonly } = await import("../memory-projection-store.js");
|
|
85
|
+
const db = openProjectionReadonly(memoryDir);
|
|
86
|
+
if (!db) throw new Error("projection was not written");
|
|
87
|
+
try {
|
|
88
|
+
const hash = createHash("sha256");
|
|
89
|
+
const counts: Record<string, number> = {};
|
|
90
|
+
const queries: Array<[string, string]> = [
|
|
91
|
+
["memory_current", "SELECT * FROM memory_current ORDER BY memory_id"],
|
|
92
|
+
["memory_timeline", "SELECT * FROM memory_timeline ORDER BY event_id"],
|
|
93
|
+
["memory_entity_mentions", "SELECT * FROM memory_entity_mentions ORDER BY memory_id, entity_ref, mention_source"],
|
|
94
|
+
];
|
|
95
|
+
for (const [name, sql] of queries) {
|
|
96
|
+
hash.update(name);
|
|
97
|
+
hash.update("\n");
|
|
98
|
+
let count = 0;
|
|
99
|
+
for (const row of db.prepare(sql).iterate()) {
|
|
100
|
+
count += 1;
|
|
101
|
+
hash.update(JSON.stringify(row));
|
|
102
|
+
hash.update("\n");
|
|
103
|
+
}
|
|
104
|
+
counts[name] = count;
|
|
105
|
+
}
|
|
106
|
+
return { hash: hash.digest("hex"), counts };
|
|
107
|
+
} finally {
|
|
108
|
+
db.close();
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async function rebuildInThisProcess(memoryDir: string): Promise<void> {
|
|
113
|
+
const { rebuildMemoryProjection } = await import("./rebuild-memory-projection.js");
|
|
114
|
+
const result = await rebuildMemoryProjection({
|
|
115
|
+
memoryDir,
|
|
116
|
+
dryRun: false,
|
|
117
|
+
now: new Date(FIXED_NOW),
|
|
118
|
+
});
|
|
119
|
+
const stats = v8.getHeapStatistics();
|
|
120
|
+
console.log(
|
|
121
|
+
`projection-memory-child rows current=${result.currentRows} timeline=${result.timelineRows} `
|
|
122
|
+
+ `heap used=${stats.used_heap_size} limit=${stats.heap_size_limit}`,
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const childDir = process.argv.indexOf(CHILD_FLAG);
|
|
127
|
+
if (childDir >= 0) {
|
|
128
|
+
const memoryDir = process.argv[childDir + 1];
|
|
129
|
+
if (!memoryDir) {
|
|
130
|
+
console.error("projection memory child requires a memory directory");
|
|
131
|
+
process.exit(2);
|
|
132
|
+
}
|
|
133
|
+
try {
|
|
134
|
+
await rebuildInThisProcess(memoryDir);
|
|
135
|
+
} catch (err) {
|
|
136
|
+
console.error(err);
|
|
137
|
+
process.exit(1);
|
|
138
|
+
}
|
|
139
|
+
process.exit(0);
|
|
140
|
+
} else {
|
|
141
|
+
const { default: test } = await import("node:test");
|
|
142
|
+
|
|
143
|
+
test("slow: 20k-fact projection rebuild matches under a 192MB heap", { timeout: 600_000 }, async () => {
|
|
144
|
+
const memoryDir = await mkdtemp(path.join(os.tmpdir(), "remnic-proj-heap-corpus-"));
|
|
145
|
+
try {
|
|
146
|
+
await writeCorpus(memoryDir);
|
|
147
|
+
await runChild(memoryDir, 192);
|
|
148
|
+
const constrained = await projectionDigest(memoryDir);
|
|
149
|
+
await runChild(memoryDir);
|
|
150
|
+
const unconstrained = await projectionDigest(memoryDir);
|
|
151
|
+
assert.equal(constrained.counts.memory_current, FACT_COUNT);
|
|
152
|
+
assert.equal(constrained.counts.memory_timeline, LEDGER_LINES);
|
|
153
|
+
assert.deepEqual(unconstrained.counts, constrained.counts);
|
|
154
|
+
assert.equal(unconstrained.hash, constrained.hash);
|
|
155
|
+
} finally {
|
|
156
|
+
await rm(memoryDir, { recursive: true, force: true });
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
}
|