@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
|
@@ -0,0 +1,498 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { fork } from "node:child_process";
|
|
3
|
+
import { once } from "node:events";
|
|
4
|
+
import { existsSync } from "node:fs";
|
|
5
|
+
import { mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
|
|
6
|
+
import os from "node:os";
|
|
7
|
+
import path from "node:path";
|
|
8
|
+
import { fileURLToPath } from "node:url";
|
|
9
|
+
import test from "node:test";
|
|
10
|
+
import { commitPreparedFileAtomically } from "../maintenance/atomic-file.js";
|
|
11
|
+
import { watchProjectionRebuildParent } from "../maintenance/projection-rebuild-abort.js";
|
|
12
|
+
import { loadProjectionTimeline } from "../maintenance/projection-timeline.js";
|
|
13
|
+
import { rebuildMemoryProjection } from "../maintenance/rebuild-memory-projection.js";
|
|
14
|
+
import { initLogger, resetLogger, type LoggerBackend } from "../logger.js";
|
|
15
|
+
import { probeProjectionHealth } from "../memory-projection-store.js";
|
|
16
|
+
import { StorageManager } from "../storage.js";
|
|
17
|
+
import type { MemoryLifecycleEvent, PluginConfig } from "../types.js";
|
|
18
|
+
import {
|
|
19
|
+
createProjectionRebuildScheduleState,
|
|
20
|
+
forkProjectionRebuild,
|
|
21
|
+
maybeRebuildMemoryProjectionScheduled,
|
|
22
|
+
projectionRebuildChildEnv,
|
|
23
|
+
projectionRebuildForkTimeoutMs,
|
|
24
|
+
terminateScheduledProjectionRebuilds,
|
|
25
|
+
type ProjectionRebuildForkRequest,
|
|
26
|
+
type ProjectionRebuildForkResult,
|
|
27
|
+
} from "./projection-rebuild-schedule.js";
|
|
28
|
+
|
|
29
|
+
const PARENT_DISCONNECT_FLAG = "--projection-parent-disconnect-child";
|
|
30
|
+
|
|
31
|
+
if (process.argv.includes(PARENT_DISCONNECT_FLAG)) {
|
|
32
|
+
const memoryDir = process.argv[process.argv.indexOf(PARENT_DISCONNECT_FLAG) + 1];
|
|
33
|
+
if (!memoryDir) process.exit(1);
|
|
34
|
+
try {
|
|
35
|
+
const signal = watchProjectionRebuildParent();
|
|
36
|
+
process.send?.({ ready: true });
|
|
37
|
+
await new Promise<void>((resolve) => {
|
|
38
|
+
if (signal.aborted) {
|
|
39
|
+
resolve();
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
signal.addEventListener("abort", () => resolve(), { once: true });
|
|
43
|
+
});
|
|
44
|
+
await rebuildMemoryProjection({
|
|
45
|
+
memoryDir,
|
|
46
|
+
dryRun: false,
|
|
47
|
+
now: new Date("2026-06-01T00:00:00.000Z"),
|
|
48
|
+
signal,
|
|
49
|
+
});
|
|
50
|
+
} catch {
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
process.exit(0);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function config(memoryDir: string, overrides: Record<string, unknown> = {}): PluginConfig {
|
|
57
|
+
return {
|
|
58
|
+
memoryDir,
|
|
59
|
+
defaultNamespace: "ns-schedule",
|
|
60
|
+
projectionRebuildEnabled: true,
|
|
61
|
+
projectionRebuildIntervalMs: 6 * 60 * 60 * 1000,
|
|
62
|
+
projectionRebuildHeapUsageLimit: 0.6,
|
|
63
|
+
...overrides,
|
|
64
|
+
} as unknown as PluginConfig;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function quietHeap() {
|
|
68
|
+
return { used_heap_size: 1, heap_size_limit: 100 };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** Windows reports TerminateProcess as an exit code, not SIGKILL. */
|
|
72
|
+
function assertChildKilled(child: { signalCode: NodeJS.Signals | null; exitCode: number | null }): void {
|
|
73
|
+
if (process.platform === "win32") {
|
|
74
|
+
assert.notEqual(child.exitCode, null);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
assert.equal(child.signalCode, "SIGKILL");
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** A storage stand-in whose corpus reads fail the test if the daemon calls them. */
|
|
81
|
+
function corpusGuard(memoryDir: string): StorageManager {
|
|
82
|
+
const fail = async (): Promise<never> => {
|
|
83
|
+
throw new Error("daemon read the corpus");
|
|
84
|
+
};
|
|
85
|
+
return {
|
|
86
|
+
dir: memoryDir,
|
|
87
|
+
isSecureStoreUnlocked: () => false,
|
|
88
|
+
readAllMemories: fail,
|
|
89
|
+
readAllColdMemories: fail,
|
|
90
|
+
readArchivedMemories: fail,
|
|
91
|
+
iterateMemoryLifecycleEvents: async function* () {
|
|
92
|
+
throw new Error("daemon read the ledger");
|
|
93
|
+
},
|
|
94
|
+
} as unknown as StorageManager;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async function seedFact(memoryDir: string): Promise<void> {
|
|
98
|
+
const dir = path.join(memoryDir, "facts", "2026-01-01");
|
|
99
|
+
await mkdir(dir, { recursive: true });
|
|
100
|
+
await writeFile(
|
|
101
|
+
path.join(dir, "fact-1.md"),
|
|
102
|
+
[
|
|
103
|
+
"---",
|
|
104
|
+
"id: fact-1",
|
|
105
|
+
"category: fact",
|
|
106
|
+
"confidence: 0.8",
|
|
107
|
+
"created: 2026-01-01T00:00:00.000Z",
|
|
108
|
+
"updated: 2026-01-01T00:00:00.000Z",
|
|
109
|
+
"status: active",
|
|
110
|
+
"source: test",
|
|
111
|
+
"---",
|
|
112
|
+
"",
|
|
113
|
+
"scheduled rebuild fixture",
|
|
114
|
+
"",
|
|
115
|
+
].join("\n"),
|
|
116
|
+
"utf8",
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
test("shutting down the parent kills the projection rebuild child", async () => {
|
|
121
|
+
const child = fork(process.execPath, ["-e", "setInterval(() => {}, 1e9);"], { stdio: "ignore" });
|
|
122
|
+
const pending = forkProjectionRebuild(
|
|
123
|
+
{ memoryDir: "/tmp/unused", defaultNamespace: "default", now: new Date("2026-06-01T00:00:00.000Z") },
|
|
124
|
+
{ timeoutMs: 60_000, spawnChild: () => child },
|
|
125
|
+
);
|
|
126
|
+
terminateScheduledProjectionRebuilds();
|
|
127
|
+
await assert.rejects(pending, /exited/);
|
|
128
|
+
assertChildKilled(child);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
test("disposing one scheduler does not kill another scheduler's rebuild", async () => {
|
|
132
|
+
const first = fork(process.execPath, ["-e", "setInterval(() => {}, 1e9);"], { stdio: "ignore" });
|
|
133
|
+
const second = fork(process.execPath, ["-e", "setInterval(() => {}, 1e9);"], { stdio: "ignore" });
|
|
134
|
+
const request = {
|
|
135
|
+
memoryDir: "/tmp/unused",
|
|
136
|
+
defaultNamespace: "default",
|
|
137
|
+
now: new Date("2026-06-01T00:00:00.000Z"),
|
|
138
|
+
};
|
|
139
|
+
const stateA = createProjectionRebuildScheduleState();
|
|
140
|
+
const stateB = createProjectionRebuildScheduleState();
|
|
141
|
+
const pendingA = forkProjectionRebuild(request, { timeoutMs: 60_000, spawnChild: () => first, owner: stateA });
|
|
142
|
+
const pendingB = forkProjectionRebuild(request, { timeoutMs: 60_000, spawnChild: () => second, owner: stateB });
|
|
143
|
+
terminateScheduledProjectionRebuilds(stateA);
|
|
144
|
+
await assert.rejects(pendingA, /exited/);
|
|
145
|
+
assertChildKilled(first);
|
|
146
|
+
assert.equal(second.exitCode, null);
|
|
147
|
+
assert.equal(second.signalCode, null);
|
|
148
|
+
terminateScheduledProjectionRebuilds(stateB);
|
|
149
|
+
await assert.rejects(pendingB, /exited/);
|
|
150
|
+
assertChildKilled(second);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
test("a disconnected parent does not let the rebuild child publish", async () => {
|
|
154
|
+
const memoryDir = await mkdtemp(path.join(os.tmpdir(), "remnic-proj-orphan-"));
|
|
155
|
+
const projection = path.join(memoryDir, "state", "memory-projection.sqlite");
|
|
156
|
+
try {
|
|
157
|
+
await seedFact(memoryDir);
|
|
158
|
+
const child = fork(fileURLToPath(import.meta.url), [
|
|
159
|
+
PARENT_DISCONNECT_FLAG,
|
|
160
|
+
memoryDir,
|
|
161
|
+
], {
|
|
162
|
+
execArgv: ["--import", "tsx"],
|
|
163
|
+
stdio: ["ignore", "inherit", "inherit", "ipc"],
|
|
164
|
+
});
|
|
165
|
+
const outcome = await Promise.race([
|
|
166
|
+
once(child, "message").then(() => "ready" as const),
|
|
167
|
+
once(child, "exit").then(() => "exit" as const),
|
|
168
|
+
]);
|
|
169
|
+
assert.equal(outcome, "ready");
|
|
170
|
+
child.disconnect();
|
|
171
|
+
const [code] = await once(child, "exit");
|
|
172
|
+
assert.notEqual(code, 0);
|
|
173
|
+
assert.equal(existsSync(projection), false);
|
|
174
|
+
assert.equal(existsSync(`${projection}.tmp`), false);
|
|
175
|
+
} finally {
|
|
176
|
+
await rm(memoryDir, { recursive: true, force: true });
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
test("a hung projection rebuild child is killed at the timeout", async () => {
|
|
181
|
+
const child = fork(process.execPath, ["-e", "setInterval(() => {}, 1e9);"], { stdio: "ignore" });
|
|
182
|
+
await assert.rejects(
|
|
183
|
+
() => forkProjectionRebuild(
|
|
184
|
+
{ memoryDir: "/tmp/unused", defaultNamespace: "default", now: new Date("2026-06-01T00:00:00.000Z") },
|
|
185
|
+
{ timeoutMs: 200, spawnChild: () => child },
|
|
186
|
+
),
|
|
187
|
+
/timed out after 200ms/,
|
|
188
|
+
);
|
|
189
|
+
await new Promise<void>((resolve) => {
|
|
190
|
+
if (child.exitCode !== null || child.signalCode !== null) resolve();
|
|
191
|
+
else child.once("exit", () => resolve());
|
|
192
|
+
});
|
|
193
|
+
assertChildKilled(child);
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
test("an aborted signal stops the corpus scan before the projection is published", async () => {
|
|
197
|
+
const memoryDir = await mkdtemp(path.join(os.tmpdir(), "remnic-proj-abort-scan-"));
|
|
198
|
+
const projection = path.join(memoryDir, "state", "memory-projection.sqlite");
|
|
199
|
+
try {
|
|
200
|
+
await seedFact(memoryDir);
|
|
201
|
+
const signal = AbortSignal.abort(new Error("projection rebuild runner parent disconnected"));
|
|
202
|
+
await assert.rejects(
|
|
203
|
+
() => rebuildMemoryProjection({
|
|
204
|
+
memoryDir,
|
|
205
|
+
dryRun: false,
|
|
206
|
+
now: new Date("2026-06-01T00:00:00.000Z"),
|
|
207
|
+
signal,
|
|
208
|
+
}),
|
|
209
|
+
(err: unknown) => {
|
|
210
|
+
assert.ok(err instanceof Error);
|
|
211
|
+
assert.equal(err.name, "AbortError");
|
|
212
|
+
assert.match(err.message, /corpus read aborted/);
|
|
213
|
+
return true;
|
|
214
|
+
},
|
|
215
|
+
);
|
|
216
|
+
assert.equal(existsSync(projection), false);
|
|
217
|
+
assert.equal(existsSync(`${projection}.tmp`), false);
|
|
218
|
+
} finally {
|
|
219
|
+
await rm(memoryDir, { recursive: true, force: true });
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
test("an aborted signal stops an archive scan and the lifecycle ledger mid-stream", async () => {
|
|
224
|
+
const memoryDir = await mkdtemp(path.join(os.tmpdir(), "remnic-proj-abort-rest-"));
|
|
225
|
+
try {
|
|
226
|
+
const storage = new StorageManager(memoryDir);
|
|
227
|
+
await assert.rejects(
|
|
228
|
+
() => storage.readArchivedMemories({ retainRaw: false, abortSignal: AbortSignal.abort() }),
|
|
229
|
+
(err: unknown) => {
|
|
230
|
+
assert.ok(err instanceof Error);
|
|
231
|
+
assert.equal(err.name, "AbortError");
|
|
232
|
+
return true;
|
|
233
|
+
},
|
|
234
|
+
);
|
|
235
|
+
const controller = new AbortController();
|
|
236
|
+
let pulled = 0;
|
|
237
|
+
await assert.rejects(
|
|
238
|
+
() => loadProjectionTimeline({
|
|
239
|
+
storage: {
|
|
240
|
+
iterateMemoryLifecycleEvents(): AsyncIterable<MemoryLifecycleEvent> {
|
|
241
|
+
return (async function* () {
|
|
242
|
+
for (let index = 0; index < 5; index += 1) {
|
|
243
|
+
pulled += 1;
|
|
244
|
+
if (index === 1) controller.abort();
|
|
245
|
+
yield {
|
|
246
|
+
eventId: `e${index}`,
|
|
247
|
+
memoryId: "m",
|
|
248
|
+
eventType: "created",
|
|
249
|
+
timestamp: "2026-01-01T00:00:00.000Z",
|
|
250
|
+
actor: "test",
|
|
251
|
+
ruleVersion: "1",
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
})();
|
|
255
|
+
},
|
|
256
|
+
},
|
|
257
|
+
memoryDir,
|
|
258
|
+
memories: [],
|
|
259
|
+
signal: controller.signal,
|
|
260
|
+
}),
|
|
261
|
+
/projection rebuild aborted/,
|
|
262
|
+
);
|
|
263
|
+
assert.ok(pulled < 5);
|
|
264
|
+
} finally {
|
|
265
|
+
await rm(memoryDir, { recursive: true, force: true });
|
|
266
|
+
}
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
test("an abort after the projection backup does not rename over the live file", async () => {
|
|
270
|
+
const dir = await mkdtemp(path.join(os.tmpdir(), "remnic-proj-rename-"));
|
|
271
|
+
const output = path.join(dir, "memory-projection.sqlite");
|
|
272
|
+
const temp = `${output}.tmp`;
|
|
273
|
+
const backup = path.join(dir, "backup.sqlite");
|
|
274
|
+
try {
|
|
275
|
+
await writeFile(output, "live");
|
|
276
|
+
await writeFile(temp, "orphan");
|
|
277
|
+
await assert.rejects(
|
|
278
|
+
() => commitPreparedFileAtomically(temp, output, backup, async () => {
|
|
279
|
+
throw new Error("projection rebuild runner parent disconnected");
|
|
280
|
+
}),
|
|
281
|
+
/parent disconnected/,
|
|
282
|
+
);
|
|
283
|
+
assert.equal(await readFile(output, "utf8"), "live");
|
|
284
|
+
assert.equal(existsSync(temp), false);
|
|
285
|
+
assert.equal(await readFile(backup, "utf8"), "live");
|
|
286
|
+
} finally {
|
|
287
|
+
await rm(dir, { recursive: true, force: true });
|
|
288
|
+
}
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
test("the fork hang cap follows a long interval and stays at least 30 minutes", () => {
|
|
292
|
+
assert.equal(projectionRebuildForkTimeoutMs(60_000), 30 * 60 * 1000);
|
|
293
|
+
assert.equal(projectionRebuildForkTimeoutMs(6 * 60 * 60 * 1000), 6 * 60 * 60 * 1000);
|
|
294
|
+
assert.equal(projectionRebuildForkTimeoutMs(Number.NaN), 30 * 60 * 1000);
|
|
295
|
+
assert.equal(projectionRebuildForkTimeoutMs(30 * 24 * 60 * 60 * 1000), 2_147_483_647);
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
test("projection rebuild child env keeps locators and drops secrets", () => {
|
|
299
|
+
const env = projectionRebuildChildEnv({
|
|
300
|
+
PATH: "/usr/bin",
|
|
301
|
+
HOME: "/tmp/remnic-home",
|
|
302
|
+
OPENAI_API_KEY: "sk-test",
|
|
303
|
+
REMNIC_SECURE_STORE_KEY: "unlock-material",
|
|
304
|
+
NODE_OPTIONS: "--conditions=remnic-source",
|
|
305
|
+
});
|
|
306
|
+
assert.equal(env.PATH, "/usr/bin");
|
|
307
|
+
assert.equal(env.HOME, "/tmp/remnic-home");
|
|
308
|
+
assert.equal(env.NODE_OPTIONS, "--conditions=remnic-source");
|
|
309
|
+
assert.equal(env.OPENAI_API_KEY, undefined);
|
|
310
|
+
assert.equal(env.REMNIC_SECURE_STORE_KEY, undefined);
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
test("scheduled projection rebuild forks and does not read the corpus in the daemon", async () => {
|
|
314
|
+
const memoryDir = await mkdtemp(path.join(os.tmpdir(), "remnic-proj-fork-"));
|
|
315
|
+
const calls: ProjectionRebuildForkRequest[] = [];
|
|
316
|
+
const timeouts: number[] = [];
|
|
317
|
+
try {
|
|
318
|
+
const state = createProjectionRebuildScheduleState();
|
|
319
|
+
await maybeRebuildMemoryProjectionScheduled({
|
|
320
|
+
config: config(memoryDir),
|
|
321
|
+
getStorage: () => corpusGuard(memoryDir),
|
|
322
|
+
state,
|
|
323
|
+
heapStatistics: quietHeap,
|
|
324
|
+
forkRebuild: async (request, options) => {
|
|
325
|
+
calls.push(request);
|
|
326
|
+
if (options?.timeoutMs !== undefined) timeouts.push(options.timeoutMs);
|
|
327
|
+
return { currentRows: 4, timelineRows: 5, entityMentionRows: 6 };
|
|
328
|
+
},
|
|
329
|
+
});
|
|
330
|
+
assert.equal(calls.length, 1);
|
|
331
|
+
assert.deepEqual(timeouts, [6 * 60 * 60 * 1000]);
|
|
332
|
+
assert.equal(calls[0]?.memoryDir, memoryDir);
|
|
333
|
+
assert.equal(calls[0]?.defaultNamespace, "ns-schedule");
|
|
334
|
+
assert.equal(state.inFlight, false);
|
|
335
|
+
assert.ok(state.lastRebuildAtMs > 0);
|
|
336
|
+
|
|
337
|
+
const again = calls.length;
|
|
338
|
+
await maybeRebuildMemoryProjectionScheduled({
|
|
339
|
+
config: config(memoryDir),
|
|
340
|
+
getStorage: () => corpusGuard(memoryDir),
|
|
341
|
+
state,
|
|
342
|
+
heapStatistics: quietHeap,
|
|
343
|
+
forkRebuild: async () => {
|
|
344
|
+
calls.push({ memoryDir, defaultNamespace: "ns-schedule", now: new Date() });
|
|
345
|
+
return { currentRows: 0, timelineRows: 0, entityMentionRows: 0 };
|
|
346
|
+
},
|
|
347
|
+
});
|
|
348
|
+
assert.equal(calls.length, again, "success interval suppresses a second fork");
|
|
349
|
+
} finally {
|
|
350
|
+
await rm(memoryDir, { recursive: true, force: true });
|
|
351
|
+
}
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
test("scheduled projection rebuild keeps in-flight and failure backoff", async () => {
|
|
355
|
+
const memoryDir = await mkdtemp(path.join(os.tmpdir(), "remnic-proj-backoff-"));
|
|
356
|
+
try {
|
|
357
|
+
const inFlight = createProjectionRebuildScheduleState();
|
|
358
|
+
inFlight.inFlight = true;
|
|
359
|
+
let forks = 0;
|
|
360
|
+
const forkRebuild = async (): Promise<ProjectionRebuildForkResult> => {
|
|
361
|
+
forks += 1;
|
|
362
|
+
return { currentRows: 0, timelineRows: 0, entityMentionRows: 0 };
|
|
363
|
+
};
|
|
364
|
+
await maybeRebuildMemoryProjectionScheduled({
|
|
365
|
+
config: config(memoryDir),
|
|
366
|
+
state: inFlight,
|
|
367
|
+
heapStatistics: quietHeap,
|
|
368
|
+
forkRebuild,
|
|
369
|
+
});
|
|
370
|
+
assert.equal(forks, 0, "in-flight rebuild is not started again");
|
|
371
|
+
|
|
372
|
+
const failing = createProjectionRebuildScheduleState();
|
|
373
|
+
await maybeRebuildMemoryProjectionScheduled({
|
|
374
|
+
config: config(memoryDir),
|
|
375
|
+
state: failing,
|
|
376
|
+
heapStatistics: quietHeap,
|
|
377
|
+
forkRebuild: async () => {
|
|
378
|
+
throw new Error("runner failed");
|
|
379
|
+
},
|
|
380
|
+
});
|
|
381
|
+
assert.equal(failing.inFlight, false);
|
|
382
|
+
assert.equal(failing.lastRebuildAtMs, 0);
|
|
383
|
+
assert.ok(failing.lastFailureAtMs > 0);
|
|
384
|
+
await maybeRebuildMemoryProjectionScheduled({
|
|
385
|
+
config: config(memoryDir),
|
|
386
|
+
state: failing,
|
|
387
|
+
heapStatistics: quietHeap,
|
|
388
|
+
forkRebuild,
|
|
389
|
+
});
|
|
390
|
+
assert.equal(forks, 0, "failure backoff suppresses the retry");
|
|
391
|
+
} finally {
|
|
392
|
+
await rm(memoryDir, { recursive: true, force: true });
|
|
393
|
+
}
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
test("heap guard postpones a scheduled projection rebuild", async () => {
|
|
397
|
+
const memoryDir = await mkdtemp(path.join(os.tmpdir(), "remnic-proj-heap-"));
|
|
398
|
+
const warnings: string[] = [];
|
|
399
|
+
const backend: LoggerBackend = {
|
|
400
|
+
info() {},
|
|
401
|
+
warn(msg) {
|
|
402
|
+
warnings.push(msg);
|
|
403
|
+
},
|
|
404
|
+
error() {},
|
|
405
|
+
debug() {},
|
|
406
|
+
};
|
|
407
|
+
initLogger(backend, false, { timestamps: false });
|
|
408
|
+
try {
|
|
409
|
+
const state = createProjectionRebuildScheduleState();
|
|
410
|
+
let forks = 0;
|
|
411
|
+
await maybeRebuildMemoryProjectionScheduled({
|
|
412
|
+
config: config(memoryDir),
|
|
413
|
+
getStorage: () => corpusGuard(memoryDir),
|
|
414
|
+
state,
|
|
415
|
+
heapStatistics: () => ({ used_heap_size: 70, heap_size_limit: 100 }),
|
|
416
|
+
forkRebuild: async () => {
|
|
417
|
+
forks += 1;
|
|
418
|
+
return { currentRows: 0, timelineRows: 0, entityMentionRows: 0 };
|
|
419
|
+
},
|
|
420
|
+
});
|
|
421
|
+
assert.equal(forks, 0);
|
|
422
|
+
assert.equal(state.inFlight, false);
|
|
423
|
+
assert.equal(state.lastRebuildAtMs, 0);
|
|
424
|
+
assert.ok(state.lastFailureAtMs > 0);
|
|
425
|
+
assert.equal(warnings.length, 1);
|
|
426
|
+
assert.match(warnings[0] ?? "", /postponed/);
|
|
427
|
+
assert.match(warnings[0] ?? "", /heap used 70 of limit 100/);
|
|
428
|
+
assert.match(warnings[0] ?? "", /ratio 0\.700/);
|
|
429
|
+
assert.match(warnings[0] ?? "", /projectionRebuildHeapUsageLimit 0\.6/);
|
|
430
|
+
} finally {
|
|
431
|
+
resetLogger();
|
|
432
|
+
await rm(memoryDir, { recursive: true, force: true });
|
|
433
|
+
}
|
|
434
|
+
});
|
|
435
|
+
|
|
436
|
+
test("heap guard does not postpone at the limit or with an unusable cap", async () => {
|
|
437
|
+
const memoryDir = await mkdtemp(path.join(os.tmpdir(), "remnic-proj-heap-ok-"));
|
|
438
|
+
try {
|
|
439
|
+
let forks = 0;
|
|
440
|
+
const forkRebuild = async (): Promise<ProjectionRebuildForkResult> => {
|
|
441
|
+
forks += 1;
|
|
442
|
+
return { currentRows: 1, timelineRows: 0, entityMentionRows: 0 };
|
|
443
|
+
};
|
|
444
|
+
const atLimit = createProjectionRebuildScheduleState();
|
|
445
|
+
await maybeRebuildMemoryProjectionScheduled({
|
|
446
|
+
config: config(memoryDir),
|
|
447
|
+
state: atLimit,
|
|
448
|
+
heapStatistics: () => ({ used_heap_size: 60, heap_size_limit: 100 }),
|
|
449
|
+
forkRebuild,
|
|
450
|
+
});
|
|
451
|
+
assert.equal(forks, 1, "ratio equal to the limit still runs");
|
|
452
|
+
|
|
453
|
+
const disabled = createProjectionRebuildScheduleState();
|
|
454
|
+
await maybeRebuildMemoryProjectionScheduled({
|
|
455
|
+
config: config(memoryDir, { projectionRebuildHeapUsageLimit: 1 }),
|
|
456
|
+
state: disabled,
|
|
457
|
+
heapStatistics: () => ({ used_heap_size: 100, heap_size_limit: 100 }),
|
|
458
|
+
forkRebuild,
|
|
459
|
+
});
|
|
460
|
+
assert.equal(forks, 2, "a limit of 1 never postpones");
|
|
461
|
+
|
|
462
|
+
const unusable = createProjectionRebuildScheduleState();
|
|
463
|
+
await maybeRebuildMemoryProjectionScheduled({
|
|
464
|
+
config: config(memoryDir),
|
|
465
|
+
state: unusable,
|
|
466
|
+
heapStatistics: () => ({ used_heap_size: 10, heap_size_limit: 0 }),
|
|
467
|
+
forkRebuild,
|
|
468
|
+
});
|
|
469
|
+
assert.equal(forks, 3, "a non-positive heap cap does not postpone");
|
|
470
|
+
} finally {
|
|
471
|
+
await rm(memoryDir, { recursive: true, force: true });
|
|
472
|
+
}
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
test("an unlocked secure store rebuilds in-process instead of forking", async () => {
|
|
476
|
+
const memoryDir = await mkdtemp(path.join(os.tmpdir(), "remnic-proj-unlocked-"));
|
|
477
|
+
try {
|
|
478
|
+
await seedFact(memoryDir);
|
|
479
|
+
const storage = new StorageManager(memoryDir);
|
|
480
|
+
storage.setSecureStoreKey(Buffer.alloc(32, 4), false);
|
|
481
|
+
assert.equal(storage.isSecureStoreUnlocked(), true);
|
|
482
|
+
let forks = 0;
|
|
483
|
+
await maybeRebuildMemoryProjectionScheduled({
|
|
484
|
+
config: config(memoryDir),
|
|
485
|
+
getStorage: () => storage,
|
|
486
|
+
state: createProjectionRebuildScheduleState(),
|
|
487
|
+
heapStatistics: quietHeap,
|
|
488
|
+
forkRebuild: async () => {
|
|
489
|
+
forks += 1;
|
|
490
|
+
throw new Error("forked an unlocked store");
|
|
491
|
+
},
|
|
492
|
+
});
|
|
493
|
+
assert.equal(forks, 0);
|
|
494
|
+
assert.equal(probeProjectionHealth(memoryDir).state, "openable");
|
|
495
|
+
} finally {
|
|
496
|
+
await rm(memoryDir, { recursive: true, force: true });
|
|
497
|
+
}
|
|
498
|
+
});
|