@remnic/core 9.69.22 → 9.69.24
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/abstraction-nodes.js +3 -2
- package/dist/access-admin-ops-surface.d.ts +14 -12
- package/dist/access-admin-ops-surface.js +94 -91
- package/dist/access-authorization-probe.d.ts +14 -12
- package/dist/access-authorization-probe.js +95 -92
- package/dist/access-boundary.d.ts +14 -12
- package/dist/access-boundary.js +94 -91
- package/dist/access-citation-namespace.js +3 -3
- package/dist/access-cli.js +200 -195
- package/dist/access-cli.js.map +1 -1
- package/dist/access-coding-context-resolution.d.ts +2 -1
- package/dist/access-extraction-force-flush.d.ts +14 -12
- package/dist/access-extraction-force-flush.js +94 -91
- package/dist/access-health-types.d.ts +2 -1
- package/dist/access-http-briefing.d.ts +156 -0
- package/dist/access-http-briefing.js +233 -0
- package/dist/access-http-lcm-compaction.d.ts +14 -12
- package/dist/access-http-lifecycle-flush.d.ts +14 -12
- package/dist/access-http-offline-stream.d.ts +14 -12
- package/dist/access-http-query.d.ts +3 -2
- package/dist/access-http-query.js +96 -93
- package/dist/access-http.d.ts +15 -13
- package/dist/access-http.js +119 -114
- package/dist/access-identity-continuity-surface.d.ts +12 -10
- package/dist/access-identity-continuity-surface.js +94 -91
- package/dist/access-lcm-surface.d.ts +14 -12
- package/dist/access-lcm-surface.js +94 -91
- package/dist/access-mcp.d.ts +14 -12
- package/dist/access-mcp.js +105 -101
- package/dist/access-memory-search-fanout.d.ts +3 -2
- package/dist/{access-namespace-preflight-BMHhxoL9.d.ts → access-namespace-preflight-BsvmyENS.d.ts} +1 -1
- package/dist/access-namespace-preflight.d.ts +4 -3
- package/dist/access-namespace-preflight.js +94 -91
- package/dist/access-observe-transcript.d.ts +12 -10
- package/dist/access-observe-write-surface.d.ts +14 -12
- package/dist/access-observe-write-surface.js +94 -91
- package/dist/access-offline-manifest.d.ts +12 -10
- package/dist/access-offline-manifest.js +15 -15
- package/dist/access-operations-batch.js +99 -95
- package/dist/access-operations.d.ts +14 -12
- package/dist/access-operations.js +104 -100
- package/dist/access-recall-concurrency.d.ts +14 -12
- package/dist/access-recall-concurrency.js +94 -91
- package/dist/access-recall-response.d.ts +14 -12
- package/dist/access-recall-response.js +94 -91
- package/dist/access-recall-surface.d.ts +14 -12
- package/dist/access-recall-surface.js +94 -91
- package/dist/access-schema.js +8 -7
- package/dist/{access-service-BlsNXFwx.d.ts → access-service-DWdY9Mlk.d.ts} +7 -7
- package/dist/access-service-helpers.d.ts +14 -12
- package/dist/access-service.d.ts +14 -12
- package/dist/access-service.js +94 -91
- package/dist/access-surface-catalog.d.ts +14 -12
- package/dist/access-surface-catalog.js +97 -93
- package/dist/access-surface-catalog.js.map +1 -1
- package/dist/access-wearables-meetings-surface.d.ts +4 -3
- package/dist/action-confidence.d.ts +2 -1
- package/dist/active-memory-bridge.d.ts +2 -1
- package/dist/active-recall.d.ts +2 -1
- package/dist/active-recall.js +16 -13
- package/dist/active-recall.js.map +1 -1
- package/dist/adapters/index.js +7 -7
- package/dist/adapters/registry.js +3 -3
- package/dist/ambient-provenance.d.ts +2 -1
- package/dist/artifact-search.d.ts +2 -1
- package/dist/{auto-sync-GRUJFD2F.js → auto-sync-WNGG2GC2.js} +9 -9
- package/dist/behavior-learner.d.ts +2 -1
- package/dist/behavior-signals.d.ts +2 -1
- package/dist/bootstrap.d.ts +12 -10
- package/dist/briefing-window.d.ts +3 -2
- package/dist/briefing.d.ts +3 -2
- package/dist/briefing.js +36 -36
- package/dist/buffer-surprise-report.d.ts +2 -1
- package/dist/buffer-turn-helpers.d.ts +2 -1
- package/dist/buffer.d.ts +3 -2
- package/dist/buffer.js +13 -13
- package/dist/bulk-import/index.d.ts +4 -3
- package/dist/calibration.d.ts +2 -1
- package/dist/calibration.js +2 -1
- package/dist/calibration.js.map +1 -1
- package/dist/capabilities.d.ts +2 -1
- package/dist/{capsule-crypto-7FJQINUR.js → capsule-crypto-YO5QJ6L3.js} +2 -2
- package/dist/{capsule-merge-SEHERWWP.js → capsule-merge-IPG4IOFP.js} +5 -5
- package/dist/{catalog-C1qHsGew.d.ts → catalog-CDZLyiXL.d.ts} +1 -1
- package/dist/causal-behavior.d.ts +2 -1
- package/dist/causal-behavior.js +4 -3
- package/dist/causal-behavior.js.map +1 -1
- package/dist/causal-chain.js +4 -3
- package/dist/causal-consolidation.d.ts +2 -1
- package/dist/causal-consolidation.js +42 -42
- package/dist/causal-retrieval.js +4 -3
- package/dist/causal-retrieval.js.map +1 -1
- package/dist/causal-trajectory-graph.d.ts +5 -2
- package/dist/causal-trajectory-graph.js +6 -2
- package/dist/causal-trajectory-graph.js.map +1 -1
- package/dist/causal-trajectory.js +3 -2
- package/dist/{chunk-KTVSPFCQ.js → chunk-22FVR2KE.js} +2 -2
- package/dist/{chunk-7Y5V3LAP.js → chunk-27QVNSQ3.js} +2 -2
- package/dist/{chunk-R7LY4YKF.js → chunk-2EKJDQZB.js} +2 -2
- package/dist/{chunk-FCATYKQA.js → chunk-2IOHTUP3.js} +263 -13
- package/dist/chunk-2IOHTUP3.js.map +1 -0
- package/dist/{chunk-HOUST5UX.js → chunk-2ITE4C2Z.js} +2 -2
- package/dist/{chunk-JOYC4RYC.js → chunk-2JVJ7UHC.js} +2 -2
- package/dist/{chunk-X2NFFBTO.js → chunk-2L5FSNPC.js} +7 -7
- package/dist/{chunk-DUAJKRR2.js → chunk-2SGLMP2E.js} +2 -2
- package/dist/{chunk-6FUMA6AA.js → chunk-2Z4GS5E6.js} +2 -2
- package/dist/{chunk-G6MXN2G5.js → chunk-37KUQPX4.js} +6 -6
- package/dist/{chunk-DDQQY2E3.js → chunk-3BTFBQLO.js} +7 -7
- package/dist/chunk-3BTFBQLO.js.map +1 -0
- package/dist/{chunk-UR6PBX2J.js → chunk-3K7V3HE6.js} +35 -60
- package/dist/chunk-3K7V3HE6.js.map +1 -0
- package/dist/{chunk-WKAXBXVA.js → chunk-3QQTXYZ6.js} +2 -2
- package/dist/{chunk-DJUKBGAD.js → chunk-46EST7JO.js} +5 -5
- package/dist/chunk-46EST7JO.js.map +1 -0
- package/dist/{chunk-4ZWCQJNM.js → chunk-46WM6JX2.js} +5 -5
- package/dist/{chunk-CASWJ2HM.js → chunk-46ZPIGWI.js} +3 -3
- package/dist/{chunk-HTIIXNJQ.js → chunk-4FIH6MPS.js} +6 -3
- package/dist/chunk-4FIH6MPS.js.map +1 -0
- package/dist/{chunk-MCA2X4WH.js → chunk-4IHBN6WU.js} +2 -2
- package/dist/{chunk-W7IGLX4G.js → chunk-4O6WRBGE.js} +7 -7
- package/dist/chunk-4O6WRBGE.js.map +1 -0
- package/dist/{chunk-FB7LAJQM.js → chunk-4TKJGOCJ.js} +2 -2
- package/dist/{chunk-H7BZBSP6.js → chunk-52II7PB6.js} +4 -4
- package/dist/{chunk-N4Y5T6VD.js → chunk-54WHLA65.js} +4 -4
- package/dist/chunk-56Y6EKZN.js +24 -0
- package/dist/chunk-56Y6EKZN.js.map +1 -0
- package/dist/{chunk-YVLENSNS.js → chunk-5ATKSPEQ.js} +2 -2
- package/dist/{chunk-OLT3YTDH.js → chunk-5FAGCOZD.js} +37 -24
- package/dist/chunk-5FAGCOZD.js.map +1 -0
- package/dist/{chunk-ZVZHG7AI.js → chunk-5UBH37SL.js} +3 -3
- package/dist/{chunk-N3WZU76A.js → chunk-66OJSCI6.js} +2 -2
- package/dist/{chunk-D35XLYQ7.js → chunk-6CVNSDCZ.js} +11 -11
- package/dist/{chunk-KA2HZCMG.js → chunk-6NXSYYGP.js} +2 -2
- package/dist/{chunk-2HIJHXB3.js → chunk-6WHJ2EAB.js} +2 -2
- package/dist/{chunk-ZZM476NV.js → chunk-6XL73HQ4.js} +4 -4
- package/dist/{chunk-EJ5RKUXW.js → chunk-AQ7SYDVO.js} +2 -2
- package/dist/{chunk-2Z3DG5CQ.js → chunk-B3O3IRZN.js} +2 -2
- package/dist/{chunk-P77QX3ID.js → chunk-B6O2YLUR.js} +21 -20
- package/dist/chunk-B6O2YLUR.js.map +1 -0
- package/dist/{chunk-RACZB5HT.js → chunk-BD7JTBYP.js} +4 -4
- package/dist/{chunk-I762GW7V.js → chunk-BLJWYCNO.js} +5 -5
- package/dist/{chunk-ACPCA4NO.js → chunk-BMWN7B3Y.js} +6 -6
- package/dist/chunk-BMWN7B3Y.js.map +1 -0
- package/dist/{chunk-2NLLXCJG.js → chunk-BXLOS5AJ.js} +2 -2
- package/dist/{chunk-KK66P5GQ.js → chunk-C553ZTLF.js} +46 -48
- package/dist/chunk-C553ZTLF.js.map +1 -0
- package/dist/{chunk-GCASGYIO.js → chunk-C7HJ3NEI.js} +2 -2
- package/dist/{chunk-C24UZSFW.js → chunk-CEH2AFXG.js} +6 -6
- package/dist/{chunk-WVGXCBOC.js → chunk-D5F7TKMH.js} +2 -2
- package/dist/chunk-DGS34QSY.js +141 -0
- package/dist/chunk-DGS34QSY.js.map +1 -0
- package/dist/{chunk-X7Y7WX73.js → chunk-DQEMWVMT.js} +1 -1
- package/dist/{chunk-2OXIPF3A.js → chunk-DZD3WGCD.js} +4 -4
- package/dist/{chunk-UMEOXLIZ.js → chunk-E2P7TZTF.js} +5 -5
- package/dist/{chunk-6FC3IZ26.js → chunk-EBA5DA77.js} +2 -2
- package/dist/{chunk-N7JK2RKG.js → chunk-EOQ6FSQP.js} +2 -2
- package/dist/chunk-EUT2WCWS.js +54 -0
- package/dist/chunk-EUT2WCWS.js.map +1 -0
- package/dist/{chunk-YNDLCWXS.js → chunk-EZ25VE3G.js} +4 -4
- package/dist/{chunk-XDMI5VKE.js → chunk-FVOP3NT7.js} +2 -2
- package/dist/{chunk-NRLELHAY.js → chunk-G7NYVKD6.js} +2 -2
- package/dist/{chunk-B3W7GWQB.js → chunk-GHTSNGBZ.js} +2 -2
- package/dist/chunk-GKCXTSFF.js +17 -0
- package/dist/chunk-GKCXTSFF.js.map +1 -0
- package/dist/{chunk-LUDBXTBJ.js → chunk-H272ZPQ3.js} +5 -5
- package/dist/{chunk-SN4JXNLL.js → chunk-H3TKENU4.js} +2 -2
- package/dist/{chunk-4HYNMLT6.js → chunk-H4SEGIZY.js} +2 -2
- package/dist/{chunk-ACZN5ZXD.js → chunk-H54VWRPS.js} +13 -13
- package/dist/{chunk-HTP6XRUI.js → chunk-HM7RHKS4.js} +2 -2
- package/dist/{chunk-Z3SQEEB6.js → chunk-HWLIMQE2.js} +2 -2
- package/dist/{chunk-NQFCLAYT.js → chunk-ICKJ52PE.js} +99 -322
- package/dist/chunk-ICKJ52PE.js.map +1 -0
- package/dist/{chunk-2QSZNTDO.js → chunk-JBPKEARU.js} +7 -7
- package/dist/{chunk-DK6GMO6D.js → chunk-JEC4DVM3.js} +16 -16
- package/dist/chunk-JEC4DVM3.js.map +1 -0
- package/dist/{chunk-ET2CNMLD.js → chunk-JN72EPOE.js} +2 -2
- package/dist/{chunk-U62OK7BB.js → chunk-JPKK466T.js} +188 -21
- package/dist/chunk-JPKK466T.js.map +1 -0
- package/dist/{chunk-JCCWJHOP.js → chunk-JYZSRNR7.js} +2 -2
- package/dist/{chunk-7D6HXODY.js → chunk-K7724CH2.js} +2 -2
- package/dist/{chunk-X33MJFD2.js → chunk-KAXCYM66.js} +3 -3
- package/dist/{chunk-7RRUHJPG.js → chunk-KB2G2I6Q.js} +3 -3
- package/dist/{chunk-ZXBTTMGZ.js → chunk-KEF46KL2.js} +5 -5
- package/dist/{chunk-5UZXUTVO.js → chunk-KNA3BXXL.js} +6 -2
- package/dist/chunk-KNA3BXXL.js.map +1 -0
- package/dist/{chunk-UWTDARPF.js → chunk-KTWG7CNY.js} +6 -6
- package/dist/{chunk-XQYTSZW3.js → chunk-L7Z57SID.js} +6 -6
- package/dist/{chunk-JMAG7WZ7.js → chunk-LO4UKPKX.js} +3 -3
- package/dist/{chunk-LDWIPYJ6.js → chunk-LS2FHUML.js} +3 -3
- package/dist/{chunk-GDB4J2H3.js → chunk-MPC7TYTZ.js} +2 -2
- package/dist/{chunk-2GF27PGH.js → chunk-MRPOZBJC.js} +3 -3
- package/dist/{chunk-BR3OOQZQ.js → chunk-MWN6QEI2.js} +2 -2
- package/dist/{chunk-NZGO2GKL.js → chunk-MY5EDR46.js} +4 -4
- package/dist/{chunk-LCMUZDBZ.js → chunk-N3ZOP7KC.js} +3 -3
- package/dist/{chunk-TVW4C7PV.js → chunk-NB3LRY4T.js} +6 -6
- package/dist/{chunk-QJCWK2A7.js → chunk-ND6IQGSZ.js} +2 -2
- package/dist/{chunk-NOB2T2FC.js → chunk-NHLLKEKK.js} +2 -2
- package/dist/{chunk-7GSSHAXC.js → chunk-NLYVH66G.js} +6 -6
- package/dist/chunk-NLYVH66G.js.map +1 -0
- package/dist/{chunk-EJIWBTVM.js → chunk-O6ZNNVSP.js} +2 -2
- package/dist/{chunk-AG46P24C.js → chunk-OB3XMWX5.js} +4 -4
- package/dist/{chunk-NYRB7BKY.js → chunk-OEYZXDQI.js} +6 -6
- package/dist/chunk-OEYZXDQI.js.map +1 -0
- package/dist/{chunk-46QMN7I7.js → chunk-PAZY2ZKP.js} +6 -6
- package/dist/{chunk-EF4JUM55.js → chunk-PX4UMA5Z.js} +2 -2
- package/dist/chunk-QLK3SL7Q.js +242 -0
- package/dist/chunk-QLK3SL7Q.js.map +1 -0
- package/dist/{chunk-GGYDJJBO.js → chunk-QOOBICJF.js} +5 -5
- package/dist/chunk-QOOBICJF.js.map +1 -0
- package/dist/{chunk-NNZFPYYX.js → chunk-R7QY5ZVB.js} +2 -2
- package/dist/{chunk-RO2X2EYM.js → chunk-R7RTXEYG.js} +2 -2
- package/dist/{chunk-3LJPSRAN.js → chunk-RBCQVOVJ.js} +3 -3
- package/dist/{chunk-N4F7ZSSG.js → chunk-RC5DXXWC.js} +2 -2
- package/dist/{chunk-VLAQV4HT.js → chunk-RJYJ7KR7.js} +1636 -611
- package/dist/chunk-RJYJ7KR7.js.map +1 -0
- package/dist/{chunk-5CF2R4NS.js → chunk-RKBLRTNT.js} +2 -2
- package/dist/{chunk-5QZR2RA3.js → chunk-ROBHL3GL.js} +2 -2
- package/dist/{chunk-D5ZNOKR7.js → chunk-S7T2TY65.js} +3 -3
- package/dist/{chunk-C2JCVZWX.js → chunk-SCXURCVT.js} +25 -13
- package/dist/chunk-SCXURCVT.js.map +1 -0
- package/dist/{chunk-3LADWWMA.js → chunk-SJDQ5RSN.js} +3 -3
- package/dist/{chunk-45XVII3J.js → chunk-SMWTZLHE.js} +99 -89
- package/dist/{chunk-45XVII3J.js.map → chunk-SMWTZLHE.js.map} +1 -1
- package/dist/{chunk-XOQGUGJL.js → chunk-SO7SV523.js} +1 -1
- package/dist/{chunk-XOQGUGJL.js.map → chunk-SO7SV523.js.map} +1 -1
- package/dist/{chunk-CHWUICVY.js → chunk-SV4Z2S67.js} +4 -4
- package/dist/{chunk-JHP45LO5.js → chunk-SVXJLDDC.js} +9 -7
- package/dist/{chunk-JHP45LO5.js.map → chunk-SVXJLDDC.js.map} +1 -1
- package/dist/{chunk-SEF4IOFE.js → chunk-SWIKABKI.js} +2 -2
- package/dist/{chunk-JRRQ747T.js → chunk-TBXAY2SS.js} +13 -13
- package/dist/{chunk-J6A3CX5N.js → chunk-TG3FAM2S.js} +33 -8
- package/dist/{chunk-J6A3CX5N.js.map → chunk-TG3FAM2S.js.map} +1 -1
- package/dist/{chunk-XZBXINXZ.js → chunk-TNSCIPBQ.js} +4 -4
- package/dist/{chunk-S37NN23M.js → chunk-TVEONGBU.js} +2 -2
- package/dist/{chunk-5RPMTFB6.js → chunk-TWF35IC6.js} +3 -3
- package/dist/{chunk-BZTJEILZ.js → chunk-U6N5IBVY.js} +2 -2
- package/dist/{chunk-7TUB6JWA.js → chunk-UCKYTA2T.js} +3 -3
- package/dist/{chunk-PPOT6SPR.js → chunk-UNNPNBR2.js} +11 -11
- package/dist/{chunk-KQAFEZQX.js → chunk-VDX2J7OX.js} +2 -2
- package/dist/{chunk-J2R275FN.js → chunk-VVX5PZFS.js} +4 -4
- package/dist/{chunk-ME35HVDJ.js → chunk-VYCOJKSX.js} +2 -2
- package/dist/{chunk-F5UWQ4LX.js → chunk-W24T5YMW.js} +2 -2
- package/dist/{chunk-HDQUQBPW.js → chunk-W3K3HITK.js} +2 -2
- package/dist/{chunk-GDASG7NC.js → chunk-W4F3C7N7.js} +2 -2
- package/dist/{chunk-LFK623SF.js → chunk-WJHGDE7X.js} +5 -5
- package/dist/{chunk-DP4EBV7C.js → chunk-WMLEPWMO.js} +16 -16
- package/dist/chunk-WMLEPWMO.js.map +1 -0
- package/dist/{chunk-3Q6RD76J.js → chunk-WQZZDN5M.js} +5 -5
- package/dist/{chunk-ZJWDWPX6.js → chunk-X5K5TAQZ.js} +4 -4
- package/dist/{chunk-ZL3XX5LW.js → chunk-XM24CDZX.js} +3 -3
- package/dist/{chunk-JTWZKNUH.js → chunk-XOVKXJEP.js} +3 -3
- package/dist/{chunk-OGTKQ5MJ.js → chunk-XSGUMQQQ.js} +62 -50
- package/dist/chunk-XSGUMQQQ.js.map +1 -0
- package/dist/{chunk-X5YYSAZX.js → chunk-XUE46UYW.js} +3 -3
- package/dist/{chunk-DJORULH3.js → chunk-YRLHEEQT.js} +2 -2
- package/dist/{chunk-TA7UZZMW.js → chunk-Z3O53WZR.js} +2 -2
- package/dist/{chunk-4LG4CPOC.js → chunk-Z6COGHHN.js} +2 -2
- package/dist/{chunk-RE3H6Y2X.js → chunk-ZIW5JVRM.js} +2 -2
- package/dist/{cli-BNtvR-nU.d.ts → cli-DD0P-lu8.d.ts} +6 -6
- package/dist/cli.d.ts +16 -14
- package/dist/cli.js +154 -149
- package/dist/coding/export-okf-codegraph.d.ts +2 -1
- package/dist/coding/export-okf-codegraph.js +37 -37
- package/dist/coding/pre-action-gate.d.ts +2 -1
- package/dist/coding/pre-action-gate.js +4 -3
- package/dist/commitment-ledger.js +3 -2
- package/dist/compounding/engine.d.ts +3 -2
- package/dist/compounding/engine.js +37 -37
- package/dist/compounding/preference-consolidator.d.ts +2 -1
- package/dist/compression-optimizer.d.ts +2 -1
- package/dist/config.d.ts +2 -1
- package/dist/config.js +16 -13
- package/dist/connectors/codex-materialize-runner.d.ts +2 -1
- package/dist/connectors/codex-materialize-runner.js +38 -38
- package/dist/connectors/codex-materialize.d.ts +2 -1
- package/dist/connectors/codex-materialize.js +4 -4
- package/dist/connectors/index.d.ts +2 -1
- package/dist/connectors/index.js +38 -38
- package/dist/consolidation-provenance-check.d.ts +3 -2
- package/dist/consolidation-provenance-check.js +4 -4
- package/dist/consolidation-undo.d.ts +3 -2
- package/dist/consolidation-undo.js +3 -3
- package/dist/contradiction/index.d.ts +4 -3
- package/dist/contradiction/index.js +9 -9
- package/dist/converge-config.d.ts +2 -1
- package/dist/convergence-refresh.d.ts +3 -2
- package/dist/convergence-refresh.js +38 -38
- package/dist/conversation-index/backend.d.ts +2 -1
- package/dist/conversation-index/chunker.d.ts +2 -1
- package/dist/conversation-index/faiss-adapter.d.ts +2 -1
- package/dist/conversation-index/indexer.d.ts +2 -1
- package/dist/conversation-index/search.d.ts +2 -1
- package/dist/corpus-watermark.d.ts +2 -1
- package/dist/corpus-watermark.js +38 -38
- package/dist/cross-lingual-recall.d.ts +2 -1
- package/dist/cue-anchors.js +4 -3
- package/dist/dashboard-runtime.js +2 -2
- package/dist/day-summary.d.ts +2 -1
- package/dist/deep-recall-http-glue.d.ts +14 -12
- package/dist/deep-recall-http-glue.js +9 -8
- package/dist/deep-recall-mcp-tools.d.ts +14 -12
- package/dist/deep-recall-policy-llm.d.ts +2 -1
- package/dist/deep-recall-seeds.d.ts +2 -1
- package/dist/deep-recall.js +2 -2
- package/dist/delinearize.d.ts +2 -1
- package/dist/dependency-propagation-config.d.ts +2 -1
- package/dist/{dependency-propagation-delivery-DjpUlhLi.d.ts → dependency-propagation-delivery-CQPCu0o5.d.ts} +1 -1
- package/dist/direct-answer-wiring.d.ts +2 -1
- package/dist/direct-answer.d.ts +2 -1
- package/dist/embedding-fallback.d.ts +2 -1
- package/dist/enrichment/index.d.ts +2 -1
- package/dist/entity-origin-fields.d.ts +2 -1
- package/dist/entity-retrieval.d.ts +3 -2
- package/dist/entity-retrieval.js +36 -36
- package/dist/entity-schema.d.ts +2 -1
- package/dist/episodic-context.d.ts +2 -1
- package/dist/eval-trajectory-contracts.d.ts +2 -1
- package/dist/eval-trajectory-store.d.ts +2 -1
- package/dist/eval-trajectory-store.js +4 -3
- package/dist/eval-trajectory.d.ts +2 -1
- package/dist/eval-trajectory.js +4 -3
- package/dist/evals.js +3 -2
- package/dist/event-order-recall.js +2 -2
- package/dist/explicit-capture.d.ts +12 -10
- package/dist/explicit-capture.js +10 -10
- package/dist/external-wiki-access.d.ts +14 -12
- package/dist/external-wiki-access.js +95 -92
- package/dist/external-wiki-collection-registration.d.ts +2 -1
- package/dist/external-wiki-collection.d.ts +2 -1
- package/dist/external-wiki-mcp-tools.d.ts +14 -12
- package/dist/extraction-error-classification.d.ts +2 -1
- package/dist/extraction-faithfulness.d.ts +2 -1
- package/dist/extraction-judge-telemetry.d.ts +2 -1
- package/dist/extraction-judge-training.d.ts +2 -1
- package/dist/extraction-judge.d.ts +2 -1
- package/dist/extraction-liveness.d.ts +2 -1
- package/dist/extraction-normalization.d.ts +2 -1
- package/dist/extraction-prompt.d.ts +2 -1
- package/dist/extraction-source-grounding-rules.d.ts +2 -1
- package/dist/extraction-source-grounding.d.ts +2 -1
- package/dist/extraction.d.ts +2 -1
- package/dist/extraction.js +43 -43
- package/dist/fallback-llm.d.ts +2 -1
- package/dist/{first-start-migration-KHIMCBIC.js → first-start-migration-RC345GPL.js} +6 -5
- package/dist/{first-start-migration-KHIMCBIC.js.map → first-start-migration-RC345GPL.js.map} +1 -1
- package/dist/{forget-2AE4MDK3.js → forget-SZ2BXDGF.js} +3 -3
- package/dist/{graph-444E-DrF.d.ts → graph-DRkl22KE.d.ts} +10 -12
- package/dist/graph-append-error.d.ts +40 -0
- package/dist/graph-append-error.js +8 -0
- package/dist/graph-dashboard-diff.d.ts +5 -2
- package/dist/graph-dashboard-key.d.ts +5 -2
- package/dist/graph-dashboard-parser.d.ts +5 -2
- package/dist/{graph-edge-decay-DGTBFALQ.js → graph-edge-decay-MKIRZ762.js} +10 -4
- package/dist/graph-edge-decay-MKIRZ762.js.map +1 -0
- package/dist/graph-edge-reinforcement.d.ts +5 -2
- package/dist/graph-jsonl.d.ts +82 -0
- package/dist/graph-jsonl.js +23 -0
- package/dist/graph-jsonl.js.map +1 -0
- package/dist/graph-path-reconstruction.d.ts +5 -2
- package/dist/graph-path-scoring.d.ts +5 -2
- package/dist/graph-snapshot.d.ts +5 -2
- package/dist/graph-snapshot.js +7 -3
- package/dist/graph-write-lock.d.ts +84 -0
- package/dist/graph-write-lock.js +15 -0
- package/dist/graph-write-lock.js.map +1 -0
- package/dist/graph.d.ts +5 -2
- package/dist/graph.js +10 -3
- package/dist/harmonic-construction.js +5 -4
- package/dist/harmonic-retrieval.js +5 -4
- package/dist/importance.d.ts +2 -1
- package/dist/importers/index.d.ts +2 -1
- package/dist/in-flight-reads.d.ts +2 -1
- package/dist/{index-CsUrdMyL.d.ts → index-C9VXenR8.d.ts} +2 -2
- package/dist/index.d.ts +39 -25
- package/dist/index.js +291 -284
- package/dist/intent.d.ts +2 -1
- package/dist/json-store.js +2 -1
- package/dist/lcm/engine.d.ts +2 -1
- package/dist/lcm/engine.js +3 -3
- package/dist/lcm/index.d.ts +2 -1
- package/dist/lcm/index.js +6 -6
- package/dist/lcm/tools.d.ts +2 -1
- package/dist/lifecycle.d.ts +2 -1
- package/dist/live-connectors-runner.d.ts +2 -1
- package/dist/live-connectors-runner.js +3 -2
- package/dist/local-llm.d.ts +2 -1
- package/dist/local-model-endpoint.d.ts +2 -1
- package/dist/location/index.d.ts +4 -3
- package/dist/location/index.js +39 -39
- package/dist/maintenance/memory-governance.d.ts +2 -1
- package/dist/maintenance/memory-governance.js +36 -36
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.d.ts +3 -2
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +36 -36
- package/dist/maintenance/rebuild-memory-projection.d.ts +3 -2
- package/dist/maintenance/rebuild-memory-projection.js +37 -37
- package/dist/{maintenance-D1GRGLTE.d.ts → maintenance-C14sorKa.d.ts} +3 -3
- package/dist/mcp-memory-inspector-app.d.ts +14 -12
- package/dist/memory-action-policy.d.ts +2 -1
- package/dist/memory-cache.d.ts +2 -1
- package/dist/memory-controller.d.ts +2 -1
- package/dist/memory-lifecycle-ledger-utils.d.ts +2 -1
- package/dist/memory-projection-mutations.js +3 -3
- package/dist/memory-projection-store.d.ts +2 -1
- package/dist/memory-provenance.d.ts +2 -1
- package/dist/memory-snapshot.d.ts +2 -1
- package/dist/memory-subject-cli.d.ts +14 -12
- package/dist/memory-subject.d.ts +14 -12
- package/dist/memory-subject.js +5 -5
- package/dist/memory-worth-outcomes.d.ts +3 -2
- package/dist/models-json.d.ts +2 -1
- package/dist/namespaces/migrate.d.ts +4 -3
- package/dist/namespaces/migrate.js +37 -37
- package/dist/namespaces/principal.d.ts +2 -1
- package/dist/namespaces/search.d.ts +2 -1
- package/dist/namespaces/search.js +11 -11
- package/dist/namespaces/storage.d.ts +4 -3
- package/dist/namespaces/storage.js +36 -36
- package/dist/native-knowledge.d.ts +2 -1
- package/dist/objective-state-writers.js +4 -3
- package/dist/objective-state.js +3 -2
- package/dist/offline-sync-file-io.js +4 -3
- package/dist/offline-sync-impression-drain.d.ts +2 -1
- package/dist/offline-sync-impression-drain.js +7 -7
- package/dist/offline-sync.js +5 -4
- package/dist/operator-doctor-corpus.d.ts +2 -1
- package/dist/operator-doctor-corpus.js +39 -39
- package/dist/operator-doctor-replica.d.ts +2 -1
- package/dist/operator-toolkit.d.ts +6 -4
- package/dist/operator-toolkit.js +61 -58
- package/dist/orchestration/compression-guideline-coordinator.d.ts +3 -2
- package/dist/orchestration/maintenance.d.ts +5 -4
- package/dist/orchestration/maintenance.js +46 -46
- package/dist/{orchestrator-DeqaPGT5.d.ts → orchestrator-DgyvFfPR.d.ts} +9 -9
- package/dist/orchestrator.d.ts +12 -10
- package/dist/orchestrator.js +201 -196
- package/dist/page-versioning.d.ts +73 -4
- package/dist/page-versioning.js +11 -3
- package/dist/patterns-cli.d.ts +2 -1
- package/dist/{pipeline-umUzU0D1.d.ts → pipeline-DRUwHb1X.d.ts} +1 -1
- package/dist/policy-runtime.d.ts +2 -1
- package/dist/proactive-contention.d.ts +2 -1
- package/dist/provenance.d.ts +2 -1
- package/dist/{public-http-Bevrjll9.d.ts → public-http-C4i8Opxc.d.ts} +1 -1
- package/dist/{purge-OP6BEDFY.js → purge-TZKRE6ZQ.js} +5 -5
- package/dist/purge-TZKRE6ZQ.js.map +1 -0
- package/dist/{qmd-BasT78PP.d.ts → qmd-BVWlg1YS.d.ts} +1 -1
- package/dist/qmd-preflight.d.ts +3 -2
- package/dist/qmd-recall-cache.d.ts +2 -1
- package/dist/qmd.d.ts +3 -2
- package/dist/qmd.js +4 -4
- package/dist/recall-concurrency-config.d.ts +2 -1
- package/dist/recall-disclosure-escalation.d.ts +2 -1
- package/dist/recall-explain-renderer.d.ts +2 -1
- package/dist/recall-explain-renderer.js +3 -3
- package/dist/recall-memory-map.d.ts +2 -1
- package/dist/recall-navigate-access.d.ts +2 -1
- package/dist/recall-navigate-access.js +2 -2
- package/dist/recall-navigate-disclosure.d.ts +2 -1
- package/dist/recall-navigate-disclosure.js +1 -1
- package/dist/recall-navigate.d.ts +2 -1
- package/dist/recall-navigate.js +2 -2
- package/dist/recall-planner-llm.d.ts +2 -1
- package/dist/recall-state.d.ts +2 -1
- package/dist/recall-state.js +3 -3
- package/dist/recall-tag-filter.d.ts +2 -1
- package/dist/recall-timings.d.ts +2 -1
- package/dist/recall-xray-cli.d.ts +2 -1
- package/dist/recall-xray-cli.js +4 -4
- package/dist/recall-xray-renderer.d.ts +2 -1
- package/dist/recall-xray-renderer.js +3 -3
- package/dist/recall-xray.d.ts +2 -1
- package/dist/recall-xray.js +2 -2
- package/dist/reconcile/cursor.d.ts +2 -1
- package/dist/reconcile/manifest.d.ts +2 -1
- package/dist/reconcile/manifest.js +7 -7
- package/dist/reconcile/plan.d.ts +2 -1
- package/dist/replay/normalizers/chatgpt.d.ts +2 -1
- package/dist/replay/normalizers/claude.d.ts +2 -1
- package/dist/replay/normalizers/openclaw.d.ts +2 -1
- package/dist/replay/normalizers/shared.d.ts +2 -1
- package/dist/replay/runner.d.ts +2 -1
- package/dist/replay/types.d.ts +2 -1
- package/dist/replica-divergence.d.ts +2 -1
- package/dist/replica-peers-config.d.ts +2 -1
- package/dist/{resolution-4DLI3IOR.js → resolution-VKATP2HT.js} +10 -10
- package/dist/resolution-VKATP2HT.js.map +1 -0
- package/dist/resolve-auth-token.d.ts +2 -1
- package/dist/resume-bundles.js +21 -18
- package/dist/retrieval-agents.d.ts +3 -2
- package/dist/retrieval-tiers.d.ts +2 -1
- package/dist/routing/engine.d.ts +2 -1
- package/dist/routing/store.d.ts +2 -1
- package/dist/salvage-envelope.d.ts +2 -1
- package/dist/salvage-envelope.js +7 -7
- package/dist/{scope-profiles-DXYzyOXW.d.ts → scope-profiles-Vl00B1KU.d.ts} +1 -1
- package/dist/search/document-scanner.js +3 -3
- package/dist/search/embed-helper.d.ts +2 -1
- package/dist/search/factory.d.ts +2 -1
- package/dist/search/factory.js +10 -10
- package/dist/search/index.d.ts +2 -1
- package/dist/search/index.js +16 -16
- package/dist/search/lancedb-backend.d.ts +2 -1
- package/dist/search/lancedb-backend.js +4 -4
- package/dist/search/meilisearch-backend.d.ts +2 -1
- package/dist/search/meilisearch-backend.js +4 -4
- package/dist/search/noop-backend.d.ts +2 -1
- package/dist/search/orama-backend.d.ts +2 -1
- package/dist/search/orama-backend.js +4 -4
- package/dist/search/port.d.ts +2 -1
- package/dist/search/remote-backend.d.ts +2 -1
- package/dist/secure-store/index.js +3 -3
- package/dist/{semantic-consolidation-CqDeaY0U.d.ts → semantic-consolidation-BC9-GFuT.d.ts} +1 -1
- package/dist/semantic-consolidation.d.ts +3 -2
- package/dist/semantic-consolidation.js +39 -39
- package/dist/semantic-merge-config.d.ts +17 -0
- package/dist/semantic-merge-config.js +1 -0
- package/dist/semantic-merge-config.js.map +1 -0
- package/dist/semantic-rule-promotion.js +36 -36
- package/dist/semantic-rule-verifier.d.ts +2 -1
- package/dist/semantic-rule-verifier.js +36 -36
- package/dist/{service-BKD-DVYZ.d.ts → service-BOy6dHfc.d.ts} +2 -2
- package/dist/session-observer-bands.d.ts +2 -1
- package/dist/session-observer-state.d.ts +2 -1
- package/dist/shared-context/manager.d.ts +2 -1
- package/dist/signal.d.ts +2 -1
- package/dist/source-agent-qualifier.d.ts +2 -1
- package/dist/source-agent-qualifier.js +42 -42
- package/dist/source-attribution.d.ts +18 -6
- package/dist/source-attribution.js +3 -1
- package/dist/standup/build.js +36 -36
- package/dist/{storage-BcFd1lN_.d.ts → storage-BLJRnFi6.d.ts} +15 -8
- package/dist/storage.d.ts +3 -2
- package/dist/storage.js +35 -35
- package/dist/subject-config.d.ts +2 -1
- package/dist/subject-config.js +6 -6
- package/dist/summarizer.d.ts +2 -1
- package/dist/summary-snapshot.d.ts +2 -1
- package/dist/support-passport/index.d.ts +17 -15
- package/dist/support-passport/index.js +97 -94
- package/dist/temporal-supersession.d.ts +3 -2
- package/dist/temporal-timeline-recall.d.ts +2 -1
- package/dist/temporal-validity.d.ts +2 -1
- package/dist/threading.d.ts +2 -1
- package/dist/tier-migration.d.ts +3 -2
- package/dist/tier-routing.d.ts +2 -1
- package/dist/{tier-stats-2TOW3QNL.js → tier-stats-KA6OZX2C.js} +6 -5
- package/dist/{tier-stats-2TOW3QNL.js.map → tier-stats-KA6OZX2C.js.map} +1 -1
- package/dist/topics.d.ts +2 -1
- package/dist/transcript.d.ts +2 -1
- package/dist/transfer/backup.js +2 -2
- package/dist/transfer/capsule-export.js +2 -2
- package/dist/transfer/capsule-import.js +6 -6
- package/dist/transfer/export-okf.d.ts +2 -1
- package/dist/transfer/export-okf.js +37 -37
- package/dist/trust-score-stage.d.ts +2 -1
- package/dist/trust-score.d.ts +2 -1
- package/dist/trust-zones.js +3 -2
- package/dist/{types-BRg3tMyp.d.ts → types-YY-eKKU7.d.ts} +3 -0
- package/dist/types.d.ts +2 -1
- package/dist/types.js +1 -1
- package/dist/utility-learner.js +4 -3
- package/dist/utility-runtime.d.ts +2 -1
- package/dist/utility-runtime.js +5 -4
- package/dist/utility-telemetry.js +3 -2
- package/dist/verified-recall.js +36 -36
- package/dist/who-knows-cli.d.ts +14 -12
- package/dist/who-knows-cli.js +37 -37
- package/dist/who-knows.d.ts +14 -12
- package/dist/who-knows.js +36 -36
- package/dist/work/board.js +3 -3
- package/dist/work/storage.js +2 -2
- package/dist/work-product-ledger.js +3 -2
- package/dist/write-envelope.d.ts +2 -1
- package/dist/write-envelope.js +6 -6
- package/package.json +2 -2
- package/src/abstraction-nodes.ts +1 -4
- package/src/access-http-briefing.ts +35 -0
- package/src/access-http.test.ts +76 -0
- package/src/access-http.ts +20 -23
- package/src/access-surface-catalog.test.ts +9 -0
- package/src/access-surface-catalog.ts +1 -0
- package/src/binary-lifecycle/backend.ts +2 -1
- package/src/config.ts +2 -0
- package/src/connectors/live/github.ts +10 -17
- package/src/connectors/live/gmail.ts +8 -19
- package/src/connectors/live/google-drive.ts +6 -13
- package/src/connectors/live/notion.ts +11 -21
- package/src/cue-anchors.ts +1 -6
- package/src/dedup/connector-scope.ts +39 -0
- package/src/dedup/merge-content.test.ts +21 -0
- package/src/dedup/merge-content.ts +21 -7
- package/src/dedup/merge-judge.ts +146 -0
- package/src/dedup/merge.test.ts +3542 -0
- package/src/dedup/merge.ts +384 -0
- package/src/dedup/novelty-gate.ts +12 -10
- package/src/dedup/semantic.ts +7 -9
- package/src/graph-append-error.ts +28 -0
- package/src/graph-jsonl.ts +267 -0
- package/src/graph-write-lock.test.ts +228 -0
- package/src/graph-write-lock.ts +132 -0
- package/src/graph.ts +49 -45
- package/src/harmonic-construction.test.ts +329 -0
- package/src/json-store.ts +17 -20
- package/src/live-connectors-runner.ts +2 -1
- package/src/location/store.ts +1 -4
- package/src/maintenance/graph-edge-decay.ts +24 -13
- package/src/maintenance/purge.ts +2 -5
- package/src/maintenance/rebuild-memory-lifecycle-ledger.ts +3 -6
- package/src/maintenance/rebuild-memory-projection.ts +2 -5
- package/src/namespaces/catalog.ts +1 -9
- package/src/offline-sync-file-io.test.ts +39 -0
- package/src/offline-sync-file-io.ts +3 -7
- package/src/orchestration/contradiction-localization.integration.test.ts +35 -0
- package/src/orchestration/extraction-anchor-snapshot.ts +6 -1
- package/src/orchestration/extraction-persist-deps.ts +2 -1
- package/src/orchestration/extraction-persist-profile-promotion.test.ts +456 -0
- package/src/orchestration/extraction-persist-promotion.test.ts +534 -0
- package/src/orchestration/extraction-persist-promotion.ts +387 -1
- package/src/orchestration/extraction-persist.ts +95 -94
- package/src/orchestration/harmonic-construction-persist.test.ts +69 -0
- package/src/orchestration/harmonic-construction-persist.ts +55 -0
- package/src/orchestration/persistence-index.ts +35 -7
- package/src/orchestration/semantic-merge-commit-effects.ts +354 -0
- package/src/orchestration/semantic-merge-persist.ts +1095 -0
- package/src/orchestration/semantic-merge-promotion-payload.ts +161 -0
- package/src/orchestrator.ts +2 -2
- package/src/page-versioning.ts +396 -22
- package/src/procedural/procedure-miner.ts +1 -9
- package/src/search/document-scanner.ts +3 -6
- package/src/semantic-merge-config.ts +16 -0
- package/src/semantic-rule-promotion.ts +1 -4
- package/src/source-attribution-roundtrip.test.ts +21 -0
- package/src/source-attribution.ts +57 -17
- package/src/storage-contract/versioning.test.ts +500 -1
- package/src/storage.ts +30 -30
- package/src/testing/subjects/graph-edge-decay.test.ts +163 -0
- package/src/testing/subjects/semantic-merge-lifecycle.test.ts +1175 -0
- package/src/types.ts +2 -0
- package/src/utils/errno.test.ts +31 -0
- package/src/utils/errno.ts +5 -0
- package/src/wearables/index.ts +1 -0
- package/src/wearables/registry.ts +23 -0
- package/dist/chunk-5UZXUTVO.js.map +0 -1
- package/dist/chunk-7GSSHAXC.js.map +0 -1
- package/dist/chunk-ACPCA4NO.js.map +0 -1
- package/dist/chunk-C2JCVZWX.js.map +0 -1
- package/dist/chunk-DDQQY2E3.js.map +0 -1
- package/dist/chunk-DJUKBGAD.js.map +0 -1
- package/dist/chunk-DK6GMO6D.js.map +0 -1
- package/dist/chunk-DP4EBV7C.js.map +0 -1
- package/dist/chunk-FCATYKQA.js.map +0 -1
- package/dist/chunk-GGYDJJBO.js.map +0 -1
- package/dist/chunk-HTIIXNJQ.js.map +0 -1
- package/dist/chunk-KK66P5GQ.js.map +0 -1
- package/dist/chunk-NQFCLAYT.js.map +0 -1
- package/dist/chunk-NYRB7BKY.js.map +0 -1
- package/dist/chunk-OGTKQ5MJ.js.map +0 -1
- package/dist/chunk-OLT3YTDH.js.map +0 -1
- package/dist/chunk-P77QX3ID.js.map +0 -1
- package/dist/chunk-U62OK7BB.js.map +0 -1
- package/dist/chunk-UR6PBX2J.js.map +0 -1
- package/dist/chunk-VLAQV4HT.js.map +0 -1
- package/dist/chunk-W7IGLX4G.js.map +0 -1
- package/dist/purge-OP6BEDFY.js.map +0 -1
- /package/dist/{capsule-crypto-7FJQINUR.js.map → access-http-briefing.js.map} +0 -0
- /package/dist/{auto-sync-GRUJFD2F.js.map → auto-sync-WNGG2GC2.js.map} +0 -0
- /package/dist/{graph-edge-decay-DGTBFALQ.js.map → capsule-crypto-YO5QJ6L3.js.map} +0 -0
- /package/dist/{capsule-merge-SEHERWWP.js.map → capsule-merge-IPG4IOFP.js.map} +0 -0
- /package/dist/{chunk-KTVSPFCQ.js.map → chunk-22FVR2KE.js.map} +0 -0
- /package/dist/{chunk-7Y5V3LAP.js.map → chunk-27QVNSQ3.js.map} +0 -0
- /package/dist/{chunk-R7LY4YKF.js.map → chunk-2EKJDQZB.js.map} +0 -0
- /package/dist/{chunk-HOUST5UX.js.map → chunk-2ITE4C2Z.js.map} +0 -0
- /package/dist/{chunk-JOYC4RYC.js.map → chunk-2JVJ7UHC.js.map} +0 -0
- /package/dist/{chunk-X2NFFBTO.js.map → chunk-2L5FSNPC.js.map} +0 -0
- /package/dist/{chunk-DUAJKRR2.js.map → chunk-2SGLMP2E.js.map} +0 -0
- /package/dist/{chunk-6FUMA6AA.js.map → chunk-2Z4GS5E6.js.map} +0 -0
- /package/dist/{chunk-G6MXN2G5.js.map → chunk-37KUQPX4.js.map} +0 -0
- /package/dist/{chunk-WKAXBXVA.js.map → chunk-3QQTXYZ6.js.map} +0 -0
- /package/dist/{chunk-4ZWCQJNM.js.map → chunk-46WM6JX2.js.map} +0 -0
- /package/dist/{chunk-CASWJ2HM.js.map → chunk-46ZPIGWI.js.map} +0 -0
- /package/dist/{chunk-MCA2X4WH.js.map → chunk-4IHBN6WU.js.map} +0 -0
- /package/dist/{chunk-FB7LAJQM.js.map → chunk-4TKJGOCJ.js.map} +0 -0
- /package/dist/{chunk-H7BZBSP6.js.map → chunk-52II7PB6.js.map} +0 -0
- /package/dist/{chunk-N4Y5T6VD.js.map → chunk-54WHLA65.js.map} +0 -0
- /package/dist/{chunk-YVLENSNS.js.map → chunk-5ATKSPEQ.js.map} +0 -0
- /package/dist/{chunk-ZVZHG7AI.js.map → chunk-5UBH37SL.js.map} +0 -0
- /package/dist/{chunk-N3WZU76A.js.map → chunk-66OJSCI6.js.map} +0 -0
- /package/dist/{chunk-D35XLYQ7.js.map → chunk-6CVNSDCZ.js.map} +0 -0
- /package/dist/{chunk-KA2HZCMG.js.map → chunk-6NXSYYGP.js.map} +0 -0
- /package/dist/{chunk-2HIJHXB3.js.map → chunk-6WHJ2EAB.js.map} +0 -0
- /package/dist/{chunk-ZZM476NV.js.map → chunk-6XL73HQ4.js.map} +0 -0
- /package/dist/{chunk-EJ5RKUXW.js.map → chunk-AQ7SYDVO.js.map} +0 -0
- /package/dist/{chunk-2Z3DG5CQ.js.map → chunk-B3O3IRZN.js.map} +0 -0
- /package/dist/{chunk-RACZB5HT.js.map → chunk-BD7JTBYP.js.map} +0 -0
- /package/dist/{chunk-I762GW7V.js.map → chunk-BLJWYCNO.js.map} +0 -0
- /package/dist/{chunk-2NLLXCJG.js.map → chunk-BXLOS5AJ.js.map} +0 -0
- /package/dist/{chunk-GCASGYIO.js.map → chunk-C7HJ3NEI.js.map} +0 -0
- /package/dist/{chunk-C24UZSFW.js.map → chunk-CEH2AFXG.js.map} +0 -0
- /package/dist/{chunk-WVGXCBOC.js.map → chunk-D5F7TKMH.js.map} +0 -0
- /package/dist/{chunk-X7Y7WX73.js.map → chunk-DQEMWVMT.js.map} +0 -0
- /package/dist/{chunk-2OXIPF3A.js.map → chunk-DZD3WGCD.js.map} +0 -0
- /package/dist/{chunk-UMEOXLIZ.js.map → chunk-E2P7TZTF.js.map} +0 -0
- /package/dist/{chunk-6FC3IZ26.js.map → chunk-EBA5DA77.js.map} +0 -0
- /package/dist/{chunk-N7JK2RKG.js.map → chunk-EOQ6FSQP.js.map} +0 -0
- /package/dist/{chunk-YNDLCWXS.js.map → chunk-EZ25VE3G.js.map} +0 -0
- /package/dist/{chunk-XDMI5VKE.js.map → chunk-FVOP3NT7.js.map} +0 -0
- /package/dist/{chunk-NRLELHAY.js.map → chunk-G7NYVKD6.js.map} +0 -0
- /package/dist/{chunk-B3W7GWQB.js.map → chunk-GHTSNGBZ.js.map} +0 -0
- /package/dist/{chunk-LUDBXTBJ.js.map → chunk-H272ZPQ3.js.map} +0 -0
- /package/dist/{chunk-SN4JXNLL.js.map → chunk-H3TKENU4.js.map} +0 -0
- /package/dist/{chunk-4HYNMLT6.js.map → chunk-H4SEGIZY.js.map} +0 -0
- /package/dist/{chunk-ACZN5ZXD.js.map → chunk-H54VWRPS.js.map} +0 -0
- /package/dist/{chunk-HTP6XRUI.js.map → chunk-HM7RHKS4.js.map} +0 -0
- /package/dist/{chunk-Z3SQEEB6.js.map → chunk-HWLIMQE2.js.map} +0 -0
- /package/dist/{chunk-2QSZNTDO.js.map → chunk-JBPKEARU.js.map} +0 -0
- /package/dist/{chunk-ET2CNMLD.js.map → chunk-JN72EPOE.js.map} +0 -0
- /package/dist/{chunk-JCCWJHOP.js.map → chunk-JYZSRNR7.js.map} +0 -0
- /package/dist/{chunk-7D6HXODY.js.map → chunk-K7724CH2.js.map} +0 -0
- /package/dist/{chunk-X33MJFD2.js.map → chunk-KAXCYM66.js.map} +0 -0
- /package/dist/{chunk-7RRUHJPG.js.map → chunk-KB2G2I6Q.js.map} +0 -0
- /package/dist/{chunk-ZXBTTMGZ.js.map → chunk-KEF46KL2.js.map} +0 -0
- /package/dist/{chunk-UWTDARPF.js.map → chunk-KTWG7CNY.js.map} +0 -0
- /package/dist/{chunk-XQYTSZW3.js.map → chunk-L7Z57SID.js.map} +0 -0
- /package/dist/{chunk-JMAG7WZ7.js.map → chunk-LO4UKPKX.js.map} +0 -0
- /package/dist/{chunk-LDWIPYJ6.js.map → chunk-LS2FHUML.js.map} +0 -0
- /package/dist/{chunk-GDB4J2H3.js.map → chunk-MPC7TYTZ.js.map} +0 -0
- /package/dist/{chunk-2GF27PGH.js.map → chunk-MRPOZBJC.js.map} +0 -0
- /package/dist/{chunk-BR3OOQZQ.js.map → chunk-MWN6QEI2.js.map} +0 -0
- /package/dist/{chunk-NZGO2GKL.js.map → chunk-MY5EDR46.js.map} +0 -0
- /package/dist/{chunk-LCMUZDBZ.js.map → chunk-N3ZOP7KC.js.map} +0 -0
- /package/dist/{chunk-TVW4C7PV.js.map → chunk-NB3LRY4T.js.map} +0 -0
- /package/dist/{chunk-QJCWK2A7.js.map → chunk-ND6IQGSZ.js.map} +0 -0
- /package/dist/{chunk-NOB2T2FC.js.map → chunk-NHLLKEKK.js.map} +0 -0
- /package/dist/{chunk-EJIWBTVM.js.map → chunk-O6ZNNVSP.js.map} +0 -0
- /package/dist/{chunk-AG46P24C.js.map → chunk-OB3XMWX5.js.map} +0 -0
- /package/dist/{chunk-46QMN7I7.js.map → chunk-PAZY2ZKP.js.map} +0 -0
- /package/dist/{chunk-EF4JUM55.js.map → chunk-PX4UMA5Z.js.map} +0 -0
- /package/dist/{chunk-NNZFPYYX.js.map → chunk-R7QY5ZVB.js.map} +0 -0
- /package/dist/{chunk-RO2X2EYM.js.map → chunk-R7RTXEYG.js.map} +0 -0
- /package/dist/{chunk-3LJPSRAN.js.map → chunk-RBCQVOVJ.js.map} +0 -0
- /package/dist/{chunk-N4F7ZSSG.js.map → chunk-RC5DXXWC.js.map} +0 -0
- /package/dist/{chunk-5CF2R4NS.js.map → chunk-RKBLRTNT.js.map} +0 -0
- /package/dist/{chunk-5QZR2RA3.js.map → chunk-ROBHL3GL.js.map} +0 -0
- /package/dist/{chunk-D5ZNOKR7.js.map → chunk-S7T2TY65.js.map} +0 -0
- /package/dist/{chunk-3LADWWMA.js.map → chunk-SJDQ5RSN.js.map} +0 -0
- /package/dist/{chunk-CHWUICVY.js.map → chunk-SV4Z2S67.js.map} +0 -0
- /package/dist/{chunk-SEF4IOFE.js.map → chunk-SWIKABKI.js.map} +0 -0
- /package/dist/{chunk-JRRQ747T.js.map → chunk-TBXAY2SS.js.map} +0 -0
- /package/dist/{chunk-XZBXINXZ.js.map → chunk-TNSCIPBQ.js.map} +0 -0
- /package/dist/{chunk-S37NN23M.js.map → chunk-TVEONGBU.js.map} +0 -0
- /package/dist/{chunk-5RPMTFB6.js.map → chunk-TWF35IC6.js.map} +0 -0
- /package/dist/{chunk-BZTJEILZ.js.map → chunk-U6N5IBVY.js.map} +0 -0
- /package/dist/{chunk-7TUB6JWA.js.map → chunk-UCKYTA2T.js.map} +0 -0
- /package/dist/{chunk-PPOT6SPR.js.map → chunk-UNNPNBR2.js.map} +0 -0
- /package/dist/{chunk-KQAFEZQX.js.map → chunk-VDX2J7OX.js.map} +0 -0
- /package/dist/{chunk-J2R275FN.js.map → chunk-VVX5PZFS.js.map} +0 -0
- /package/dist/{chunk-ME35HVDJ.js.map → chunk-VYCOJKSX.js.map} +0 -0
- /package/dist/{chunk-F5UWQ4LX.js.map → chunk-W24T5YMW.js.map} +0 -0
- /package/dist/{chunk-HDQUQBPW.js.map → chunk-W3K3HITK.js.map} +0 -0
- /package/dist/{chunk-GDASG7NC.js.map → chunk-W4F3C7N7.js.map} +0 -0
- /package/dist/{chunk-LFK623SF.js.map → chunk-WJHGDE7X.js.map} +0 -0
- /package/dist/{chunk-3Q6RD76J.js.map → chunk-WQZZDN5M.js.map} +0 -0
- /package/dist/{chunk-ZJWDWPX6.js.map → chunk-X5K5TAQZ.js.map} +0 -0
- /package/dist/{chunk-ZL3XX5LW.js.map → chunk-XM24CDZX.js.map} +0 -0
- /package/dist/{chunk-JTWZKNUH.js.map → chunk-XOVKXJEP.js.map} +0 -0
- /package/dist/{chunk-X5YYSAZX.js.map → chunk-XUE46UYW.js.map} +0 -0
- /package/dist/{chunk-DJORULH3.js.map → chunk-YRLHEEQT.js.map} +0 -0
- /package/dist/{chunk-TA7UZZMW.js.map → chunk-Z3O53WZR.js.map} +0 -0
- /package/dist/{chunk-4LG4CPOC.js.map → chunk-Z6COGHHN.js.map} +0 -0
- /package/dist/{chunk-RE3H6Y2X.js.map → chunk-ZIW5JVRM.js.map} +0 -0
- /package/dist/{forget-2AE4MDK3.js.map → forget-SZ2BXDGF.js.map} +0 -0
- /package/dist/{resolution-4DLI3IOR.js.map → graph-append-error.js.map} +0 -0
|
@@ -0,0 +1,1095 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Persist-side executor for judge-mediated merge-on-write (issue #2330, step 3).
|
|
3
|
+
*
|
|
4
|
+
* Lives beside `extraction-persist.ts` so that file stays within its
|
|
5
|
+
* file-size ratchet ceiling. Called once per surviving fact, right before
|
|
6
|
+
* the normal (non-chunked) sealed-envelope write. On every failure mode —
|
|
7
|
+
* disabled gate, empty band, judge refusal, invalid verdict, snapshot or
|
|
8
|
+
* update failure — the outcome is "created" and the caller falls through
|
|
9
|
+
* to the unchanged write path.
|
|
10
|
+
*
|
|
11
|
+
* Exact mutation order (checklist #14/#42): version snapshot FIRST, then
|
|
12
|
+
* the in-place content update, then the frontmatter patch, then hash-index
|
|
13
|
+
* remove/add, then reindex. The new fact's content is never indexed as
|
|
14
|
+
* its own entry — it was never persisted (checklist #32).
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import path from "node:path";
|
|
18
|
+
|
|
19
|
+
import { buildBehaviorSignalsForMemory } from "../behavior-signals.js";
|
|
20
|
+
|
|
21
|
+
import { isUntrustedOrigin, parseOriginClass } from "../security/origin-authority.js";
|
|
22
|
+
import {
|
|
23
|
+
FallbackLlmClient,
|
|
24
|
+
fallbackLlmRuntimeContextFromConfig,
|
|
25
|
+
} from "../fallback-llm.js";
|
|
26
|
+
import { log } from "../logger.js";
|
|
27
|
+
import { sanitizeMemoryContent } from "../sanitize.js";
|
|
28
|
+
import { ContentHashIndex } from "../storage/content-hash-index.js";
|
|
29
|
+
import type { VersioningConfig } from "../page-versioning.js";
|
|
30
|
+
import {
|
|
31
|
+
committedMergedFactHash,
|
|
32
|
+
discardMergedTargetSnapshot,
|
|
33
|
+
finalizeMergedVersionPrune,
|
|
34
|
+
persistRepairedContentHash,
|
|
35
|
+
rawPreCitationMergedBody,
|
|
36
|
+
readTargetSnapshot,
|
|
37
|
+
revertMergedContent,
|
|
38
|
+
rewriteMergedTargetGraphEdges,
|
|
39
|
+
stageMergedTargetSnapshot,
|
|
40
|
+
} from "./semantic-merge-commit-effects.js";
|
|
41
|
+
import {
|
|
42
|
+
buildMergeFrontmatterUpdate,
|
|
43
|
+
type MergeFrontmatterUpdate,
|
|
44
|
+
type MergeProvenanceSource,
|
|
45
|
+
} from "../dedup/merge-provenance.js";
|
|
46
|
+
import {
|
|
47
|
+
callMergeJudge,
|
|
48
|
+
type MergeJudgeCallOptions,
|
|
49
|
+
} from "../dedup/merge-judge.js";
|
|
50
|
+
import {
|
|
51
|
+
decideSemanticMerge,
|
|
52
|
+
type MergeCreateReason,
|
|
53
|
+
type MergeJudgeRawVerdict,
|
|
54
|
+
} from "../dedup/merge.js";
|
|
55
|
+
import { confidenceTier } from "../types.js";
|
|
56
|
+
import { REFUSED_MERGE_CATEGORIES } from "../dedup/merge-on-write.js";
|
|
57
|
+
import { inferMemoryStatus } from "../memory-lifecycle-ledger-utils.js";
|
|
58
|
+
import { inferIntentFromText } from "../intent.js";
|
|
59
|
+
import {
|
|
60
|
+
hasCitationForTemplate,
|
|
61
|
+
lastCitationMarkerForTemplate,
|
|
62
|
+
stripCitationForTemplate,
|
|
63
|
+
} from "../source-attribution.js";
|
|
64
|
+
import {
|
|
65
|
+
resolveConversationContextCapabilities,
|
|
66
|
+
resolvePipelineProcessingCapabilities,
|
|
67
|
+
resolvePresentationCapabilities,
|
|
68
|
+
resolveRecallAuxiliaryCapabilities,
|
|
69
|
+
type GraphConstructionCapabilitySet,
|
|
70
|
+
} from "../capabilities.js";
|
|
71
|
+
import type { GraphType } from "../graph.js";
|
|
72
|
+
import type {
|
|
73
|
+
BehaviorSignalEvent,
|
|
74
|
+
FaithfulnessFrontmatter,
|
|
75
|
+
MemoryCategory,
|
|
76
|
+
MemoryFile,
|
|
77
|
+
MemoryFrontmatter,
|
|
78
|
+
MemoryLink,
|
|
79
|
+
MemorySubject,
|
|
80
|
+
ProvenanceSource,
|
|
81
|
+
} from "../types.js";
|
|
82
|
+
import { normalizeConnectorScope } from "../dedup/connector-scope.js";
|
|
83
|
+
import type { StorageManager } from "../index.js";
|
|
84
|
+
import type { ExtractionPersistDeps } from "./extraction-persist-deps.js";
|
|
85
|
+
|
|
86
|
+
export type SemanticMergeCreateReason =
|
|
87
|
+
| MergeCreateReason
|
|
88
|
+
| "target_inactive"
|
|
89
|
+
| "target_changed"
|
|
90
|
+
| "metadata_unpreservable"
|
|
91
|
+
| "promoted_copy_present"
|
|
92
|
+
| "snapshot_unavailable"
|
|
93
|
+
| "snapshot_failed"
|
|
94
|
+
| "update_failed"
|
|
95
|
+
| "shadow_would_merge";
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
export type SemanticMergePersistOutcome =
|
|
99
|
+
| {
|
|
100
|
+
action: "merged";
|
|
101
|
+
targetId: string;
|
|
102
|
+
/**
|
|
103
|
+
* The committed merged body (finding A): the caller's merged-target
|
|
104
|
+
* promotion must copy THIS body — not the incoming fact — so the
|
|
105
|
+
* shared/profile copy serves the same claims its `sourceMemoryId`
|
|
106
|
+
* points at. Present on the degraded branch too, where the merged
|
|
107
|
+
* text is committed even though the provenance patch failed.
|
|
108
|
+
*/
|
|
109
|
+
mergedContent: string;
|
|
110
|
+
/**
|
|
111
|
+
* False only in the degraded case where the content update committed,
|
|
112
|
+
* the frontmatter patch failed, AND the automatic rollback could not
|
|
113
|
+
* run. The merged text IS in the target, so the caller must not write
|
|
114
|
+
* the fact again; the pre-merge snapshot is named in the error log.
|
|
115
|
+
*/
|
|
116
|
+
provenancePatched: boolean;
|
|
117
|
+
}
|
|
118
|
+
| { action: "created"; reason: SemanticMergeCreateReason };
|
|
119
|
+
|
|
120
|
+
/** Categories that never merge (episodic / immutable by nature). */
|
|
121
|
+
const REFUSED = REFUSED_MERGE_CATEGORIES as readonly string[];
|
|
122
|
+
|
|
123
|
+
export interface ApplySemanticMergeOptions {
|
|
124
|
+
storage: StorageManager;
|
|
125
|
+
/** RAW (pre-citation) fact content — the same form the dedup band scores. */
|
|
126
|
+
content: string;
|
|
127
|
+
category: string;
|
|
128
|
+
/** The incoming fact's claim-level provenance spans, appended to the target. */
|
|
129
|
+
sources?: ProvenanceSource[];
|
|
130
|
+
/**
|
|
131
|
+
* Provenance connector of the incoming fact. Forwarded to the decision so a
|
|
132
|
+
* cross-connector neighbor is never a merge target (same scope contract the
|
|
133
|
+
* novelty and semantic-dedup gates apply).
|
|
134
|
+
*/
|
|
135
|
+
sourceConnector?: string;
|
|
136
|
+
/**
|
|
137
|
+
* Extraction metadata the normal write path persists but a merge cannot
|
|
138
|
+
* carry onto the target (item A). A merge forwards only content, category,
|
|
139
|
+
* sources, and connector; when any entry here is NEW information relative
|
|
140
|
+
* to the target, merging is bypassed so nothing is silently discarded.
|
|
141
|
+
*/
|
|
142
|
+
incomingMetadata?: {
|
|
143
|
+
tags?: readonly string[];
|
|
144
|
+
entityRef?: string;
|
|
145
|
+
structuredAttributes?: Record<string, string>;
|
|
146
|
+
/** Effective `validAt` the write path would persist (bi-temporal or source). */
|
|
147
|
+
validAt?: string;
|
|
148
|
+
/** True when bi-temporal bounds (validFrom/validUntil/observedAt/eventTimeSource) apply. */
|
|
149
|
+
biTemporal?: boolean;
|
|
150
|
+
/** Importance score the write path would stamp. */
|
|
151
|
+
importanceScore?: number;
|
|
152
|
+
/**
|
|
153
|
+
* Confidence the write path would stamp (final round A). The merged
|
|
154
|
+
* record keeps the LOWER side — min(incoming, target), with the tier
|
|
155
|
+
* that score maps to — matching what the create path would have stored
|
|
156
|
+
* for the incoming fact alone; an incoming value at or above the
|
|
157
|
+
* target's needs no rewrite. An unreadable value (non-finite or outside
|
|
158
|
+
* [0, 1]) bypasses the merge rather than guessing a floor.
|
|
159
|
+
*/
|
|
160
|
+
confidence?: number;
|
|
161
|
+
/**
|
|
162
|
+
* Provenance strength the write path would stamp.
|
|
163
|
+
*/
|
|
164
|
+
provenanceStrength?: "verified" | "unverified" | "none";
|
|
165
|
+
/**
|
|
166
|
+
* True when the write path would stamp `toolScoped: true` (finding A):
|
|
167
|
+
* connector-scoped claims must never widen into an unscoped target, so
|
|
168
|
+
* such a fact is created through the write that stamps the flag. A
|
|
169
|
+
* target that already carries `toolScoped: true` keeps its stricter
|
|
170
|
+
* scope — the merge patch never touches that field.
|
|
171
|
+
*/
|
|
172
|
+
toolScoped?: boolean;
|
|
173
|
+
/**
|
|
174
|
+
* Subject the write path would stamp when subject classification is
|
|
175
|
+
* enabled (finding A): the merge patch has no carrier for `subject`, so
|
|
176
|
+
* a fact whose effective subject differs from the target's must be
|
|
177
|
+
* created rather than merged into a target that keeps a different label.
|
|
178
|
+
*/
|
|
179
|
+
subject?: MemorySubject;
|
|
180
|
+
/**
|
|
181
|
+
* Authority origin the write path would stamp (finding A). The merge
|
|
182
|
+
* patch has no carrier for `origin`, so the merged body would render
|
|
183
|
+
* under the TARGET's authority at recall; a cross-origin merge is
|
|
184
|
+
* refused outright (see {@link createPathMergeParity}).
|
|
185
|
+
*/
|
|
186
|
+
origin?: string;
|
|
187
|
+
/**
|
|
188
|
+
* Faithfulness verdict the write path would stamp (finding C). In
|
|
189
|
+
* shadow mode a contradicted/unsupported fact receives this frontmatter
|
|
190
|
+
* without pending_review status, so the verdict — not just the enforce
|
|
191
|
+
* status — gates parity: the merge patch cannot compose an honest
|
|
192
|
+
* verdict for a combined body, so an incoming verdict that differs from
|
|
193
|
+
* the target's effective one bypasses the merge.
|
|
194
|
+
*/
|
|
195
|
+
faithfulness?: FaithfulnessFrontmatter;
|
|
196
|
+
/**
|
|
197
|
+
* Episode/note classification the write path would stamp when
|
|
198
|
+
* `episodeNoteMode` is on (round N+2, finding B). The merge patch has
|
|
199
|
+
* no `memoryKind` carrier, so the merged record keeps the target's
|
|
200
|
+
* kind — the classification that drives episode-cache membership and
|
|
201
|
+
* the episode-only verification/promotion paths. A computed incoming
|
|
202
|
+
* kind that differs from the target's committed kind (including a
|
|
203
|
+
* kinded fact into an unkinded legacy target) bypasses the merge.
|
|
204
|
+
* Undefined means classification is off and no episode path consults
|
|
205
|
+
* the field, so those merges keep passing.
|
|
206
|
+
*/
|
|
207
|
+
memoryKind?: MemoryFrontmatter["memoryKind"];
|
|
208
|
+
};
|
|
209
|
+
/**
|
|
210
|
+
* Navigation links the caller suggested for the incoming fact (finding B).
|
|
211
|
+
* The create path stamps these on the fact it writes; a merge that dropped
|
|
212
|
+
* them would permanently lose the relationships, so a successful merge
|
|
213
|
+
* attaches them to the TARGET record in the same conditional frontmatter
|
|
214
|
+
* patch that stamps provenance — deduped on (targetId, linkType), the key
|
|
215
|
+
* `StorageManager.addLinksToMemory` uses.
|
|
216
|
+
*/
|
|
217
|
+
incomingLinks?: readonly MemoryLink[];
|
|
218
|
+
/**
|
|
219
|
+
* Round N+7 (C): the caller's single cited body for this write — the raw
|
|
220
|
+
* fact content with this write's citation marker attached (the same
|
|
221
|
+
* string the verbatim artifact stores). The committed merged body reuses
|
|
222
|
+
* that exact marker so the incoming claims stay attributed alongside the
|
|
223
|
+
* target's older citation, and memory and artifact share one timestamp.
|
|
224
|
+
* Absent (or citation-free) commits the judge's merged body unchanged.
|
|
225
|
+
*/
|
|
226
|
+
incomingCitedContent?: string;
|
|
227
|
+
/**
|
|
228
|
+
* Finding C: async probe run after a merge verdict but before any
|
|
229
|
+
* mutation. True when the would-be target already has promoted
|
|
230
|
+
* shared/profile copies the merge cannot reconcile — the fact is created
|
|
231
|
+
* through the normal write (whose promotion step owns those copies)
|
|
232
|
+
* instead of merged.
|
|
233
|
+
*/
|
|
234
|
+
targetHasPromotedCopies?: (targetId: string) => Promise<boolean>;
|
|
235
|
+
/** Caller-side bypass: contradiction detected or pending_review routing. */
|
|
236
|
+
skip?: boolean;
|
|
237
|
+
/** Injection seam for tests. */
|
|
238
|
+
now?: () => Date;
|
|
239
|
+
judgeCall?: (options: MergeJudgeCallOptions) => Promise<MergeJudgeRawVerdict | null>;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
const PROVENANCE_STRENGTH_RANK: Record<string, number> = {
|
|
243
|
+
none: 0,
|
|
244
|
+
unverified: 1,
|
|
245
|
+
verified: 2,
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Create-path parity gate (final round, PR #2771). Nine review rounds all
|
|
250
|
+
* found the same defect class: the merge path did less, or something wider,
|
|
251
|
+
* than the create path. Instead of patching call sites, this ONE gate
|
|
252
|
+
* enumerates every trust/scope/verdict field the create path's write stamps
|
|
253
|
+
* (`writeSealedMemory` + extras in `extraction-persist.ts`) and decides,
|
|
254
|
+
* per field, whether the merge preserves it. A merge proceeds only when
|
|
255
|
+
* EVERY field is carried by the merge patch, preserved monotonically, or
|
|
256
|
+
* provably equal. Anything else — including anything unknown or unreadable —
|
|
257
|
+
* bypasses to a create, because a create is always safe and a merge is not.
|
|
258
|
+
*
|
|
259
|
+
* Field classification, from the create-path write:
|
|
260
|
+
* - carried by the merge patch: content (judge-composed merged body),
|
|
261
|
+
* sources (appended by buildMergeFrontmatterUpdate), contentHash
|
|
262
|
+
* (restamped off the canonical merged form), status (a merge-eligible
|
|
263
|
+
* fact is never pending_review — the caller skips — and the target is
|
|
264
|
+
* re-verified active), provenance (only when downgraded to the
|
|
265
|
+
* least-trusted side, see below).
|
|
266
|
+
* - equality-required (bypass on mismatch): category (also enforced at
|
|
267
|
+
* candidate resolution), entityRef, effective validity bounds — both
|
|
268
|
+
* sides unbounded, or an incoming validAt identical to the target's
|
|
269
|
+
* valid_at; a target carrying invalid_at never merges (round N+2 A) —
|
|
270
|
+
* subject (effective), memoryKind when the incoming fact carries a
|
|
271
|
+
* computed kind (round N+2 B), sourceConnector (against the COLD-AWARE
|
|
272
|
+
* target snapshot, finding D), faithfulness verdict (effective, finding C).
|
|
273
|
+
* - monotone-preservable (bypass when the incoming side exceeds what the
|
|
274
|
+
* target retains): tags (target must be a superset), importance score
|
|
275
|
+
* (incoming may not exceed), confidence score (incoming may not exceed;
|
|
276
|
+
* a lower incoming DOWNGRADES the record to min(incoming, target) with
|
|
277
|
+
* the tier that score maps to — final round A), provenance strength
|
|
278
|
+
* (incoming may not be stronger — and a weaker incoming downgrades the
|
|
279
|
+
* target so the merged body never carries trust its new claims did not
|
|
280
|
+
* earn, finding B).
|
|
281
|
+
* - bypass-on-presence (no carrier exists at all): structuredAttributes,
|
|
282
|
+
* bi-temporal bounds.
|
|
283
|
+
* - escalation-refused: origin (an untrusted incoming origin may never
|
|
284
|
+
* merge into a trusted target; equal or fence-safe mismatches may).
|
|
285
|
+
* - scope-widening-refused: toolScoped (an unscoped target may never gain
|
|
286
|
+
* tool-scoped claims; a scoped target keeps its stricter flag).
|
|
287
|
+
*/
|
|
288
|
+
export type CreatePathMergeParity =
|
|
289
|
+
| {
|
|
290
|
+
ok: true;
|
|
291
|
+
provenanceFloor?: "verified" | "unverified" | "none";
|
|
292
|
+
/** Lower incoming confidence the patch must stamp (final round A). */
|
|
293
|
+
confidenceFloor?: number;
|
|
294
|
+
}
|
|
295
|
+
| { ok: false; field: string };
|
|
296
|
+
|
|
297
|
+
/** Effective provenance rank; absent reads as "none" (legacy contract, types.ts). */
|
|
298
|
+
function provenanceRank(value: string | undefined): number {
|
|
299
|
+
return PROVENANCE_STRENGTH_RANK[value ?? "none"] ?? 0;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Effective faithfulness verdict using the trust stage's own normalization
|
|
304
|
+
* (`skipped_no_span` reads as "unchecked"); undefined = gate never ran.
|
|
305
|
+
*/
|
|
306
|
+
function effectiveFaithfulness(
|
|
307
|
+
verdict: FaithfulnessFrontmatter["verdict"] | undefined,
|
|
308
|
+
): FaithfulnessFrontmatter["verdict"] | undefined {
|
|
309
|
+
if (verdict === undefined) return undefined;
|
|
310
|
+
return verdict === "skipped_no_span" ? "unchecked" : verdict;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
export function createPathMergeParity(input: {
|
|
314
|
+
/** Cold-aware committed snapshot of the merge target (never a lookup hit). */
|
|
315
|
+
target: MemoryFile;
|
|
316
|
+
incoming: ApplySemanticMergeOptions["incomingMetadata"];
|
|
317
|
+
/** The incoming fact's connector scope (finding D). */
|
|
318
|
+
sourceConnector?: string;
|
|
319
|
+
untrustedOrigins: readonly string[];
|
|
320
|
+
}): CreatePathMergeParity {
|
|
321
|
+
const { target, untrustedOrigins } = input;
|
|
322
|
+
const md = input.incoming;
|
|
323
|
+
if (md?.structuredAttributes && Object.keys(md.structuredAttributes).length > 0) {
|
|
324
|
+
return { ok: false, field: "structuredAttributes" };
|
|
325
|
+
}
|
|
326
|
+
if (md?.biTemporal === true) return { ok: false, field: "biTemporal" };
|
|
327
|
+
if (md?.entityRef !== undefined && md.entityRef !== target.frontmatter.entityRef) {
|
|
328
|
+
return { ok: false, field: "entityRef" };
|
|
329
|
+
}
|
|
330
|
+
if (md?.validAt !== undefined && md.validAt !== target.frontmatter.valid_at) {
|
|
331
|
+
return { ok: false, field: "validAt" };
|
|
332
|
+
}
|
|
333
|
+
// Round N+2 (A) — effective validity bounds. The merged body inherits the
|
|
334
|
+
// TARGET's valid_at/invalid_at (the patch has no carrier for either), and
|
|
335
|
+
// inferMemoryStatus ignores temporal validity, so a lifecycle-active
|
|
336
|
+
// target with a past invalid_at takes the fresh claims out of normal
|
|
337
|
+
// recall the moment they merge in (isValidityExpiredNow reads the bound).
|
|
338
|
+
// Refuse instead: a target carrying invalid_at never merges (a non-
|
|
339
|
+
// bi-temporal incoming fact cannot carry one — bi-temporal facts already
|
|
340
|
+
// bypass above), and a target carrying valid_at merges only with an
|
|
341
|
+
// incoming fact carrying the same bound via the equality check above.
|
|
342
|
+
// Both-unbounded pairs keep merging.
|
|
343
|
+
if (target.frontmatter.invalid_at !== undefined) {
|
|
344
|
+
return { ok: false, field: "validity_bounds" };
|
|
345
|
+
}
|
|
346
|
+
if (target.frontmatter.valid_at !== undefined && md?.validAt === undefined) {
|
|
347
|
+
return { ok: false, field: "validity_bounds" };
|
|
348
|
+
}
|
|
349
|
+
const targetTags = new Set(target.frontmatter.tags ?? []);
|
|
350
|
+
if ((md?.tags ?? []).some((tag) => !targetTags.has(tag))) {
|
|
351
|
+
return { ok: false, field: "tags" };
|
|
352
|
+
}
|
|
353
|
+
if (
|
|
354
|
+
md?.importanceScore !== undefined &&
|
|
355
|
+
md.importanceScore > (target.frontmatter.importance?.score ?? 0)
|
|
356
|
+
) {
|
|
357
|
+
return { ok: false, field: "importance" };
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// Finding B — provenance must not be upgraded by merging. A stronger
|
|
361
|
+
// incoming strength has no carrier (create stamps it on its own fact);
|
|
362
|
+
// a weaker one must RETAG the combined body to the least-trusted value,
|
|
363
|
+
// because `trust-score.ts` maps the memory-level tag straight to a
|
|
364
|
+
// provenance contribution and unverified new claims must not inherit
|
|
365
|
+
// `verified`'s maximum.
|
|
366
|
+
const incomingProvenanceRank = provenanceRank(md?.provenanceStrength);
|
|
367
|
+
const targetProvenanceRank = provenanceRank(target.frontmatter.provenance);
|
|
368
|
+
if (incomingProvenanceRank > targetProvenanceRank) {
|
|
369
|
+
return { ok: false, field: "provenance" };
|
|
370
|
+
}
|
|
371
|
+
const provenanceFloor =
|
|
372
|
+
incomingProvenanceRank < targetProvenanceRank
|
|
373
|
+
? ((md?.provenanceStrength ?? "none") as "verified" | "unverified" | "none")
|
|
374
|
+
: undefined;
|
|
375
|
+
|
|
376
|
+
// Final round (A) — confidence must not be upgraded by merging either.
|
|
377
|
+
// Lifecycle scoring, preference consolidation, and the merged-target
|
|
378
|
+
// promotion all read the record's confidence, so a low-confidence
|
|
379
|
+
// extraction merging into a higher-confidence target must DOWNGRADE the
|
|
380
|
+
// record to min(incoming, target) — the value the create path would have
|
|
381
|
+
// stored for the incoming fact alone. A value at or above the target's
|
|
382
|
+
// needs no rewrite (the min IS the target's); an unreadable one bypasses
|
|
383
|
+
// the merge rather than guessing a floor. A legacy target with no
|
|
384
|
+
// confidence reads as the write path's 0.8 default (parseMemoryFrontmatter).
|
|
385
|
+
if (md?.confidence !== undefined) {
|
|
386
|
+
if (
|
|
387
|
+
typeof md.confidence !== "number" ||
|
|
388
|
+
!Number.isFinite(md.confidence) ||
|
|
389
|
+
md.confidence < 0 ||
|
|
390
|
+
md.confidence > 1
|
|
391
|
+
) {
|
|
392
|
+
return { ok: false, field: "confidence" };
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
const confidenceFloor =
|
|
396
|
+
md?.confidence !== undefined && md.confidence < (target.frontmatter.confidence ?? 0.8)
|
|
397
|
+
? md.confidence
|
|
398
|
+
: undefined;
|
|
399
|
+
|
|
400
|
+
// Finding D — connector scope from the COLD-AWARE target snapshot. The
|
|
401
|
+
// lookup's hit enrichment reads `sourceConnector` hot-only
|
|
402
|
+
// (persistence-index.ts), so a cold-tier (or read-failed) target looks
|
|
403
|
+
// unscoped and an unscoped incoming fact slips past the lookup-side
|
|
404
|
+
// comparison. The re-read target carries the authoritative frontmatter;
|
|
405
|
+
// fail closed on any mismatch (both sides unscoped, or identical).
|
|
406
|
+
if (
|
|
407
|
+
normalizeConnectorScope(target.frontmatter.sourceConnector) !==
|
|
408
|
+
normalizeConnectorScope(input.sourceConnector)
|
|
409
|
+
) {
|
|
410
|
+
return { ok: false, field: "sourceConnector" };
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
if (md?.toolScoped === true && target.frontmatter.toolScoped !== true) {
|
|
414
|
+
return { ok: false, field: "toolScoped" };
|
|
415
|
+
}
|
|
416
|
+
// Subject: the write path stamps `subject`, and the merge patch has no
|
|
417
|
+
// carrier for it, so a fact whose EFFECTIVE subject differs from the
|
|
418
|
+
// target's must be created. An absent subject resolves to the
|
|
419
|
+
// least-privileged `user` — the same default the subject guard applies —
|
|
420
|
+
// so classification being disabled never lets an unclassified fact merge
|
|
421
|
+
// into an `agent`-labeled target.
|
|
422
|
+
if ((md?.subject ?? "user") !== (target.frontmatter.subject ?? "user")) {
|
|
423
|
+
return { ok: false, field: "subject" };
|
|
424
|
+
}
|
|
425
|
+
// Round N+2 (B) — memoryKind parity. See the field doc on
|
|
426
|
+
// ApplySemanticMergeOptions.incomingMetadata.memoryKind.
|
|
427
|
+
if (md?.memoryKind !== undefined && md.memoryKind !== target.frontmatter.memoryKind) {
|
|
428
|
+
return { ok: false, field: "memoryKind" };
|
|
429
|
+
}
|
|
430
|
+
// Origin: the merged body renders under the TARGET's origin at recall, so
|
|
431
|
+
// an UNTRUSTED incoming origin (per the deployment's untrustedOrigins)
|
|
432
|
+
// merging into a TRUSTED target would hand injected text unfenced,
|
|
433
|
+
// user-authority rendering — the escalation the recall fence exists to
|
|
434
|
+
// prevent. That merge is refused; the fact is created through the write
|
|
435
|
+
// that stamps its own origin. Mismatches that never reduce fencing still
|
|
436
|
+
// merge, so legacy unstamped targets (`origin` absent → `unknown`, the
|
|
437
|
+
// fence's least-privilege default) keep receiving user-origin facts.
|
|
438
|
+
if (
|
|
439
|
+
isUntrustedOrigin(parseOriginClass(md?.origin), untrustedOrigins) &&
|
|
440
|
+
!isUntrustedOrigin(parseOriginClass(target.frontmatter.origin), untrustedOrigins)
|
|
441
|
+
) {
|
|
442
|
+
return { ok: false, field: "origin" };
|
|
443
|
+
}
|
|
444
|
+
// Finding C — faithfulness verdicts must be preserved. The create path
|
|
445
|
+
// stamps `faithfulnessFm` whenever the gate ran (shadow mode included: a
|
|
446
|
+
// contradicted fact gets the verdict without pending_review status). The
|
|
447
|
+
// merge patch cannot compose an honest verdict for a combined body, so the
|
|
448
|
+
// effective verdicts must be EQUAL — both present and identical, or both
|
|
449
|
+
// absent. One-sided verdicts (gate ran on one side only) bypass too: the
|
|
450
|
+
// trust stage maps `entailed` to the maximum contribution, so claims the
|
|
451
|
+
// gate never checked must not inherit an entailment rendered over the
|
|
452
|
+
// other side's claims alone. The create path runs instead and persists
|
|
453
|
+
// the verdict it computed (or none).
|
|
454
|
+
const incomingFaithfulness = effectiveFaithfulness(md?.faithfulness?.verdict);
|
|
455
|
+
const targetFaithfulness = effectiveFaithfulness(target.frontmatter.faithfulness?.verdict);
|
|
456
|
+
if (incomingFaithfulness !== targetFaithfulness) {
|
|
457
|
+
return { ok: false, field: "faithfulness" };
|
|
458
|
+
}
|
|
459
|
+
// Round N+9 (A): an EQUAL verdict cannot survive either — the target's
|
|
460
|
+
// `entailed` was rendered over its pre-merge body, never the judge's
|
|
461
|
+
// merged text, so bypass (entailment only; others under-claim / no signal).
|
|
462
|
+
if (targetFaithfulness === "entailed") {
|
|
463
|
+
return { ok: false, field: "faithfulness" };
|
|
464
|
+
}
|
|
465
|
+
return {
|
|
466
|
+
ok: true,
|
|
467
|
+
...(provenanceFloor !== undefined ? { provenanceFloor } : {}),
|
|
468
|
+
...(confidenceFloor !== undefined ? { confidenceFloor } : {}),
|
|
469
|
+
};
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
/**
|
|
473
|
+
* Round N+7 (C): the committed merged body carries the incoming extraction's
|
|
474
|
+
* citation marker (lifted from the caller's cited body for this write)
|
|
475
|
+
* appended after the judge's merged text, so incoming claims stay attributed
|
|
476
|
+
* even when the merged text embeds the target's older citation — quoted
|
|
477
|
+
* excerpts travel without frontmatter, so `sources` alone is not enough.
|
|
478
|
+
* Idempotent; a no-op when attribution is off or no marker exists.
|
|
479
|
+
*/
|
|
480
|
+
function committedMergedBody(
|
|
481
|
+
deps: ExtractionPersistDeps,
|
|
482
|
+
mergedContent: string,
|
|
483
|
+
incomingCitedContent: string | undefined,
|
|
484
|
+
): string {
|
|
485
|
+
if (
|
|
486
|
+
resolvePipelineProcessingCapabilities(deps.config).inlineSourceAttribution !== true ||
|
|
487
|
+
incomingCitedContent === undefined
|
|
488
|
+
) {
|
|
489
|
+
return mergedContent;
|
|
490
|
+
}
|
|
491
|
+
const marker = lastCitationMarkerForTemplate(
|
|
492
|
+
incomingCitedContent,
|
|
493
|
+
deps.config.inlineSourceAttributionFormat,
|
|
494
|
+
);
|
|
495
|
+
if (!marker || mergedContent.endsWith(marker)) return mergedContent;
|
|
496
|
+
return `${mergedContent} ${marker}`;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
function versioningConfigFrom(deps: ExtractionPersistDeps): VersioningConfig {
|
|
500
|
+
return {
|
|
501
|
+
enabled: resolveRecallAuxiliaryCapabilities(deps.config).versioning,
|
|
502
|
+
maxVersionsPerPage: deps.config.versioningMaxPerPage,
|
|
503
|
+
sidecarDir: deps.config.versioningSidecarDir,
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
/**
|
|
508
|
+
* Finding B — union the incoming fact's suggested navigation links into the
|
|
509
|
+
* target's committed links, deduping on (targetId, linkType): the same key
|
|
510
|
+
* `StorageManager.addLinksToMemory` uses. A suggestion naming the surviving
|
|
511
|
+
* target itself is DISCARDED — memory linking and the merge judge both
|
|
512
|
+
* search on the incoming content, so the suggested neighbor is often the
|
|
513
|
+
* merge target itself, and attaching it would make the record its own
|
|
514
|
+
* neighbor (recall-navigate would return the current memory and burn
|
|
515
|
+
* traversal budget). Returns undefined when there is nothing to attach so
|
|
516
|
+
* the patch carries no `links` key at all (an empty array would ERASE
|
|
517
|
+
* committed links).
|
|
518
|
+
*/
|
|
519
|
+
export function mergeMemoryLinks(
|
|
520
|
+
existing: readonly MemoryLink[] | undefined,
|
|
521
|
+
incoming: readonly MemoryLink[] | undefined,
|
|
522
|
+
survivingTargetId: string,
|
|
523
|
+
): MemoryLink[] | undefined {
|
|
524
|
+
if (!incoming || incoming.length === 0) return undefined;
|
|
525
|
+
const merged = [...(existing ?? [])];
|
|
526
|
+
for (const link of incoming) {
|
|
527
|
+
if (link.targetId === survivingTargetId) continue;
|
|
528
|
+
if (!merged.some((l) => l.targetId === link.targetId && l.linkType === link.linkType)) {
|
|
529
|
+
merged.push({ ...link });
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
return merged.length > 0 ? merged : undefined;
|
|
533
|
+
}
|
|
534
|
+
export async function applySemanticMergeAtPersist(
|
|
535
|
+
deps: ExtractionPersistDeps,
|
|
536
|
+
options: ApplySemanticMergeOptions,
|
|
537
|
+
): Promise<SemanticMergePersistOutcome> {
|
|
538
|
+
const config = deps.config.semanticMerge;
|
|
539
|
+
if (config?.enabled !== true || options.skip === true) {
|
|
540
|
+
return { action: "created", reason: "disabled" };
|
|
541
|
+
}
|
|
542
|
+
if (REFUSED.includes(options.category)) {
|
|
543
|
+
return { action: "created", reason: "disabled" };
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
const decision = await decideSemanticMerge({
|
|
547
|
+
content: options.content,
|
|
548
|
+
category: options.category,
|
|
549
|
+
config,
|
|
550
|
+
sourceConnector: options.sourceConnector,
|
|
551
|
+
dedupThreshold: deps.config.semanticDedupThreshold,
|
|
552
|
+
lookup: (content, limit) => deps.semanticDedupLookup(content, limit, options.storage),
|
|
553
|
+
resolveCandidate: async (memoryId) => {
|
|
554
|
+
const memory = await options.storage.getMemoryByIdIncludingArchived(memoryId);
|
|
555
|
+
if (!memory) return null;
|
|
556
|
+
return {
|
|
557
|
+
content: memory.content,
|
|
558
|
+
category: memory.frontmatter.category,
|
|
559
|
+
status: inferMemoryStatus(memory.frontmatter, memory.path),
|
|
560
|
+
};
|
|
561
|
+
},
|
|
562
|
+
judge: async (input) => {
|
|
563
|
+
const call = options.judgeCall ?? callMergeJudge;
|
|
564
|
+
// A null (no backend answered / schema-failed) maps to a thrown error
|
|
565
|
+
// so decideSemanticMerge classifies it as judge_error → create.
|
|
566
|
+
const verdict = await call({
|
|
567
|
+
...input,
|
|
568
|
+
candidates: [...input.candidates],
|
|
569
|
+
config: deps.config,
|
|
570
|
+
localLlm: deps.getLocalLlm(),
|
|
571
|
+
fallbackLlm: new FallbackLlmClient(
|
|
572
|
+
deps.config.gatewayConfig,
|
|
573
|
+
fallbackLlmRuntimeContextFromConfig(deps.config),
|
|
574
|
+
),
|
|
575
|
+
});
|
|
576
|
+
if (!verdict) throw new Error("merge judge unavailable or unparseable");
|
|
577
|
+
return verdict;
|
|
578
|
+
},
|
|
579
|
+
});
|
|
580
|
+
|
|
581
|
+
if (decision.action === "create") {
|
|
582
|
+
if (config.shadowMode) {
|
|
583
|
+
// Telemetry only — category, length, reason. Never fact content: log
|
|
584
|
+
// sinks generally have broader access and retention than the memory
|
|
585
|
+
// store, and extracted facts can hold personal material.
|
|
586
|
+
log.info(
|
|
587
|
+
`semantic-merge[shadow]: would create category=${options.category} length=${options.content.length} reason="${decision.reason}"`,
|
|
588
|
+
);
|
|
589
|
+
}
|
|
590
|
+
return { action: "created", reason: decision.reason };
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
if (config.shadowMode) {
|
|
594
|
+
// Rollout lever: decision-only. Never mutate in shadow mode.
|
|
595
|
+
log.info(
|
|
596
|
+
`semantic-merge[shadow]: would merge into ${decision.targetId} reason="judge_merge"`,
|
|
597
|
+
);
|
|
598
|
+
return { action: "created", reason: "shadow_would_merge" };
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
// Step 1 — re-read the target; gone or no-longer-active → create.
|
|
602
|
+
const target = await options.storage.getMemoryByIdIncludingArchived(decision.targetId);
|
|
603
|
+
if (!target || inferMemoryStatus(target.frontmatter, target.path) !== "active") {
|
|
604
|
+
return { action: "created", reason: "target_inactive" };
|
|
605
|
+
}
|
|
606
|
+
// `mergedContent` was composed from the body the judge was shown. In a
|
|
607
|
+
// multi-writer deployment another extraction can update the target between
|
|
608
|
+
// that resolve and now; writing the merge would silently drop the
|
|
609
|
+
// concurrent writer's details. A changed body creates instead.
|
|
610
|
+
if (target.content !== decision.targetContent) {
|
|
611
|
+
return { action: "created", reason: "target_changed" };
|
|
612
|
+
}
|
|
613
|
+
// Round N+7 (C): every mutation below commits THIS body — the judge's
|
|
614
|
+
// merged text plus the incoming citation marker — and the outcome reports
|
|
615
|
+
// it, so caller comparisons describe what storage actually holds.
|
|
616
|
+
const committedContent = committedMergedBody(deps, decision.mergedContent, options.incomingCitedContent);
|
|
617
|
+
|
|
618
|
+
// Create-path parity gate (final round) — ONE place enumerates every
|
|
619
|
+
// trust/scope/verdict field the normal write stamps and bypasses the
|
|
620
|
+
// merge unless each is carried, preserved, or provably equal. See
|
|
621
|
+
// {@link createPathMergeParity} for the field classification.
|
|
622
|
+
const parity = createPathMergeParity({
|
|
623
|
+
target,
|
|
624
|
+
incoming: options.incomingMetadata,
|
|
625
|
+
sourceConnector: options.sourceConnector,
|
|
626
|
+
untrustedOrigins: deps.config.untrustedOrigins,
|
|
627
|
+
});
|
|
628
|
+
if (!parity.ok) {
|
|
629
|
+
return { action: "created", reason: "metadata_unpreservable" };
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
// Finding C — the create path's promotion step owns shared/profile copies
|
|
633
|
+
// linked by `sourceMemoryId`; a merge mutates only this storage, so those
|
|
634
|
+
// copies would keep serving the pre-merge body. When any promoted copy
|
|
635
|
+
// exists, bypass the merge so the normal write (with its own promotion
|
|
636
|
+
// reconciliation) runs instead. Probed before any mutation, so a bypass
|
|
637
|
+
// leaves the target — and its page-version history — untouched.
|
|
638
|
+
if (
|
|
639
|
+
options.targetHasPromotedCopies &&
|
|
640
|
+
(await options.targetHasPromotedCopies(decision.targetId))
|
|
641
|
+
) {
|
|
642
|
+
return { action: "created", reason: "promoted_copy_present" };
|
|
643
|
+
}
|
|
644
|
+
// Step 2 — rollback data BEFORE any mutation (checklist #14). A failed
|
|
645
|
+
// snapshot must leave the target untouched. Versioning disabled means no
|
|
646
|
+
// rollback story exists at all, so merge refuses to run.
|
|
647
|
+
const versioning = versioningConfigFrom(deps);
|
|
648
|
+
if (!versioning.enabled) {
|
|
649
|
+
return { action: "created", reason: "snapshot_unavailable" };
|
|
650
|
+
}
|
|
651
|
+
let versionId: string;
|
|
652
|
+
try {
|
|
653
|
+
versionId = await stageMergedTargetSnapshot(target, versioning, options.storage.dir);
|
|
654
|
+
} catch (err) {
|
|
655
|
+
log.warn(
|
|
656
|
+
`semantic-merge: snapshot failed for ${decision.targetId}; creating new fact instead: ${err instanceof Error ? err.message : String(err)}`,
|
|
657
|
+
);
|
|
658
|
+
return { action: "created", reason: "snapshot_failed" };
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
// Round N+16 (C): `committedFactHash` — passed ONLY on the degraded path —
|
|
662
|
+
// registers the hash of what storage actually HOLDS. The default reader
|
|
663
|
+
// (`restoreFactHashAfterApproval` → `corpusRegisteredHashes`) prefers the
|
|
664
|
+
// PERSISTED frontmatter `contentHash`, which the degraded record still
|
|
665
|
+
// carries from BEFORE the merge, so routing the degraded repair through it
|
|
666
|
+
// re-registered the stale pre-merge identity and left the merged body
|
|
667
|
+
// unindexed for exact dedup.
|
|
668
|
+
const repairIndexes = async (committedFactHash?: string): Promise<void> => {
|
|
669
|
+
// Hash index: remove the old form, add the new form, in the same
|
|
670
|
+
// corpus-registered identity the write path uses. Both helpers are
|
|
671
|
+
// fact-category no-ops, mirroring `contentHashSource` on the write path.
|
|
672
|
+
// The index rebuilds from the corpus on restart, so a failure here is
|
|
673
|
+
// logged, not fatal.
|
|
674
|
+
try {
|
|
675
|
+
await options.storage.removeFactContentHashesForMemories([target]);
|
|
676
|
+
if (committedFactHash !== undefined) {
|
|
677
|
+
// Round N+20 (B): body-coupled — a writer that replaced the target
|
|
678
|
+
// after this writer's content commit keeps its own record (and its
|
|
679
|
+
// own hash); registering ours would be a phantom exact-dedup hit.
|
|
680
|
+
await options.storage.registerFactContentHash(decision.targetId, committedFactHash, committedContent);
|
|
681
|
+
// Round N+19 (B): also persist the repaired identity in the
|
|
682
|
+
// frontmatter — the index registration is process-local state over
|
|
683
|
+
// a persisted stale `contentHash`, and a restart's corpus rebuild
|
|
684
|
+
// prefers the persisted value, which would discard this repair.
|
|
685
|
+
// CAS-guarded and non-fatal: a concurrent writer that already
|
|
686
|
+
// replaced the record keeps its own (correct) identity.
|
|
687
|
+
await persistRepairedContentHash(
|
|
688
|
+
options.storage,
|
|
689
|
+
decision.targetId,
|
|
690
|
+
committedContent,
|
|
691
|
+
committedFactHash,
|
|
692
|
+
);
|
|
693
|
+
} else {
|
|
694
|
+
await options.storage.restoreFactHashAfterApproval(decision.targetId);
|
|
695
|
+
}
|
|
696
|
+
} catch (err) {
|
|
697
|
+
log.warn(
|
|
698
|
+
`semantic-merge: hash-index sync failed for ${decision.targetId} (non-fatal; index rebuilds from corpus): ${err instanceof Error ? err.message : String(err)}`,
|
|
699
|
+
);
|
|
700
|
+
}
|
|
701
|
+
// Reindex the merged target or its new content stays undiscoverable until
|
|
702
|
+
// unrelated maintenance runs (checklist #31).
|
|
703
|
+
try {
|
|
704
|
+
await deps.indexPersistedMemory(options.storage, decision.targetId);
|
|
705
|
+
} catch (err) {
|
|
706
|
+
log.warn(
|
|
707
|
+
`semantic-merge: reindex failed for ${decision.targetId} (non-fatal): ${err instanceof Error ? err.message : String(err)}`,
|
|
708
|
+
);
|
|
709
|
+
}
|
|
710
|
+
};
|
|
711
|
+
|
|
712
|
+
// Step 3 — update in place, preserving the id, via compare-and-swap on the
|
|
713
|
+
// snapshot just read: a writer landing between the read and the write must
|
|
714
|
+
// win rather than be overwritten from a stale body. The active-target check
|
|
715
|
+
// above makes a tombstone block structurally impossible here.
|
|
716
|
+
const nowIso = (options.now ? options.now() : new Date()).toISOString();
|
|
717
|
+
let contentCommitted = false;
|
|
718
|
+
// Captured so the success return can describe the COMMITTED frontmatter
|
|
719
|
+
// (the patch's appended sources) without widening the try's scope.
|
|
720
|
+
let mergePatch: MergeFrontmatterUpdate | undefined;
|
|
721
|
+
try {
|
|
722
|
+
mergePatch = buildMergeFrontmatterUpdate({
|
|
723
|
+
targetSources: (target.frontmatter.sources ?? []) as MergeProvenanceSource[],
|
|
724
|
+
incomingSources: (options.sources ?? []) as MergeProvenanceSource[],
|
|
725
|
+
targetReinforcementCount: target.frontmatter.reinforcement_count,
|
|
726
|
+
nowIso,
|
|
727
|
+
});
|
|
728
|
+
const updated = await options.storage.updateMemoryIfUnchanged(target, committedContent, {
|
|
729
|
+
actor: "semantic-merge",
|
|
730
|
+
});
|
|
731
|
+
if (!updated) {
|
|
732
|
+
// Round N+13 (B): the CAS lost the race, so no merge committed — roll
|
|
733
|
+
// the staged snapshot back out or this failed attempt leaves a
|
|
734
|
+
// duplicate history entry a later successful prune would trade real
|
|
735
|
+
// rollback states for.
|
|
736
|
+
await discardMergedTargetSnapshot(
|
|
737
|
+
target.path,
|
|
738
|
+
versioning,
|
|
739
|
+
options.storage.dir,
|
|
740
|
+
decision.targetId,
|
|
741
|
+
versionId,
|
|
742
|
+
);
|
|
743
|
+
return { action: "created", reason: "target_changed" };
|
|
744
|
+
}
|
|
745
|
+
contentCommitted = true;
|
|
746
|
+
// The provenance patch must land on OUR merged body. An id-keyed patch
|
|
747
|
+
// re-reads and stamps whatever the latest row holds, so a writer landing
|
|
748
|
+
// after the content commit would receive this merge's provenance while
|
|
749
|
+
// its own body stood — and the caller would still be told "merged".
|
|
750
|
+
// Verify the snapshot, then patch it through the conditional API so the
|
|
751
|
+
// window between the verify and the patch is closed by storage itself.
|
|
752
|
+
const merged = await readTargetSnapshot(options.storage, target);
|
|
753
|
+
if (!merged || merged.content !== committedContent) {
|
|
754
|
+
throw new Error("target replaced before the provenance patch");
|
|
755
|
+
}
|
|
756
|
+
const mergedRawBody = rawPreCitationMergedBody(deps, committedContent);
|
|
757
|
+
// Finding A (round N+3) — intent parity. With intent routing on, the
|
|
758
|
+
// create path stamps intentGoal/intentActionType/intentEntityTypes from
|
|
759
|
+
// the body it persists, and recall-search-pipeline scores intent
|
|
760
|
+
// compatibility from exactly those fields; a merge that left the
|
|
761
|
+
// target's stale values would misroute the newly merged claims. The
|
|
762
|
+
// patch recomputes them with the SAME call the write path runs
|
|
763
|
+
// (`${category} ${tags} ${content}`) over the committed record — its own
|
|
764
|
+
// category and tags plus the canonical RAW pre-citation merged body — so
|
|
765
|
+
// the committed values equal what an ordinary write of the same body
|
|
766
|
+
// would persist. An empty entityTypes list clears a stale field, exactly
|
|
767
|
+
// as serialization omits it on a fresh write.
|
|
768
|
+
const mergedIntent = resolveConversationContextCapabilities(deps.config).intentRouting
|
|
769
|
+
? inferIntentFromText(
|
|
770
|
+
`${merged.frontmatter.category} ${(merged.frontmatter.tags ?? []).join(" ")} ${mergedRawBody}`,
|
|
771
|
+
)
|
|
772
|
+
: undefined;
|
|
773
|
+
// Item B — `updateMemoryIfUnchanged` keeps the target's old
|
|
774
|
+
// `frontmatter.contentHash`, so the patch must restamp the identity the
|
|
775
|
+
// write path would register: the hash of the SAME canonical form normal
|
|
776
|
+
// persistence hashes — the sanitized RAW pre-citation body (round N+2 C).
|
|
777
|
+
// Facts only, mirroring `contentHashSource` on the write path.
|
|
778
|
+
const mergedFactHash =
|
|
779
|
+
options.category === "fact"
|
|
780
|
+
? ContentHashIndex.computeHash(sanitizeMemoryContent(mergedRawBody).text)
|
|
781
|
+
: undefined;
|
|
782
|
+
// Finding B — the incoming fact's suggested navigation links attach to
|
|
783
|
+
// the target here, in the same conditional patch (the fact is never
|
|
784
|
+
// created on this path, so the create path's `links` carrier cannot run).
|
|
785
|
+
const mergedLinks = mergeMemoryLinks(
|
|
786
|
+
merged.frontmatter.links,
|
|
787
|
+
options.incomingLinks,
|
|
788
|
+
decision.targetId,
|
|
789
|
+
);
|
|
790
|
+
const patched = await options.storage.writeMemoryFrontmatterIfUnchanged(
|
|
791
|
+
merged,
|
|
792
|
+
{
|
|
793
|
+
updated: mergePatch.updated,
|
|
794
|
+
derived_via: mergePatch.derived_via,
|
|
795
|
+
reinforcement_count: mergePatch.reinforcement_count,
|
|
796
|
+
sources: mergePatch.sources,
|
|
797
|
+
...(mergedFactHash !== undefined ? { contentHash: mergedFactHash } : {}),
|
|
798
|
+
// Finding B — a weaker incoming provenance retags the combined body
|
|
799
|
+
// to the least-trusted value instead of letting unverified new
|
|
800
|
+
// claims ride the target's stronger memory-level tag.
|
|
801
|
+
...(parity.ok && parity.provenanceFloor !== undefined
|
|
802
|
+
? { provenance: parity.provenanceFloor }
|
|
803
|
+
: {}),
|
|
804
|
+
// Final round (A) — min(incoming, target) confidence: the merged
|
|
805
|
+
// record keeps the lower score, and the tier that score maps to,
|
|
806
|
+
// so lifecycle scoring and the committed-record promotion payload
|
|
807
|
+
// can never treat the combined body as higher-confidence than the
|
|
808
|
+
// create path would have stored for the incoming fact alone.
|
|
809
|
+
...(parity.ok && parity.confidenceFloor !== undefined
|
|
810
|
+
? {
|
|
811
|
+
confidence: parity.confidenceFloor,
|
|
812
|
+
confidenceTier: confidenceTier(parity.confidenceFloor),
|
|
813
|
+
}
|
|
814
|
+
: {}),
|
|
815
|
+
...(mergedLinks !== undefined ? { links: mergedLinks } : {}),
|
|
816
|
+
...(mergedIntent !== undefined
|
|
817
|
+
? {
|
|
818
|
+
intentGoal: mergedIntent.goal,
|
|
819
|
+
intentActionType: mergedIntent.actionType,
|
|
820
|
+
intentEntityTypes: mergedIntent.entityTypes,
|
|
821
|
+
}
|
|
822
|
+
: {}),
|
|
823
|
+
},
|
|
824
|
+
{ actor: "semantic-merge" },
|
|
825
|
+
);
|
|
826
|
+
if (!patched) throw new Error("frontmatter patch rejected (target changed)");
|
|
827
|
+
} catch (err) {
|
|
828
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
829
|
+
// A committed content update means the merged text may be IN the target.
|
|
830
|
+
// Reporting `created` while it is would write the fact a second time and
|
|
831
|
+
// leave those claims without the incoming provenance, so `created` is
|
|
832
|
+
// reachable only once storage has been re-read and confirms the target no
|
|
833
|
+
// longer holds this merge's unprovenanced body.
|
|
834
|
+
if (
|
|
835
|
+
contentCommitted &&
|
|
836
|
+
!(await revertMergedContent(options.storage, target, committedContent))
|
|
837
|
+
) {
|
|
838
|
+
log.error(
|
|
839
|
+
`semantic-merge: ${decision.targetId} holds merged content without provenance metadata and the rollback failed (${detail}); snapshot ${versionId} holds the pre-merge state — recover with revertToVersion. Not creating a duplicate fact.`,
|
|
840
|
+
);
|
|
841
|
+
// Item C — the merged body IS committed; repair the indexes before
|
|
842
|
+
// reporting the degraded success. N+16 (C): the record's persisted
|
|
843
|
+
// frontmatter hash is the stale PRE-merge identity, so register the
|
|
844
|
+
// COMMITTED body's hash instead.
|
|
845
|
+
// Round N+20 (C): degraded SUCCESS commits the recovery snapshot —
|
|
846
|
+
// the body is committed and kept, so the staged version clears its
|
|
847
|
+
// `pending` flag under the same page lock as a finalizing prune.
|
|
848
|
+
// Without it pruneExcessVersions excluded every degraded recovery
|
|
849
|
+
// point forever, so repeated degraded merges grew the manifest and
|
|
850
|
+
// snapshot directory past maxVersionsPerPage and no later successful
|
|
851
|
+
// merge could prune them back.
|
|
852
|
+
await finalizeMergedVersionPrune(target.path, versioning, options.storage.dir, decision.targetId, versionId);
|
|
853
|
+
await repairIndexes(committedMergedFactHash(deps, options.category, committedContent));
|
|
854
|
+
return {
|
|
855
|
+
action: "merged",
|
|
856
|
+
targetId: decision.targetId,
|
|
857
|
+
mergedContent: committedContent,
|
|
858
|
+
provenancePatched: false,
|
|
859
|
+
};
|
|
860
|
+
}
|
|
861
|
+
// Round N+13 (B): with the content committed and the revert above
|
|
862
|
+
// confirmed, storage holds the pre-merge body, so the staged snapshot of
|
|
863
|
+
// that same body is a duplicate and goes. (The degraded-success branch
|
|
864
|
+
// above keeps it as the recovery point. A throw BEFORE the content
|
|
865
|
+
// commit leaves storage state unverifiable — keep-side, the snapshot
|
|
866
|
+
// stays.)
|
|
867
|
+
if (contentCommitted) {
|
|
868
|
+
await discardMergedTargetSnapshot(
|
|
869
|
+
target.path,
|
|
870
|
+
versioning,
|
|
871
|
+
options.storage.dir,
|
|
872
|
+
decision.targetId,
|
|
873
|
+
versionId,
|
|
874
|
+
);
|
|
875
|
+
}
|
|
876
|
+
log.warn(
|
|
877
|
+
`semantic-merge: update failed for ${decision.targetId}${contentCommitted ? ` (content rolled back; snapshot ${versionId} rolled back out of history)` : ` (snapshot ${versionId} staged)`}: ${detail}`,
|
|
878
|
+
);
|
|
879
|
+
return { action: "created", reason: "update_failed" };
|
|
880
|
+
}
|
|
881
|
+
// Round N+12 (A): the prune finalizes only after BOTH compare-and-swaps
|
|
882
|
+
// commit — full rationale on finalizeMergedVersionPrune. A degraded
|
|
883
|
+
await finalizeMergedVersionPrune(target.path, versioning, options.storage.dir, decision.targetId, versionId);
|
|
884
|
+
|
|
885
|
+
// Steps 4+5 — hash-index resync and reindex (see repairIndexes above).
|
|
886
|
+
await repairIndexes();
|
|
887
|
+
|
|
888
|
+
log.info(
|
|
889
|
+
`semantic-merge: merged fact into ${decision.targetId} (version ${versionId})`,
|
|
890
|
+
);
|
|
891
|
+
return {
|
|
892
|
+
action: "merged",
|
|
893
|
+
targetId: decision.targetId,
|
|
894
|
+
mergedContent: committedContent,
|
|
895
|
+
provenancePatched: true,
|
|
896
|
+
};
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
/**
|
|
900
|
+
* Finding B: for a qualifying write the create path stores the incoming
|
|
901
|
+
* extraction's text as a verbatim artifact anchored to the memory it just
|
|
902
|
+
* wrote. A merge that skipped that step would permanently drop the anchor
|
|
903
|
+
* the same extraction would have stored, so a successful merge persists the
|
|
904
|
+
* artifact against the MERGED target — same gates as the write path
|
|
905
|
+
* (verbatim artifacts enabled, category qualifies, confidence at or above
|
|
906
|
+
* the threshold). A merge target is always active and a pending_review fact
|
|
907
|
+
* never reaches the merge, so the write path's post-write guards cannot
|
|
908
|
+
* apply here. Round N+17 (B): this write is the merge path's LAST durable
|
|
909
|
+
* effect (create-path ordering), and a rejection is isolated inside to a
|
|
910
|
+
* logged warning — the committed target stays discoverable; failures never
|
|
911
|
+
* abort the merge batch.
|
|
912
|
+
*/
|
|
913
|
+
export async function writeMergedVerbatimArtifact(
|
|
914
|
+
deps: ExtractionPersistDeps,
|
|
915
|
+
storage: StorageManager,
|
|
916
|
+
targetId: string,
|
|
917
|
+
input: {
|
|
918
|
+
category: string;
|
|
919
|
+
/** Incoming fact body, cited exactly once for this write by the caller. */
|
|
920
|
+
citedContent: string;
|
|
921
|
+
confidence: number;
|
|
922
|
+
tags: readonly string[];
|
|
923
|
+
intent?: { goal?: string; actionType?: string; entityTypes?: string[] };
|
|
924
|
+
sourceConnector?: string;
|
|
925
|
+
origin?: string;
|
|
926
|
+
toolScoped?: boolean;
|
|
927
|
+
},
|
|
928
|
+
): Promise<void> {
|
|
929
|
+
if (!resolvePresentationCapabilities(deps.config).verbatimArtifacts) return;
|
|
930
|
+
if (!deps.config.verbatimArtifactCategories.includes(input.category as MemoryCategory)) {
|
|
931
|
+
return;
|
|
932
|
+
}
|
|
933
|
+
if (!(input.confidence >= deps.config.verbatimArtifactsMinConfidence)) return;
|
|
934
|
+
try {
|
|
935
|
+
await storage.writeArtifact(input.citedContent, {
|
|
936
|
+
confidence: input.confidence,
|
|
937
|
+
tags: [...input.tags, "artifact"],
|
|
938
|
+
artifactType: deps.artifactTypeForCategory(input.category),
|
|
939
|
+
sourceMemoryId: targetId,
|
|
940
|
+
intentGoal: input.intent?.goal,
|
|
941
|
+
intentActionType: input.intent?.actionType,
|
|
942
|
+
intentEntityTypes: input.intent?.entityTypes,
|
|
943
|
+
...(input.sourceConnector ? { sourceConnector: input.sourceConnector } : {}),
|
|
944
|
+
...(input.origin ? { origin: input.origin } : {}),
|
|
945
|
+
...(input.toolScoped ? { toolScoped: true as const } : {}),
|
|
946
|
+
});
|
|
947
|
+
} catch (err) {
|
|
948
|
+
// Round N+17 (B): the caller runs this LAST, after every durable merge
|
|
949
|
+
// effect — log and skip; the committed target stays discoverable.
|
|
950
|
+
log.warn(
|
|
951
|
+
`semantic-merge: verbatim artifact write failed for ${targetId} (non-fatal; the committed target remains discoverable): ${err instanceof Error ? err.message : String(err)}`,
|
|
952
|
+
);
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
/**
|
|
957
|
+
* Round N+5 (A+B) + N+6 (A/B/C): the create path's write is followed by
|
|
958
|
+
* post-write stages that OBSERVE the persisted record — graph-edge
|
|
959
|
+
* construction and the behavior-signal ledger. The merge path's `continue`
|
|
960
|
+
* skipped both, so a judge-accepted merge committed new claims that
|
|
961
|
+
* graph-mode recall and runtime-policy learning could never see. This helper
|
|
962
|
+
* runs them for the SURVIVING target, derived from the re-read committed
|
|
963
|
+
* record exactly like `buildMergedTargetPromotionPayload`: category,
|
|
964
|
+
* entityRef, graph content, AND the relative graph path come from the
|
|
965
|
+
* committed record, never the incoming extraction and never a hot-only
|
|
966
|
+
* path-map fallback — the graph context is built from a hot-only corpus
|
|
967
|
+
* scan, so a cold-tier target is absent from it and only `committed.path`
|
|
968
|
+
* carries the true location (N+6 A). The graph content is the canonical RAW
|
|
969
|
+
* pre-citation form — the same body the create path hands `buildGraphEdge`.
|
|
970
|
+
* The target's prior generated edges are removed first in EVERY enabled
|
|
971
|
+
* graph type — entity from-side, time/causal inbound — so a re-merge
|
|
972
|
+
* REPLACES them instead of re-appending duplicates into append-only JSONLs
|
|
973
|
+
* that spreadingActivation would then double-count (N+6 B, N+7 G); if the
|
|
974
|
+
* replacement build fails, the rows the failed build already appended are
|
|
975
|
+
* removed and the removed edges restored, so failure leaves EXACTLY the old
|
|
976
|
+
* set (N+7 E, N+10 C). The target joins the
|
|
977
|
+
* batch's thread episode list at its END — deduped first when already
|
|
978
|
+
* present — so the merge is the thread's latest event and later facts in
|
|
979
|
+
* the same extraction chain their time/causal adjacency through it (N+6 C,
|
|
980
|
+
* N+7 F). Graph failures are fail-open (the committed merge stands,
|
|
981
|
+
* matching the create path's try/catch); the caller owns the ledger flush,
|
|
982
|
+
* so the behavior events are RETURNED for its storage.
|
|
983
|
+
*/
|
|
984
|
+
export async function runMergedTargetPostEffects(
|
|
985
|
+
deps: ExtractionPersistDeps,
|
|
986
|
+
storage: StorageManager,
|
|
987
|
+
merge: { targetId: string; mergedContent: string },
|
|
988
|
+
input: {
|
|
989
|
+
/** Written category (parity-guaranteed equal to the committed record's). */
|
|
990
|
+
category: MemoryCategory;
|
|
991
|
+
incomingContent: string;
|
|
992
|
+
incomingConfidence: number;
|
|
993
|
+
namespace: string;
|
|
994
|
+
graphCaps: GraphConstructionCapabilitySet;
|
|
995
|
+
graphContext: {
|
|
996
|
+
allMemsForGraph: MemoryFile[] | null;
|
|
997
|
+
memoryPathById: Map<string, string>;
|
|
998
|
+
previousPersistedRelPath?: string;
|
|
999
|
+
};
|
|
1000
|
+
threadIdForEdge: string | undefined;
|
|
1001
|
+
threadEpisodeIdsForGraph: string[] | undefined;
|
|
1002
|
+
},
|
|
1003
|
+
): Promise<BehaviorSignalEvent[]> {
|
|
1004
|
+
const signals = buildBehaviorSignalsForMemory({
|
|
1005
|
+
memoryId: merge.targetId,
|
|
1006
|
+
category: input.category,
|
|
1007
|
+
content: input.incomingContent,
|
|
1008
|
+
namespace: input.namespace,
|
|
1009
|
+
confidence: input.incomingConfidence,
|
|
1010
|
+
source: "extraction",
|
|
1011
|
+
});
|
|
1012
|
+
// N+6 C + round N+7 (F): the normal write path pushes every freshly
|
|
1013
|
+
// persisted memory onto the thread episode list (extraction-persist.ts
|
|
1014
|
+
// post-write block); the merge must record the target as the thread's
|
|
1015
|
+
// LATEST event. A target already in the list (created in this thread
|
|
1016
|
+
// earlier, or pushed by a previous merge in this batch) must MOVE to the
|
|
1017
|
+
// end: leaving it at its old position makes the NEXT fact's
|
|
1018
|
+
// recent-in-thread resolution chain through an older episode instead of
|
|
1019
|
+
// the merge that just happened.
|
|
1020
|
+
if (input.threadEpisodeIdsForGraph) {
|
|
1021
|
+
const episodes = input.threadEpisodeIdsForGraph;
|
|
1022
|
+
const existing = episodes.lastIndexOf(merge.targetId);
|
|
1023
|
+
if (existing !== -1) episodes.splice(existing, 1);
|
|
1024
|
+
episodes.push(merge.targetId);
|
|
1025
|
+
}
|
|
1026
|
+
if (!input.graphCaps.multiGraphMemory) return signals;
|
|
1027
|
+
try {
|
|
1028
|
+
const committed = await storage.getMemoryByIdIncludingArchived(merge.targetId);
|
|
1029
|
+
if (!committed || committed.content !== merge.mergedContent) return signals;
|
|
1030
|
+
if (inferMemoryStatus(committed.frontmatter, committed.path) !== "active") return signals;
|
|
1031
|
+
const entityRef = committed.frontmatter.entityRef;
|
|
1032
|
+
const rawBody = rawPreCitationMergedBody(deps, merge.mergedContent);
|
|
1033
|
+
// N+6 A: derive the relative path from the cold-aware committed record.
|
|
1034
|
+
// The graph context's path map was built from a HOT-only corpus scan, so
|
|
1035
|
+
// a cold-tier target is missing from it and the category-dir fallback
|
|
1036
|
+
// would fabricate a hot node path that graph recall cannot resolve.
|
|
1037
|
+
const memoryRelPath = path.relative(storage.dir, committed.path);
|
|
1038
|
+
input.graphContext.memoryPathById.set(merge.targetId, memoryRelPath);
|
|
1039
|
+
// The target predates this extraction, so a corpus-loaded context already
|
|
1040
|
+
// holds an entry for it — refresh that entry's body in place instead of
|
|
1041
|
+
// appending a duplicate the sibling scan would double-count.
|
|
1042
|
+
const all = input.graphContext.allMemsForGraph;
|
|
1043
|
+
if (Array.isArray(all)) {
|
|
1044
|
+
const entry = all.find((m) => path.relative(storage.dir, m.path) === memoryRelPath);
|
|
1045
|
+
if (entry) entry.content = rawBody;
|
|
1046
|
+
}
|
|
1047
|
+
// N+6 B + round N+7 (E/G) + round N+10 (C) + round N+12 (C): onMemoryWritten is
|
|
1048
|
+
// append-only, so a re-merge must first drop the target's prior
|
|
1049
|
+
// generated edges in EVERY enabled graph type — entity from-side,
|
|
1050
|
+
// time/causal inbound — or each later merge re-appends them and
|
|
1051
|
+
// spreadingActivation double-counts the duplicates while the JSONLs
|
|
1052
|
+
// The removed edges are captured; when the replacement build FAILS or is
|
|
1053
|
+
// superseded, the rows THAT build appended — returned by identity from
|
|
1054
|
+
// onMemoryWritten (round N+14), or carried on its rejection when it
|
|
1055
|
+
// throws mid-append (round N+16 A) — are removed first, never a
|
|
1056
|
+
// node-wide sweep, so rows a newer writer rebuilt after this removal
|
|
1057
|
+
// survive. The
|
|
1058
|
+
// prior edges are then RESTORED per graph type, skipped only where a
|
|
1059
|
+
// newer writer's rebuilt rows are live in the file — so failure leaves
|
|
1060
|
+
// EXACTLY the old set, and supersession leaves exactly the newer
|
|
1061
|
+
// writer's set: never none, never old plus partial-new.
|
|
1062
|
+
const rewriteTypes: GraphType[] = [];
|
|
1063
|
+
if (input.graphCaps.entityGraph) rewriteTypes.push("entity");
|
|
1064
|
+
if (input.graphCaps.timeGraph) rewriteTypes.push("time");
|
|
1065
|
+
if (input.graphCaps.causalGraph) rewriteTypes.push("causal");
|
|
1066
|
+
// Round N+12 (C): the remove-and-rebuild is revision-guarded end to end
|
|
1067
|
+
// (see rewriteMergedTargetGraphEdges) — a writer committing a newer body
|
|
1068
|
+
// mid-rebuild aborts this install instead of clobbering its edges.
|
|
1069
|
+
const installed = await rewriteMergedTargetGraphEdges(storage, {
|
|
1070
|
+
targetId: merge.targetId,
|
|
1071
|
+
memoryRelPath,
|
|
1072
|
+
mergedContent: merge.mergedContent,
|
|
1073
|
+
revisionChecked: committed.frontmatter.updated,
|
|
1074
|
+
rewriteTypes,
|
|
1075
|
+
build: () =>
|
|
1076
|
+
deps.buildGraphEdge(
|
|
1077
|
+
storage,
|
|
1078
|
+
memoryRelPath,
|
|
1079
|
+
entityRef,
|
|
1080
|
+
merge.targetId,
|
|
1081
|
+
rawBody,
|
|
1082
|
+
all,
|
|
1083
|
+
input.graphContext.memoryPathById,
|
|
1084
|
+
input.threadIdForEdge,
|
|
1085
|
+
input.threadEpisodeIdsForGraph,
|
|
1086
|
+
input.graphContext.previousPersistedRelPath,
|
|
1087
|
+
input.graphCaps,
|
|
1088
|
+
),
|
|
1089
|
+
});
|
|
1090
|
+
if (installed) input.graphContext.previousPersistedRelPath = memoryRelPath;
|
|
1091
|
+
} catch {
|
|
1092
|
+
/* fail-open: the committed merge stands; the create path's graph block fails open too */
|
|
1093
|
+
}
|
|
1094
|
+
return signals;
|
|
1095
|
+
}
|