@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
|
@@ -8,6 +8,15 @@
|
|
|
8
8
|
* maintenance request — interval-throttled, single-flighted, never awaited,
|
|
9
9
|
* never throws.
|
|
10
10
|
*
|
|
11
|
+
* The rebuild runs in a forked child so the corpus and lifecycle ledger are
|
|
12
|
+
* not allocated in the daemon heap. An unlocked secure store stays in-process:
|
|
13
|
+
* the key is process-local and is never placed on the child's argv or env.
|
|
14
|
+
* The child is killed on scheduler dispose and on parent exit. If the parent
|
|
15
|
+
* dies without running those hooks, the child sees the IPC channel close and
|
|
16
|
+
* stops before it publishes the projection.
|
|
17
|
+
* When the daemon heap is already above `projectionRebuildHeapUsageLimit`,
|
|
18
|
+
* the job is postponed with the failure backoff instead of starting.
|
|
19
|
+
*
|
|
11
20
|
* `projectionRebuildEnabled` gates it (via the lifecycle capabilities
|
|
12
21
|
* resolver, default true); `projectionRebuildIntervalMs` is the cadence.
|
|
13
22
|
*
|
|
@@ -24,6 +33,11 @@
|
|
|
24
33
|
* not yet scheduled here (they are still served by their own rebuild paths);
|
|
25
34
|
* the fallback WARN surfaces staleness for any namespace that lags.
|
|
26
35
|
*/
|
|
36
|
+
import { existsSync } from "node:fs";
|
|
37
|
+
import { fork, type ChildProcess } from "node:child_process";
|
|
38
|
+
import path from "node:path";
|
|
39
|
+
import v8 from "node:v8";
|
|
40
|
+
import { fileURLToPath } from "node:url";
|
|
27
41
|
import { log } from "../logger.js";
|
|
28
42
|
import type { PluginConfig } from "../types.js";
|
|
29
43
|
import { resolveMemoryLifecycleCapabilities } from "../capabilities.js";
|
|
@@ -31,7 +45,10 @@ import {
|
|
|
31
45
|
rebuildMemoryProjection,
|
|
32
46
|
type RebuildMemoryProjectionOptions,
|
|
33
47
|
} from "../maintenance/rebuild-memory-projection.js";
|
|
48
|
+
import { PROJECTION_REBUILD_RUNNER_FLAG } from "../maintenance/rebuild-memory-projection-runner.js";
|
|
34
49
|
import { readProjectionRebuiltAt } from "../maintenance/projection-support.js";
|
|
50
|
+
import { DEFAULT_PROJECTION_REBUILD_HEAP_USAGE_LIMIT } from "../projection-rebuild-config.js";
|
|
51
|
+
import { fullCorpusHeapPressure, type HeapLimitStatistics } from "./full-corpus-heap-guard.js";
|
|
35
52
|
|
|
36
53
|
type InjectedStorage = NonNullable<RebuildMemoryProjectionOptions["storage"]>;
|
|
37
54
|
|
|
@@ -45,20 +62,250 @@ export interface ProjectionRebuildScheduleState {
|
|
|
45
62
|
lastFailureAtMs: number;
|
|
46
63
|
}
|
|
47
64
|
|
|
65
|
+
export interface ProjectionRebuildForkRequest {
|
|
66
|
+
memoryDir: string;
|
|
67
|
+
defaultNamespace: string;
|
|
68
|
+
now: Date;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export interface ProjectionRebuildForkResult {
|
|
72
|
+
currentRows: number;
|
|
73
|
+
timelineRows: number;
|
|
74
|
+
entityMentionRows: number;
|
|
75
|
+
}
|
|
76
|
+
|
|
48
77
|
/** Bounded retry cadence after a failure — short enough to self-heal promptly
|
|
49
78
|
* once a transient cause clears, long enough to keep a persistently-failing
|
|
50
79
|
* rebuild off every maintenance tick. */
|
|
51
80
|
const FAILURE_RETRY_BACKOFF_MS = 5 * 60 * 1000;
|
|
52
81
|
|
|
82
|
+
/** Floor for a hung child. A short interval must not make this tighter:
|
|
83
|
+
* a large corpus can legitimately run longer than 30 minutes. When the
|
|
84
|
+
* configured interval is longer, the child may use that whole interval. */
|
|
85
|
+
const FORK_TIMEOUT_MS = 30 * 60 * 1000;
|
|
86
|
+
|
|
87
|
+
/** Hang cap for one scheduled fork. At least 30 minutes, otherwise the interval,
|
|
88
|
+
* and never above Node's 32-bit setTimeout limit. A larger delay overflows
|
|
89
|
+
* and fires in 1ms, which would kill every rebuild. */
|
|
90
|
+
const MAX_SETTIMEOUT_MS = 2_147_483_647;
|
|
91
|
+
|
|
92
|
+
export function projectionRebuildForkTimeoutMs(intervalMs: number): number {
|
|
93
|
+
const timeout = !Number.isFinite(intervalMs) || intervalMs < FORK_TIMEOUT_MS
|
|
94
|
+
? FORK_TIMEOUT_MS
|
|
95
|
+
: intervalMs;
|
|
96
|
+
return Math.min(timeout, MAX_SETTIMEOUT_MS);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const projectionRebuildChildren = new Map<ProjectionRebuildScheduleState | undefined, Set<ChildProcess>>();
|
|
100
|
+
let parentExitHookInstalled = false;
|
|
101
|
+
|
|
102
|
+
function rememberProjectionRebuildChild(
|
|
103
|
+
child: ChildProcess,
|
|
104
|
+
owner: ProjectionRebuildScheduleState | undefined,
|
|
105
|
+
): void {
|
|
106
|
+
let children = projectionRebuildChildren.get(owner);
|
|
107
|
+
if (!children) {
|
|
108
|
+
children = new Set();
|
|
109
|
+
projectionRebuildChildren.set(owner, children);
|
|
110
|
+
}
|
|
111
|
+
children.add(child);
|
|
112
|
+
const forget = () => {
|
|
113
|
+
children.delete(child);
|
|
114
|
+
if (children.size === 0) projectionRebuildChildren.delete(owner);
|
|
115
|
+
};
|
|
116
|
+
child.once("exit", forget);
|
|
117
|
+
child.once("error", forget);
|
|
118
|
+
if (!parentExitHookInstalled) {
|
|
119
|
+
parentExitHookInstalled = true;
|
|
120
|
+
// The exit listener is invoked with the exit code. Ignore that argument
|
|
121
|
+
// so a non-zero code is not treated as a scheduler owner.
|
|
122
|
+
process.on("exit", () => {
|
|
123
|
+
terminateScheduledProjectionRebuilds();
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* SIGKILL scheduled rebuild children. A scheduler dispose passes its own
|
|
130
|
+
* state so another instance's child keeps running. Process exit passes no
|
|
131
|
+
* owner and kills every child.
|
|
132
|
+
*/
|
|
133
|
+
export function terminateScheduledProjectionRebuilds(
|
|
134
|
+
owner?: ProjectionRebuildScheduleState,
|
|
135
|
+
): void {
|
|
136
|
+
const groups = owner
|
|
137
|
+
? [projectionRebuildChildren.get(owner)]
|
|
138
|
+
: [...projectionRebuildChildren.values()];
|
|
139
|
+
for (const children of groups) {
|
|
140
|
+
if (!children) continue;
|
|
141
|
+
for (const child of children) child.kill("SIGKILL");
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const CHILD_ENV_ALLOWLIST = [
|
|
146
|
+
"PATH",
|
|
147
|
+
"NODE_OPTIONS",
|
|
148
|
+
"HOME",
|
|
149
|
+
"USERPROFILE",
|
|
150
|
+
"TMPDIR",
|
|
151
|
+
"TMP",
|
|
152
|
+
"TEMP",
|
|
153
|
+
"LANG",
|
|
154
|
+
"LC_ALL",
|
|
155
|
+
"LC_CTYPE",
|
|
156
|
+
"TZ",
|
|
157
|
+
"SystemRoot",
|
|
158
|
+
"windir",
|
|
159
|
+
"PATHEXT",
|
|
160
|
+
"COMSPEC",
|
|
161
|
+
"NODE_PATH",
|
|
162
|
+
] as const;
|
|
163
|
+
|
|
53
164
|
export function createProjectionRebuildScheduleState(): ProjectionRebuildScheduleState {
|
|
54
165
|
return { inFlight: false, lastRebuildAtMs: 0, lastFailureAtMs: 0 };
|
|
55
166
|
}
|
|
56
167
|
|
|
168
|
+
/**
|
|
169
|
+
* Environment for the rebuild child. Only runtime locators are copied.
|
|
170
|
+
* API keys, tokens, and secure-store unlock material are not passed.
|
|
171
|
+
*/
|
|
172
|
+
export function projectionRebuildChildEnv(parent: NodeJS.ProcessEnv): NodeJS.ProcessEnv {
|
|
173
|
+
const env: NodeJS.ProcessEnv = {};
|
|
174
|
+
for (const key of CHILD_ENV_ALLOWLIST) {
|
|
175
|
+
const value = parent[key];
|
|
176
|
+
if (value !== undefined) env[key] = value;
|
|
177
|
+
}
|
|
178
|
+
return env;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function projectionRebuildRunnerPath(): string {
|
|
182
|
+
const here = fileURLToPath(import.meta.url);
|
|
183
|
+
const dir = path.dirname(here);
|
|
184
|
+
const candidates = [
|
|
185
|
+
path.join(dir, "..", "maintenance", "rebuild-memory-projection-runner.ts"),
|
|
186
|
+
path.join(dir, "..", "maintenance", "rebuild-memory-projection-runner.js"),
|
|
187
|
+
path.join(dir, "maintenance", "rebuild-memory-projection-runner.js"),
|
|
188
|
+
path.join(dir, "rebuild-memory-projection-runner.js"),
|
|
189
|
+
];
|
|
190
|
+
for (const candidate of candidates) {
|
|
191
|
+
if (existsSync(candidate)) return candidate;
|
|
192
|
+
}
|
|
193
|
+
throw new Error("projection rebuild runner was not found next to the maintenance bundle");
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function runnerExecArgv(runnerPath: string): string[] {
|
|
197
|
+
const argv: string[] = [];
|
|
198
|
+
const source = process.execArgv;
|
|
199
|
+
for (let index = 0; index < source.length; index += 1) {
|
|
200
|
+
const arg = source[index] ?? "";
|
|
201
|
+
if (arg === "--inspect" || arg === "--inspect-brk" || arg === "--inspect-port") {
|
|
202
|
+
if (arg === "--inspect-port") index += 1;
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
if (arg.startsWith("--inspect=") || arg.startsWith("--inspect-brk=") || arg.startsWith("--inspect-port=")) {
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
argv.push(arg);
|
|
209
|
+
}
|
|
210
|
+
if (runnerPath.endsWith(".ts") && !argv.some((arg) => arg === "tsx" || arg.includes(`${path.sep}tsx`))) {
|
|
211
|
+
argv.unshift("--import", "tsx");
|
|
212
|
+
}
|
|
213
|
+
return argv;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function isRunnerSuccess(message: unknown): message is ProjectionRebuildForkResult & { ok: true } {
|
|
217
|
+
if (!message || typeof message !== "object") return false;
|
|
218
|
+
const row = message as Record<string, unknown>;
|
|
219
|
+
return row.ok === true
|
|
220
|
+
&& typeof row.currentRows === "number"
|
|
221
|
+
&& typeof row.timelineRows === "number"
|
|
222
|
+
&& typeof row.entityMentionRows === "number";
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/** Fork the projection rebuild. Arguments are path, namespace, and timestamp only. */
|
|
226
|
+
export async function forkProjectionRebuild(
|
|
227
|
+
request: ProjectionRebuildForkRequest,
|
|
228
|
+
options?: {
|
|
229
|
+
timeoutMs?: number;
|
|
230
|
+
spawnChild?: () => ChildProcess;
|
|
231
|
+
/** Scheduler that owns this child. Dispose kills only that owner's child. */
|
|
232
|
+
owner?: ProjectionRebuildScheduleState;
|
|
233
|
+
/** Invoked after exit listeners are attached. Tests use this to disconnect. */
|
|
234
|
+
onChild?: (child: ChildProcess) => void;
|
|
235
|
+
},
|
|
236
|
+
): Promise<ProjectionRebuildForkResult> {
|
|
237
|
+
const timeoutMs = Math.min(options?.timeoutMs ?? FORK_TIMEOUT_MS, MAX_SETTIMEOUT_MS);
|
|
238
|
+
const child = options?.spawnChild
|
|
239
|
+
? options.spawnChild()
|
|
240
|
+
: spawnProjectionRebuildChild(request);
|
|
241
|
+
rememberProjectionRebuildChild(child, options?.owner);
|
|
242
|
+
return await new Promise((resolve, reject) => {
|
|
243
|
+
let settled = false;
|
|
244
|
+
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
245
|
+
const finish = (err?: Error, result?: ProjectionRebuildForkResult) => {
|
|
246
|
+
if (settled) return;
|
|
247
|
+
settled = true;
|
|
248
|
+
if (timer) clearTimeout(timer);
|
|
249
|
+
if (err) reject(err);
|
|
250
|
+
else if (result) resolve(result);
|
|
251
|
+
else reject(new Error("projection rebuild runner returned no result"));
|
|
252
|
+
};
|
|
253
|
+
timer = setTimeout(() => {
|
|
254
|
+
child.kill("SIGKILL");
|
|
255
|
+
finish(new Error(`projection rebuild runner timed out after ${timeoutMs}ms`));
|
|
256
|
+
}, timeoutMs);
|
|
257
|
+
timer.unref?.();
|
|
258
|
+
child.on("message", (message: unknown) => {
|
|
259
|
+
if (isRunnerSuccess(message)) {
|
|
260
|
+
finish(undefined, message);
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
const error = message && typeof message === "object" && "error" in message
|
|
264
|
+
? String((message as { error?: unknown }).error ?? "projection rebuild runner failed")
|
|
265
|
+
: "projection rebuild runner failed";
|
|
266
|
+
finish(new Error(error));
|
|
267
|
+
});
|
|
268
|
+
child.on("error", (err) => finish(err));
|
|
269
|
+
child.on("exit", (code) => {
|
|
270
|
+
if (!settled) {
|
|
271
|
+
finish(new Error(`projection rebuild runner exited ${code ?? "null"}`));
|
|
272
|
+
}
|
|
273
|
+
});
|
|
274
|
+
options?.onChild?.(child);
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
function spawnProjectionRebuildChild(request: ProjectionRebuildForkRequest): ChildProcess {
|
|
279
|
+
const runnerPath = projectionRebuildRunnerPath();
|
|
280
|
+
const nowIso = request.now.toISOString();
|
|
281
|
+
return fork(
|
|
282
|
+
runnerPath,
|
|
283
|
+
[PROJECTION_REBUILD_RUNNER_FLAG, request.memoryDir, request.defaultNamespace, nowIso],
|
|
284
|
+
{
|
|
285
|
+
execArgv: runnerExecArgv(runnerPath),
|
|
286
|
+
env: projectionRebuildChildEnv(process.env),
|
|
287
|
+
stdio: ["ignore", "inherit", "inherit", "ipc"],
|
|
288
|
+
},
|
|
289
|
+
);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function storageIsUnlocked(storage: InjectedStorage | undefined): boolean {
|
|
293
|
+
if (!storage || typeof storage.isSecureStoreUnlocked !== "function") return false;
|
|
294
|
+
return storage.isSecureStoreUnlocked();
|
|
295
|
+
}
|
|
296
|
+
|
|
57
297
|
export async function maybeRebuildMemoryProjectionScheduled(opts: {
|
|
58
298
|
config: PluginConfig;
|
|
59
299
|
/** The daemon's live root storage, when available (secure-store unlock). */
|
|
60
300
|
getStorage?: () => InjectedStorage | undefined;
|
|
61
301
|
state: ProjectionRebuildScheduleState;
|
|
302
|
+
/** Test seam. Production forks {@link forkProjectionRebuild}. */
|
|
303
|
+
forkRebuild?: (
|
|
304
|
+
request: ProjectionRebuildForkRequest,
|
|
305
|
+
options?: { timeoutMs?: number; owner?: ProjectionRebuildScheduleState },
|
|
306
|
+
) => Promise<ProjectionRebuildForkResult>;
|
|
307
|
+
/** Test seam. Production reads `v8.getHeapStatistics()`. */
|
|
308
|
+
heapStatistics?: () => HeapLimitStatistics;
|
|
62
309
|
}): Promise<void> {
|
|
63
310
|
const { config, state } = opts;
|
|
64
311
|
if (!resolveMemoryLifecycleCapabilities(config).projectionRebuild) return;
|
|
@@ -81,17 +328,41 @@ export async function maybeRebuildMemoryProjectionScheduled(opts: {
|
|
|
81
328
|
return;
|
|
82
329
|
}
|
|
83
330
|
|
|
331
|
+
const heapLimit = config.projectionRebuildHeapUsageLimit ?? DEFAULT_PROJECTION_REBUILD_HEAP_USAGE_LIMIT;
|
|
332
|
+
const heapStats = (opts.heapStatistics ?? (() => v8.getHeapStatistics()))();
|
|
333
|
+
const pressure = fullCorpusHeapPressure(heapStats, heapLimit);
|
|
334
|
+
if (pressure.postpone) {
|
|
335
|
+
state.lastFailureAtMs = Date.now();
|
|
336
|
+
log.warn(
|
|
337
|
+
`memory projection scheduled rebuild postponed for ${memoryDir}: `
|
|
338
|
+
+ `heap used ${heapStats.used_heap_size} of limit ${heapStats.heap_size_limit} `
|
|
339
|
+
+ `(ratio ${pressure.ratio.toFixed(3)}) exceeds projectionRebuildHeapUsageLimit ${heapLimit}`,
|
|
340
|
+
);
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
const forkRebuild = opts.forkRebuild ?? forkProjectionRebuild;
|
|
345
|
+
const unlocked = storageIsUnlocked(rootStorage);
|
|
84
346
|
state.inFlight = true;
|
|
85
347
|
try {
|
|
86
|
-
const result =
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
348
|
+
const result = unlocked
|
|
349
|
+
? await rebuildMemoryProjection({
|
|
350
|
+
memoryDir,
|
|
351
|
+
// Reuse the daemon's live storage so secure-store deployments rebuild
|
|
352
|
+
// through the unlocked instance instead of a fresh locked one.
|
|
353
|
+
...(rootStorage ? { storage: rootStorage } : {}),
|
|
354
|
+
defaultNamespace: config.defaultNamespace,
|
|
355
|
+
dryRun: false,
|
|
356
|
+
now: new Date(now),
|
|
357
|
+
})
|
|
358
|
+
: await forkRebuild({
|
|
359
|
+
memoryDir,
|
|
360
|
+
defaultNamespace: config.defaultNamespace || "default",
|
|
361
|
+
now: new Date(now),
|
|
362
|
+
}, {
|
|
363
|
+
timeoutMs: projectionRebuildForkTimeoutMs(intervalMs),
|
|
364
|
+
owner: state,
|
|
365
|
+
});
|
|
95
366
|
state.lastRebuildAtMs = Date.now();
|
|
96
367
|
const lag = Number.isFinite(rebuiltAtMs)
|
|
97
368
|
? `${Math.max(0, Math.round((now - rebuiltAtMs) / 60_000))}m stale`
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scheduled projection-rebuild knobs (issue #2119, heap guard).
|
|
3
|
+
*
|
|
4
|
+
* Extracted from `config.ts` / `types.ts` so those god-file ceilings do not
|
|
5
|
+
* grow. `PluginConfig` extends the interface and `parseConfig` spreads
|
|
6
|
+
* {@link parseProjectionRebuildScheduleConfig}.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { coerceBooleanLike, coerceNumber } from "./connectors/coerce.js";
|
|
10
|
+
|
|
11
|
+
/** Knobs for the maintenance-scheduled memory-projection rebuild. */
|
|
12
|
+
export interface ProjectionRebuildScheduleConfig {
|
|
13
|
+
/**
|
|
14
|
+
* Enable the maintenance-scheduled rebuild of `state/memory-projection.sqlite`.
|
|
15
|
+
* The projection is otherwise rebuild-only (manual CLI / tests), so it rots as
|
|
16
|
+
* the lifecycle ledger grows and timeline/browse consumers silently regress to
|
|
17
|
+
* full-corpus fallback scans. Default true — mirrors lifecycle-ledger
|
|
18
|
+
* auto-compaction, which is likewise enabled-by-default to keep a state file
|
|
19
|
+
* healthy. Set false to disable (operators can still cron the CLI rebuild).
|
|
20
|
+
*/
|
|
21
|
+
projectionRebuildEnabled: boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Minimum interval between scheduled projection rebuilds. A rebuild is skipped
|
|
24
|
+
* when the projection's `rebuiltAt` meta is younger than this, so a burst of
|
|
25
|
+
* maintenance requests cannot fan into back-to-back rebuilds. Floored at 60s.
|
|
26
|
+
* Default 6h (matches memoryLifecycleLedgerCompactMinIntervalMs).
|
|
27
|
+
*/
|
|
28
|
+
projectionRebuildIntervalMs: number;
|
|
29
|
+
/**
|
|
30
|
+
* Postpone a scheduled full-corpus projection rebuild when
|
|
31
|
+
* `v8.getHeapStatistics().used_heap_size / heap_size_limit` exceeds this
|
|
32
|
+
* ratio. Finite number in `(0, 1]`. Default `0.6`. `1` disables the guard
|
|
33
|
+
* (usage cannot exceed the limit). A postponed rebuild uses the failure
|
|
34
|
+
* backoff and does not start the multi-GB allocation.
|
|
35
|
+
*/
|
|
36
|
+
projectionRebuildHeapUsageLimit: number;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Integer coercion contract shared with `config.ts` (`parseIntegerAtLeast`). */
|
|
40
|
+
type IntegerParser = (value: unknown, fallback: number, min: number, keyName: string) => number;
|
|
41
|
+
|
|
42
|
+
/** Default heap-usage ratio above which a scheduled full-corpus rebuild waits. */
|
|
43
|
+
export const DEFAULT_PROJECTION_REBUILD_HEAP_USAGE_LIMIT = 0.6;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Parse `projectionRebuildHeapUsageLimit`. Absent values take the default.
|
|
47
|
+
* A present value must be a finite number in `(0, 1]` (numeric strings
|
|
48
|
+
* coerced). `0` is rejected: it would postpone every rebuild.
|
|
49
|
+
*/
|
|
50
|
+
export function parseProjectionRebuildHeapUsageLimit(value: unknown): number {
|
|
51
|
+
if (value === undefined || value === null) return DEFAULT_PROJECTION_REBUILD_HEAP_USAGE_LIMIT;
|
|
52
|
+
const coerced = coerceNumber(value);
|
|
53
|
+
if (coerced === undefined || !Number.isFinite(coerced) || coerced <= 0 || coerced > 1) {
|
|
54
|
+
throw new Error(
|
|
55
|
+
`projectionRebuildHeapUsageLimit must be a finite number in (0, 1]; got ${JSON.stringify(value)}`,
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
return coerced;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Parse the scheduled projection-rebuild knobs. Boolean coercion follows
|
|
63
|
+
* gotcha #36 (`"false"` is false). The interval uses the shared integer
|
|
64
|
+
* parser (strings coerced, non-integers and sub-60s values rejected).
|
|
65
|
+
*/
|
|
66
|
+
export function parseProjectionRebuildScheduleConfig(
|
|
67
|
+
cfg: Record<string, unknown>,
|
|
68
|
+
parseIntegerAtLeast: IntegerParser,
|
|
69
|
+
): ProjectionRebuildScheduleConfig {
|
|
70
|
+
return {
|
|
71
|
+
projectionRebuildEnabled: coerceBooleanLike(cfg.projectionRebuildEnabled) ?? true,
|
|
72
|
+
projectionRebuildIntervalMs: parseIntegerAtLeast(
|
|
73
|
+
cfg.projectionRebuildIntervalMs,
|
|
74
|
+
6 * 60 * 60 * 1000,
|
|
75
|
+
60_000,
|
|
76
|
+
"projectionRebuildIntervalMs",
|
|
77
|
+
),
|
|
78
|
+
projectionRebuildHeapUsageLimit: parseProjectionRebuildHeapUsageLimit(
|
|
79
|
+
cfg.projectionRebuildHeapUsageLimit,
|
|
80
|
+
),
|
|
81
|
+
};
|
|
82
|
+
}
|
|
@@ -46,33 +46,41 @@ export type LedgerSecureReader = (filePath: string) => Promise<string>;
|
|
|
46
46
|
export type LedgerSecureBufferReader = (filePath: string) => Promise<Buffer>;
|
|
47
47
|
|
|
48
48
|
/**
|
|
49
|
-
* Parse
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
|
|
49
|
+
* Parse one ledger line. Permissive validation (any `eventType` string is
|
|
50
|
+
* admitted) — matches the historical `readAllMemoryLifecycleEvents` contract
|
|
51
|
+
* that projection rebuild depends on. Fail-open on malformed rows.
|
|
52
|
+
*/
|
|
53
|
+
function parseLifecycleLedgerLine(line: string): MemoryLifecycleEvent | null {
|
|
54
|
+
const row = line.trim();
|
|
55
|
+
if (!row) return null;
|
|
56
|
+
try {
|
|
57
|
+
const parsed = JSON.parse(row) as Partial<MemoryLifecycleEvent>;
|
|
58
|
+
if (
|
|
59
|
+
typeof parsed.eventId === "string" &&
|
|
60
|
+
typeof parsed.memoryId === "string" &&
|
|
61
|
+
typeof parsed.eventType === "string" &&
|
|
62
|
+
typeof parsed.timestamp === "string" &&
|
|
63
|
+
typeof parsed.actor === "string" &&
|
|
64
|
+
typeof parsed.ruleVersion === "string"
|
|
65
|
+
) {
|
|
66
|
+
return parsed as MemoryLifecycleEvent;
|
|
67
|
+
}
|
|
68
|
+
} catch {
|
|
69
|
+
// Ignore malformed rows (fail-open).
|
|
70
|
+
}
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Parse + canonically sort lifecycle rows from a line source.
|
|
53
76
|
*/
|
|
54
77
|
async function collectLifecycleEvents(
|
|
55
78
|
lines: AsyncIterable<string>,
|
|
56
79
|
): Promise<MemoryLifecycleEvent[]> {
|
|
57
80
|
const out: MemoryLifecycleEvent[] = [];
|
|
58
81
|
for await (const line of lines) {
|
|
59
|
-
const
|
|
60
|
-
if (
|
|
61
|
-
try {
|
|
62
|
-
const parsed = JSON.parse(row) as Partial<MemoryLifecycleEvent>;
|
|
63
|
-
if (
|
|
64
|
-
typeof parsed.eventId === "string" &&
|
|
65
|
-
typeof parsed.memoryId === "string" &&
|
|
66
|
-
typeof parsed.eventType === "string" &&
|
|
67
|
-
typeof parsed.timestamp === "string" &&
|
|
68
|
-
typeof parsed.actor === "string" &&
|
|
69
|
-
typeof parsed.ruleVersion === "string"
|
|
70
|
-
) {
|
|
71
|
-
out.push(parsed as MemoryLifecycleEvent);
|
|
72
|
-
}
|
|
73
|
-
} catch {
|
|
74
|
-
// Ignore malformed rows (fail-open).
|
|
75
|
-
}
|
|
82
|
+
const parsed = parseLifecycleLedgerLine(line);
|
|
83
|
+
if (parsed) out.push(parsed);
|
|
76
84
|
}
|
|
77
85
|
return sortMemoryLifecycleEvents(out);
|
|
78
86
|
}
|
|
@@ -116,27 +124,47 @@ async function* linesFromBuffer(
|
|
|
116
124
|
}
|
|
117
125
|
|
|
118
126
|
/**
|
|
119
|
-
*
|
|
120
|
-
*
|
|
121
|
-
*
|
|
127
|
+
* Yield each valid ledger row in file order. Unlike
|
|
128
|
+
* {@link readAllLifecycleEventsFromLedger}, this does not accumulate or sort
|
|
129
|
+
* the ledger first. ENOENT yields nothing; a locked secure store rethrows.
|
|
130
|
+
* Malformed rows are skipped.
|
|
122
131
|
*/
|
|
123
|
-
export async function
|
|
132
|
+
export async function* iterateLifecycleEventsFromLedger(
|
|
124
133
|
ledgerPath: string,
|
|
125
134
|
readSecureFile: LedgerSecureReader,
|
|
126
|
-
):
|
|
135
|
+
): AsyncGenerator<MemoryLifecycleEvent> {
|
|
136
|
+
let lines: AsyncGenerator<string>;
|
|
127
137
|
try {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
STATE_FILE_MAX_DECRYPT_BYTES,
|
|
133
|
-
),
|
|
138
|
+
lines = readMaybeEncryptedLines(
|
|
139
|
+
ledgerPath,
|
|
140
|
+
() => readSecureFile(ledgerPath),
|
|
141
|
+
STATE_FILE_MAX_DECRYPT_BYTES,
|
|
134
142
|
);
|
|
135
143
|
} catch (err) {
|
|
136
144
|
if (err instanceof SecureStoreLockedError) throw err;
|
|
137
145
|
if (!isErrnoCode(err, "ENOENT")) throw err;
|
|
138
|
-
return
|
|
146
|
+
return;
|
|
139
147
|
}
|
|
148
|
+
try {
|
|
149
|
+
for await (const line of lines) {
|
|
150
|
+
const parsed = parseLifecycleLedgerLine(line);
|
|
151
|
+
if (parsed) yield parsed;
|
|
152
|
+
}
|
|
153
|
+
} catch (err) {
|
|
154
|
+
if (err instanceof SecureStoreLockedError) throw err;
|
|
155
|
+
if (!isErrnoCode(err, "ENOENT")) throw err;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export async function readAllLifecycleEventsFromLedger(
|
|
160
|
+
ledgerPath: string,
|
|
161
|
+
readSecureFile: LedgerSecureReader,
|
|
162
|
+
): Promise<MemoryLifecycleEvent[]> {
|
|
163
|
+
const out: MemoryLifecycleEvent[] = [];
|
|
164
|
+
for await (const event of iterateLifecycleEventsFromLedger(ledgerPath, readSecureFile)) {
|
|
165
|
+
out.push(event);
|
|
166
|
+
}
|
|
167
|
+
return sortMemoryLifecycleEvents(out);
|
|
140
168
|
}
|
|
141
169
|
|
|
142
170
|
/**
|
|
@@ -69,6 +69,7 @@ export interface MemoryReadStoreDeps {
|
|
|
69
69
|
readParsedMemoriesFromPaths(
|
|
70
70
|
filePaths: string[],
|
|
71
71
|
batchSize?: number,
|
|
72
|
+
options?: CorpusReadOptions,
|
|
72
73
|
): Promise<MemoryFile[]>;
|
|
73
74
|
readStorageSecureFile(filePath: string): Promise<string>;
|
|
74
75
|
readWindowBoundedBatch(
|
|
@@ -466,7 +467,9 @@ export class MemoryReadStore {
|
|
|
466
467
|
* covers any hot→cold demotion that changes cold-tier contents) and
|
|
467
468
|
* expires after COLD_SCAN_CACHE_TTL_MS as a safety net.
|
|
468
469
|
*/
|
|
469
|
-
async readAllColdMemories(): Promise<MemoryFile[]> {
|
|
470
|
+
async readAllColdMemories(options?: CorpusReadOptions): Promise<MemoryFile[]> {
|
|
471
|
+
checkCorpusReadAbort(options);
|
|
472
|
+
const retainRaw = options?.retainRaw !== false;
|
|
470
473
|
const coldRoot = this.deps.resolveTierRootDir("cold");
|
|
471
474
|
|
|
472
475
|
// Read the on-disk cold-version sentinel BEFORE checking the cache so that
|
|
@@ -480,7 +483,7 @@ export class MemoryReadStore {
|
|
|
480
483
|
// decrypts per this manager's key, so a differently-keyed manager must not
|
|
481
484
|
// read a cold corpus another instance decrypted for the same coldRoot.
|
|
482
485
|
const keyId = this.deps.hotCacheKeyId();
|
|
483
|
-
const cached = this.deps.storageManagerClass.coldMemoriesCache.get(coldRoot);
|
|
486
|
+
const cached = retainRaw ? this.deps.storageManagerClass.coldMemoriesCache.get(coldRoot) : undefined;
|
|
484
487
|
if (
|
|
485
488
|
cached &&
|
|
486
489
|
cached.keyId === keyId &&
|
|
@@ -518,11 +521,14 @@ export class MemoryReadStore {
|
|
|
518
521
|
// and ensures that any memory with structuredAttributes is found regardless
|
|
519
522
|
// of which category it was written with.
|
|
520
523
|
await collectPaths(coldRoot);
|
|
521
|
-
const memories = await this.deps.readParsedMemoriesFromPaths(filePaths, 50);
|
|
524
|
+
const memories = await this.deps.readParsedMemoriesFromPaths(filePaths, 50, options);
|
|
522
525
|
|
|
523
526
|
// Store in cache with the sentinel version captured above so that any
|
|
524
527
|
// subsequent cold-version bump (by this or another process) invalidates it.
|
|
525
|
-
|
|
528
|
+
// A raw-free read stays out of the cache so it cannot evict the raw copy.
|
|
529
|
+
if (retainRaw) {
|
|
530
|
+
this.deps.storageManagerClass.coldMemoriesCache.set(coldRoot, { memories, loadedAt: Date.now(), coldVersion: currentColdVersion, keyId });
|
|
531
|
+
}
|
|
526
532
|
return memories;
|
|
527
533
|
}
|
|
528
534
|
|