@remnic/core 10.0.68 → 10.0.70
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 +80 -77
- package/dist/access-authorization-probe.js +81 -78
- package/dist/access-boundary.js +80 -77
- package/dist/access-cli.js +194 -191
- package/dist/access-cli.js.map +1 -1
- package/dist/access-extraction-force-flush.js +80 -77
- package/dist/access-http-briefing.js +81 -78
- package/dist/access-http-query.js +82 -79
- package/dist/access-http.js +104 -101
- package/dist/access-identity-continuity-surface.js +80 -77
- package/dist/access-lcm-surface.js +80 -77
- package/dist/access-mcp.js +93 -90
- package/dist/access-namespace-preflight.js +80 -77
- package/dist/access-observe-lcm-archive.d.ts +5 -0
- package/dist/access-observe-lcm-archive.d.ts.map +1 -0
- package/dist/access-observe-lcm-archive.js +9 -0
- package/dist/access-observe-objective-state.d.ts +8 -0
- package/dist/access-observe-objective-state.d.ts.map +1 -0
- package/dist/access-observe-objective-state.js +17 -0
- package/dist/access-observe-stage-timing.d.ts +11 -0
- package/dist/access-observe-stage-timing.d.ts.map +1 -0
- package/dist/access-observe-stage-timing.js +9 -0
- package/dist/access-observe-stage-timing.js.map +1 -0
- package/dist/access-observe-write-category.js +13 -13
- package/dist/access-observe-write-surface.d.ts.map +1 -1
- package/dist/access-observe-write-surface.js +80 -77
- package/dist/access-offline-manifest.js +8 -8
- package/dist/access-operations-batch.js +83 -80
- package/dist/access-operations.js +90 -87
- package/dist/access-recall-concurrency.js +80 -77
- package/dist/access-recall-response.js +80 -77
- package/dist/access-recall-surface.js +80 -77
- package/dist/access-schema.js +12 -12
- package/dist/access-service.d.ts +1 -0
- package/dist/access-service.d.ts.map +1 -1
- package/dist/access-service.js +80 -77
- package/dist/access-surface-catalog.js +82 -79
- package/dist/access-surface-catalog.js.map +1 -1
- package/dist/active-recall.js +24 -24
- package/dist/adapters/index.js +4 -4
- package/dist/adapters/registry.js +2 -2
- package/dist/briefing.js +15 -15
- package/dist/buffer.js +8 -8
- package/dist/calibration.js +4 -4
- package/dist/{capsule-crypto-GWVG7LGC.js → capsule-crypto-7FJQINUR.js} +2 -2
- package/dist/capsule-crypto-7FJQINUR.js.map +1 -0
- package/dist/causal-behavior.js +2 -2
- package/dist/causal-chain.js +2 -2
- package/dist/causal-consolidation.js +20 -20
- package/dist/causal-retrieval.js +2 -2
- package/dist/causal-trajectory-graph.js +3 -3
- package/dist/causal-trajectory.js +1 -1
- package/dist/{chunk-NCZGGGWI.js → chunk-2BBMCMJS.js} +12 -12
- package/dist/{chunk-UVVFPCPX.js → chunk-2GV24KFD.js} +2 -2
- package/dist/{chunk-OWHERGF2.js → chunk-2NLLXCJG.js} +2 -2
- package/dist/{chunk-QDZWQBLU.js → chunk-2R7OCBRR.js} +2 -2
- package/dist/{chunk-U2CAGMUD.js → chunk-2YPIFJHL.js} +2 -2
- package/dist/{chunk-4SOWMA55.js → chunk-3AASGFR4.js} +3 -3
- package/dist/{chunk-CI3MAAIY.js → chunk-3F7N4I5I.js} +2 -2
- package/dist/{chunk-PLCYKAWH.js → chunk-3ICAWZUU.js} +4 -4
- package/dist/{chunk-B4DXREWC.js → chunk-3W37NITP.js} +2 -2
- package/dist/{chunk-R4ZHF4K2.js → chunk-43FLJLT2.js} +4 -4
- package/dist/{chunk-RLVI2M5Q.js → chunk-4BOBW6H4.js} +2 -2
- package/dist/{chunk-HXFIILGF.js → chunk-4RASUAN3.js} +4 -4
- package/dist/{chunk-JLMOH5JA.js → chunk-5FMGPV34.js} +2 -2
- package/dist/{chunk-EI6M5NVK.js → chunk-5JBVPJCT.js} +2 -2
- package/dist/{chunk-74AY3S7U.js → chunk-5RW247F5.js} +10 -10
- package/dist/{chunk-P4TV3TQ2.js → chunk-5VQSKBNF.js} +5 -5
- package/dist/{chunk-H5DIRCZP.js → chunk-64U42JL5.js} +18 -18
- package/dist/{chunk-4XINO77L.js → chunk-66JOLMWS.js} +2 -2
- package/dist/{chunk-WHROVQ5Q.js → chunk-66PEACZP.js} +3 -3
- package/dist/{chunk-GKL6QYZO.js → chunk-6L63IN3M.js} +1 -1
- package/dist/{chunk-Y3XPSGNF.js → chunk-6QS5NQJH.js} +2 -2
- package/dist/{chunk-KOLJJN5G.js → chunk-AQNHEDXP.js} +11 -4
- package/dist/chunk-AQNHEDXP.js.map +1 -0
- package/dist/{chunk-6WKWL53V.js → chunk-B7YAIHAV.js} +127 -121
- package/dist/chunk-B7YAIHAV.js.map +1 -0
- package/dist/{chunk-M4IEZGCK.js → chunk-BFBS7OFQ.js} +7 -7
- package/dist/{chunk-4PG5R6BJ.js → chunk-BNU5TQLR.js} +2 -2
- package/dist/{chunk-B3MWELZ3.js → chunk-BW33YTF3.js} +2 -2
- package/dist/{chunk-H6VJSITJ.js → chunk-CIL4GHMD.js} +3 -3
- package/dist/{chunk-5UM4H64E.js → chunk-D3AGBN6G.js} +2 -2
- package/dist/{chunk-IG3EZ3TF.js → chunk-DM3UZUCN.js} +2 -2
- package/dist/{chunk-PMDOGXA2.js → chunk-EF3HNVL4.js} +2 -2
- package/dist/{chunk-TVE7W67N.js → chunk-EIZ4MK33.js} +2 -2
- package/dist/{chunk-AUFPPBC3.js → chunk-ENPJX3BB.js} +3 -3
- package/dist/{chunk-63A2BU2B.js → chunk-F4ZHKDBW.js} +5 -5
- package/dist/{chunk-3LBDEGQA.js → chunk-FDUCIUQ5.js} +2 -2
- package/dist/{chunk-CTH2CQCB.js → chunk-FM6UKZAE.js} +2 -2
- package/dist/{chunk-XVZA6VXC.js → chunk-FZVROZKC.js} +4 -4
- package/dist/{chunk-IAGVM7EY.js → chunk-GMSRLDVR.js} +2 -2
- package/dist/{chunk-HPB5TF55.js → chunk-GT4G2S2Y.js} +3 -3
- package/dist/{chunk-645WOU5I.js → chunk-H7BZBSP6.js} +4 -4
- package/dist/{chunk-5MGKGEL7.js → chunk-HNF7IMYQ.js} +2 -2
- package/dist/{chunk-W7K5BP3M.js → chunk-HQPFACXO.js} +2 -2
- package/dist/{chunk-TECK5IVY.js → chunk-HZNURRDS.js} +2 -2
- package/dist/{chunk-SXTVZ6JL.js → chunk-I6HRHJRA.js} +2 -2
- package/dist/{chunk-NHWDTOBN.js → chunk-ILD7N3XC.js} +20 -20
- package/dist/{chunk-4NV7O76Q.js → chunk-J5TVHCDG.js} +2 -2
- package/dist/{chunk-AL3TCNSJ.js → chunk-J6GZ2254.js} +4 -4
- package/dist/{chunk-OH55F76C.js → chunk-JEX2JL3U.js} +2 -2
- package/dist/{chunk-AVGDYYMA.js → chunk-JGQEF33K.js} +3 -3
- package/dist/{chunk-6P7U3HWG.js → chunk-K36RMINQ.js} +3 -3
- package/dist/{chunk-UW6LBFYN.js → chunk-K47G2CBP.js} +6 -6
- package/dist/{chunk-3RHU5MD7.js → chunk-K7LLGCPH.js} +2 -2
- package/dist/{chunk-BUKK5SWA.js → chunk-KQAFEZQX.js} +2 -2
- package/dist/chunk-L3E7MLMK.js +20 -0
- package/dist/chunk-L3E7MLMK.js.map +1 -0
- package/dist/{chunk-R2BIVU2Z.js → chunk-LTST4R7U.js} +2 -2
- package/dist/{chunk-IZBIDPMY.js → chunk-LYTKNK5O.js} +2 -2
- package/dist/{chunk-HH73M65H.js → chunk-MCFI6G7U.js} +3 -3
- package/dist/{chunk-V6SBA5O7.js → chunk-MFKBYXBY.js} +5 -5
- package/dist/{chunk-AYJQ644F.js → chunk-MK7HXGM6.js} +2 -2
- package/dist/{chunk-MOIJWWNA.js → chunk-MKMIY3E7.js} +13 -13
- package/dist/{chunk-EXQJMFRF.js → chunk-MRN2MKB7.js} +2 -2
- package/dist/{chunk-W26D2USW.js → chunk-MUH7RR6T.js} +6 -6
- package/dist/{chunk-64JLBFCQ.js → chunk-N7NAGECH.js} +2 -2
- package/dist/{chunk-75DCOS6I.js → chunk-OAKOAOEG.js} +67 -67
- package/dist/{chunk-FAF4A76Q.js → chunk-OAQO5AZJ.js} +512 -378
- package/dist/chunk-OAQO5AZJ.js.map +1 -0
- package/dist/{chunk-OPFVXM2D.js → chunk-OX6GY22Q.js} +2 -2
- package/dist/chunk-QCFZDYYG.js +34 -0
- package/dist/chunk-QCFZDYYG.js.map +1 -0
- package/dist/{chunk-MMCO4SRG.js → chunk-QNN4FYBD.js} +19 -11
- package/dist/chunk-QNN4FYBD.js.map +1 -0
- package/dist/{chunk-XWE3EOWX.js → chunk-RZED7FAU.js} +2 -2
- package/dist/{chunk-YSATDEBY.js → chunk-SBRUTD5O.js} +5 -5
- package/dist/{chunk-WVP2I6C5.js → chunk-SDL22RRW.js} +50 -11
- package/dist/chunk-SDL22RRW.js.map +1 -0
- package/dist/{chunk-ZORS5BG5.js → chunk-SQO5SEKA.js} +10 -10
- package/dist/{chunk-PRKNAIGZ.js → chunk-T6T2RMWW.js} +5 -5
- package/dist/{chunk-O5ZINWW3.js → chunk-THFSCUFC.js} +2 -2
- package/dist/{chunk-CIEEYVUB.js → chunk-TPX7XXY4.js} +2 -2
- package/dist/{chunk-OCK4D5PC.js → chunk-TSZP7GGJ.js} +2 -2
- package/dist/{chunk-L334PSLW.js → chunk-U2A56LLF.js} +8 -8
- package/dist/{chunk-TGQW3AMK.js → chunk-UIFWZMO7.js} +23 -23
- package/dist/{chunk-LN4X2NDA.js → chunk-UJ2FP2RV.js} +10 -10
- package/dist/{chunk-4MPAKYZU.js → chunk-VDYRXI3E.js} +5 -5
- package/dist/{chunk-IHCXMYSE.js → chunk-VPEIWMUT.js} +2 -2
- package/dist/{chunk-ELHJK7LM.js → chunk-WBN66HPI.js} +6 -6
- package/dist/{chunk-BJLCV2P3.js → chunk-WGRAB3KB.js} +2 -2
- package/dist/{chunk-IVUVQPTQ.js → chunk-WIW4LOZL.js} +2 -2
- package/dist/{chunk-5KDDAQ6K.js → chunk-WN2DASTS.js} +10 -10
- package/dist/chunk-WZU5AGO6.js +38 -0
- package/dist/chunk-WZU5AGO6.js.map +1 -0
- package/dist/{chunk-UVYI6VIX.js → chunk-X7Y7WX73.js} +1 -1
- package/dist/{chunk-5Y6VA6X2.js → chunk-XDUP6SWG.js} +2 -2
- package/dist/{chunk-GHHQLSSY.js → chunk-XR4QR3JT.js} +3 -3
- package/dist/{chunk-64S6X4IU.js → chunk-XTN54NML.js} +4 -4
- package/dist/{chunk-QGFR73SN.js → chunk-YIG4HPY4.js} +2 -2
- package/dist/{chunk-UYUFDTIX.js → chunk-YIXWY4OZ.js} +2 -2
- package/dist/{chunk-GJ4HU6LK.js → chunk-YLMTFRZR.js} +2 -2
- package/dist/{chunk-GGD2YDS6.js → chunk-YRMSHA7H.js} +2 -2
- package/dist/{chunk-Y7427SL7.js → chunk-ZG4GNPRE.js} +216 -50
- package/dist/chunk-ZG4GNPRE.js.map +1 -0
- package/dist/{chunk-KZZ5EZ7O.js → chunk-ZS5LREUZ.js} +2 -2
- package/dist/{chunk-JLGBJ4AX.js → chunk-ZYEWTVNQ.js} +10 -10
- package/dist/cli.js +131 -128
- package/dist/coding/export-okf-codegraph.js +14 -14
- package/dist/coding/pre-action-gate.js +2 -2
- package/dist/compounding/engine.js +16 -16
- package/dist/config.js +24 -24
- package/dist/connectors/codex-materialize-runner.js +17 -17
- package/dist/connectors/codex-materialize.js +2 -2
- package/dist/connectors/index.js +18 -18
- package/dist/consolidation-provenance-check.js +2 -2
- package/dist/convergence-refresh.js +18 -18
- package/dist/corpus-watermark.js +18 -18
- package/dist/deep-recall-http-glue.js +13 -13
- package/dist/deep-recall.js +2 -2
- package/dist/embedding-fallback.js +2 -2
- package/dist/entity-retrieval.js +18 -18
- package/dist/event-order-recall.js +2 -2
- package/dist/external-wiki-access.js +81 -78
- package/dist/extraction-span-materialize.js +2 -2
- package/dist/extraction.js +30 -30
- package/dist/{graph-edge-decay-OEX5OGMI.js → graph-edge-decay-CXTNJ7IQ.js} +6 -6
- package/dist/graph-edge-decay-CXTNJ7IQ.js.map +1 -0
- package/dist/graph-jsonl.js +4 -4
- package/dist/graph-snapshot.js +4 -4
- package/dist/graph.js +3 -3
- package/dist/harmonic-retrieval.js +15 -15
- package/dist/index.js +279 -276
- package/dist/lcm/archive.d.ts +12 -2
- package/dist/lcm/archive.d.ts.map +1 -1
- package/dist/lcm/archive.js +2 -2
- package/dist/lcm/engine.d.ts +59 -8
- package/dist/lcm/engine.d.ts.map +1 -1
- package/dist/lcm/engine.js +6 -6
- package/dist/lcm/index.js +16 -16
- package/dist/lcm/queue.d.ts +7 -3
- package/dist/lcm/queue.d.ts.map +1 -1
- package/dist/lcm/queue.js +1 -1
- package/dist/lcm/summarizer.d.ts +8 -1
- package/dist/lcm/summarizer.d.ts.map +1 -1
- package/dist/lcm/summarizer.js +3 -3
- package/dist/local-llm.js +4 -4
- package/dist/location/index.js +17 -17
- package/dist/maintenance/memory-governance.js +16 -16
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +15 -15
- package/dist/maintenance/rebuild-memory-projection-runner.js +18 -18
- package/dist/maintenance/rebuild-memory-projection.js +17 -17
- package/dist/memory-browse-operations.js +81 -78
- package/dist/memory-browse.js +6 -6
- package/dist/namespaces/migrate.js +16 -16
- package/dist/namespaces/search.js +7 -7
- package/dist/namespaces/storage.js +15 -15
- package/dist/offline-sync-changeset-generations.js +5 -5
- package/dist/offline-sync-embedding-generation.js +4 -4
- package/dist/offline-sync-snapshot-stream.js +7 -7
- package/dist/offline-sync.js +9 -9
- package/dist/operator-doctor-corpus.js +19 -19
- package/dist/operator-toolkit.js +39 -39
- package/dist/orchestration/buffer-drain-runner.js +194 -191
- package/dist/orchestration/buffer-drain-runner.js.map +1 -1
- package/dist/orchestration/extraction-persist.d.ts.map +1 -1
- package/dist/orchestration/maintenance.js +31 -31
- package/dist/orchestration/promotion-dedup-window.d.ts +118 -0
- package/dist/orchestration/promotion-dedup-window.d.ts.map +1 -0
- package/dist/orchestrator.js +195 -192
- package/dist/qmd.js +4 -4
- package/dist/recall-navigation-operations.js +81 -78
- package/dist/recall-navigation.js +80 -77
- package/dist/recall-state-view-admission.js +6 -6
- package/dist/recall-why-service.js +6 -6
- package/dist/resume-bundles.js +27 -27
- package/dist/search/factory.js +6 -6
- package/dist/search/index.js +12 -12
- package/dist/semantic-consolidation.js +23 -23
- package/dist/semantic-rule-promotion.js +15 -15
- package/dist/semantic-rule-verifier.js +15 -15
- package/dist/source-agent-qualifier.js +22 -22
- package/dist/standup/build.js +15 -15
- package/dist/storage.js +14 -14
- package/dist/summarizer.js +6 -6
- package/dist/support-passport/index.js +83 -80
- package/dist/transfer/backup.js +2 -2
- package/dist/transfer/capsule-export.js +2 -2
- package/dist/transfer/capsule-import.js +2 -2
- package/dist/transfer/export-okf.js +15 -15
- package/dist/verified-recall.js +15 -15
- package/dist/who-knows-cli.js +16 -16
- package/dist/who-knows.js +15 -15
- package/package.json +2 -2
- package/src/access-observe-lcm-archive.ts +30 -0
- package/src/access-observe-lcm-fail-closed.test.ts +118 -0
- package/src/access-observe-objective-state.ts +40 -0
- package/src/access-observe-stage-timing.test.ts +246 -0
- package/src/access-observe-stage-timing.ts +58 -0
- package/src/access-observe-write-surface.ts +45 -45
- package/src/access-service.ts +1 -0
- package/src/extraction-shared-promotion-parallel-dedup.test.ts +212 -0
- package/src/lcm/archive.ts +59 -7
- package/src/lcm/engine.ts +256 -53
- package/src/lcm/queue.test.ts +32 -14
- package/src/lcm/queue.ts +24 -16
- package/src/lcm/summarizer.ts +15 -2
- package/src/lcm-durability.test.ts +856 -0
- package/src/lcm-engine.test.ts +12 -3
- package/src/orchestration/extraction-persist.ts +40 -35
- package/src/orchestration/promotion-dedup-window.ts +319 -0
- package/dist/chunk-6WKWL53V.js.map +0 -1
- package/dist/chunk-FAF4A76Q.js.map +0 -1
- package/dist/chunk-KOLJJN5G.js.map +0 -1
- package/dist/chunk-MMCO4SRG.js.map +0 -1
- package/dist/chunk-WVP2I6C5.js.map +0 -1
- package/dist/chunk-Y7427SL7.js.map +0 -1
- /package/dist/{capsule-crypto-GWVG7LGC.js.map → access-observe-lcm-archive.js.map} +0 -0
- /package/dist/{graph-edge-decay-OEX5OGMI.js.map → access-observe-objective-state.js.map} +0 -0
- /package/dist/{chunk-NCZGGGWI.js.map → chunk-2BBMCMJS.js.map} +0 -0
- /package/dist/{chunk-UVVFPCPX.js.map → chunk-2GV24KFD.js.map} +0 -0
- /package/dist/{chunk-OWHERGF2.js.map → chunk-2NLLXCJG.js.map} +0 -0
- /package/dist/{chunk-QDZWQBLU.js.map → chunk-2R7OCBRR.js.map} +0 -0
- /package/dist/{chunk-U2CAGMUD.js.map → chunk-2YPIFJHL.js.map} +0 -0
- /package/dist/{chunk-4SOWMA55.js.map → chunk-3AASGFR4.js.map} +0 -0
- /package/dist/{chunk-CI3MAAIY.js.map → chunk-3F7N4I5I.js.map} +0 -0
- /package/dist/{chunk-PLCYKAWH.js.map → chunk-3ICAWZUU.js.map} +0 -0
- /package/dist/{chunk-B4DXREWC.js.map → chunk-3W37NITP.js.map} +0 -0
- /package/dist/{chunk-R4ZHF4K2.js.map → chunk-43FLJLT2.js.map} +0 -0
- /package/dist/{chunk-RLVI2M5Q.js.map → chunk-4BOBW6H4.js.map} +0 -0
- /package/dist/{chunk-HXFIILGF.js.map → chunk-4RASUAN3.js.map} +0 -0
- /package/dist/{chunk-JLMOH5JA.js.map → chunk-5FMGPV34.js.map} +0 -0
- /package/dist/{chunk-EI6M5NVK.js.map → chunk-5JBVPJCT.js.map} +0 -0
- /package/dist/{chunk-74AY3S7U.js.map → chunk-5RW247F5.js.map} +0 -0
- /package/dist/{chunk-P4TV3TQ2.js.map → chunk-5VQSKBNF.js.map} +0 -0
- /package/dist/{chunk-H5DIRCZP.js.map → chunk-64U42JL5.js.map} +0 -0
- /package/dist/{chunk-4XINO77L.js.map → chunk-66JOLMWS.js.map} +0 -0
- /package/dist/{chunk-WHROVQ5Q.js.map → chunk-66PEACZP.js.map} +0 -0
- /package/dist/{chunk-GKL6QYZO.js.map → chunk-6L63IN3M.js.map} +0 -0
- /package/dist/{chunk-Y3XPSGNF.js.map → chunk-6QS5NQJH.js.map} +0 -0
- /package/dist/{chunk-M4IEZGCK.js.map → chunk-BFBS7OFQ.js.map} +0 -0
- /package/dist/{chunk-4PG5R6BJ.js.map → chunk-BNU5TQLR.js.map} +0 -0
- /package/dist/{chunk-B3MWELZ3.js.map → chunk-BW33YTF3.js.map} +0 -0
- /package/dist/{chunk-H6VJSITJ.js.map → chunk-CIL4GHMD.js.map} +0 -0
- /package/dist/{chunk-5UM4H64E.js.map → chunk-D3AGBN6G.js.map} +0 -0
- /package/dist/{chunk-IG3EZ3TF.js.map → chunk-DM3UZUCN.js.map} +0 -0
- /package/dist/{chunk-PMDOGXA2.js.map → chunk-EF3HNVL4.js.map} +0 -0
- /package/dist/{chunk-TVE7W67N.js.map → chunk-EIZ4MK33.js.map} +0 -0
- /package/dist/{chunk-AUFPPBC3.js.map → chunk-ENPJX3BB.js.map} +0 -0
- /package/dist/{chunk-63A2BU2B.js.map → chunk-F4ZHKDBW.js.map} +0 -0
- /package/dist/{chunk-3LBDEGQA.js.map → chunk-FDUCIUQ5.js.map} +0 -0
- /package/dist/{chunk-CTH2CQCB.js.map → chunk-FM6UKZAE.js.map} +0 -0
- /package/dist/{chunk-XVZA6VXC.js.map → chunk-FZVROZKC.js.map} +0 -0
- /package/dist/{chunk-IAGVM7EY.js.map → chunk-GMSRLDVR.js.map} +0 -0
- /package/dist/{chunk-HPB5TF55.js.map → chunk-GT4G2S2Y.js.map} +0 -0
- /package/dist/{chunk-645WOU5I.js.map → chunk-H7BZBSP6.js.map} +0 -0
- /package/dist/{chunk-5MGKGEL7.js.map → chunk-HNF7IMYQ.js.map} +0 -0
- /package/dist/{chunk-W7K5BP3M.js.map → chunk-HQPFACXO.js.map} +0 -0
- /package/dist/{chunk-TECK5IVY.js.map → chunk-HZNURRDS.js.map} +0 -0
- /package/dist/{chunk-SXTVZ6JL.js.map → chunk-I6HRHJRA.js.map} +0 -0
- /package/dist/{chunk-NHWDTOBN.js.map → chunk-ILD7N3XC.js.map} +0 -0
- /package/dist/{chunk-4NV7O76Q.js.map → chunk-J5TVHCDG.js.map} +0 -0
- /package/dist/{chunk-AL3TCNSJ.js.map → chunk-J6GZ2254.js.map} +0 -0
- /package/dist/{chunk-OH55F76C.js.map → chunk-JEX2JL3U.js.map} +0 -0
- /package/dist/{chunk-AVGDYYMA.js.map → chunk-JGQEF33K.js.map} +0 -0
- /package/dist/{chunk-6P7U3HWG.js.map → chunk-K36RMINQ.js.map} +0 -0
- /package/dist/{chunk-UW6LBFYN.js.map → chunk-K47G2CBP.js.map} +0 -0
- /package/dist/{chunk-3RHU5MD7.js.map → chunk-K7LLGCPH.js.map} +0 -0
- /package/dist/{chunk-BUKK5SWA.js.map → chunk-KQAFEZQX.js.map} +0 -0
- /package/dist/{chunk-R2BIVU2Z.js.map → chunk-LTST4R7U.js.map} +0 -0
- /package/dist/{chunk-IZBIDPMY.js.map → chunk-LYTKNK5O.js.map} +0 -0
- /package/dist/{chunk-HH73M65H.js.map → chunk-MCFI6G7U.js.map} +0 -0
- /package/dist/{chunk-V6SBA5O7.js.map → chunk-MFKBYXBY.js.map} +0 -0
- /package/dist/{chunk-AYJQ644F.js.map → chunk-MK7HXGM6.js.map} +0 -0
- /package/dist/{chunk-MOIJWWNA.js.map → chunk-MKMIY3E7.js.map} +0 -0
- /package/dist/{chunk-EXQJMFRF.js.map → chunk-MRN2MKB7.js.map} +0 -0
- /package/dist/{chunk-W26D2USW.js.map → chunk-MUH7RR6T.js.map} +0 -0
- /package/dist/{chunk-64JLBFCQ.js.map → chunk-N7NAGECH.js.map} +0 -0
- /package/dist/{chunk-75DCOS6I.js.map → chunk-OAKOAOEG.js.map} +0 -0
- /package/dist/{chunk-OPFVXM2D.js.map → chunk-OX6GY22Q.js.map} +0 -0
- /package/dist/{chunk-XWE3EOWX.js.map → chunk-RZED7FAU.js.map} +0 -0
- /package/dist/{chunk-YSATDEBY.js.map → chunk-SBRUTD5O.js.map} +0 -0
- /package/dist/{chunk-ZORS5BG5.js.map → chunk-SQO5SEKA.js.map} +0 -0
- /package/dist/{chunk-PRKNAIGZ.js.map → chunk-T6T2RMWW.js.map} +0 -0
- /package/dist/{chunk-O5ZINWW3.js.map → chunk-THFSCUFC.js.map} +0 -0
- /package/dist/{chunk-CIEEYVUB.js.map → chunk-TPX7XXY4.js.map} +0 -0
- /package/dist/{chunk-OCK4D5PC.js.map → chunk-TSZP7GGJ.js.map} +0 -0
- /package/dist/{chunk-L334PSLW.js.map → chunk-U2A56LLF.js.map} +0 -0
- /package/dist/{chunk-TGQW3AMK.js.map → chunk-UIFWZMO7.js.map} +0 -0
- /package/dist/{chunk-LN4X2NDA.js.map → chunk-UJ2FP2RV.js.map} +0 -0
- /package/dist/{chunk-4MPAKYZU.js.map → chunk-VDYRXI3E.js.map} +0 -0
- /package/dist/{chunk-IHCXMYSE.js.map → chunk-VPEIWMUT.js.map} +0 -0
- /package/dist/{chunk-ELHJK7LM.js.map → chunk-WBN66HPI.js.map} +0 -0
- /package/dist/{chunk-BJLCV2P3.js.map → chunk-WGRAB3KB.js.map} +0 -0
- /package/dist/{chunk-IVUVQPTQ.js.map → chunk-WIW4LOZL.js.map} +0 -0
- /package/dist/{chunk-5KDDAQ6K.js.map → chunk-WN2DASTS.js.map} +0 -0
- /package/dist/{chunk-UVYI6VIX.js.map → chunk-X7Y7WX73.js.map} +0 -0
- /package/dist/{chunk-5Y6VA6X2.js.map → chunk-XDUP6SWG.js.map} +0 -0
- /package/dist/{chunk-GHHQLSSY.js.map → chunk-XR4QR3JT.js.map} +0 -0
- /package/dist/{chunk-64S6X4IU.js.map → chunk-XTN54NML.js.map} +0 -0
- /package/dist/{chunk-QGFR73SN.js.map → chunk-YIG4HPY4.js.map} +0 -0
- /package/dist/{chunk-UYUFDTIX.js.map → chunk-YIXWY4OZ.js.map} +0 -0
- /package/dist/{chunk-GJ4HU6LK.js.map → chunk-YLMTFRZR.js.map} +0 -0
- /package/dist/{chunk-GGD2YDS6.js.map → chunk-YRMSHA7H.js.map} +0 -0
- /package/dist/{chunk-KZZ5EZ7O.js.map → chunk-ZS5LREUZ.js.map} +0 -0
- /package/dist/{chunk-JLGBJ4AX.js.map → chunk-ZYEWTVNQ.js.map} +0 -0
package/src/lcm-engine.test.ts
CHANGED
|
@@ -887,9 +887,18 @@ test("close prevents deferred observe work from reinitializing the engine", asyn
|
|
|
887
887
|
await engine.ensureInitialized();
|
|
888
888
|
engine.close();
|
|
889
889
|
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
890
|
+
// SAFETY: the private summarize job runner is called directly to prove a
|
|
891
|
+
// closed engine never reopens.
|
|
892
|
+
await (engine as any).processSummarizeJob("session-1");
|
|
893
|
+
// A closed engine must reject acceptance: acknowledging a turn it can no
|
|
894
|
+
// longer archive would claim a copy that does not exist.
|
|
895
|
+
await assert.rejects(
|
|
896
|
+
() =>
|
|
897
|
+
engine.observeMessages("session-1", [
|
|
898
|
+
{ role: "user", content: "should not reopen" },
|
|
899
|
+
]),
|
|
900
|
+
/closed/,
|
|
901
|
+
);
|
|
893
902
|
|
|
894
903
|
assert.equal((engine as any).db, null);
|
|
895
904
|
assert.equal((engine as any).archive, null);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { classifyExtractionOrigin, type ExtractionSourceContext } from "./extraction-origin-context.js";
|
|
2
2
|
import { confirmActiveFactDuplicate, persistUnderFactDedupWindow } from "./fact-dedup-window.js";
|
|
3
|
+
import { writePromotionUnderFactDedupWindow } from "./promotion-dedup-window.js";
|
|
3
4
|
import { evaluateInjectionScreen, screenEntityForIndex, screenPersistStrings } from "./extraction-injection-gate.js";
|
|
4
5
|
/**
|
|
5
6
|
* Extraction persistence coordinator (issue #1526, seam 16).
|
|
@@ -464,25 +465,26 @@ export class ExtractionPersistCoordinator {
|
|
|
464
465
|
continue;
|
|
465
466
|
}
|
|
466
467
|
}
|
|
467
|
-
//
|
|
468
|
-
// the
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
...(options.toolScoped ? { toolScoped: true as const } : {}),
|
|
468
|
+
// Windowed destination write (PRRT_kwDORJXyws6zzdG4): the gate above
|
|
469
|
+
// ran outside the window; a twin committing meanwhile is suppressed
|
|
470
|
+
// by the in-window recheck. Contention throws fail-closed below.
|
|
471
|
+
const promotionWrite = await writePromotionUnderFactDedupWindow({
|
|
472
|
+
destination: targetStorage,
|
|
473
|
+
envelope: targetPromotionEnvelope,
|
|
474
|
+
promotion: options,
|
|
475
|
+
dedupContent,
|
|
476
|
+
rawContent,
|
|
477
|
+
toolScoped: options.toolScoped === true,
|
|
478
|
+
connector: sourceContext?.sourceConnector,
|
|
479
|
+
citationEnabled,
|
|
480
|
+
citationTemplate,
|
|
481
|
+
sameEntityGuardEnabled: false,
|
|
482
|
+
survivingAttrs: targetSurvivingAttrs,
|
|
483
|
+
supersession: { supersessionEnabled: lifecycleCaps.temporalSupersession, dependencyPropagationDelivery: this.dependencyPropagationDelivery, namespaceScope: this.deps.storageDirNamespace(targetStorage.dir) },
|
|
484
|
+
backfillTemporalBoundsOnDedupHit: this.deps.backfillTemporalBoundsOnDedupHit,
|
|
485
485
|
});
|
|
486
|
+
if (promotionWrite.suppressed) continue;
|
|
487
|
+
const targetPromotion = promotionWrite.result;
|
|
486
488
|
const promotedId = targetPromotion.id;
|
|
487
489
|
// #1645: if the TARGET namespace's own tombstone blocked this promotion,
|
|
488
490
|
// the row lands pending_review — do NOT supersede active target memories.
|
|
@@ -860,24 +862,27 @@ export class ExtractionPersistCoordinator {
|
|
|
860
862
|
// No same-connector active shared fact — fall through to write.
|
|
861
863
|
}
|
|
862
864
|
}
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
865
|
+
// Windowed destination write (PRRT_kwDORJXyws6zzdG4): the shared gate
|
|
866
|
+
// ran outside the window; a twin committing meanwhile is suppressed by
|
|
867
|
+
// the in-window recheck; contention throws fail-closed to the outer
|
|
868
|
+
// catch. Supersession rides along for the suppressed branch.
|
|
869
|
+
const sharedWrite = await writePromotionUnderFactDedupWindow({
|
|
870
|
+
destination: sharedStorage,
|
|
871
|
+
envelope: sharedPromotionEnvelope,
|
|
872
|
+
promotion: options,
|
|
873
|
+
dedupContent,
|
|
874
|
+
rawContent,
|
|
875
|
+
toolScoped,
|
|
876
|
+
connector: sourceContext?.sourceConnector,
|
|
877
|
+
citationEnabled,
|
|
878
|
+
citationTemplate,
|
|
879
|
+
sameEntityGuardEnabled: lifecycleCaps.temporalSupersession,
|
|
880
|
+
survivingAttrs: sharedSurvivingAttrs,
|
|
881
|
+
supersession: { supersessionEnabled: lifecycleCaps.temporalSupersession, dependencyPropagationDelivery: this.dependencyPropagationDelivery, namespaceScope: this.deps.storageDirNamespace(sharedStorage.dir) },
|
|
882
|
+
backfillTemporalBoundsOnDedupHit: this.deps.backfillTemporalBoundsOnDedupHit,
|
|
880
883
|
});
|
|
884
|
+
if (sharedWrite.suppressed) return promotedCopyId;
|
|
885
|
+
const sharedPromotion = sharedWrite.result;
|
|
881
886
|
const promotedId = sharedPromotion.id;
|
|
882
887
|
// #1645: if the shared namespace's own tombstone blocked this promotion,
|
|
883
888
|
// leave the row pending_review but do NOT supersede active shared memories.
|
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Promotion destination writes under the fact-dedup commit window (codex P2,
|
|
3
|
+
* PRRT_kwDORJXyws6zzdG4 / #3311). The persist-time promotion gates run
|
|
4
|
+
* OUTSIDE the window that serializes source writes, so with
|
|
5
|
+
* extractionQueueConcurrency > 1 two sessions promoting the same fact from
|
|
6
|
+
* different source namespaces can both gate-miss and both write the shared
|
|
7
|
+
* destination — duplicate active copies, because writeMemory mints a fresh id
|
|
8
|
+
* per write and does not coalesce active duplicates. This module wraps each
|
|
9
|
+
* promotion destination write in `persistUnderFactDedupWindow` keyed on the
|
|
10
|
+
* DESTINATION storage dir and rechecks with the same connector/content
|
|
11
|
+
* predicate the promotion gates confirm with, so a twin promotion committed
|
|
12
|
+
* between the gate and the write is suppressed instead of duplicated.
|
|
13
|
+
*
|
|
14
|
+
* Split out of extraction-persist.ts for the structural ratchet (#1995);
|
|
15
|
+
* behavior is a verbatim move of the wrapped writes the persist closure
|
|
16
|
+
* previously inlined.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { ContentHashIndex, type StorageManager } from "../index.js";
|
|
20
|
+
import { log } from "../logger.js";
|
|
21
|
+
import type { ImportanceScore, MemoryFile, MemoryFrontmatter, ProvenanceSource } from "../types.js";
|
|
22
|
+
import type { SealedMemoryEnvelope } from "../write-envelope.js";
|
|
23
|
+
import type { SealedWriteExtras } from "../storage.js";
|
|
24
|
+
import { normalizeStoredHashSource } from "./extraction-persist-citation.js";
|
|
25
|
+
import { readActiveMemoriesBothTiers } from "./extraction-persist-promotion.js";
|
|
26
|
+
import { applyTemporalSupersession, normalizeSupersessionKey } from "../temporal-supersession.js";
|
|
27
|
+
import type { DependencyPropagationDeliveryPort } from "./dependency-propagation-delivery.js";
|
|
28
|
+
import { persistUnderFactDedupWindow, type FactDedupWindowOutcome } from "./fact-dedup-window.js";
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* The promotion fields the windowed write and its suppression-path bounds
|
|
32
|
+
* backfill read. Structurally satisfied by both promotion closures' options
|
|
33
|
+
* objects in extraction-persist.ts (profile-target and shared).
|
|
34
|
+
*/
|
|
35
|
+
export interface PromotionDedupWriteFields {
|
|
36
|
+
category: string;
|
|
37
|
+
sourceMemoryId: string;
|
|
38
|
+
importance?: ImportanceScore;
|
|
39
|
+
intentGoal?: string;
|
|
40
|
+
intentActionType?: string;
|
|
41
|
+
intentEntityTypes?: string[];
|
|
42
|
+
memoryKind?: MemoryFrontmatter["memoryKind"];
|
|
43
|
+
validAt?: string;
|
|
44
|
+
invalidAt?: string;
|
|
45
|
+
observedAt?: string;
|
|
46
|
+
eventTimeSource?: "extracted" | "assumed";
|
|
47
|
+
sources?: ProvenanceSource[];
|
|
48
|
+
provenance?: "verified" | "unverified" | "none";
|
|
49
|
+
toolScoped?: true;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface PromotionDedupWriteArgs {
|
|
53
|
+
/** Promotion DESTINATION storage — the commit window is keyed on its dir. */
|
|
54
|
+
destination: StorageManager;
|
|
55
|
+
/** Composed sealed envelope for the promoted copy. */
|
|
56
|
+
envelope: SealedMemoryEnvelope;
|
|
57
|
+
/** The promotion closure's options (read structurally). */
|
|
58
|
+
promotion: PromotionDedupWriteFields;
|
|
59
|
+
/** Fact hash-key content (attrs-suffixed); pre-citation raw for others. */
|
|
60
|
+
dedupContent: string;
|
|
61
|
+
rawContent: string;
|
|
62
|
+
/** promotionWithholdsToolScope() result (shared) / forwarded marker (profile). */
|
|
63
|
+
toolScoped: boolean;
|
|
64
|
+
/** Raw source connector: trimmed for the recheck, raw for bounds backfill. */
|
|
65
|
+
connector: string | undefined;
|
|
66
|
+
citationEnabled: boolean;
|
|
67
|
+
citationTemplate: string;
|
|
68
|
+
/** lifecycleCaps.temporalSupersession on the shared path; false on the
|
|
69
|
+
* profile path (its gate predicate has no entity filter). When true AND the
|
|
70
|
+
* envelope carries an entityRef with non-empty surviving attrs, the recheck
|
|
71
|
+
* additionally requires the same entity (PR #402 Uybg guard parity). */
|
|
72
|
+
sameEntityGuardEnabled: boolean;
|
|
73
|
+
/** Envelope-surviving structured attributes (PR #402 Uybg guard input). */
|
|
74
|
+
survivingAttrs?: Record<string, string> | null;
|
|
75
|
+
/** When set, the suppression path runs the post-write supersession pass
|
|
76
|
+
* against the winning copy (PRRT_kwDORJXyws6z18qb): a suppressed
|
|
77
|
+
* later-dated promotion must still retire a conflicting shared fact dated
|
|
78
|
+
* between the events, mirroring the gate's hash-dedup-hit pass. */
|
|
79
|
+
supersession?: {
|
|
80
|
+
/** Caller's lifecycleCaps.temporalSupersession — without it the pass
|
|
81
|
+
* must not run even though the event-time `enabled` gate passes
|
|
82
|
+
* (PRRT_kwDORJXyws6z2o1t). */
|
|
83
|
+
supersessionEnabled: boolean;
|
|
84
|
+
dependencyPropagationDelivery?: DependencyPropagationDeliveryPort;
|
|
85
|
+
namespaceScope?: string;
|
|
86
|
+
};
|
|
87
|
+
backfillTemporalBoundsOnDedupHit: ExtractionPersistDeps["backfillTemporalBoundsOnDedupHit"];
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
type ExtractionPersistDeps = {
|
|
91
|
+
backfillTemporalBoundsOnDedupHit: (
|
|
92
|
+
targetStorage: StorageManager,
|
|
93
|
+
dedupContent: string,
|
|
94
|
+
bounds: {
|
|
95
|
+
invalidAt?: string;
|
|
96
|
+
validFrom?: string;
|
|
97
|
+
observedAt?: string;
|
|
98
|
+
eventTimeSource?: "extracted" | "assumed";
|
|
99
|
+
},
|
|
100
|
+
entityRef?: string,
|
|
101
|
+
sourceConnector?: string,
|
|
102
|
+
) => Promise<void>;
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Locate the active same-destination fact a raced promotion would duplicate:
|
|
107
|
+
* same connector/content predicate the promotion gates confirm with
|
|
108
|
+
* (`readActiveMemoriesBothTiers` + citation-aware stored normalization vs the
|
|
109
|
+
* normalized dedup key). The optional entity key mirrors the shared
|
|
110
|
+
* supersession branch's same-entity guard (PR #402 Uybg) so cross-entity
|
|
111
|
+
* same-content facts stay distinct. Returns undefined on scan error — an
|
|
112
|
+
* unverifiable state never suppresses (fail-open).
|
|
113
|
+
*/
|
|
114
|
+
async function findPromotionRaceDuplicate(args: {
|
|
115
|
+
storage: StorageManager;
|
|
116
|
+
dedupContent: string;
|
|
117
|
+
category: string;
|
|
118
|
+
connector: string | undefined;
|
|
119
|
+
citationEnabled: boolean;
|
|
120
|
+
citationTemplate: string;
|
|
121
|
+
entityKeyNorm?: string;
|
|
122
|
+
}): Promise<MemoryFile | undefined> {
|
|
123
|
+
try {
|
|
124
|
+
const allMems = await readActiveMemoriesBothTiers(args.storage);
|
|
125
|
+
const nc = args.connector?.trim() || undefined;
|
|
126
|
+
const incomingNormalized = ContentHashIndex.normalizeContent(args.dedupContent);
|
|
127
|
+
return allMems.find((m: MemoryFile) => {
|
|
128
|
+
if (m.frontmatter.category !== args.category) return false;
|
|
129
|
+
if ((m.frontmatter.status ?? "active") !== "active") return false;
|
|
130
|
+
if (
|
|
131
|
+
normalizeStoredHashSource(m.content ?? "", args.citationEnabled, args.citationTemplate) !==
|
|
132
|
+
incomingNormalized
|
|
133
|
+
)
|
|
134
|
+
return false;
|
|
135
|
+
if (args.entityKeyNorm !== undefined) {
|
|
136
|
+
if (!m.frontmatter.entityRef) return false;
|
|
137
|
+
if (normalizeSupersessionKey(m.frontmatter.entityRef) !== args.entityKeyNorm) return false;
|
|
138
|
+
}
|
|
139
|
+
return (m.frontmatter.sourceConnector?.trim() || undefined) === nc;
|
|
140
|
+
});
|
|
141
|
+
} catch (err) {
|
|
142
|
+
log.warn(
|
|
143
|
+
`promotion race recheck scan failed for ${args.storage.dir}; writing fail-open: ${err instanceof Error ? err.message : String(err)}`,
|
|
144
|
+
);
|
|
145
|
+
return undefined;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* In-window race confirm for a promotion destination. Authoritative index +
|
|
151
|
+
* miss is a clean NOT-duplicate answered WITHOUT the corpus scan, mirroring
|
|
152
|
+
* `confirmActiveFactDuplicate` and the window contract (one corpus confirm +
|
|
153
|
+
* one write, not a scan-length lock) — PRRT_kwDORJXyws6z18qS. On a hit, a
|
|
154
|
+
* non-authoritative index, or a probe failure, confirm against the corpus
|
|
155
|
+
* with the gates' predicate.
|
|
156
|
+
*/
|
|
157
|
+
export async function confirmPromotionRaceDuplicate(args: {
|
|
158
|
+
storage: StorageManager;
|
|
159
|
+
dedupContent: string;
|
|
160
|
+
category: string;
|
|
161
|
+
connector: string | undefined;
|
|
162
|
+
citationEnabled: boolean;
|
|
163
|
+
citationTemplate: string;
|
|
164
|
+
entityKeyNorm?: string;
|
|
165
|
+
}): Promise<boolean> {
|
|
166
|
+
try {
|
|
167
|
+
if (await args.storage.isFactContentHashAuthoritative()) {
|
|
168
|
+
if (!(await args.storage.hasFactContentHash(args.dedupContent))) return false;
|
|
169
|
+
}
|
|
170
|
+
} catch (probeErr) {
|
|
171
|
+
log.warn(
|
|
172
|
+
`promotion race recheck authoritative probe failed for ${args.storage.dir}; confirming against corpus: ${probeErr instanceof Error ? probeErr.message : String(probeErr)}`,
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
return (await findPromotionRaceDuplicate(args)) !== undefined;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Presence-iff-set extras, byte-equivalent to the conditional spreads the
|
|
180
|
+
* persist closure previously inlined at both promotion call sites.
|
|
181
|
+
*/
|
|
182
|
+
function buildPromotionWriteOptions(args: PromotionDedupWriteArgs): SealedWriteExtras {
|
|
183
|
+
const { promotion } = args;
|
|
184
|
+
const writeOptions: SealedWriteExtras = {
|
|
185
|
+
importance: promotion.importance,
|
|
186
|
+
lineage: [promotion.sourceMemoryId],
|
|
187
|
+
sourceMemoryId: promotion.sourceMemoryId,
|
|
188
|
+
intentGoal: promotion.intentGoal,
|
|
189
|
+
intentActionType: promotion.intentActionType,
|
|
190
|
+
intentEntityTypes: promotion.intentEntityTypes,
|
|
191
|
+
memoryKind: promotion.memoryKind,
|
|
192
|
+
contentHashSource: promotion.category === "fact" ? args.dedupContent : args.rawContent,
|
|
193
|
+
};
|
|
194
|
+
if (promotion.invalidAt) writeOptions.invalidAt = promotion.invalidAt;
|
|
195
|
+
// #1578 — forward bi-temporal bounds + ingestion provenance.
|
|
196
|
+
if (promotion.observedAt) writeOptions.observedAt = promotion.observedAt;
|
|
197
|
+
if (promotion.eventTimeSource) writeOptions.eventTimeSource = promotion.eventTimeSource;
|
|
198
|
+
if (promotion.sources && promotion.sources.length > 0) writeOptions.sources = promotion.sources;
|
|
199
|
+
if (promotion.provenance) writeOptions.provenance = promotion.provenance;
|
|
200
|
+
if (args.toolScoped) writeOptions.toolScoped = true;
|
|
201
|
+
return writeOptions;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Run one promotion destination write inside the destination-dir fact-dedup
|
|
206
|
+
* commit window: recheck (facts only; the same confirm the persist-time gate
|
|
207
|
+
* used, now under the lock) and suppress as a duplicate when a concurrent
|
|
208
|
+
* promotion committed the same content first. A suppressed promotion backfills
|
|
209
|
+
* temporal bounds onto the winning copy (#1671 parity with the gates' skip
|
|
210
|
+
* paths) and reports `{ suppressed: true }`. Lock contention throws
|
|
211
|
+
* fail-closed to the caller — never an unlocked write; the durable source
|
|
212
|
+
* fact is unaffected.
|
|
213
|
+
*/
|
|
214
|
+
export async function writePromotionUnderFactDedupWindow(
|
|
215
|
+
args: PromotionDedupWriteArgs,
|
|
216
|
+
): Promise<FactDedupWindowOutcome<Awaited<ReturnType<StorageManager["writeSealedMemory"]>>>> {
|
|
217
|
+
const { destination, envelope, promotion } = args;
|
|
218
|
+
// PRRT_kwDORJXyws6z2WsK: the recheck is fact-only, so for other categories
|
|
219
|
+
// the window would add lock acquisition and fail-closed throw risk with no
|
|
220
|
+
// dedup value. Write directly with the same extras.
|
|
221
|
+
if (promotion.category !== "fact") {
|
|
222
|
+
return {
|
|
223
|
+
suppressed: false,
|
|
224
|
+
result: await destination.writeSealedMemory(envelope, buildPromotionWriteOptions(args)),
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
const entityKeyNorm =
|
|
228
|
+
args.sameEntityGuardEnabled &&
|
|
229
|
+
envelope.entityRef &&
|
|
230
|
+
args.survivingAttrs &&
|
|
231
|
+
Object.keys(args.survivingAttrs).length > 0
|
|
232
|
+
? normalizeSupersessionKey(envelope.entityRef)
|
|
233
|
+
: undefined;
|
|
234
|
+
const outcome = await persistUnderFactDedupWindow(
|
|
235
|
+
destination.dir,
|
|
236
|
+
() =>
|
|
237
|
+
promotion.category === "fact"
|
|
238
|
+
? confirmPromotionRaceDuplicate({
|
|
239
|
+
storage: destination,
|
|
240
|
+
dedupContent: args.dedupContent,
|
|
241
|
+
category: promotion.category,
|
|
242
|
+
connector: args.connector,
|
|
243
|
+
citationEnabled: args.citationEnabled,
|
|
244
|
+
citationTemplate: args.citationTemplate,
|
|
245
|
+
entityKeyNorm,
|
|
246
|
+
})
|
|
247
|
+
: Promise.resolve(false),
|
|
248
|
+
() => destination.writeSealedMemory(envelope, buildPromotionWriteOptions(args)),
|
|
249
|
+
);
|
|
250
|
+
if (outcome.suppressed) {
|
|
251
|
+
log.info(`dedup: raced promotion duplicate suppressed in fact-dedup window for ${destination.dir}`);
|
|
252
|
+
if (promotion.invalidAt || promotion.validAt) {
|
|
253
|
+
await args.backfillTemporalBoundsOnDedupHit(
|
|
254
|
+
destination,
|
|
255
|
+
args.dedupContent,
|
|
256
|
+
{
|
|
257
|
+
invalidAt: promotion.invalidAt,
|
|
258
|
+
// #1707 thread 2 — carry the corrected start bound.
|
|
259
|
+
validFrom: promotion.validAt,
|
|
260
|
+
observedAt: promotion.observedAt,
|
|
261
|
+
eventTimeSource: promotion.eventTimeSource,
|
|
262
|
+
},
|
|
263
|
+
envelope.entityRef,
|
|
264
|
+
args.connector,
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
// PRRT_kwDORJXyws6z18qb: the unsuppressed path runs temporal supersession
|
|
268
|
+
// after the write; the suppressed promotion must too, or a later-dated
|
|
269
|
+
// raced promotion leaves a conflicting shared fact dated between the
|
|
270
|
+
// events active. Anchor to the winning copy with useCallerTimestamp —
|
|
271
|
+
// the gate's hash-dedup-hit shape (PR #402 Uyui/UvU1) — and fail open.
|
|
272
|
+
if (
|
|
273
|
+
args.supersession?.supersessionEnabled &&
|
|
274
|
+
promotion.category === "fact" &&
|
|
275
|
+
envelope.entityRef &&
|
|
276
|
+
args.survivingAttrs &&
|
|
277
|
+
Object.keys(args.survivingAttrs).length > 0
|
|
278
|
+
) {
|
|
279
|
+
try {
|
|
280
|
+
const winning = await findPromotionRaceDuplicate({
|
|
281
|
+
storage: destination,
|
|
282
|
+
dedupContent: args.dedupContent,
|
|
283
|
+
category: promotion.category,
|
|
284
|
+
connector: args.connector,
|
|
285
|
+
citationEnabled: args.citationEnabled,
|
|
286
|
+
citationTemplate: args.citationTemplate,
|
|
287
|
+
// PRRT_kwDORJXyws6z2o11: the winner ANCHORS the pass, so the lookup
|
|
288
|
+
// is always entity-guarded — without it (profile path) it could
|
|
289
|
+
// match another entity's equal-content fact and combine that fact's
|
|
290
|
+
// id with the incoming entity's attributes (PR #402 Uybg hazard).
|
|
291
|
+
entityKeyNorm: normalizeSupersessionKey(envelope.entityRef),
|
|
292
|
+
});
|
|
293
|
+
if (winning) {
|
|
294
|
+
await applyTemporalSupersession({
|
|
295
|
+
storage: destination,
|
|
296
|
+
newMemoryId: winning.frontmatter.id,
|
|
297
|
+
entityRef: envelope.entityRef,
|
|
298
|
+
structuredAttributes: { ...args.survivingAttrs },
|
|
299
|
+
// Ordering anchor mirrors the gate's supersessionOrderingAt: an
|
|
300
|
+
// incoming event date orders the pass, wall clock otherwise.
|
|
301
|
+
createdAt:
|
|
302
|
+
promotion.validAt && promotion.validAt.length > 0
|
|
303
|
+
? promotion.validAt
|
|
304
|
+
: new Date().toISOString(),
|
|
305
|
+
useCallerTimestamp: true,
|
|
306
|
+
enabled: !(promotion.eventTimeSource === "extracted" && !promotion.validAt),
|
|
307
|
+
namespaceScope: args.supersession.namespaceScope,
|
|
308
|
+
dependencyPropagationDelivery: args.supersession.dependencyPropagationDelivery,
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
} catch (supersessionErr) {
|
|
312
|
+
log.warn(
|
|
313
|
+
`promotion window supersession failed open for ${destination.dir}: ${supersessionErr instanceof Error ? supersessionErr.message : String(supersessionErr)}`,
|
|
314
|
+
);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
return outcome;
|
|
319
|
+
}
|