@remnic/core 10.0.49 → 10.0.51
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 +49 -48
- package/dist/access-authorization-probe.js +50 -49
- package/dist/access-boundary.d.ts +6 -1
- package/dist/access-boundary.d.ts.map +1 -1
- package/dist/access-boundary.js +49 -48
- package/dist/access-cli.js +120 -119
- package/dist/access-cli.js.map +1 -1
- package/dist/access-errors.d.ts.map +1 -1
- package/dist/access-errors.js +1 -1
- package/dist/access-extraction-force-flush.js +49 -48
- package/dist/access-http-briefing.d.ts +2 -0
- package/dist/access-http-briefing.d.ts.map +1 -1
- package/dist/access-http-briefing.js +50 -49
- package/dist/access-http-query.js +51 -50
- package/dist/access-http.d.ts.map +1 -1
- package/dist/access-http.js +70 -69
- package/dist/access-identity-continuity-surface.js +49 -48
- package/dist/access-lcm-surface.js +49 -48
- package/dist/access-mcp.js +60 -59
- package/dist/access-memory-search-fanout.js +2 -2
- package/dist/access-namespace-preflight.js +49 -48
- package/dist/access-observe-write-category.d.ts +1 -1
- package/dist/access-observe-write-category.d.ts.map +1 -1
- package/dist/access-observe-write-category.js +10 -10
- package/dist/access-observe-write-surface.d.ts.map +1 -1
- package/dist/access-observe-write-surface.js +49 -48
- package/dist/access-offline-manifest.js +6 -6
- package/dist/access-operations-batch.js +50 -49
- package/dist/access-operations.js +57 -56
- package/dist/access-recall-concurrency.js +49 -48
- package/dist/access-recall-diagnostics.js +2 -2
- package/dist/access-recall-response.js +49 -48
- package/dist/access-recall-surface.js +49 -48
- package/dist/access-schema.d.ts +245 -773
- package/dist/access-schema.d.ts.map +1 -1
- package/dist/access-schema.js +6 -6
- package/dist/access-service.d.ts +5 -0
- package/dist/access-service.d.ts.map +1 -1
- package/dist/access-service.js +49 -48
- package/dist/access-surface-catalog.js +51 -50
- package/dist/access-surface-catalog.js.map +1 -1
- package/dist/access-token-capabilities.js +2 -2
- package/dist/active-recall.js +16 -16
- package/dist/{auto-sync-BNCBMU6H.js → auto-sync-UQQ6YJSB.js} +3 -3
- package/dist/briefing-deadline.d.ts +7 -0
- package/dist/briefing-deadline.d.ts.map +1 -0
- package/dist/briefing-deadline.js +13 -0
- package/dist/briefing-window.d.ts +12 -4
- package/dist/briefing-window.d.ts.map +1 -1
- package/dist/briefing-window.js +3 -1
- package/dist/briefing.d.ts +2 -0
- package/dist/briefing.d.ts.map +1 -1
- package/dist/briefing.js +12 -11
- package/dist/buffer.js +3 -3
- package/dist/calibration.js +2 -2
- package/dist/{capsule-merge-RNQUP2VL.js → capsule-merge-D4BTIJGK.js} +3 -3
- package/dist/causal-consolidation.js +11 -11
- package/dist/{chunk-ESDOQEJM.js → chunk-2AKIVSHP.js} +2 -2
- package/dist/{chunk-BWF3KNSQ.js → chunk-2EXF6IIB.js} +2 -2
- package/dist/{chunk-AGCEJDG2.js → chunk-2ONKXEC7.js} +3 -3
- package/dist/{chunk-3NYIXVCT.js → chunk-2S6JADBB.js} +2 -2
- package/dist/chunk-2S6JADBB.js.map +1 -0
- package/dist/{chunk-ZSEFGDCI.js → chunk-2TGIKMMZ.js} +2 -2
- package/dist/{chunk-MCS4LIXP.js → chunk-43TUWQNR.js} +2 -2
- package/dist/{chunk-Q6PYCPHO.js → chunk-44EUCSG4.js} +2 -2
- package/dist/{chunk-2J6BIOVN.js → chunk-4FM35YIA.js} +3 -3
- package/dist/{chunk-2NLCDNGA.js → chunk-564YP3DW.js} +2 -2
- package/dist/{chunk-YY6PWFUC.js → chunk-6ZAU7LAR.js} +3 -3
- package/dist/{chunk-HCPVL6SF.js → chunk-72CBJ5VP.js} +3 -3
- package/dist/{chunk-AF4UIHON.js → chunk-74AY3S7U.js} +9 -9
- package/dist/{chunk-N6EM7ESC.js → chunk-75BK4XGV.js} +27 -19
- package/dist/chunk-75BK4XGV.js.map +1 -0
- package/dist/{chunk-5IDQVE4U.js → chunk-A3UFV4K3.js} +6 -6
- package/dist/{chunk-LWRIGKFM.js → chunk-A5L5KGDI.js} +19 -1
- package/dist/chunk-A5L5KGDI.js.map +1 -0
- package/dist/{chunk-RZDDNH63.js → chunk-A6JDT6QF.js} +27 -15
- package/dist/chunk-A6JDT6QF.js.map +1 -0
- package/dist/{chunk-XFND63OB.js → chunk-AHGOOZUC.js} +2 -2
- package/dist/{chunk-A7AISUJI.js → chunk-AJG2TAMR.js} +2 -2
- package/dist/{chunk-2BY56HE6.js → chunk-AZLD4CKG.js} +36 -36
- package/dist/{chunk-E47UQBYW.js → chunk-B35EQQ7L.js} +3 -3
- package/dist/{chunk-E6UNM67F.js → chunk-B6SPPYGJ.js} +2 -2
- package/dist/{chunk-CMOYD5CA.js → chunk-CCL2OXFO.js} +2 -2
- package/dist/{chunk-SVJ5JVZO.js → chunk-CK6GCGMX.js} +4 -4
- package/dist/{chunk-QZK6HG4J.js → chunk-DF6ZF2BN.js} +3 -3
- package/dist/{chunk-CPBYKOTC.js → chunk-DJFULNXK.js} +4 -4
- package/dist/{chunk-VGRBKEBH.js → chunk-DQOGZZJA.js} +3 -3
- package/dist/{chunk-YB3WN6XA.js → chunk-EC2KVA27.js} +12 -12
- package/dist/{chunk-FRXS6SC7.js → chunk-EKUI7I2X.js} +20 -4
- package/dist/chunk-EKUI7I2X.js.map +1 -0
- package/dist/{chunk-YNDLCWXS.js → chunk-EZ25VE3G.js} +4 -4
- package/dist/{chunk-WIBWONL4.js → chunk-EZS6QJNC.js} +2 -2
- package/dist/{chunk-C7WDWD35.js → chunk-FNNGWN3O.js} +5 -5
- package/dist/{chunk-PBWJVRKE.js → chunk-FT75AQ74.js} +3 -3
- package/dist/{chunk-VY6BD5KB.js → chunk-G6A3SGCE.js} +14 -12
- package/dist/chunk-G6A3SGCE.js.map +1 -0
- package/dist/{chunk-O4CA53UY.js → chunk-GBGU5OZP.js} +111 -104
- package/dist/chunk-GBGU5OZP.js.map +1 -0
- package/dist/{chunk-WJCAAVZX.js → chunk-GE3ZX6YK.js} +2 -2
- package/dist/{chunk-3PVKV3XL.js → chunk-HCIRR3BA.js} +3 -3
- package/dist/{chunk-BJTFLP3J.js → chunk-HEF4BRMT.js} +3 -3
- package/dist/{chunk-BMG3SAXA.js → chunk-HQUYENKK.js} +9 -12
- package/dist/chunk-HQUYENKK.js.map +1 -0
- package/dist/{chunk-6UDLN4IA.js → chunk-I6ORDT54.js} +2 -2
- package/dist/{chunk-N5PISU26.js → chunk-IN6U2BS5.js} +2 -2
- package/dist/{chunk-3INLTKTW.js → chunk-IUQXRRJB.js} +4 -4
- package/dist/{chunk-6W3QH3QW.js → chunk-JIJAWTRK.js} +388 -292
- package/dist/chunk-JIJAWTRK.js.map +1 -0
- package/dist/{chunk-4KS4IHKH.js → chunk-JX3E4XCW.js} +2 -2
- package/dist/{chunk-SW3WHVKN.js → chunk-KAJYSO3U.js} +2 -2
- package/dist/{chunk-D76AHITY.js → chunk-KETS5A62.js} +2 -2
- package/dist/{chunk-LCHFINL4.js → chunk-KIWO4327.js} +2 -2
- package/dist/{chunk-FC3ZFEXF.js → chunk-KJ3MDX4V.js} +2 -2
- package/dist/{chunk-7LCGFUMB.js → chunk-KQAFEZQX.js} +4 -4
- package/dist/{chunk-6TIVVOVU.js → chunk-L3EFYRYK.js} +9 -9
- package/dist/{chunk-3MUKQQ7A.js → chunk-LGEH473S.js} +24 -29
- package/dist/chunk-LGEH473S.js.map +1 -0
- package/dist/{chunk-X52SXKVC.js → chunk-LMB47UM6.js} +2 -2
- package/dist/{chunk-A3WXV7YU.js → chunk-LWMLBVGB.js} +2 -2
- package/dist/{chunk-KMTMMBEB.js → chunk-MC7PXJDP.js} +7 -7
- package/dist/{chunk-NMOEOQV4.js → chunk-MUNYUSBU.js} +2 -2
- package/dist/{chunk-VKUMKVEU.js → chunk-OCRLJJOV.js} +5 -5
- package/dist/{chunk-XSJL62LE.js → chunk-ON77FQXH.js} +2 -2
- package/dist/{chunk-C3HRYVGD.js → chunk-POJRFSJM.js} +3 -3
- package/dist/{chunk-LKRXGOQW.js → chunk-QA3Y2O6H.js} +2 -2
- package/dist/{chunk-23ZLN5X6.js → chunk-QT5MGENX.js} +14 -6
- package/dist/chunk-QT5MGENX.js.map +1 -0
- package/dist/{chunk-QFTNWDJ6.js → chunk-RA7YRL6H.js} +2 -2
- package/dist/{chunk-WQFHHJWK.js → chunk-RJDYDF5K.js} +4 -4
- package/dist/{chunk-E3DL3CAL.js → chunk-ROAKLYOL.js} +2 -2
- package/dist/{chunk-YFXRLRKP.js → chunk-S4OZKPBS.js} +7 -11
- package/dist/{chunk-YFXRLRKP.js.map → chunk-S4OZKPBS.js.map} +1 -1
- package/dist/{chunk-NWIT3U2N.js → chunk-S5CQI2KF.js} +2 -2
- package/dist/{chunk-P6P35FY4.js → chunk-SDESKFKT.js} +2 -2
- package/dist/{chunk-OD7ZXCUF.js → chunk-SFRBUBUZ.js} +10 -10
- package/dist/{chunk-7XFQSQYS.js → chunk-SHNM7RIT.js} +5 -4
- package/dist/chunk-SHNM7RIT.js.map +1 -0
- package/dist/{chunk-NH37SP6V.js → chunk-SJD334AG.js} +81 -78
- package/dist/chunk-SJD334AG.js.map +1 -0
- package/dist/{chunk-O7GNSHFF.js → chunk-SPTPBKZX.js} +2 -2
- package/dist/{chunk-HI53EK3Y.js → chunk-TC55M5PO.js} +2 -2
- package/dist/{chunk-SA6P2KS3.js → chunk-TEH5MDX5.js} +33 -17
- package/dist/chunk-TEH5MDX5.js.map +1 -0
- package/dist/{chunk-FOKTW6GY.js → chunk-UCLJTGP3.js} +2 -2
- package/dist/{chunk-FYZXZUGU.js → chunk-UQBOPCDK.js} +2 -2
- package/dist/{chunk-SUKPM6DV.js → chunk-UWMZT2TY.js} +47 -4
- package/dist/chunk-UWMZT2TY.js.map +1 -0
- package/dist/{chunk-754XN6LX.js → chunk-V27FK6D6.js} +2 -2
- package/dist/{chunk-DM6SOROF.js → chunk-V6Z4NAJZ.js} +2 -2
- package/dist/{chunk-5FNZ3G6X.js → chunk-VKQI47DJ.js} +4 -4
- package/dist/{chunk-NUK5XU3P.js → chunk-VUMWVYMR.js} +28 -13
- package/dist/chunk-VUMWVYMR.js.map +1 -0
- package/dist/{chunk-MPJDQC4G.js → chunk-WJCXUE2J.js} +2 -2
- package/dist/{chunk-VCBS5GRQ.js → chunk-WY66J4PI.js} +2 -2
- package/dist/chunk-X2KO4FK5.js +40 -0
- package/dist/chunk-X2KO4FK5.js.map +1 -0
- package/dist/{chunk-OOOX5C4X.js → chunk-XL2EE5W3.js} +4 -4
- package/dist/{chunk-V4GPFCJ7.js → chunk-XWJUFER3.js} +3 -3
- package/dist/{chunk-CZVNOTLR.js → chunk-XXR4A33N.js} +2 -2
- package/dist/{chunk-CRJX4OMN.js → chunk-Y3T5KQCR.js} +2 -2
- package/dist/{chunk-KNCR7APE.js → chunk-Y77FXULH.js} +8 -8
- package/dist/{chunk-2AV2U2SZ.js → chunk-YDIOMDBW.js} +2 -2
- package/dist/{chunk-IG747LRN.js → chunk-YO2JEX2Z.js} +2 -2
- package/dist/{chunk-HDT3DMUF.js → chunk-ZKK3VMWU.js} +2 -2
- package/dist/cli.js +92 -91
- package/dist/coding/export-okf-codegraph.js +9 -9
- package/dist/compounding/engine.js +11 -11
- package/dist/config.js +16 -16
- package/dist/connectors/codex-materialize-runner.js +10 -10
- package/dist/connectors/index.js +14 -14
- package/dist/consolidation-provenance-check.js +4 -4
- package/dist/consolidation-undo.js +2 -2
- package/dist/convergence-refresh.js +12 -12
- package/dist/corpus-watermark.js +14 -14
- package/dist/correction/passive-correction-notifications.d.ts +1 -1
- package/dist/correction/passive-correction-notifications.d.ts.map +1 -1
- package/dist/dashboard-runtime.js +2 -2
- package/dist/deep-recall-config.js +2 -2
- package/dist/deep-recall-http-glue.js +9 -9
- package/dist/{entity-canonical-id-migration-2YOLMOFC.js → entity-canonical-id-migration-F3JVG6K6.js} +3 -3
- package/dist/entity-canonical-id-migration-F3JVG6K6.js.map +1 -0
- package/dist/entity-retrieval.js +11 -11
- package/dist/explicit-capture.js +2 -2
- package/dist/external-wiki-access.js +50 -49
- package/dist/extraction-span-wire.d.ts.map +1 -1
- package/dist/extraction-span-wire.js +1 -1
- package/dist/extraction.js +22 -22
- package/dist/{first-start-migration-QVD22WWS.js → first-start-migration-A6PHIBUQ.js} +4 -4
- package/dist/harmonic-retrieval.js +10 -10
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +143 -135
- package/dist/lcm/engine.js +2 -2
- package/dist/lcm/index.js +5 -5
- package/dist/local-llm.js +2 -2
- package/dist/location/index.js +16 -16
- package/dist/location/tagging.d.ts +7 -5
- package/dist/location/tagging.d.ts.map +1 -1
- package/dist/maintenance/memory-governance.js +10 -10
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +10 -10
- package/dist/maintenance/rebuild-memory-projection-runner.js +12 -12
- package/dist/maintenance/rebuild-memory-projection.js +11 -11
- package/dist/memory-browse-http-glue.js +2 -2
- package/dist/memory-browse-operations.js +50 -49
- package/dist/namespaces/migrate.js +11 -11
- package/dist/namespaces/storage.js +10 -10
- package/dist/offline-sync-file-io.d.ts +15 -0
- package/dist/offline-sync-file-io.d.ts.map +1 -1
- package/dist/offline-sync-file-io.js +9 -1
- package/dist/offline-sync-impression-drain.js +2 -2
- package/dist/offline-sync-snapshot-stream.js +4 -4
- package/dist/offline-sync-upload-staging.js +2 -2
- package/dist/offline-sync.d.ts.map +1 -1
- package/dist/offline-sync.js +3 -3
- package/dist/operator-doctor-corpus.js +17 -17
- package/dist/operator-doctor-replica.js +4 -4
- package/dist/operator-toolkit.js +29 -29
- package/dist/orchestration/maintenance.js +25 -25
- package/dist/orchestrator.d.ts +1 -0
- package/dist/orchestrator.d.ts.map +1 -1
- package/dist/orchestrator.js +122 -121
- package/dist/page-versioning.js +2 -2
- package/dist/recall-navigation-http-glue.js +2 -2
- package/dist/recall-navigation-operations.js +50 -49
- package/dist/recall-navigation.js +49 -48
- package/dist/relay/mission.d.ts +731 -3403
- package/dist/relay/mission.d.ts.map +1 -1
- package/dist/replica-divergence.js +3 -3
- package/dist/resume-bundles.js +17 -17
- package/dist/schemas.d.ts +346 -1771
- package/dist/schemas.d.ts.map +1 -1
- package/dist/semantic-consolidation.js +15 -15
- package/dist/semantic-rule-promotion.js +10 -10
- package/dist/semantic-rule-verifier.js +10 -10
- package/dist/shared-context/manager.d.ts +13 -33
- package/dist/shared-context/manager.d.ts.map +1 -1
- package/dist/source-agent-qualifier.js +18 -18
- package/dist/standup/build.js +11 -11
- package/dist/storage/entity-canonical-id-migration-adapter.d.ts +1 -0
- package/dist/storage/entity-canonical-id-migration-adapter.d.ts.map +1 -1
- package/dist/storage/entity-canonical-id-migration.d.ts +7 -0
- package/dist/storage/entity-canonical-id-migration.d.ts.map +1 -1
- package/dist/storage/entity-canonical-id-references.d.ts +2 -0
- package/dist/storage/entity-canonical-id-references.d.ts.map +1 -1
- package/dist/storage/entity-migration-completion-cache.d.ts +46 -0
- package/dist/storage/entity-migration-completion-cache.d.ts.map +1 -0
- package/dist/storage/entity-store.d.ts +1 -8
- package/dist/storage/entity-store.d.ts.map +1 -1
- package/dist/storage/memory-read-store.d.ts +3 -2
- package/dist/storage/memory-read-store.d.ts.map +1 -1
- package/dist/storage/windowed-memory-read.d.ts +6 -0
- package/dist/storage/windowed-memory-read.d.ts.map +1 -1
- package/dist/storage.d.ts +2 -0
- package/dist/storage.d.ts.map +1 -1
- package/dist/storage.js +9 -9
- package/dist/summarizer.js +3 -3
- package/dist/support-passport/access-operations.d.ts +51 -51
- package/dist/support-passport/contracts.d.ts +85 -126
- package/dist/support-passport/contracts.d.ts.map +1 -1
- package/dist/support-passport/generated-batch.d.ts +3 -17
- package/dist/support-passport/generated-batch.d.ts.map +1 -1
- package/dist/support-passport/grant-contracts.d.ts +75 -381
- package/dist/support-passport/grant-contracts.d.ts.map +1 -1
- package/dist/support-passport/index.js +52 -51
- package/dist/support-passport/model-audit.d.ts +17 -82
- package/dist/support-passport/model-audit.d.ts.map +1 -1
- package/dist/support-passport/model-bridge.d.ts +12 -51
- package/dist/support-passport/model-bridge.d.ts.map +1 -1
- package/dist/support-passport/model-contracts.d.ts +53 -192
- package/dist/support-passport/model-contracts.d.ts.map +1 -1
- package/dist/tokens.js +3 -3
- package/dist/transfer/capsule-export.js +2 -2
- package/dist/transfer/capsule-import.js +4 -4
- package/dist/transfer/export-okf.js +10 -10
- package/dist/transfer/types.d.ts +32 -737
- package/dist/transfer/types.d.ts.map +1 -1
- package/dist/verified-recall.js +10 -10
- package/dist/who-knows-cli.js +12 -12
- package/dist/who-knows.js +11 -11
- package/package.json +3 -4
- package/src/access-boundary.test.ts +14 -0
- package/src/access-boundary.ts +48 -42
- package/src/access-category-alias.test.ts +9 -7
- package/src/access-errors.test.ts +21 -9
- package/src/access-errors.ts +45 -31
- package/src/access-http-briefing.ts +4 -0
- package/src/access-http.test.ts +41 -12
- package/src/access-http.ts +6 -0
- package/src/access-observe-write-category.ts +1 -1
- package/src/access-observe-write-surface.ts +24 -19
- package/src/access-operations-batch.ts +18 -2
- package/src/access-schema.ts +13 -17
- package/src/access-service.ts +5 -0
- package/src/briefing-abort.test.ts +173 -0
- package/src/briefing-deadline.test.ts +84 -0
- package/src/briefing-deadline.ts +33 -0
- package/src/briefing-window-abort.test.ts +47 -0
- package/src/briefing-window.ts +33 -4
- package/src/briefing.ts +23 -16
- package/src/correction/passive-correction-notifications-abort.test.ts +42 -0
- package/src/correction/passive-correction-notifications.ts +4 -0
- package/src/entity-lazy-compatibility.test.ts +229 -0
- package/src/entity-migration-completion-notification.test.ts +148 -0
- package/src/extraction-scope-source-agent.test.ts +6 -6
- package/src/extraction-span-wire.test.ts +28 -0
- package/src/extraction-span-wire.ts +34 -6
- package/src/index.ts +1 -1
- package/src/location/tagging.ts +14 -12
- package/src/offline-sync-file-io.ts +61 -2
- package/src/offline-sync.test.ts +313 -20
- package/src/offline-sync.ts +43 -21
- package/src/orchestrator-init.test.ts +49 -1
- package/src/orchestrator.ts +9 -8
- package/src/storage/entity-canonical-id-migration-adapter.ts +16 -1
- package/src/storage/entity-canonical-id-migration.ts +19 -10
- package/src/storage/entity-canonical-id-references.ts +24 -0
- package/src/storage/entity-migration-completion-cache.nofollow.test.ts +223 -0
- package/src/storage/entity-migration-completion-cache.ts +153 -0
- package/src/storage/entity-store.ts +58 -86
- package/src/storage/memory-read-store.ts +9 -3
- package/src/storage/windowed-memory-read.ts +8 -0
- package/src/storage-startup-nonblocking-migration.test.ts +183 -0
- package/src/storage.ts +7 -2
- package/src/testing/subjects/entity-migration-completion-cache.test.ts +90 -0
- package/dist/chunk-23ZLN5X6.js.map +0 -1
- package/dist/chunk-3MUKQQ7A.js.map +0 -1
- package/dist/chunk-3NYIXVCT.js.map +0 -1
- package/dist/chunk-6W3QH3QW.js.map +0 -1
- package/dist/chunk-7XFQSQYS.js.map +0 -1
- package/dist/chunk-BMG3SAXA.js.map +0 -1
- package/dist/chunk-FRXS6SC7.js.map +0 -1
- package/dist/chunk-LWRIGKFM.js.map +0 -1
- package/dist/chunk-N6EM7ESC.js.map +0 -1
- package/dist/chunk-NH37SP6V.js.map +0 -1
- package/dist/chunk-NUK5XU3P.js.map +0 -1
- package/dist/chunk-O4CA53UY.js.map +0 -1
- package/dist/chunk-RZDDNH63.js.map +0 -1
- package/dist/chunk-SA6P2KS3.js.map +0 -1
- package/dist/chunk-SUKPM6DV.js.map +0 -1
- package/dist/chunk-VY6BD5KB.js.map +0 -1
- /package/dist/{auto-sync-BNCBMU6H.js.map → auto-sync-UQQ6YJSB.js.map} +0 -0
- /package/dist/{entity-canonical-id-migration-2YOLMOFC.js.map → briefing-deadline.js.map} +0 -0
- /package/dist/{capsule-merge-RNQUP2VL.js.map → capsule-merge-D4BTIJGK.js.map} +0 -0
- /package/dist/{chunk-ESDOQEJM.js.map → chunk-2AKIVSHP.js.map} +0 -0
- /package/dist/{chunk-BWF3KNSQ.js.map → chunk-2EXF6IIB.js.map} +0 -0
- /package/dist/{chunk-AGCEJDG2.js.map → chunk-2ONKXEC7.js.map} +0 -0
- /package/dist/{chunk-ZSEFGDCI.js.map → chunk-2TGIKMMZ.js.map} +0 -0
- /package/dist/{chunk-MCS4LIXP.js.map → chunk-43TUWQNR.js.map} +0 -0
- /package/dist/{chunk-Q6PYCPHO.js.map → chunk-44EUCSG4.js.map} +0 -0
- /package/dist/{chunk-2J6BIOVN.js.map → chunk-4FM35YIA.js.map} +0 -0
- /package/dist/{chunk-2NLCDNGA.js.map → chunk-564YP3DW.js.map} +0 -0
- /package/dist/{chunk-YY6PWFUC.js.map → chunk-6ZAU7LAR.js.map} +0 -0
- /package/dist/{chunk-HCPVL6SF.js.map → chunk-72CBJ5VP.js.map} +0 -0
- /package/dist/{chunk-AF4UIHON.js.map → chunk-74AY3S7U.js.map} +0 -0
- /package/dist/{chunk-5IDQVE4U.js.map → chunk-A3UFV4K3.js.map} +0 -0
- /package/dist/{chunk-XFND63OB.js.map → chunk-AHGOOZUC.js.map} +0 -0
- /package/dist/{chunk-A7AISUJI.js.map → chunk-AJG2TAMR.js.map} +0 -0
- /package/dist/{chunk-2BY56HE6.js.map → chunk-AZLD4CKG.js.map} +0 -0
- /package/dist/{chunk-E47UQBYW.js.map → chunk-B35EQQ7L.js.map} +0 -0
- /package/dist/{chunk-E6UNM67F.js.map → chunk-B6SPPYGJ.js.map} +0 -0
- /package/dist/{chunk-CMOYD5CA.js.map → chunk-CCL2OXFO.js.map} +0 -0
- /package/dist/{chunk-SVJ5JVZO.js.map → chunk-CK6GCGMX.js.map} +0 -0
- /package/dist/{chunk-QZK6HG4J.js.map → chunk-DF6ZF2BN.js.map} +0 -0
- /package/dist/{chunk-CPBYKOTC.js.map → chunk-DJFULNXK.js.map} +0 -0
- /package/dist/{chunk-VGRBKEBH.js.map → chunk-DQOGZZJA.js.map} +0 -0
- /package/dist/{chunk-YB3WN6XA.js.map → chunk-EC2KVA27.js.map} +0 -0
- /package/dist/{chunk-YNDLCWXS.js.map → chunk-EZ25VE3G.js.map} +0 -0
- /package/dist/{chunk-WIBWONL4.js.map → chunk-EZS6QJNC.js.map} +0 -0
- /package/dist/{chunk-C7WDWD35.js.map → chunk-FNNGWN3O.js.map} +0 -0
- /package/dist/{chunk-PBWJVRKE.js.map → chunk-FT75AQ74.js.map} +0 -0
- /package/dist/{chunk-WJCAAVZX.js.map → chunk-GE3ZX6YK.js.map} +0 -0
- /package/dist/{chunk-3PVKV3XL.js.map → chunk-HCIRR3BA.js.map} +0 -0
- /package/dist/{chunk-BJTFLP3J.js.map → chunk-HEF4BRMT.js.map} +0 -0
- /package/dist/{chunk-6UDLN4IA.js.map → chunk-I6ORDT54.js.map} +0 -0
- /package/dist/{chunk-N5PISU26.js.map → chunk-IN6U2BS5.js.map} +0 -0
- /package/dist/{chunk-3INLTKTW.js.map → chunk-IUQXRRJB.js.map} +0 -0
- /package/dist/{chunk-4KS4IHKH.js.map → chunk-JX3E4XCW.js.map} +0 -0
- /package/dist/{chunk-SW3WHVKN.js.map → chunk-KAJYSO3U.js.map} +0 -0
- /package/dist/{chunk-D76AHITY.js.map → chunk-KETS5A62.js.map} +0 -0
- /package/dist/{chunk-LCHFINL4.js.map → chunk-KIWO4327.js.map} +0 -0
- /package/dist/{chunk-FC3ZFEXF.js.map → chunk-KJ3MDX4V.js.map} +0 -0
- /package/dist/{chunk-7LCGFUMB.js.map → chunk-KQAFEZQX.js.map} +0 -0
- /package/dist/{chunk-6TIVVOVU.js.map → chunk-L3EFYRYK.js.map} +0 -0
- /package/dist/{chunk-X52SXKVC.js.map → chunk-LMB47UM6.js.map} +0 -0
- /package/dist/{chunk-A3WXV7YU.js.map → chunk-LWMLBVGB.js.map} +0 -0
- /package/dist/{chunk-KMTMMBEB.js.map → chunk-MC7PXJDP.js.map} +0 -0
- /package/dist/{chunk-NMOEOQV4.js.map → chunk-MUNYUSBU.js.map} +0 -0
- /package/dist/{chunk-VKUMKVEU.js.map → chunk-OCRLJJOV.js.map} +0 -0
- /package/dist/{chunk-XSJL62LE.js.map → chunk-ON77FQXH.js.map} +0 -0
- /package/dist/{chunk-C3HRYVGD.js.map → chunk-POJRFSJM.js.map} +0 -0
- /package/dist/{chunk-LKRXGOQW.js.map → chunk-QA3Y2O6H.js.map} +0 -0
- /package/dist/{chunk-QFTNWDJ6.js.map → chunk-RA7YRL6H.js.map} +0 -0
- /package/dist/{chunk-WQFHHJWK.js.map → chunk-RJDYDF5K.js.map} +0 -0
- /package/dist/{chunk-E3DL3CAL.js.map → chunk-ROAKLYOL.js.map} +0 -0
- /package/dist/{chunk-NWIT3U2N.js.map → chunk-S5CQI2KF.js.map} +0 -0
- /package/dist/{chunk-P6P35FY4.js.map → chunk-SDESKFKT.js.map} +0 -0
- /package/dist/{chunk-OD7ZXCUF.js.map → chunk-SFRBUBUZ.js.map} +0 -0
- /package/dist/{chunk-O7GNSHFF.js.map → chunk-SPTPBKZX.js.map} +0 -0
- /package/dist/{chunk-HI53EK3Y.js.map → chunk-TC55M5PO.js.map} +0 -0
- /package/dist/{chunk-FOKTW6GY.js.map → chunk-UCLJTGP3.js.map} +0 -0
- /package/dist/{chunk-FYZXZUGU.js.map → chunk-UQBOPCDK.js.map} +0 -0
- /package/dist/{chunk-754XN6LX.js.map → chunk-V27FK6D6.js.map} +0 -0
- /package/dist/{chunk-DM6SOROF.js.map → chunk-V6Z4NAJZ.js.map} +0 -0
- /package/dist/{chunk-5FNZ3G6X.js.map → chunk-VKQI47DJ.js.map} +0 -0
- /package/dist/{chunk-MPJDQC4G.js.map → chunk-WJCXUE2J.js.map} +0 -0
- /package/dist/{chunk-VCBS5GRQ.js.map → chunk-WY66J4PI.js.map} +0 -0
- /package/dist/{chunk-OOOX5C4X.js.map → chunk-XL2EE5W3.js.map} +0 -0
- /package/dist/{chunk-V4GPFCJ7.js.map → chunk-XWJUFER3.js.map} +0 -0
- /package/dist/{chunk-CZVNOTLR.js.map → chunk-XXR4A33N.js.map} +0 -0
- /package/dist/{chunk-CRJX4OMN.js.map → chunk-Y3T5KQCR.js.map} +0 -0
- /package/dist/{chunk-KNCR7APE.js.map → chunk-Y77FXULH.js.map} +0 -0
- /package/dist/{chunk-2AV2U2SZ.js.map → chunk-YDIOMDBW.js.map} +0 -0
- /package/dist/{chunk-IG747LRN.js.map → chunk-YO2JEX2Z.js.map} +0 -0
- /package/dist/{chunk-HDT3DMUF.js.map → chunk-ZKK3VMWU.js.map} +0 -0
- /package/dist/{first-start-migration-QVD22WWS.js.map → first-start-migration-A6PHIBUQ.js.map} +0 -0
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Amortized entity canonical-id migration completion verdict for entity reads.
|
|
3
|
+
*
|
|
4
|
+
* Key covers every getFingerprint input that is cheap to lstat: entity mutation
|
|
5
|
+
* version, entities/ dir, config/aliases.json, the journal, and reconcile
|
|
6
|
+
* markers. Full fingerprint (including entity entry enumeration) runs only on
|
|
7
|
+
* key miss.
|
|
8
|
+
*
|
|
9
|
+
* ponytail: in-place entity *content* edits still skip revalidation.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { lstat, realpath } from "node:fs/promises";
|
|
13
|
+
import path from "node:path";
|
|
14
|
+
import { SecureStoreLockedError } from "../secure-store/secure-fs.js";
|
|
15
|
+
import { isErrnoCode } from "../utils/errno.js";
|
|
16
|
+
import { pathIsInside } from "../utils/path-containment.js";
|
|
17
|
+
import { getFingerprint } from "./entity-canonical-id-migration.js";
|
|
18
|
+
import {
|
|
19
|
+
ENTITY_CANONICAL_ID_MIGRATION_FILE,
|
|
20
|
+
ENTITY_CANONICAL_ID_RECONCILE_CONSUMING_MARKER,
|
|
21
|
+
ENTITY_CANONICAL_ID_RECONCILE_MARKER,
|
|
22
|
+
entityCanonicalIdMigrationCompleted,
|
|
23
|
+
resolveHistoricalEntityCanonicalId,
|
|
24
|
+
} from "./entity-canonical-id-references.js";
|
|
25
|
+
|
|
26
|
+
export type EntityMigrationCompletionCacheDeps = {
|
|
27
|
+
readonly baseDir: string;
|
|
28
|
+
readonly entitiesDir: string;
|
|
29
|
+
getEntityMutationVersion(): number;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* True when `entitiesDir` is a real directory inside `baseDir`.
|
|
34
|
+
* lstat first so a symlinked root is never realpath'd into trust.
|
|
35
|
+
*/
|
|
36
|
+
export async function isTrustedEntitiesRoot(
|
|
37
|
+
baseDir: string,
|
|
38
|
+
entitiesDir: string,
|
|
39
|
+
): Promise<boolean> {
|
|
40
|
+
let rootStat;
|
|
41
|
+
try {
|
|
42
|
+
rootStat = await lstat(entitiesDir);
|
|
43
|
+
} catch (err) {
|
|
44
|
+
if (isErrnoCode(err, "ENOENT")) return false;
|
|
45
|
+
throw err;
|
|
46
|
+
}
|
|
47
|
+
if (rootStat.isSymbolicLink() || !rootStat.isDirectory()) return false;
|
|
48
|
+
let memoryRoot: string;
|
|
49
|
+
let entitiesReal: string;
|
|
50
|
+
try {
|
|
51
|
+
memoryRoot = await realpath(baseDir);
|
|
52
|
+
entitiesReal = await realpath(entitiesDir);
|
|
53
|
+
} catch (err) {
|
|
54
|
+
if (isErrnoCode(err, "ENOENT")) return false;
|
|
55
|
+
throw err;
|
|
56
|
+
}
|
|
57
|
+
return pathIsInside(memoryRoot, entitiesReal);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export class EntityMigrationCompletionCache {
|
|
61
|
+
private cache: { version: number; inputSig: string; completed: boolean } | undefined;
|
|
62
|
+
|
|
63
|
+
/** lstat identity for one fingerprint input path; nofollow; missing → stable token. */
|
|
64
|
+
private async inputPathSig(filePath: string): Promise<string> {
|
|
65
|
+
try {
|
|
66
|
+
const s = await lstat(filePath);
|
|
67
|
+
return `${s.dev}:${s.ino}:${s.mtimeMs}:${s.ctimeMs}:${s.size}:${s.isSymbolicLink() ? "L" : s.isDirectory() ? "D" : "F"}`;
|
|
68
|
+
} catch {
|
|
69
|
+
return "missing";
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Cheap coherence key. entities/ absence is uncacheable (random suffix) so
|
|
75
|
+
* we never pin a false complete.
|
|
76
|
+
*/
|
|
77
|
+
private async inputSig(deps: EntityMigrationCompletionCacheDeps): Promise<string> {
|
|
78
|
+
const stateDir = path.join(deps.baseDir, "state");
|
|
79
|
+
const entitiesSig = await this.inputPathSig(deps.entitiesDir);
|
|
80
|
+
if (entitiesSig === "missing") return `entities-missing:${Math.random()}`;
|
|
81
|
+
const [aliasesSig, journalSig, reconcileSig, consumingSig] = await Promise.all([
|
|
82
|
+
this.inputPathSig(path.join(deps.baseDir, "config", "aliases.json")),
|
|
83
|
+
this.inputPathSig(path.join(stateDir, ENTITY_CANONICAL_ID_MIGRATION_FILE)),
|
|
84
|
+
this.inputPathSig(path.join(stateDir, ENTITY_CANONICAL_ID_RECONCILE_MARKER)),
|
|
85
|
+
this.inputPathSig(path.join(stateDir, ENTITY_CANONICAL_ID_RECONCILE_CONSUMING_MARKER)),
|
|
86
|
+
]);
|
|
87
|
+
return [entitiesSig, aliasesSig, journalSig, reconcileSig, consumingSig].join("|");
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* True when journal completion attests to live fingerprint inputs.
|
|
92
|
+
* Untrusted entities roots (symlink / outside base) never reach getFingerprint.
|
|
93
|
+
*/
|
|
94
|
+
async isCompleted(deps: EntityMigrationCompletionCacheDeps): Promise<boolean> {
|
|
95
|
+
const version = deps.getEntityMutationVersion();
|
|
96
|
+
const inputSig = await this.inputSig(deps);
|
|
97
|
+
const cached = this.cache;
|
|
98
|
+
if (cached && cached.version === version && cached.inputSig === inputSig) return cached.completed;
|
|
99
|
+
if (!(await isTrustedEntitiesRoot(deps.baseDir, deps.entitiesDir))) {
|
|
100
|
+
this.cache = { version, inputSig, completed: false };
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
const completed = await entityCanonicalIdMigrationCompleted(path.join(deps.baseDir, "state"), () =>
|
|
104
|
+
getFingerprint(deps.baseDir, deps.entitiesDir, () => String(version)));
|
|
105
|
+
this.cache = { version, inputSig, completed };
|
|
106
|
+
return completed;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export type ReadEntityRenameDeps = {
|
|
111
|
+
readonly entitiesDir: string;
|
|
112
|
+
currentHistoricalIds(): Readonly<Record<string, string>>;
|
|
113
|
+
readStorageSecureFile(filePath: string): Promise<string>;
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
/** Resolve a physical entity path through historical id rename candidates. */
|
|
117
|
+
export async function readEntityFileResolvingRename(
|
|
118
|
+
deps: ReadEntityRenameDeps,
|
|
119
|
+
firstPath: string,
|
|
120
|
+
entityId: string,
|
|
121
|
+
): Promise<{ id: string; content: string } | null> {
|
|
122
|
+
try {
|
|
123
|
+
return { id: entityId, content: await deps.readStorageSecureFile(firstPath) };
|
|
124
|
+
} catch (err) {
|
|
125
|
+
if (err instanceof SecureStoreLockedError) throw err;
|
|
126
|
+
// ENOENT: renamed away (retry below). ELOOP: planted symlink — skip.
|
|
127
|
+
if (isErrnoCode(err, "ELOOP")) return null;
|
|
128
|
+
if (!isErrnoCode(err, "ENOENT")) throw err;
|
|
129
|
+
}
|
|
130
|
+
const historicalIds = deps.currentHistoricalIds();
|
|
131
|
+
const canonical = resolveHistoricalEntityCanonicalId(entityId, historicalIds);
|
|
132
|
+
const candidates = new Set([canonical]);
|
|
133
|
+
for (const legacy of Object.keys(historicalIds)) {
|
|
134
|
+
if (resolveHistoricalEntityCanonicalId(legacy, historicalIds) === canonical) candidates.add(legacy);
|
|
135
|
+
}
|
|
136
|
+
candidates.delete(entityId);
|
|
137
|
+
for (const candidate of candidates) {
|
|
138
|
+
try {
|
|
139
|
+
return {
|
|
140
|
+
id: candidate,
|
|
141
|
+
content: await deps.readStorageSecureFile(
|
|
142
|
+
path.join(deps.entitiesDir, `${candidate}.md`),
|
|
143
|
+
),
|
|
144
|
+
};
|
|
145
|
+
} catch (err) {
|
|
146
|
+
if (err instanceof SecureStoreLockedError) throw err;
|
|
147
|
+
// ENOENT/ELOOP: gone or a planted symlink — try the next candidate.
|
|
148
|
+
if (isErrnoCode(err, "ENOENT") || isErrnoCode(err, "ELOOP")) continue;
|
|
149
|
+
throw err;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
@@ -10,11 +10,17 @@
|
|
|
10
10
|
* - fragmented-entity merge (consolidation)
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
import { lstat, readdir,
|
|
13
|
+
import { lstat, readdir, unlink } from "node:fs/promises";
|
|
14
14
|
import path from "node:path";
|
|
15
15
|
import { checkCorpusReadAbort, type CorpusReadOptions } from "../corpus-read-cancellation.js";
|
|
16
16
|
import { normalizeEntityStructuredSection, sortStructuredSectionsBySchema } from "../entity-schema.js";
|
|
17
17
|
import { withEntityCanonicalMutationLock } from "./entity-canonical-id-lock.js";
|
|
18
|
+
import { assertSafeEntityId } from "./entity-id-safety.js";
|
|
19
|
+
import {
|
|
20
|
+
EntityMigrationCompletionCache,
|
|
21
|
+
isTrustedEntitiesRoot,
|
|
22
|
+
readEntityFileResolvingRename,
|
|
23
|
+
} from "./entity-migration-completion-cache.js";
|
|
18
24
|
import { isMalformedEntityRecord } from "../utils/malformed-entity-record.js";
|
|
19
25
|
import { log } from "../logger.js";
|
|
20
26
|
import {
|
|
@@ -33,7 +39,6 @@ import type {
|
|
|
33
39
|
ScoredEntity,
|
|
34
40
|
} from "../types.js";
|
|
35
41
|
import { isErrnoCode } from "../utils/errno.js";
|
|
36
|
-
import { pathIsInside } from "../utils/path-containment.js";
|
|
37
42
|
import {
|
|
38
43
|
reconcileIfJournalMovedSync,
|
|
39
44
|
requestEntityCanonicalIdReconcileSync,
|
|
@@ -89,6 +94,7 @@ export interface EntityStoreDeps {
|
|
|
89
94
|
export class EntityStore {
|
|
90
95
|
/** Filename → file identity of the last indexable check. Unchanged files are not reread. */
|
|
91
96
|
private readonly entityIndexableCache = new Map<string, { identity: string; ok: boolean }>();
|
|
97
|
+
private readonly migrationCompletion = new EntityMigrationCompletionCache();
|
|
92
98
|
|
|
93
99
|
constructor(
|
|
94
100
|
private readonly deps: EntityStoreDeps,
|
|
@@ -111,6 +117,7 @@ export class EntityStore {
|
|
|
111
117
|
method: string,
|
|
112
118
|
historicalIds: Readonly<Record<string, string>>,
|
|
113
119
|
): Promise<{ filePath: string; entity: EntityFile } | null> {
|
|
120
|
+
assertSafeEntityId(name);
|
|
114
121
|
const canonical = resolveHistoricalEntityCanonicalId(name, historicalIds);
|
|
115
122
|
for (const candidate of canonical === name ? [name] : [canonical, name]) {
|
|
116
123
|
const filePath = path.join(this.deps.entitiesDir, `${candidate}.md`);
|
|
@@ -143,6 +150,7 @@ export class EntityStore {
|
|
|
143
150
|
method: string,
|
|
144
151
|
mutate: (entity: EntityFile, historicalIds: Readonly<Record<string, string>>) => boolean,
|
|
145
152
|
): Promise<boolean> {
|
|
153
|
+
await this.deps.ensureDirectories();
|
|
146
154
|
const stateDir = path.join(this.deps.baseDir, "state");
|
|
147
155
|
return withEntityCanonicalMutationLock(stateDir, async () => {
|
|
148
156
|
for (let attempt = 0; attempt < 3; attempt += 1) {
|
|
@@ -375,83 +383,46 @@ export class EntityStore {
|
|
|
375
383
|
return normalized;
|
|
376
384
|
}
|
|
377
385
|
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
if (isErrnoCode(err, "ELOOP")) return null;
|
|
385
|
-
if (!isErrnoCode(err, "ENOENT")) throw err;
|
|
386
|
-
}
|
|
387
|
-
const historicalIds = this.deps.currentHistoricalIds();
|
|
388
|
-
const canonical = resolveHistoricalEntityCanonicalId(entityId, historicalIds);
|
|
389
|
-
const candidates = new Set([canonical]);
|
|
390
|
-
for (const legacy of Object.keys(historicalIds)) {
|
|
391
|
-
if (resolveHistoricalEntityCanonicalId(legacy, historicalIds) === canonical) candidates.add(legacy);
|
|
392
|
-
}
|
|
393
|
-
candidates.delete(entityId);
|
|
394
|
-
for (const candidate of candidates) {
|
|
386
|
+
async readEntity(name: string): Promise<string> {
|
|
387
|
+
const filePath = this.deps.resolveEntityFilePath(name);
|
|
388
|
+
if (filePath === null) return "";
|
|
389
|
+
// Physical hit wins. Miss skips rename/body-scan only when amortized
|
|
390
|
+
// completion attests live fingerprint inputs (not bare journal.complete).
|
|
391
|
+
const readDirect = async (): Promise<string | undefined> => {
|
|
395
392
|
try {
|
|
396
|
-
return
|
|
397
|
-
path.join(this.deps.entitiesDir, `${candidate}.md`),
|
|
398
|
-
) };
|
|
393
|
+
return await this.deps.readStorageSecureFile(filePath);
|
|
399
394
|
} catch (err) {
|
|
400
395
|
if (err instanceof SecureStoreLockedError) throw err;
|
|
401
|
-
|
|
402
|
-
if (isErrnoCode(err, "ENOENT") || isErrnoCode(err, "ELOOP")) continue;
|
|
396
|
+
if (isErrnoCode(err, "ENOENT") || isErrnoCode(err, "ELOOP")) return undefined;
|
|
403
397
|
throw err;
|
|
404
398
|
}
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
// resolving once the journal says migration completed and its file is gone.
|
|
417
|
-
if (this.deps.entityCanonicalMigrationComplete()) {
|
|
399
|
+
};
|
|
400
|
+
const direct = await readDirect();
|
|
401
|
+
if (direct !== undefined) return direct;
|
|
402
|
+
if (await this.migrationCompletion.isCompleted(this.deps)) return "";
|
|
403
|
+
const renamed = await readEntityFileResolvingRename(this.deps, filePath, name);
|
|
404
|
+
if (renamed !== null) return renamed.content;
|
|
405
|
+
// Deferred / fingerprint-stale: match legacy filename by body id.
|
|
406
|
+
let legacy: string | undefined;
|
|
407
|
+
for (const file of await this.readEntityDirectoryNames()) {
|
|
408
|
+
if (!file.endsWith(".md") || !(await this.isContainedRegularEntityFile(file))) continue;
|
|
409
|
+
let content: string;
|
|
418
410
|
try {
|
|
419
|
-
|
|
411
|
+
content = await this.deps.readStorageSecureFile(path.join(this.deps.entitiesDir, file));
|
|
420
412
|
} catch (err) {
|
|
421
413
|
if (err instanceof SecureStoreLockedError) throw err;
|
|
422
|
-
|
|
423
|
-
if (isErrnoCode(err, "ENOENT") || isErrnoCode(err, "ELOOP")) return "";
|
|
414
|
+
if (isErrnoCode(err, "ENOENT") || isErrnoCode(err, "ELOOP")) continue;
|
|
424
415
|
throw err;
|
|
425
416
|
}
|
|
417
|
+
if (isMalformedEntityRecord(content)) continue;
|
|
418
|
+
const entity = parseEntityFile(content, this.deps.entitySchemas);
|
|
419
|
+
if (!entity.name.trim() || !entity.type.trim()) continue;
|
|
420
|
+
if (this.deps.normalizeEntityName(entity.name, entity.type) !== name) continue;
|
|
421
|
+
if (legacy !== undefined) throw new Error(`Ambiguous legacy entity id: ${name}`);
|
|
422
|
+
legacy = content;
|
|
426
423
|
}
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
/**
|
|
431
|
-
* True when `entities/` is a real directory inside this memory root.
|
|
432
|
-
* `realpath` on a symlinked root would treat the link target as trusted,
|
|
433
|
-
* so the check uses `lstat` first and refuses the scan when the root
|
|
434
|
-
* itself is a symlink.
|
|
435
|
-
*/
|
|
436
|
-
private async isTrustedEntitiesRoot(): Promise<boolean> {
|
|
437
|
-
let rootStat;
|
|
438
|
-
try {
|
|
439
|
-
rootStat = await lstat(this.deps.entitiesDir);
|
|
440
|
-
} catch (err) {
|
|
441
|
-
if (isErrnoCode(err, "ENOENT")) return false;
|
|
442
|
-
throw err;
|
|
443
|
-
}
|
|
444
|
-
if (rootStat.isSymbolicLink() || !rootStat.isDirectory()) return false;
|
|
445
|
-
let memoryRoot: string;
|
|
446
|
-
let entitiesReal: string;
|
|
447
|
-
try {
|
|
448
|
-
memoryRoot = await realpath(this.deps.baseDir);
|
|
449
|
-
entitiesReal = await realpath(this.deps.entitiesDir);
|
|
450
|
-
} catch (err) {
|
|
451
|
-
if (isErrnoCode(err, "ENOENT")) return false;
|
|
452
|
-
throw err;
|
|
453
|
-
}
|
|
454
|
-
return pathIsInside(memoryRoot, entitiesReal);
|
|
424
|
+
// A peer may have renamed the legacy entry after the directory listing.
|
|
425
|
+
return (await readDirect()) ?? legacy ?? "";
|
|
455
426
|
}
|
|
456
427
|
|
|
457
428
|
/**
|
|
@@ -460,7 +431,7 @@ export class EntityStore {
|
|
|
460
431
|
* A symlinked entities root yields an empty list and is not read.
|
|
461
432
|
*/
|
|
462
433
|
private async readEntityDirectoryNames(): Promise<string[]> {
|
|
463
|
-
if (!(await this.
|
|
434
|
+
if (!(await isTrustedEntitiesRoot(this.deps.baseDir, this.deps.entitiesDir))) return [];
|
|
464
435
|
const entries = await readdir(this.deps.entitiesDir);
|
|
465
436
|
const { kept, ignored } = withoutMacOsReplicationJunk(entries, (name) => name);
|
|
466
437
|
warnIgnoredMacOsReplicationJunk(this.deps.entitiesDir, ignored, (message) => {
|
|
@@ -595,6 +566,7 @@ export class EntityStore {
|
|
|
595
566
|
|
|
596
567
|
/** Re-serialize every entity file into the compiled-truth timeline shape. */
|
|
597
568
|
async migrateEntityFilesToCompiledTruthTimeline(): Promise<{ total: number; migrated: number }> {
|
|
569
|
+
await this.deps.ensureDirectories();
|
|
598
570
|
const entityNames = await this.listEntityNames();
|
|
599
571
|
const stateDir = path.join(this.deps.baseDir, "state");
|
|
600
572
|
let migrated = 0;
|
|
@@ -605,14 +577,20 @@ export class EntityStore {
|
|
|
605
577
|
// path bumps only the status version, which never re-triggers the
|
|
606
578
|
// completed migration's reference scan.
|
|
607
579
|
const wrote = await withEntityCanonicalMutationLock(stateDir, async () => {
|
|
608
|
-
const
|
|
580
|
+
const filePath = this.deps.resolveEntityFilePath(entityName);
|
|
581
|
+
if (filePath === null) return false;
|
|
582
|
+
let raw: string;
|
|
583
|
+
try {
|
|
584
|
+
raw = await this.deps.readStorageSecureFile(filePath);
|
|
585
|
+
} catch (err) {
|
|
586
|
+
if (err instanceof SecureStoreLockedError || !isErrnoCode(err, "ENOENT")) throw err;
|
|
587
|
+
return false;
|
|
588
|
+
}
|
|
609
589
|
if (!raw || isMalformedEntityRecord(raw)) return false;
|
|
610
590
|
const parsed = parseEntityFile(raw, this.deps.entitySchemas);
|
|
611
591
|
if (!parsed.name.trim() || !parsed.type.trim()) return false;
|
|
612
592
|
const serialized = serializeEntityFile(parsed, this.deps.entitySchemas, this.deps.okfConformanceEnabled());
|
|
613
593
|
if (raw.trimEnd() === serialized.trimEnd()) return false;
|
|
614
|
-
const filePath = this.deps.resolveEntityFilePath(entityName);
|
|
615
|
-
if (filePath === null) return false;
|
|
616
594
|
await this.deps.writeStorageSecureFile(filePath, serialized);
|
|
617
595
|
this.deps.bumpEntityMutationVersion();
|
|
618
596
|
return true;
|
|
@@ -641,6 +619,7 @@ export class EntityStore {
|
|
|
641
619
|
incrementVersion?: boolean;
|
|
642
620
|
} = {},
|
|
643
621
|
): Promise<void> {
|
|
622
|
+
await this.deps.ensureDirectories();
|
|
644
623
|
return withEntityCanonicalMutationLock(
|
|
645
624
|
path.join(this.deps.baseDir, "state"),
|
|
646
625
|
() => this.updateEntitySynthesisUnlocked(name, synthesis, options),
|
|
@@ -659,17 +638,9 @@ export class EntityStore {
|
|
|
659
638
|
incrementVersion?: boolean;
|
|
660
639
|
} = {},
|
|
661
640
|
): Promise<void> {
|
|
662
|
-
const
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
const content = await this.deps.readStorageSecureFile(filePath);
|
|
666
|
-
entity = parseEntityFile(content, this.deps.entitySchemas);
|
|
667
|
-
} catch (err) {
|
|
668
|
-
if (err instanceof SecureStoreLockedError) throw err;
|
|
669
|
-
if (!isErrnoCode(err, "ENOENT")) throw err;
|
|
670
|
-
log.debug(`updateEntitySynthesis: entity file ${name}.md not found`);
|
|
671
|
-
return;
|
|
672
|
-
}
|
|
641
|
+
const located = await this.readEntityForMutation(name, "updateEntitySynthesis", this.deps.currentHistoricalIds());
|
|
642
|
+
if (!located) return;
|
|
643
|
+
const { filePath, entity } = located;
|
|
673
644
|
|
|
674
645
|
const updatedAt = options.updatedAt?.trim() || entity.synthesisUpdatedAt?.trim() || undefined;
|
|
675
646
|
const entityUpdatedAt = options.entityUpdatedAt?.trim() || updatedAt || entity.updated || new Date().toISOString();
|
|
@@ -721,7 +692,7 @@ export class EntityStore {
|
|
|
721
692
|
// #3207 semantics): resolve empty rather than following it. Within a
|
|
722
693
|
// trusted root, readdir directly so planted entries reach the secure
|
|
723
694
|
// read instead of being swallowed by the listing guards.
|
|
724
|
-
if (!(await this.
|
|
695
|
+
if (!(await isTrustedEntitiesRoot(this.deps.baseDir, this.deps.entitiesDir))) return [];
|
|
725
696
|
const listed = await readdir(this.deps.entitiesDir);
|
|
726
697
|
const { kept: entries, ignored } = withoutMacOsReplicationJunk(listed, (name) => name);
|
|
727
698
|
warnIgnoredMacOsReplicationJunk(this.deps.entitiesDir, ignored, (message) => {
|
|
@@ -753,11 +724,11 @@ export class EntityStore {
|
|
|
753
724
|
// rename: resolve through historical ids like a direct read.
|
|
754
725
|
// Still-present symlinks/dirs stay skipped.
|
|
755
726
|
if (await this.isMissingEntry(entry)) {
|
|
756
|
-
return await this.
|
|
727
|
+
return await readEntityFileResolvingRename(this.deps, entryPath, entityId);
|
|
757
728
|
}
|
|
758
729
|
return null;
|
|
759
730
|
}
|
|
760
|
-
return await this.
|
|
731
|
+
return await readEntityFileResolvingRename(this.deps, entryPath, entityId);
|
|
761
732
|
} catch (err) {
|
|
762
733
|
// Lock and decrypt failures abort the corpus read (the store
|
|
763
734
|
// is unusable or tampered); anything else isolates to this entry.
|
|
@@ -873,6 +844,7 @@ export class EntityStore {
|
|
|
873
844
|
* Returns count of files merged.
|
|
874
845
|
*/
|
|
875
846
|
async mergeFragmentedEntities(): Promise<number> {
|
|
847
|
+
await this.deps.ensureDirectories();
|
|
876
848
|
return withEntityCanonicalMutationLock(
|
|
877
849
|
path.join(this.deps.baseDir, "state"),
|
|
878
850
|
() => this.mergeFragmentedEntitiesUnlocked(),
|
|
@@ -41,7 +41,7 @@ export interface MemoryReadStoreDeps {
|
|
|
41
41
|
readonly artifactsDir: string;
|
|
42
42
|
readonly baseDir: string;
|
|
43
43
|
readonly bufferSurpriseLedgerPath: string;
|
|
44
|
-
collectActiveMemoryPaths(): Promise<string[]>;
|
|
44
|
+
collectActiveMemoryPaths(options?: CorpusReadOptions): Promise<string[]>;
|
|
45
45
|
readonly correctionsDir: string;
|
|
46
46
|
ensureDirectories(): Promise<void>;
|
|
47
47
|
readonly entitySchemas: PluginConfig["entitySchemas"] | undefined;
|
|
@@ -79,6 +79,7 @@ export interface MemoryReadStoreDeps {
|
|
|
79
79
|
remainingSlots: number,
|
|
80
80
|
remainingInspectionBudget: number,
|
|
81
81
|
readBatchSize: number,
|
|
82
|
+
options?: CorpusReadOptions,
|
|
82
83
|
): Promise<{ memories: MemoryFile[]; filePaths: string[] }>;
|
|
83
84
|
readonly reasoningTracesDir: string;
|
|
84
85
|
resolveTierRootDir(tier: "hot" | "cold"): string;
|
|
@@ -408,8 +409,10 @@ export class MemoryReadStore {
|
|
|
408
409
|
maxMemories?: number;
|
|
409
410
|
batchSize?: number;
|
|
410
411
|
updatedAfter?: Date;
|
|
412
|
+
abortSignal?: AbortSignal;
|
|
411
413
|
} = {}): Promise<{ memories: MemoryFile[]; filePaths: string[] }> {
|
|
412
|
-
const
|
|
414
|
+
const readOptions: CorpusReadOptions = { abortSignal: options.abortSignal };
|
|
415
|
+
const allPaths = await this.deps.collectActiveMemoryPaths(readOptions);
|
|
413
416
|
const sortedPaths = this.deps.orderWindowPaths(allPaths);
|
|
414
417
|
const maxMemories =
|
|
415
418
|
typeof options.maxMemories === "number" && Number.isFinite(options.maxMemories)
|
|
@@ -422,6 +425,8 @@ export class MemoryReadStore {
|
|
|
422
425
|
const selectedPaths: string[] = [];
|
|
423
426
|
|
|
424
427
|
for (let i = 0; i < sortedPaths.length; i += normalizedBatchSize) {
|
|
428
|
+
// Per batch: an abandoned caller must not keep the scan running (#3139).
|
|
429
|
+
checkCorpusReadAbort(readOptions);
|
|
425
430
|
if (
|
|
426
431
|
maxMemories !== undefined
|
|
427
432
|
&& (memories.length >= maxMemories || (maxCandidatePaths !== undefined && selectedPaths.length >= maxCandidatePaths))
|
|
@@ -436,7 +441,7 @@ export class MemoryReadStore {
|
|
|
436
441
|
const remainingInspectionBudget = maxCandidatePaths === undefined ? undefined : Math.max(0, maxCandidatePaths - selectedPaths.length);
|
|
437
442
|
const { memories: batchMemories, filePaths: parsedCandidatePaths } = remainingSlots === undefined
|
|
438
443
|
? {
|
|
439
|
-
memories: await this.deps.readParsedMemoriesFromPaths(candidateBatchPaths, normalizedBatchSize),
|
|
444
|
+
memories: await this.deps.readParsedMemoriesFromPaths(candidateBatchPaths, normalizedBatchSize, readOptions),
|
|
440
445
|
filePaths: candidateBatchPaths,
|
|
441
446
|
}
|
|
442
447
|
: await this.deps.readWindowBoundedBatch(
|
|
@@ -444,6 +449,7 @@ export class MemoryReadStore {
|
|
|
444
449
|
remainingSlots,
|
|
445
450
|
remainingInspectionBudget ?? remainingSlots,
|
|
446
451
|
normalizedBatchSize,
|
|
452
|
+
readOptions,
|
|
447
453
|
);
|
|
448
454
|
selectedPaths.push(...parsedCandidatePaths);
|
|
449
455
|
for (const memory of batchMemories) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { stat } from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
|
+
import { throwIfAborted } from "../abort-error.js";
|
|
3
4
|
import { getCachedMemories } from "../memory-cache.js";
|
|
4
5
|
import { readRawMemoryDocument } from "./memory-frontmatter-metadata.js";
|
|
5
6
|
import type { MemoryFile } from "../types.js";
|
|
@@ -11,6 +12,12 @@ export interface WindowedMemoryReadOptions {
|
|
|
11
12
|
updatedAfter?: Date;
|
|
12
13
|
/** Require candidate paths so malformed files can be reported by governance. */
|
|
13
14
|
includeMalformedPaths?: boolean;
|
|
15
|
+
/**
|
|
16
|
+
* Caller cancellation. The windowed scan stops at its next batch boundary
|
|
17
|
+
* when this aborts, so a cancelled read leaves no work running behind it
|
|
18
|
+
* (issue #3139).
|
|
19
|
+
*/
|
|
20
|
+
abortSignal?: AbortSignal;
|
|
14
21
|
}
|
|
15
22
|
|
|
16
23
|
export interface WindowedMemoryReadResult {
|
|
@@ -62,6 +69,7 @@ export async function readWindowedMemories(
|
|
|
62
69
|
})
|
|
63
70
|
)
|
|
64
71
|
).filter((memory): memory is MemoryFile => memory !== null);
|
|
72
|
+
throwIfAborted(options.abortSignal, "windowed memory read aborted");
|
|
65
73
|
const memories = orderWindowMemories(selected, hotCache.baseDir);
|
|
66
74
|
return { memories: rememberMemorySnapshots(memories), filePaths: memories.map((memory) => memory.path) };
|
|
67
75
|
}
|