@remnic/core 1.1.30 → 9.3.515
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 +2 -2
- package/dist/access-cli.d.ts +1 -1
- package/dist/access-cli.js +156 -119
- package/dist/access-cli.js.map +1 -1
- package/dist/access-http.d.ts +9 -5
- package/dist/access-http.js +51 -51
- package/dist/access-idempotency.d.ts +2 -0
- package/dist/access-idempotency.js +1 -1
- package/dist/access-mcp.d.ts +16 -9
- package/dist/access-mcp.js +44 -44
- package/dist/access-schema.d.ts +47 -15
- package/dist/access-schema.js +8 -8
- package/dist/{access-service-B5hgZPCN.d.ts → access-service-qrrIrC-0.d.ts} +11 -129
- package/dist/access-service.d.ts +7 -4
- package/dist/access-service.js +40 -40
- package/dist/action-confidence.d.ts +1 -0
- package/dist/active-memory-bridge.d.ts +1 -0
- package/dist/active-memory-bridge.js +3 -2
- package/dist/active-recall.d.ts +1 -0
- package/dist/active-recall.js +14 -9
- package/dist/active-recall.js.map +1 -1
- package/dist/adapters/claude-code.d.ts +6 -2
- package/dist/adapters/claude-code.js +2 -2
- package/dist/adapters/codex.d.ts +5 -1
- package/dist/adapters/codex.js +2 -2
- package/dist/adapters/hermes.js +2 -2
- package/dist/adapters/index.js +6 -6
- package/dist/adapters/registry.js +6 -6
- package/dist/adapters/replit.d.ts +4 -2
- package/dist/adapters/replit.js +2 -2
- package/dist/adapters/types.d.ts +4 -0
- package/dist/adapters/types.js +1 -1
- package/dist/behavior-learner.d.ts +1 -0
- package/dist/behavior-signals.d.ts +1 -0
- package/dist/behavior-signals.js +1 -1
- package/dist/bootstrap.d.ts +5 -3
- package/dist/bootstrap.js +2 -2
- package/dist/boxes.d.ts +1 -0
- package/dist/boxes.js +1 -1
- package/dist/briefing.d.ts +1 -0
- package/dist/briefing.js +9 -9
- package/dist/buffer-surprise-report.d.ts +1 -0
- package/dist/buffer.d.ts +1 -0
- package/dist/buffer.js +2 -2
- package/dist/bulk-import/index.d.ts +28 -0
- package/dist/bulk-import/index.js +31 -0
- package/dist/calibration.d.ts +2 -0
- package/dist/calibration.js +50 -17
- package/dist/calibration.js.map +1 -1
- package/dist/{capsule-crypto-5CYAGVC5.js → capsule-crypto-7FJQINUR.js} +2 -2
- package/dist/{capsule-merge-4MGKE7C5.js → capsule-merge-T2JRE46P.js} +3 -3
- package/dist/causal-behavior.d.ts +1 -0
- package/dist/causal-behavior.js +4 -4
- package/dist/causal-chain.js +4 -4
- package/dist/causal-consolidation.d.ts +16 -1
- package/dist/causal-consolidation.js +115 -32
- package/dist/causal-consolidation.js.map +1 -1
- package/dist/causal-retrieval.js +14 -6
- package/dist/causal-retrieval.js.map +1 -1
- package/dist/causal-trajectory-graph.js +2 -2
- package/dist/causal-trajectory.js +2 -2
- package/dist/{chunk-SAZS2QZB.js → chunk-23UORJ4S.js} +3 -3
- package/dist/{chunk-76FLAAUC.js → chunk-2AN2L4NL.js} +17 -6
- package/dist/chunk-2AN2L4NL.js.map +1 -0
- package/dist/{chunk-W4L6CZKA.js → chunk-2DL3OFLD.js} +15 -10
- package/dist/chunk-2DL3OFLD.js.map +1 -0
- package/dist/{chunk-7OZ53EXP.js → chunk-2NLLXCJG.js} +21 -10
- package/dist/chunk-2NLLXCJG.js.map +1 -0
- package/dist/{chunk-PK7H5L6Y.js → chunk-2NM43EWN.js} +2 -2
- package/dist/{chunk-PYXS46O7.js → chunk-3BP57I6J.js} +2 -2
- package/dist/{chunk-FBYESMQ2.js → chunk-3C5RPJAX.js} +2 -2
- package/dist/{chunk-QKZGQIPJ.js → chunk-3Q4H3OBR.js} +171 -64
- package/dist/chunk-3Q4H3OBR.js.map +1 -0
- package/dist/{chunk-FKFMOY3N.js → chunk-42NQ7AVG.js} +3 -4
- package/dist/{chunk-FKFMOY3N.js.map → chunk-42NQ7AVG.js.map} +1 -1
- package/dist/chunk-4426WSWL.js +73 -0
- package/dist/chunk-4426WSWL.js.map +1 -0
- package/dist/{chunk-LIRZNNUP.js → chunk-44442YCH.js} +5 -2
- package/dist/chunk-44442YCH.js.map +1 -0
- package/dist/{chunk-H3ME6L6D.js → chunk-46GJIW5M.js} +23 -20
- package/dist/chunk-46GJIW5M.js.map +1 -0
- package/dist/{chunk-QDZ2RLEC.js → chunk-472U7RDF.js} +3 -3
- package/dist/chunk-472U7RDF.js.map +1 -0
- package/dist/{chunk-NN2DKE4T.js → chunk-4H5ZJHEN.js} +16 -3
- package/dist/{chunk-NN2DKE4T.js.map → chunk-4H5ZJHEN.js.map} +1 -1
- package/dist/{chunk-56K5QLHX.js → chunk-4HP7HIE3.js} +56 -13
- package/dist/chunk-4HP7HIE3.js.map +1 -0
- package/dist/{chunk-RK2Y4XOM.js → chunk-4JRRISLU.js} +9 -6
- package/dist/chunk-4JRRISLU.js.map +1 -0
- package/dist/{chunk-MG7NA5H3.js → chunk-4Q73JBSM.js} +17 -12
- package/dist/chunk-4Q73JBSM.js.map +1 -0
- package/dist/{chunk-XKLD5OK4.js → chunk-4RR6ROTB.js} +10 -11
- package/dist/chunk-4RR6ROTB.js.map +1 -0
- package/dist/{chunk-4KGVTPGD.js → chunk-6F6BXB7A.js} +9 -8
- package/dist/chunk-6F6BXB7A.js.map +1 -0
- package/dist/{chunk-NMZY542O.js → chunk-6URPAY2D.js} +41 -17
- package/dist/chunk-6URPAY2D.js.map +1 -0
- package/dist/{chunk-N53K2EXC.js → chunk-6VF75M3X.js} +2 -2
- package/dist/{chunk-XSZEP4SF.js → chunk-6XSPNR6L.js} +6 -5
- package/dist/chunk-6XSPNR6L.js.map +1 -0
- package/dist/{chunk-6H2TESSP.js → chunk-765K3SAT.js} +3 -3
- package/dist/{chunk-EDTHC6UD.js → chunk-77NAFXUD.js} +2 -2
- package/dist/{chunk-S7KDBTWT.js → chunk-7F7Z6MOS.js} +29 -14
- package/dist/chunk-7F7Z6MOS.js.map +1 -0
- package/dist/{chunk-MZH6EHNR.js → chunk-7H6CFEBJ.js} +41 -14
- package/dist/chunk-7H6CFEBJ.js.map +1 -0
- package/dist/{chunk-575RMLWN.js → chunk-7MV5CWTE.js} +26 -20
- package/dist/chunk-7MV5CWTE.js.map +1 -0
- package/dist/{chunk-MGKYQQYF.js → chunk-7Q3RCKAQ.js} +2 -2
- package/dist/chunk-7RXCMVFQ.js +27 -0
- package/dist/chunk-7RXCMVFQ.js.map +1 -0
- package/dist/{chunk-DGXUHMOV.js → chunk-A2IYSXDQ.js} +25 -6
- package/dist/chunk-A2IYSXDQ.js.map +1 -0
- package/dist/{chunk-EABGC2TL.js → chunk-A2Z6UCWT.js} +26 -4
- package/dist/chunk-A2Z6UCWT.js.map +1 -0
- package/dist/{chunk-3VAL7ZL2.js → chunk-A52AKD7C.js} +59 -24
- package/dist/chunk-A52AKD7C.js.map +1 -0
- package/dist/{chunk-5375UYTQ.js → chunk-A6D7A2FW.js} +4 -4
- package/dist/chunk-A6D7A2FW.js.map +1 -0
- package/dist/{chunk-FAAFWE4G.js → chunk-ALEPI75L.js} +24 -6
- package/dist/chunk-ALEPI75L.js.map +1 -0
- package/dist/{chunk-3SLRNYNG.js → chunk-AUDJPF4N.js} +15 -4
- package/dist/chunk-AUDJPF4N.js.map +1 -0
- package/dist/chunk-B5XMS73R.js +145 -0
- package/dist/chunk-B5XMS73R.js.map +1 -0
- package/dist/{chunk-HXXBL2KD.js → chunk-BECQDWBA.js} +44 -4
- package/dist/chunk-BECQDWBA.js.map +1 -0
- package/dist/{chunk-7SEAZFFB.js → chunk-BEUDU7Y4.js} +24 -4
- package/dist/chunk-BEUDU7Y4.js.map +1 -0
- package/dist/{chunk-XVVIG67A.js → chunk-BLZAVUD2.js} +61 -17
- package/dist/chunk-BLZAVUD2.js.map +1 -0
- package/dist/chunk-CHBI22MI.js +159 -0
- package/dist/chunk-CHBI22MI.js.map +1 -0
- package/dist/{chunk-GDFS42HT.js → chunk-CHCA44C3.js} +15 -8
- package/dist/chunk-CHCA44C3.js.map +1 -0
- package/dist/chunk-CINZGPSJ.js +22 -0
- package/dist/chunk-CINZGPSJ.js.map +1 -0
- package/dist/chunk-CMTINOFS.js +36 -0
- package/dist/chunk-CMTINOFS.js.map +1 -0
- package/dist/{chunk-34DQE4KF.js → chunk-CO7ZO4TU.js} +2 -2
- package/dist/{chunk-PFV5C235.js → chunk-CPPS65WS.js} +2 -1
- package/dist/{chunk-PFV5C235.js.map → chunk-CPPS65WS.js.map} +1 -1
- package/dist/{chunk-DINWEURR.js → chunk-CSKLPDN6.js} +20 -6
- package/dist/chunk-CSKLPDN6.js.map +1 -0
- package/dist/chunk-CWWMTTQE.js +566 -0
- package/dist/chunk-CWWMTTQE.js.map +1 -0
- package/dist/{chunk-IQT3XTKW.js → chunk-D24OXEPB.js} +13 -7
- package/dist/chunk-D24OXEPB.js.map +1 -0
- package/dist/{chunk-KRBK4BQH.js → chunk-D6WE5MTW.js} +272 -411
- package/dist/chunk-D6WE5MTW.js.map +1 -0
- package/dist/{chunk-WIICJPET.js → chunk-DEUNUKTD.js} +6 -4
- package/dist/{chunk-WIICJPET.js.map → chunk-DEUNUKTD.js.map} +1 -1
- package/dist/{chunk-ZYVPLJ4T.js → chunk-DHGSZ3UD.js} +9 -7
- package/dist/chunk-DHGSZ3UD.js.map +1 -0
- package/dist/{chunk-JR4ZC3G4.js → chunk-DLJ4IR6M.js} +91 -41
- package/dist/chunk-DLJ4IR6M.js.map +1 -0
- package/dist/{chunk-U4SCL7B7.js → chunk-DRD2Q7HQ.js} +82 -18
- package/dist/chunk-DRD2Q7HQ.js.map +1 -0
- package/dist/{chunk-2IWUMAES.js → chunk-E62SBGQ3.js} +28 -13
- package/dist/chunk-E62SBGQ3.js.map +1 -0
- package/dist/{chunk-C5BCH4ZS.js → chunk-EAZGEEG2.js} +21 -3
- package/dist/chunk-EAZGEEG2.js.map +1 -0
- package/dist/{chunk-TPB3I2AC.js → chunk-ECZU5BJH.js} +31 -10
- package/dist/chunk-ECZU5BJH.js.map +1 -0
- package/dist/{chunk-77H5NU3M.js → chunk-EDBEWFJO.js} +82 -18
- package/dist/chunk-EDBEWFJO.js.map +1 -0
- package/dist/chunk-EDQVAMQI.js +308 -0
- package/dist/chunk-EDQVAMQI.js.map +1 -0
- package/dist/{chunk-RRF5UOBJ.js → chunk-EI6V5UXY.js} +22 -15
- package/dist/chunk-EI6V5UXY.js.map +1 -0
- package/dist/{chunk-I5GLV3VE.js → chunk-EIPUHVKE.js} +31 -24
- package/dist/{chunk-I5GLV3VE.js.map → chunk-EIPUHVKE.js.map} +1 -1
- package/dist/{chunk-ZKSK55RC.js → chunk-ETUPBUHB.js} +2 -2
- package/dist/{chunk-25MQ7IHJ.js → chunk-EUML3N6B.js} +17 -6
- package/dist/chunk-EUML3N6B.js.map +1 -0
- package/dist/{chunk-5RGLBDQF.js → chunk-EVZFIAPG.js} +12 -12
- package/dist/chunk-EVZFIAPG.js.map +1 -0
- package/dist/{chunk-QRNI5JBH.js → chunk-EYIEWJNI.js} +2 -2
- package/dist/{chunk-3OWUCDKH.js → chunk-FER4WARO.js} +176 -42
- package/dist/chunk-FER4WARO.js.map +1 -0
- package/dist/{chunk-43PJZYGL.js → chunk-FPGE5NVO.js} +45 -10
- package/dist/chunk-FPGE5NVO.js.map +1 -0
- package/dist/{chunk-C6QPK5GG.js → chunk-FZZ2QTJI.js} +2 -2
- package/dist/{chunk-D46YSIYX.js → chunk-G3Z3QEF5.js} +19 -11
- package/dist/{chunk-D46YSIYX.js.map → chunk-G3Z3QEF5.js.map} +1 -1
- package/dist/{chunk-3JXBXXM2.js → chunk-G4IAEX6D.js} +2 -2
- package/dist/{chunk-MSWG7JI6.js → chunk-G56P5RLD.js} +8 -2
- package/dist/chunk-G56P5RLD.js.map +1 -0
- package/dist/{chunk-AGZQD76C.js → chunk-GCGJW34D.js} +48 -2
- package/dist/chunk-GCGJW34D.js.map +1 -0
- package/dist/chunk-H2NCNXMS.js +159 -0
- package/dist/chunk-H2NCNXMS.js.map +1 -0
- package/dist/{chunk-XYIK4LF6.js → chunk-H3FZVNRN.js} +8 -2
- package/dist/chunk-H3FZVNRN.js.map +1 -0
- package/dist/{chunk-TK4UEOSK.js → chunk-HDDRVXX4.js} +8 -8
- package/dist/chunk-HDDRVXX4.js.map +1 -0
- package/dist/{chunk-LLQ2LLWF.js → chunk-HENLZHIT.js} +15 -5
- package/dist/chunk-HENLZHIT.js.map +1 -0
- package/dist/{chunk-N2D6GXBM.js → chunk-HINSGUA7.js} +28 -20
- package/dist/chunk-HINSGUA7.js.map +1 -0
- package/dist/{chunk-APO3DCMU.js → chunk-HLAVGJ62.js} +30 -8
- package/dist/chunk-HLAVGJ62.js.map +1 -0
- package/dist/{chunk-TPMQ3G6Z.js → chunk-HOJZMQ4J.js} +2 -2
- package/dist/chunk-HOJZMQ4J.js.map +1 -0
- package/dist/{chunk-LUDTDZLK.js → chunk-HPWVAEET.js} +33 -7
- package/dist/chunk-HPWVAEET.js.map +1 -0
- package/dist/{chunk-NZL6GGQE.js → chunk-HQ6NIBL6.js} +92 -30
- package/dist/chunk-HQ6NIBL6.js.map +1 -0
- package/dist/{chunk-UWVJF25J.js → chunk-HWVTS5NO.js} +20 -6
- package/dist/chunk-HWVTS5NO.js.map +1 -0
- package/dist/{chunk-2WWLHTZY.js → chunk-IC4GELZE.js} +2 -2
- package/dist/{chunk-QA2ZAPBU.js → chunk-IPLYGWQF.js} +28 -20
- package/dist/chunk-IPLYGWQF.js.map +1 -0
- package/dist/{chunk-A6KTB5R6.js → chunk-IQ3OI2RR.js} +3 -3
- package/dist/chunk-IQ3OI2RR.js.map +1 -0
- package/dist/{chunk-6LVVDPJ4.js → chunk-J64TK33U.js} +3 -4
- package/dist/chunk-J64TK33U.js.map +1 -0
- package/dist/{chunk-6FC5EGNV.js → chunk-JBPKEARU.js} +15 -5
- package/dist/{chunk-6FC5EGNV.js.map → chunk-JBPKEARU.js.map} +1 -1
- package/dist/{chunk-RHY3HH7P.js → chunk-JFEKNTX7.js} +125 -33
- package/dist/chunk-JFEKNTX7.js.map +1 -0
- package/dist/{chunk-TZOLIGIG.js → chunk-JJEJJ7RK.js} +4 -2
- package/dist/chunk-JJEJJ7RK.js.map +1 -0
- package/dist/{chunk-PCUKNJAZ.js → chunk-JKV57BTN.js} +2 -2
- package/dist/{chunk-EJI5XIBB.js → chunk-JLNBQWZ2.js} +55 -7
- package/dist/chunk-JLNBQWZ2.js.map +1 -0
- package/dist/{chunk-XIG5PDM7.js → chunk-JUC24CTX.js} +8 -12
- package/dist/chunk-JUC24CTX.js.map +1 -0
- package/dist/{chunk-UL2NNBUL.js → chunk-JYIKKAK3.js} +106 -44
- package/dist/chunk-JYIKKAK3.js.map +1 -0
- package/dist/{chunk-OIGNEXKZ.js → chunk-K5O2QY6T.js} +5 -1
- package/dist/{chunk-OIGNEXKZ.js.map → chunk-K5O2QY6T.js.map} +1 -1
- package/dist/{chunk-ZTFCYYEZ.js → chunk-KCYE2MZM.js} +3 -3
- package/dist/chunk-KCYE2MZM.js.map +1 -0
- package/dist/{chunk-JWPLJLDU.js → chunk-KD3QD3A5.js} +2 -2
- package/dist/{chunk-JWPLJLDU.js.map → chunk-KD3QD3A5.js.map} +1 -1
- package/dist/{chunk-YRMVARQP.js → chunk-KFY3SGN7.js} +49 -2
- package/dist/chunk-KFY3SGN7.js.map +1 -0
- package/dist/{chunk-CYFQJMUV.js → chunk-KIB7SDIJ.js} +15 -10
- package/dist/chunk-KIB7SDIJ.js.map +1 -0
- package/dist/{chunk-3KW65B36.js → chunk-KILOTVIF.js} +95 -48
- package/dist/chunk-KILOTVIF.js.map +1 -0
- package/dist/{chunk-MXFBBHJU.js → chunk-KJMYHC7K.js} +10 -5
- package/dist/chunk-KJMYHC7K.js.map +1 -0
- package/dist/{chunk-W3LR522O.js → chunk-KM2A35EO.js} +36 -34
- package/dist/chunk-KM2A35EO.js.map +1 -0
- package/dist/{chunk-WELDCG6C.js → chunk-L227SKTB.js} +109 -36
- package/dist/chunk-L227SKTB.js.map +1 -0
- package/dist/{chunk-W6AQJ2PY.js → chunk-L7S47WZT.js} +35 -16
- package/dist/chunk-L7S47WZT.js.map +1 -0
- package/dist/{chunk-BVF3AGJP.js → chunk-LJBOVCQG.js} +26 -11
- package/dist/chunk-LJBOVCQG.js.map +1 -0
- package/dist/{chunk-2KI4QFHU.js → chunk-LMDRGRJ2.js} +2 -2
- package/dist/{chunk-MY6TPVXW.js → chunk-LMPHTYJC.js} +2 -2
- package/dist/{chunk-EHRTFRWW.js → chunk-LQHDIS7L.js} +10 -5
- package/dist/chunk-LQHDIS7L.js.map +1 -0
- package/dist/chunk-LUDUFZTV.js +170 -0
- package/dist/chunk-LUDUFZTV.js.map +1 -0
- package/dist/{chunk-5HRY2WRF.js → chunk-LZ3VEOU5.js} +2 -2
- package/dist/{chunk-Q7P4WJDP.js → chunk-M5T4Q2ZU.js} +1 -1
- package/dist/chunk-M5T4Q2ZU.js.map +1 -0
- package/dist/{chunk-ICRIXAP2.js → chunk-MC4FJXPA.js} +16 -6
- package/dist/chunk-MC4FJXPA.js.map +1 -0
- package/dist/{chunk-WPGJYVUH.js → chunk-MGVIEM2O.js} +23 -6
- package/dist/chunk-MGVIEM2O.js.map +1 -0
- package/dist/{chunk-OC7KHOOX.js → chunk-O27WNHTT.js} +22 -6
- package/dist/chunk-O27WNHTT.js.map +1 -0
- package/dist/{chunk-NGAVDO7E.js → chunk-OADWQ5CR.js} +2 -2
- package/dist/{chunk-2NMMFZ5T.js → chunk-OD4FM2U7.js} +6 -3
- package/dist/chunk-OD4FM2U7.js.map +1 -0
- package/dist/{chunk-OZHRDTDX.js → chunk-OKTXM5H4.js} +11 -1
- package/dist/chunk-OKTXM5H4.js.map +1 -0
- package/dist/{chunk-RXDLTSWT.js → chunk-ONPLNAPX.js} +16 -7
- package/dist/chunk-ONPLNAPX.js.map +1 -0
- package/dist/{chunk-FJ43PRLT.js → chunk-ORFGK3XI.js} +20 -14
- package/dist/chunk-ORFGK3XI.js.map +1 -0
- package/dist/{chunk-DOM4GKSW.js → chunk-OZKVVUJB.js} +3 -3
- package/dist/{chunk-MT4HVDUZ.js → chunk-PM3QHTFT.js} +3 -3
- package/dist/{chunk-4DWOBS2A.js → chunk-PRQJ5ESM.js} +27 -2
- package/dist/{chunk-4DWOBS2A.js.map → chunk-PRQJ5ESM.js.map} +1 -1
- package/dist/chunk-PU44GBL4.js +52 -0
- package/dist/chunk-PU44GBL4.js.map +1 -0
- package/dist/{chunk-MJFNCJXV.js → chunk-Q4CAQGKQ.js} +47 -9
- package/dist/chunk-Q4CAQGKQ.js.map +1 -0
- package/dist/{chunk-U3WSW6PZ.js → chunk-QMYXNM4P.js} +90 -35
- package/dist/chunk-QMYXNM4P.js.map +1 -0
- package/dist/{chunk-NBNN5GOB.js → chunk-QY7YA7OL.js} +11 -2
- package/dist/chunk-QY7YA7OL.js.map +1 -0
- package/dist/{chunk-QLLBRHAT.js → chunk-R26QUUQN.js} +181 -257
- package/dist/chunk-R26QUUQN.js.map +1 -0
- package/dist/{chunk-ZK7I7JYV.js → chunk-R3PS27B4.js} +7 -7
- package/dist/{chunk-TMQLARTH.js → chunk-RCTS5CKK.js} +33 -14
- package/dist/chunk-RCTS5CKK.js.map +1 -0
- package/dist/{chunk-2PRLKQAH.js → chunk-RLV3PQGH.js} +35 -19
- package/dist/chunk-RLV3PQGH.js.map +1 -0
- package/dist/{chunk-WW3QQF4H.js → chunk-ROZJACKP.js} +16 -1
- package/dist/chunk-ROZJACKP.js.map +1 -0
- package/dist/{chunk-7MNMYOFP.js → chunk-RSUYKGGZ.js} +3 -4
- package/dist/chunk-RSUYKGGZ.js.map +1 -0
- package/dist/{chunk-LT3NLYSI.js → chunk-RUZOJKNF.js} +10 -7
- package/dist/chunk-RUZOJKNF.js.map +1 -0
- package/dist/{chunk-326G7DJK.js → chunk-RW5DGAGO.js} +67 -13
- package/dist/chunk-RW5DGAGO.js.map +1 -0
- package/dist/{chunk-KOSORCJG.js → chunk-S53PKKWK.js} +63 -24
- package/dist/chunk-S53PKKWK.js.map +1 -0
- package/dist/{chunk-65PG43EQ.js → chunk-S7WU3Y3D.js} +21 -4
- package/dist/chunk-S7WU3Y3D.js.map +1 -0
- package/dist/{chunk-SKE7JYKA.js → chunk-SFXKHM7P.js} +2 -2
- package/dist/{chunk-HMDCOMYU.js → chunk-SKGV326D.js} +3 -3
- package/dist/chunk-T2PO5MUF.js +62 -0
- package/dist/chunk-T2PO5MUF.js.map +1 -0
- package/dist/{chunk-C7VW7C3F.js → chunk-TDKQGLJW.js} +3 -3
- package/dist/chunk-TDKQGLJW.js.map +1 -0
- package/dist/{chunk-3QKK7QOS.js → chunk-TERNBNJB.js} +3 -3
- package/dist/chunk-TERNBNJB.js.map +1 -0
- package/dist/{chunk-MXC3AP5I.js → chunk-TGQ2NTWH.js} +12 -7
- package/dist/chunk-TGQ2NTWH.js.map +1 -0
- package/dist/{chunk-3Y4P7RXM.js → chunk-TMSXWOBZ.js} +3 -4
- package/dist/chunk-TMSXWOBZ.js.map +1 -0
- package/dist/{chunk-3TNBOMQT.js → chunk-TVRN5QKH.js} +11 -11
- package/dist/{chunk-3TNBOMQT.js.map → chunk-TVRN5QKH.js.map} +1 -1
- package/dist/{chunk-5UM2VJ6D.js → chunk-UEY3VB6W.js} +2 -2
- package/dist/{chunk-I6K5FBRQ.js → chunk-UI3NYK34.js} +4 -1
- package/dist/{chunk-I6K5FBRQ.js.map → chunk-UI3NYK34.js.map} +1 -1
- package/dist/{chunk-VBJ7V5SK.js → chunk-UIPDNLXA.js} +21 -8
- package/dist/chunk-UIPDNLXA.js.map +1 -0
- package/dist/{chunk-GIF42EW3.js → chunk-UP6MOYCB.js} +3 -3
- package/dist/{chunk-K4FLSOR5.js → chunk-USYGGIJZ.js} +44 -15
- package/dist/chunk-USYGGIJZ.js.map +1 -0
- package/dist/{chunk-FIT6DMX6.js → chunk-UWY7GIVS.js} +152 -54
- package/dist/chunk-UWY7GIVS.js.map +1 -0
- package/dist/{chunk-MRILGULB.js → chunk-V2RCP53Q.js} +2 -2
- package/dist/{chunk-XKECPATV.js → chunk-VFUEZZBS.js} +113 -4
- package/dist/chunk-VFUEZZBS.js.map +1 -0
- package/dist/{chunk-FSFEQI74.js → chunk-W7L6HXUC.js} +2 -2
- package/dist/{chunk-3IQ2TR4N.js → chunk-WLEB7WCG.js} +2 -2
- package/dist/{chunk-GL6I6MEQ.js → chunk-WSGF57U2.js} +3 -3
- package/dist/{chunk-KNKUID7G.js → chunk-X7Y7WX73.js} +72 -6
- package/dist/chunk-X7Y7WX73.js.map +1 -0
- package/dist/{chunk-5NPGSAVB.js → chunk-XEKAG3FM.js} +23 -5
- package/dist/chunk-XEKAG3FM.js.map +1 -0
- package/dist/{chunk-3APJ5EVB.js → chunk-XKIQZXUB.js} +41 -26
- package/dist/chunk-XKIQZXUB.js.map +1 -0
- package/dist/chunk-XKXKSQU7.js +92 -0
- package/dist/chunk-XKXKSQU7.js.map +1 -0
- package/dist/{chunk-JA3AK3PT.js → chunk-XNLXAWHX.js} +4 -4
- package/dist/{chunk-CULXMQJH.js → chunk-XPXEJRUB.js} +3 -3
- package/dist/chunk-XPXEJRUB.js.map +1 -0
- package/dist/{chunk-PZIAX57I.js → chunk-XR6DNK4U.js} +7 -4
- package/dist/chunk-XR6DNK4U.js.map +1 -0
- package/dist/{chunk-47VWKCAF.js → chunk-XSQ4SGM5.js} +33 -4
- package/dist/chunk-XSQ4SGM5.js.map +1 -0
- package/dist/{chunk-66DHUKLO.js → chunk-XSWKORGM.js} +16 -14
- package/dist/chunk-XSWKORGM.js.map +1 -0
- package/dist/{chunk-QR3C7BKQ.js → chunk-XZ4WBBB5.js} +7 -8
- package/dist/chunk-XZ4WBBB5.js.map +1 -0
- package/dist/{chunk-WNARATI3.js → chunk-Y2SXZ5KZ.js} +59 -11
- package/dist/chunk-Y2SXZ5KZ.js.map +1 -0
- package/dist/{chunk-SIC6U3GZ.js → chunk-YHV3KRKS.js} +3 -3
- package/dist/{chunk-ZPKBYX2F.js → chunk-YNDLCWXS.js} +85 -9
- package/dist/chunk-YNDLCWXS.js.map +1 -0
- package/dist/{chunk-VLXA6PI2.js → chunk-YQMZ7IH2.js} +4 -4
- package/dist/{chunk-TMM4S4IJ.js → chunk-YR6GIWWY.js} +58 -8
- package/dist/chunk-YR6GIWWY.js.map +1 -0
- package/dist/{chunk-DK5LDEQM.js → chunk-YR7XMOWK.js} +39 -23
- package/dist/chunk-YR7XMOWK.js.map +1 -0
- package/dist/chunk-ZFXCQPNO.js +27 -0
- package/dist/chunk-ZFXCQPNO.js.map +1 -0
- package/dist/citations.js +1 -1
- package/dist/{cli-CJKI2JIe.d.ts → cli-X4NJoqSe.d.ts} +8 -31
- package/dist/cli.d.ts +10 -6
- package/dist/cli.js +122 -117
- package/dist/commitment-ledger.js +2 -2
- package/dist/compat/checks.js +1 -2
- package/dist/compounding/engine.d.ts +3 -2
- package/dist/compounding/engine.js +11 -11
- package/dist/compounding/preference-consolidator.d.ts +1 -0
- package/dist/compounding/preference-consolidator.js +8 -8
- package/dist/compounding/preference-consolidator.js.map +1 -1
- package/dist/compression-optimizer.d.ts +1 -0
- package/dist/compression-optimizer.js +1 -1
- package/dist/config.d.ts +1 -0
- package/dist/config.js +3 -2
- package/dist/connectors/codex-materialize-runner.d.ts +1 -0
- package/dist/connectors/codex-materialize-runner.js +12 -11
- package/dist/connectors/codex-materialize.d.ts +1 -0
- package/dist/connectors/codex-materialize.js +3 -2
- package/dist/connectors/index.d.ts +1 -0
- package/dist/connectors/index.js +14 -14
- package/dist/{connectors-cli-CwbyjGR7.d.ts → connectors-cli-DbTPNj2H.d.ts} +7 -1
- package/dist/connectors-cli.d.ts +1 -1
- package/dist/connectors-cli.js +3 -1
- package/dist/consolidation-provenance-check.d.ts +1 -0
- package/dist/consolidation-provenance-check.js +2 -2
- package/dist/consolidation-undo.d.ts +1 -0
- package/dist/consolidation-undo.js +1 -1
- package/dist/contradiction/index.d.ts +3 -1
- package/dist/contradiction/index.js +3 -3
- package/dist/{contradiction-review-ATP4S6IC.js → contradiction-review-6V2LXXK6.js} +2 -2
- package/dist/{contradiction-scan-5A4IDZV5.js → contradiction-scan-GIRVC4C7.js} +3 -3
- package/dist/conversation-index/backend.d.ts +3 -1
- package/dist/conversation-index/backend.js +3 -3
- package/dist/conversation-index/chunker.d.ts +1 -0
- package/dist/conversation-index/cleanup.js +1 -1
- package/dist/conversation-index/faiss-adapter.d.ts +2 -1
- package/dist/conversation-index/faiss-adapter.js +1 -1
- package/dist/conversation-index/indexer.d.ts +5 -2
- package/dist/conversation-index/indexer.js +1 -1
- package/dist/conversation-index/search.d.ts +2 -1
- package/dist/cross-namespace-budget.js +1 -1
- package/dist/cue-anchors.js +2 -2
- package/dist/dashboard-runtime.d.ts +6 -0
- package/dist/dashboard-runtime.js +3 -3
- package/dist/day-summary.d.ts +1 -0
- package/dist/day-summary.js +2 -2
- package/dist/delinearize.d.ts +1 -0
- package/dist/direct-answer-wiring.d.ts +1 -0
- package/dist/direct-answer.d.ts +1 -0
- package/dist/{dreams-ledger-LR2NBAZE.js → dreams-ledger-3WSCI5V4.js} +5 -4
- package/dist/{dreams-ledger-LR2NBAZE.js.map → dreams-ledger-3WSCI5V4.js.map} +1 -1
- package/dist/embedding-fallback.d.ts +3 -0
- package/dist/embedding-fallback.js +2 -2
- package/dist/enrichment/index.d.ts +1 -0
- package/dist/enrichment/index.js +1 -1
- package/dist/entity-retrieval.d.ts +2 -0
- package/dist/entity-retrieval.js +9 -9
- package/dist/entity-schema.d.ts +1 -0
- package/dist/evals.js +1 -1
- package/dist/explicit-capture.d.ts +5 -3
- package/dist/explicit-capture.js +2 -2
- package/dist/extraction-judge-telemetry.d.ts +2 -0
- package/dist/extraction-judge-training.d.ts +2 -0
- package/dist/extraction-judge.d.ts +2 -0
- package/dist/extraction.d.ts +2 -0
- package/dist/extraction.js +12 -12
- package/dist/{faiss-adapter-CzPghc4C.d.ts → faiss-adapter-BHecI1fF.d.ts} +4 -1
- package/dist/fallback-llm.d.ts +11 -1
- package/dist/fallback-llm.js +8 -6
- package/dist/{first-start-migration-4MHQEOSD.js → first-start-migration-CKTCTCQI.js} +5 -5
- package/dist/graph-dashboard-diff.d.ts +4 -0
- package/dist/graph-dashboard-diff.js +1 -1
- package/dist/graph-dashboard-parser.js +1 -1
- package/dist/{graph-edge-decay-5DI5GUNL.js → graph-edge-decay-MUP5J7CC.js} +6 -6
- package/dist/graph-events.js +1 -1
- package/dist/graph-snapshot.js +3 -3
- package/dist/graph.js +2 -2
- package/dist/harmonic-retrieval.js +4 -4
- package/dist/identity-continuity.d.ts +1 -0
- package/dist/importance.d.ts +1 -0
- package/dist/importers/index.d.ts +244 -0
- package/dist/importers/index.js +20 -0
- package/dist/index.d.ts +21 -351
- package/dist/index.js +886 -561
- package/dist/index.js.map +1 -1
- package/dist/intent.d.ts +1 -0
- package/dist/lcm/archive.d.ts +2 -2
- package/dist/lcm/archive.js +2 -2
- package/dist/lcm/engine.d.ts +3 -2
- package/dist/lcm/engine.js +6 -6
- package/dist/lcm/index.d.ts +1 -0
- package/dist/lcm/index.js +8 -8
- package/dist/lcm/recall.js +1 -1
- package/dist/lcm/summarizer.js +3 -3
- package/dist/lcm/tools.d.ts +1 -0
- package/dist/lifecycle.d.ts +1 -0
- package/dist/live-connectors-runner.d.ts +1 -0
- package/dist/live-connectors-runner.js +6 -6
- package/dist/local-llm.d.ts +1 -0
- package/dist/local-llm.js +2 -2
- package/dist/maintenance/archive-observations.js +1 -1
- package/dist/maintenance/memory-governance.d.ts +3 -1
- package/dist/maintenance/memory-governance.js +10 -8
- package/dist/maintenance/migrate-observations.js +3 -2
- package/dist/maintenance/observation-ledger-utils.d.ts +3 -0
- package/dist/maintenance/observation-ledger-utils.js +2 -1
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.d.ts +2 -1
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +11 -8
- package/dist/maintenance/rebuild-memory-projection.d.ts +2 -1
- package/dist/maintenance/rebuild-memory-projection.js +13 -10
- package/dist/maintenance/rebuild-observations.d.ts +1 -0
- package/dist/maintenance/rebuild-observations.js +3 -2
- package/dist/mcp-memory-inspector-app.d.ts +7 -4
- package/dist/mcp-memory-inspector-app.js +1 -1
- package/dist/memory-action-policy.d.ts +1 -0
- package/dist/memory-cache.d.ts +1 -0
- package/dist/memory-cache.js +1 -1
- package/dist/memory-lifecycle-ledger-utils.d.ts +1 -0
- package/dist/memory-projection-store.d.ts +1 -0
- package/dist/memory-projection-store.js +1 -1
- package/dist/memory-provenance.d.ts +1 -0
- package/dist/memory-worth-outcomes.d.ts +1 -0
- package/dist/migrate/from-engram.js +2 -2
- package/dist/{migrate-from-identity-anchor-G27MCD6A.js → migrate-from-identity-anchor-EB4XI4Q2.js} +2 -2
- package/dist/model-registry.js +1 -1
- package/dist/models-json.d.ts +1 -0
- package/dist/namespaces/migrate.d.ts +3 -0
- package/dist/namespaces/migrate.js +24 -22
- package/dist/namespaces/principal.d.ts +1 -0
- package/dist/namespaces/principal.js +2 -1
- package/dist/namespaces/search.d.ts +1 -0
- package/dist/namespaces/search.js +15 -13
- package/dist/namespaces/storage.d.ts +4 -2
- package/dist/namespaces/storage.js +10 -9
- package/dist/native-knowledge.d.ts +1 -0
- package/dist/native-knowledge.js +1 -1
- package/dist/negative.js +1 -1
- package/dist/network/webdav.d.ts +16 -1
- package/dist/network/webdav.js +5 -3
- package/dist/objective-state-writers.js +4 -4
- package/dist/objective-state.js +2 -2
- package/dist/offline-sync.d.ts +8 -1
- package/dist/offline-sync.js +6 -4
- package/dist/operator-toolkit.d.ts +1 -0
- package/dist/operator-toolkit.js +35 -32
- package/dist/opik-exporter.js +1 -1
- package/dist/{orchestrator-DuWl9Hwx.d.ts → orchestrator-Co9nxRLF.d.ts} +4 -74
- package/dist/orchestrator.d.ts +5 -3
- package/dist/orchestrator.js +99 -96
- package/dist/page-versioning.js +1 -1
- package/dist/path-X2K5XCHL.js +9 -0
- package/dist/patterns-cli.d.ts +1 -0
- package/dist/peers/index.d.ts +328 -0
- package/dist/{peers-HCVGHMAE.js → peers/index.js} +4 -4
- package/dist/pipeline-D18UAKlN.d.ts +32 -0
- package/dist/plugin-entry-resolver.d.ts +9 -0
- package/dist/plugin-entry-resolver.js +8 -0
- package/dist/plugin-entry-resolver.js.map +1 -0
- package/dist/plugin-id.d.ts +2 -21
- package/dist/plugin-id.js +33 -4
- package/dist/plugin-id.js.map +1 -1
- package/dist/policy-runtime.d.ts +4 -0
- package/dist/policy-runtime.js +1 -1
- package/dist/profiling.js +1 -1
- package/dist/qmd-recall-cache.d.ts +1 -0
- package/dist/qmd.d.ts +1 -0
- package/dist/qmd.js +3 -3
- package/dist/recall-disclosure-escalation.d.ts +1 -0
- package/dist/recall-explain-renderer.d.ts +1 -0
- package/dist/recall-explain-renderer.js +3 -3
- package/dist/recall-state.d.ts +8 -1
- package/dist/recall-state.js +2 -1
- package/dist/recall-tag-filter.d.ts +1 -0
- package/dist/recall-xray-cli.d.ts +1 -0
- package/dist/recall-xray-cli.js +4 -4
- package/dist/recall-xray-renderer.d.ts +1 -0
- package/dist/recall-xray-renderer.js +3 -3
- package/dist/recall-xray.d.ts +1 -0
- package/dist/recall-xray.js +2 -2
- package/dist/relevance.d.ts +7 -1
- package/dist/relevance.js +2 -1
- package/dist/replay/normalizers/chatgpt.js +2 -2
- package/dist/replay/normalizers/claude.js +2 -2
- package/dist/replay/normalizers/openclaw.js +2 -2
- package/dist/replay/normalizers/shared.js +1 -1
- package/dist/replay/runner.js +1 -1
- package/dist/rerank.js +1 -1
- package/dist/{resolution-B7FNQSSP.js → resolution-ZY7VM6WS.js} +3 -3
- package/dist/resolution-ZY7VM6WS.js.map +1 -0
- package/dist/resolve-auth-token.d.ts +1 -0
- package/dist/resolve-auth-token.js +1 -1
- package/dist/resolve-provider-secret.d.ts +19 -29
- package/dist/resolve-provider-secret.js +2 -6
- package/dist/resume-bundles.js +10 -9
- package/dist/retrieval-agents.d.ts +2 -1
- package/dist/retrieval-agents.js +2 -1
- package/dist/retrieval-tiers.d.ts +1 -0
- package/dist/routing/engine.d.ts +1 -0
- package/dist/routing/store.d.ts +3 -0
- package/dist/routing/store.js +1 -1
- package/dist/runtime/env.js +1 -1
- package/dist/schemas.d.ts +213 -39
- package/dist/schemas.js +1 -1
- package/dist/sdk-compat.js +1 -1
- package/dist/search/document-scanner.js +1 -1
- package/dist/search/embed-helper.d.ts +7 -2
- package/dist/search/embed-helper.js +3 -1
- package/dist/search/factory.d.ts +2 -1
- package/dist/search/factory.js +13 -12
- package/dist/search/index.d.ts +2 -1
- package/dist/search/index.js +19 -18
- package/dist/search/lancedb-backend.d.ts +7 -6
- package/dist/search/lancedb-backend.js +4 -2
- package/dist/search/meilisearch-backend.d.ts +7 -6
- package/dist/search/meilisearch-backend.js +4 -2
- package/dist/search/noop-backend.d.ts +1 -0
- package/dist/search/orama-backend.d.ts +9 -7
- package/dist/search/orama-backend.js +8 -4
- package/dist/search/port.d.ts +1 -0
- package/dist/search/remote-backend.d.ts +1 -0
- package/dist/secure-store/index.d.ts +16 -3
- package/dist/secure-store/index.js +2 -2
- package/dist/{semantic-VwGI14Ok.d.ts → semantic-SLAa_prH.d.ts} +5 -3
- package/dist/semantic-consolidation.d.ts +1 -0
- package/dist/semantic-consolidation.js +14 -13
- package/dist/semantic-rule-promotion.js +8 -8
- package/dist/semantic-rule-verifier.d.ts +1 -0
- package/dist/semantic-rule-verifier.js +8 -8
- package/dist/session-integrity.d.ts +1 -0
- package/dist/session-integrity.js +1 -1
- package/dist/session-observer-bands.d.ts +1 -0
- package/dist/session-observer-state.d.ts +6 -1
- package/dist/session-observer-state.js +1 -1
- package/dist/shared-context/manager.d.ts +5 -0
- package/dist/shared-context/manager.js +3 -3
- package/dist/signal.d.ts +1 -0
- package/dist/signal.js +1 -1
- package/dist/source-attribution.js +1 -1
- package/dist/state-store-4QZISH3J.js +30 -0
- package/dist/state-store-4QZISH3J.js.map +1 -0
- package/dist/storage-C4DX8CuG.d.ts +157 -0
- package/dist/storage.d.ts +2 -0
- package/dist/storage.js +7 -7
- package/dist/store-contract.js +1 -1
- package/dist/summarizer.d.ts +1 -0
- package/dist/summarizer.js +7 -7
- package/dist/summary-snapshot.d.ts +1 -0
- package/dist/surfaces/dreams.js +48 -17
- package/dist/surfaces/dreams.js.map +1 -1
- package/dist/temporal-supersession.d.ts +1 -0
- package/dist/temporal-supersession.js +1 -1
- package/dist/temporal-validity.d.ts +1 -0
- package/dist/threading.d.ts +1 -0
- package/dist/tier-migration.d.ts +1 -0
- package/dist/tier-routing.d.ts +1 -0
- package/dist/{tier-stats-62ZVDFKS.js → tier-stats-SKML2OSF.js} +5 -5
- package/dist/tmt.js +1 -1
- package/dist/tokens.js +2 -2
- package/dist/topics.d.ts +1 -0
- package/dist/{trace-C5ETWBEF.js → trace-WM7V4CKI.js} +31 -1
- package/dist/trace-WM7V4CKI.js.map +1 -0
- package/dist/transcript.d.ts +1 -0
- package/dist/transcript.js +2 -2
- package/dist/transfer/autodetect.js +7 -7
- package/dist/transfer/backup.js +5 -5
- package/dist/transfer/capsule-export.js +5 -5
- package/dist/transfer/capsule-import.d.ts +6 -0
- package/dist/transfer/capsule-import.js +4 -4
- package/dist/transfer/export-json.js +3 -3
- package/dist/transfer/export-md.js +3 -3
- package/dist/transfer/export-sqlite.js +3 -3
- package/dist/transfer/fs-utils.d.ts +2 -1
- package/dist/transfer/fs-utils.js +5 -3
- package/dist/transfer/import-json.js +3 -3
- package/dist/transfer/import-md.js +3 -3
- package/dist/transfer/import-sqlite.js +3 -3
- package/dist/transfer/types.d.ts +12 -12
- package/dist/trust-zones.js +2 -2
- package/dist/types-B1VHaf2w.d.ts +126 -0
- package/dist/types-BliCnURB.d.ts +83 -0
- package/dist/types.d.ts +35 -0
- package/dist/types.js +1 -1
- package/dist/utility-learner.js +3 -3
- package/dist/utility-runtime.d.ts +1 -0
- package/dist/utility-runtime.js +4 -4
- package/dist/utility-telemetry.js +2 -2
- package/dist/verified-recall.js +9 -9
- package/dist/work/board.js +2 -2
- package/dist/work/boundary.js +1 -1
- package/dist/work/storage.d.ts +5 -0
- package/dist/work/storage.js +1 -1
- package/dist/work-product-ledger.js +2 -2
- package/package.json +74 -3
- package/scripts/ensure-better-sqlite3.mjs +8 -7
- package/scripts/faiss_index.py +141 -29
- package/src/access-cli.test.ts +87 -2
- package/src/access-cli.ts +59 -5
- package/src/access-http.test.ts +321 -0
- package/src/access-http.ts +201 -35
- package/src/access-idempotency.ts +136 -3
- package/src/access-mcp.test.ts +155 -0
- package/src/access-mcp.ts +116 -30
- package/src/access-schema.ts +22 -4
- package/src/access-service-project-tag.test.ts +37 -0
- package/src/access-service.ts +42 -10
- package/src/active-recall.test.ts +29 -1
- package/src/active-recall.ts +11 -7
- package/src/adapters/claude-code.ts +7 -8
- package/src/adapters/codex.ts +6 -7
- package/src/adapters/hermes.ts +1 -5
- package/src/adapters/registry.test.ts +63 -0
- package/src/adapters/registry.ts +10 -0
- package/src/adapters/replit.ts +5 -7
- package/src/adapters/types.ts +24 -1
- package/src/behavior-signals.ts +1 -1
- package/src/binary-lifecycle/backend.ts +16 -4
- package/src/binary-lifecycle/pipeline.test.ts +149 -0
- package/src/binary-lifecycle/pipeline.ts +49 -7
- package/src/binary-lifecycle/scanner.ts +19 -4
- package/src/boxes.ts +119 -32
- package/src/buffer-session.test.ts +28 -0
- package/src/buffer.ts +10 -14
- package/src/bulk-import/types.ts +10 -0
- package/src/calibration.test.ts +99 -0
- package/src/calibration.ts +57 -13
- package/src/causal-consolidation.test.ts +214 -0
- package/src/causal-consolidation.ts +131 -14
- package/src/causal-retrieval.ts +16 -3
- package/src/citations.test.ts +75 -0
- package/src/citations.ts +19 -6
- package/src/cli.ts +134 -109
- package/src/coding/coding-namespace.test.ts +7 -0
- package/src/coding/coding-namespace.ts +8 -0
- package/src/coding/review-context.test.ts +30 -0
- package/src/coding/review-context.ts +79 -9
- package/src/coding/wire-coding-context.test.ts +16 -0
- package/src/compat/checks.test.ts +33 -0
- package/src/compat/checks.ts +64 -4
- package/src/compounding/engine.ts +2 -2
- package/src/compounding/preference-consolidator.test.ts +47 -0
- package/src/compounding/preference-consolidator.ts +8 -8
- package/src/compression-optimizer.ts +5 -2
- package/src/config.test.ts +34 -2
- package/src/config.ts +62 -18
- package/src/connectors/codex-materialize-runner.ts +4 -3
- package/src/connectors/codex-materialize.ts +149 -34
- package/src/connectors/index.test.ts +144 -7
- package/src/connectors/index.ts +86 -15
- package/src/connectors/live/github.test.ts +47 -0
- package/src/connectors/live/github.ts +29 -1
- package/src/connectors/live/index.ts +2 -0
- package/src/connectors/live/live-connectors.test.ts +359 -73
- package/src/connectors/live/notion.test.ts +84 -0
- package/src/connectors/live/notion.ts +18 -1
- package/src/connectors/live/state-store.ts +419 -38
- package/src/connectors/weclone-installer.test.ts +16 -18
- package/src/connectors-cli.ts +19 -0
- package/src/console/trace.test.ts +28 -0
- package/src/console/trace.ts +42 -5
- package/src/contradiction/contradiction-judge.test.ts +49 -0
- package/src/contradiction/contradiction-judge.ts +15 -5
- package/src/contradiction/contradiction-review.ts +31 -7
- package/src/contradiction/contradiction-scan.ts +28 -18
- package/src/contradiction/contradiction.test.ts +237 -1
- package/src/contradiction/resolution.ts +43 -4
- package/src/conversation-index/backend.ts +13 -5
- package/src/conversation-index/cleanup.ts +25 -4
- package/src/conversation-index/faiss-adapter.ts +24 -15
- package/src/conversation-index/indexer.test.ts +71 -10
- package/src/conversation-index/indexer.ts +22 -3
- package/src/cross-namespace-budget.test.ts +59 -0
- package/src/cross-namespace-budget.ts +15 -7
- package/src/curation/index.ts +18 -17
- package/src/dashboard-runtime.test.ts +98 -0
- package/src/dashboard-runtime.ts +96 -6
- package/src/dedup/index.test.ts +133 -0
- package/src/dedup/index.ts +73 -10
- package/src/dedup/semantic.test.ts +77 -2
- package/src/dedup/semantic.ts +26 -6
- package/src/embedding-fallback.ts +47 -15
- package/src/enrichment/audit.ts +8 -1
- package/src/enrichment/pipeline.ts +21 -13
- package/src/enrichment/web-search-provider.ts +1 -6
- package/src/entity-retrieval.ts +57 -6
- package/src/evals.ts +22 -13
- package/src/explicit-capture.test.ts +40 -0
- package/src/explicit-capture.ts +14 -2
- package/src/extraction.ts +42 -30
- package/src/fallback-llm.ts +35 -2
- package/src/graph-dashboard-diff.test.ts +57 -0
- package/src/graph-dashboard-diff.ts +24 -2
- package/src/graph-dashboard-parser.test.ts +31 -0
- package/src/graph-dashboard-parser.ts +4 -1
- package/src/graph-events.ts +6 -4
- package/src/graph.test.ts +69 -0
- package/src/graph.ts +7 -4
- package/src/importers/base.test.ts +70 -0
- package/src/importers/base.ts +56 -7
- package/src/index.ts +6 -2
- package/src/lcm/archive.ts +65 -16
- package/src/lcm/engine.ts +27 -8
- package/src/lcm/recall.ts +5 -5
- package/src/lcm-engine.test.ts +87 -1
- package/src/lcm-recall.test.ts +71 -0
- package/src/live-connectors-runner.ts +100 -36
- package/src/maintenance/archive-observations.ts +24 -3
- package/src/maintenance/atomic-file.ts +85 -0
- package/src/maintenance/dreams-ledger.ts +15 -8
- package/src/maintenance/memory-governance.test.ts +53 -0
- package/src/maintenance/memory-governance.ts +15 -5
- package/src/maintenance/observation-ledger-utils.ts +6 -5
- package/src/maintenance/purge.test.ts +64 -0
- package/src/maintenance/rebuild-memory-lifecycle-ledger.ts +22 -9
- package/src/maintenance/rebuild-memory-projection.ts +22 -9
- package/src/maintenance/rebuild-observations.ts +7 -3
- package/src/mcp-memory-inspector-app.ts +26 -3
- package/src/memory-cache.test.ts +19 -0
- package/src/memory-cache.ts +1 -0
- package/src/memory-extension/codex-publisher.ts +25 -4
- package/src/memory-extension-host/host-discovery.test.ts +69 -0
- package/src/memory-extension-host/host-discovery.ts +63 -6
- package/src/memory-projection-store.ts +114 -62
- package/src/message-parts/index.ts +46 -31
- package/src/message-parts/message-parts.test.ts +77 -0
- package/src/migrate/from-engram.ts +68 -14
- package/src/model-registry.test.ts +38 -0
- package/src/model-registry.ts +12 -7
- package/src/namespaces/identity.test.ts +66 -0
- package/src/namespaces/identity.ts +23 -0
- package/src/namespaces/migrate.test.ts +62 -0
- package/src/namespaces/migrate.ts +82 -14
- package/src/namespaces/principal.test.ts +37 -1
- package/src/namespaces/principal.ts +18 -7
- package/src/namespaces/search.test.ts +76 -6
- package/src/namespaces/search.ts +22 -21
- package/src/namespaces/storage.ts +93 -11
- package/src/native-knowledge.ts +23 -3
- package/src/negative.ts +50 -5
- package/src/network/webdav.ts +177 -58
- package/src/offline-sync.ts +22 -11
- package/src/onboarding/index.test.ts +105 -0
- package/src/onboarding/index.ts +17 -5
- package/src/operator-toolkit.ts +39 -4
- package/src/orchestrator.ts +52 -17
- package/src/page-versioning.ts +31 -5
- package/src/peers/peers.test.ts +70 -0
- package/src/peers/storage.ts +32 -3
- package/src/plugin-entry-resolver.test.ts +60 -0
- package/src/plugin-entry-resolver.ts +48 -0
- package/src/plugin-id.test.ts +38 -0
- package/src/plugin-id.ts +31 -64
- package/src/policy-runtime.test.ts +75 -0
- package/src/policy-runtime.ts +32 -3
- package/src/procedural/procedure-miner.test.ts +152 -0
- package/src/procedural/procedure-miner.ts +124 -19
- package/src/profiling.test.ts +23 -0
- package/src/profiling.ts +10 -1
- package/src/projection/index.test.ts +253 -0
- package/src/projection/index.ts +159 -18
- package/src/qmd-client.test.ts +45 -0
- package/src/qmd.ts +8 -8
- package/src/recall-disclosure.test.ts +15 -1
- package/src/recall-state.ts +24 -5
- package/src/relevance.ts +24 -5
- package/src/replay/normalizers/chatgpt.ts +14 -4
- package/src/replay/normalizers/claude.ts +8 -3
- package/src/replay/normalizers/openclaw.ts +35 -12
- package/src/replay/normalizers/replay-normalizers.test.ts +65 -0
- package/src/replay/normalizers/shared.ts +4 -1
- package/src/replay/runner.ts +1 -1
- package/src/rerank.test.ts +41 -1
- package/src/rerank.ts +2 -2
- package/src/resolve-auth-token.test.ts +29 -0
- package/src/resolve-auth-token.ts +12 -7
- package/src/resolve-provider-secret.test.ts +78 -22
- package/src/resolve-provider-secret.ts +55 -223
- package/src/retrieval-agents.ts +51 -14
- package/src/review/index.test.ts +75 -1
- package/src/review/index.ts +88 -30
- package/src/routing/store.ts +36 -6
- package/src/runtime/env.test.ts +73 -0
- package/src/runtime/env.ts +7 -11
- package/src/schemas.ts +16 -1
- package/src/search/abort.ts +18 -0
- package/src/search/document-scanner.test.ts +80 -0
- package/src/search/document-scanner.ts +51 -9
- package/src/search/embed-helper.ts +19 -6
- package/src/search/factory.ts +9 -5
- package/src/search/lancedb-backend.ts +62 -22
- package/src/search/meilisearch-backend.ts +35 -12
- package/src/search/orama-backend.test.ts +27 -0
- package/src/search/orama-backend.ts +65 -15
- package/src/secure-store/cli-handlers.ts +70 -6
- package/src/secure-store/cli-renderer.ts +13 -7
- package/src/secure-store/secure-fs.ts +11 -5
- package/src/secure-store/secure-store.test.ts +70 -0
- package/src/semantic-consolidation.test.ts +45 -0
- package/src/semantic-consolidation.ts +3 -3
- package/src/session-integrity.test.ts +98 -0
- package/src/session-integrity.ts +51 -1
- package/src/session-observer-state.ts +108 -41
- package/src/shared-context/manager.ts +93 -15
- package/src/signal.test.ts +14 -0
- package/src/signal.ts +8 -1
- package/src/source-attribution.test.ts +8 -0
- package/src/source-attribution.ts +24 -2
- package/src/spaces/index.test.ts +93 -0
- package/src/spaces/index.ts +75 -9
- package/src/storage.ts +14 -1
- package/src/store-contract.test.ts +35 -0
- package/src/store-contract.ts +39 -5
- package/src/summarizer.ts +24 -18
- package/src/summary-snapshot.test.ts +77 -0
- package/src/surfaces/dreams.test.ts +73 -0
- package/src/surfaces/dreams.ts +53 -19
- package/src/sync/index.ts +42 -17
- package/src/taxonomy/taxonomy-loader.ts +43 -4
- package/src/temporal-supersession.test.ts +67 -0
- package/src/temporal-supersession.ts +8 -0
- package/src/tmt.test.ts +50 -0
- package/src/tmt.ts +35 -11
- package/src/tokens.test.ts +18 -0
- package/src/tokens.ts +7 -0
- package/src/training-export/converter.test.ts +55 -2
- package/src/training-export/converter.ts +36 -10
- package/src/training-export/registry.test.ts +17 -0
- package/src/training-export/registry.ts +19 -1
- package/src/transcript.ts +2 -2
- package/src/transfer/backup.ts +18 -7
- package/src/transfer/capsule-crypto.ts +105 -21
- package/src/transfer/capsule-encrypt.test.ts +106 -7
- package/src/transfer/capsule-export.ts +23 -14
- package/src/transfer/capsule-import.ts +11 -2
- package/src/transfer/exclusions.ts +7 -0
- package/src/transfer/export-sqlite.ts +14 -13
- package/src/transfer/fs-utils.ts +52 -1
- package/src/transfer/import-json.ts +12 -7
- package/src/transfer/import-md.ts +5 -5
- package/src/transfer/import-sqlite.ts +4 -5
- package/src/trust-zones.ts +1 -1
- package/src/types.ts +25 -0
- package/src/utility-telemetry.ts +1 -1
- package/src/utils/category-dir.test.ts +15 -0
- package/src/utils/category-dir.ts +3 -1
- package/src/work/boundary.ts +30 -18
- package/src/work/storage.ts +116 -38
- package/src/work-product-ledger.ts +1 -1
- package/dist/chunk-25MQ7IHJ.js.map +0 -1
- package/dist/chunk-2IWUMAES.js.map +0 -1
- package/dist/chunk-2NMMFZ5T.js.map +0 -1
- package/dist/chunk-2PRLKQAH.js.map +0 -1
- package/dist/chunk-326G7DJK.js.map +0 -1
- package/dist/chunk-3APJ5EVB.js.map +0 -1
- package/dist/chunk-3KW65B36.js.map +0 -1
- package/dist/chunk-3OWUCDKH.js.map +0 -1
- package/dist/chunk-3QKK7QOS.js.map +0 -1
- package/dist/chunk-3SLRNYNG.js.map +0 -1
- package/dist/chunk-3VAL7ZL2.js.map +0 -1
- package/dist/chunk-3Y4P7RXM.js.map +0 -1
- package/dist/chunk-43PJZYGL.js.map +0 -1
- package/dist/chunk-47VWKCAF.js.map +0 -1
- package/dist/chunk-4KGVTPGD.js.map +0 -1
- package/dist/chunk-5375UYTQ.js.map +0 -1
- package/dist/chunk-56K5QLHX.js.map +0 -1
- package/dist/chunk-575RMLWN.js.map +0 -1
- package/dist/chunk-5NPGSAVB.js.map +0 -1
- package/dist/chunk-5RGLBDQF.js.map +0 -1
- package/dist/chunk-65PG43EQ.js.map +0 -1
- package/dist/chunk-66DHUKLO.js.map +0 -1
- package/dist/chunk-6LVVDPJ4.js.map +0 -1
- package/dist/chunk-76FLAAUC.js.map +0 -1
- package/dist/chunk-77H5NU3M.js.map +0 -1
- package/dist/chunk-7MNMYOFP.js.map +0 -1
- package/dist/chunk-7OZ53EXP.js.map +0 -1
- package/dist/chunk-7SEAZFFB.js.map +0 -1
- package/dist/chunk-A6KTB5R6.js.map +0 -1
- package/dist/chunk-AGZQD76C.js.map +0 -1
- package/dist/chunk-APO3DCMU.js.map +0 -1
- package/dist/chunk-BVF3AGJP.js.map +0 -1
- package/dist/chunk-C5BCH4ZS.js.map +0 -1
- package/dist/chunk-C7VW7C3F.js.map +0 -1
- package/dist/chunk-CULXMQJH.js.map +0 -1
- package/dist/chunk-CYFQJMUV.js.map +0 -1
- package/dist/chunk-D654IBA6.js +0 -61
- package/dist/chunk-D654IBA6.js.map +0 -1
- package/dist/chunk-DGXUHMOV.js.map +0 -1
- package/dist/chunk-DINWEURR.js.map +0 -1
- package/dist/chunk-DK5LDEQM.js.map +0 -1
- package/dist/chunk-EABGC2TL.js.map +0 -1
- package/dist/chunk-EHRTFRWW.js.map +0 -1
- package/dist/chunk-EJI5XIBB.js.map +0 -1
- package/dist/chunk-FAAFWE4G.js.map +0 -1
- package/dist/chunk-FAJ7FZYM.js +0 -11
- package/dist/chunk-FAJ7FZYM.js.map +0 -1
- package/dist/chunk-FDU6HUUL.js +0 -147
- package/dist/chunk-FDU6HUUL.js.map +0 -1
- package/dist/chunk-FIT6DMX6.js.map +0 -1
- package/dist/chunk-FJ43PRLT.js.map +0 -1
- package/dist/chunk-FLTNHQK6.js +0 -262
- package/dist/chunk-FLTNHQK6.js.map +0 -1
- package/dist/chunk-GDFS42HT.js.map +0 -1
- package/dist/chunk-H3ME6L6D.js.map +0 -1
- package/dist/chunk-HXXBL2KD.js.map +0 -1
- package/dist/chunk-ICRIXAP2.js.map +0 -1
- package/dist/chunk-IQT3XTKW.js.map +0 -1
- package/dist/chunk-JR4ZC3G4.js.map +0 -1
- package/dist/chunk-K4FLSOR5.js.map +0 -1
- package/dist/chunk-KNKUID7G.js.map +0 -1
- package/dist/chunk-KOSORCJG.js.map +0 -1
- package/dist/chunk-KRBK4BQH.js.map +0 -1
- package/dist/chunk-LIRZNNUP.js.map +0 -1
- package/dist/chunk-LLQ2LLWF.js.map +0 -1
- package/dist/chunk-LPMVBPA3.js +0 -236
- package/dist/chunk-LPMVBPA3.js.map +0 -1
- package/dist/chunk-LT3NLYSI.js.map +0 -1
- package/dist/chunk-LUDTDZLK.js.map +0 -1
- package/dist/chunk-MG7NA5H3.js.map +0 -1
- package/dist/chunk-MJFNCJXV.js.map +0 -1
- package/dist/chunk-MSWG7JI6.js.map +0 -1
- package/dist/chunk-MXC3AP5I.js.map +0 -1
- package/dist/chunk-MXFBBHJU.js.map +0 -1
- package/dist/chunk-MZH6EHNR.js.map +0 -1
- package/dist/chunk-N2D6GXBM.js.map +0 -1
- package/dist/chunk-NBNN5GOB.js.map +0 -1
- package/dist/chunk-NMZY542O.js.map +0 -1
- package/dist/chunk-NZL6GGQE.js.map +0 -1
- package/dist/chunk-OC7KHOOX.js.map +0 -1
- package/dist/chunk-OZHRDTDX.js.map +0 -1
- package/dist/chunk-PZIAX57I.js.map +0 -1
- package/dist/chunk-Q7P4WJDP.js.map +0 -1
- package/dist/chunk-QA2ZAPBU.js.map +0 -1
- package/dist/chunk-QDZ2RLEC.js.map +0 -1
- package/dist/chunk-QKZGQIPJ.js.map +0 -1
- package/dist/chunk-QLLBRHAT.js.map +0 -1
- package/dist/chunk-QR3C7BKQ.js.map +0 -1
- package/dist/chunk-RHY3HH7P.js.map +0 -1
- package/dist/chunk-RK2Y4XOM.js.map +0 -1
- package/dist/chunk-RR2PKP3I.js +0 -63
- package/dist/chunk-RR2PKP3I.js.map +0 -1
- package/dist/chunk-RRF5UOBJ.js.map +0 -1
- package/dist/chunk-RXDLTSWT.js.map +0 -1
- package/dist/chunk-RYED3SPJ.js +0 -42
- package/dist/chunk-RYED3SPJ.js.map +0 -1
- package/dist/chunk-S7KDBTWT.js.map +0 -1
- package/dist/chunk-TK4UEOSK.js.map +0 -1
- package/dist/chunk-TMM4S4IJ.js.map +0 -1
- package/dist/chunk-TMQLARTH.js.map +0 -1
- package/dist/chunk-TPB3I2AC.js.map +0 -1
- package/dist/chunk-TPMQ3G6Z.js.map +0 -1
- package/dist/chunk-TZOLIGIG.js.map +0 -1
- package/dist/chunk-U3WSW6PZ.js.map +0 -1
- package/dist/chunk-U4SCL7B7.js.map +0 -1
- package/dist/chunk-U66YHYC7.js +0 -31
- package/dist/chunk-U66YHYC7.js.map +0 -1
- package/dist/chunk-UL2NNBUL.js.map +0 -1
- package/dist/chunk-UWVJF25J.js.map +0 -1
- package/dist/chunk-VBJ7V5SK.js.map +0 -1
- package/dist/chunk-W3LR522O.js.map +0 -1
- package/dist/chunk-W4L6CZKA.js.map +0 -1
- package/dist/chunk-W6AQJ2PY.js.map +0 -1
- package/dist/chunk-WELDCG6C.js.map +0 -1
- package/dist/chunk-WNARATI3.js.map +0 -1
- package/dist/chunk-WPGJYVUH.js.map +0 -1
- package/dist/chunk-WW3QQF4H.js.map +0 -1
- package/dist/chunk-XIG5PDM7.js.map +0 -1
- package/dist/chunk-XKECPATV.js.map +0 -1
- package/dist/chunk-XKLD5OK4.js.map +0 -1
- package/dist/chunk-XSZEP4SF.js.map +0 -1
- package/dist/chunk-XVVIG67A.js.map +0 -1
- package/dist/chunk-XYIK4LF6.js.map +0 -1
- package/dist/chunk-YRMVARQP.js.map +0 -1
- package/dist/chunk-ZPKBYX2F.js.map +0 -1
- package/dist/chunk-ZTFCYYEZ.js.map +0 -1
- package/dist/chunk-ZYVPLJ4T.js.map +0 -1
- package/dist/path-MR5JPYOP.js +0 -9
- package/dist/state-store-VZU2IA53.js +0 -16
- package/dist/trace-C5ETWBEF.js.map +0 -1
- /package/dist/{capsule-crypto-5CYAGVC5.js.map → bulk-import/index.js.map} +0 -0
- /package/dist/{contradiction-review-ATP4S6IC.js.map → capsule-crypto-7FJQINUR.js.map} +0 -0
- /package/dist/{capsule-merge-4MGKE7C5.js.map → capsule-merge-T2JRE46P.js.map} +0 -0
- /package/dist/{chunk-SAZS2QZB.js.map → chunk-23UORJ4S.js.map} +0 -0
- /package/dist/{chunk-PK7H5L6Y.js.map → chunk-2NM43EWN.js.map} +0 -0
- /package/dist/{chunk-PYXS46O7.js.map → chunk-3BP57I6J.js.map} +0 -0
- /package/dist/{chunk-FBYESMQ2.js.map → chunk-3C5RPJAX.js.map} +0 -0
- /package/dist/{chunk-N53K2EXC.js.map → chunk-6VF75M3X.js.map} +0 -0
- /package/dist/{chunk-6H2TESSP.js.map → chunk-765K3SAT.js.map} +0 -0
- /package/dist/{chunk-EDTHC6UD.js.map → chunk-77NAFXUD.js.map} +0 -0
- /package/dist/{chunk-MGKYQQYF.js.map → chunk-7Q3RCKAQ.js.map} +0 -0
- /package/dist/{chunk-34DQE4KF.js.map → chunk-CO7ZO4TU.js.map} +0 -0
- /package/dist/{chunk-ZKSK55RC.js.map → chunk-ETUPBUHB.js.map} +0 -0
- /package/dist/{chunk-QRNI5JBH.js.map → chunk-EYIEWJNI.js.map} +0 -0
- /package/dist/{chunk-C6QPK5GG.js.map → chunk-FZZ2QTJI.js.map} +0 -0
- /package/dist/{chunk-3JXBXXM2.js.map → chunk-G4IAEX6D.js.map} +0 -0
- /package/dist/{chunk-2WWLHTZY.js.map → chunk-IC4GELZE.js.map} +0 -0
- /package/dist/{chunk-PCUKNJAZ.js.map → chunk-JKV57BTN.js.map} +0 -0
- /package/dist/{chunk-2KI4QFHU.js.map → chunk-LMDRGRJ2.js.map} +0 -0
- /package/dist/{chunk-MY6TPVXW.js.map → chunk-LMPHTYJC.js.map} +0 -0
- /package/dist/{chunk-5HRY2WRF.js.map → chunk-LZ3VEOU5.js.map} +0 -0
- /package/dist/{chunk-NGAVDO7E.js.map → chunk-OADWQ5CR.js.map} +0 -0
- /package/dist/{chunk-DOM4GKSW.js.map → chunk-OZKVVUJB.js.map} +0 -0
- /package/dist/{chunk-MT4HVDUZ.js.map → chunk-PM3QHTFT.js.map} +0 -0
- /package/dist/{chunk-ZK7I7JYV.js.map → chunk-R3PS27B4.js.map} +0 -0
- /package/dist/{chunk-SKE7JYKA.js.map → chunk-SFXKHM7P.js.map} +0 -0
- /package/dist/{chunk-HMDCOMYU.js.map → chunk-SKGV326D.js.map} +0 -0
- /package/dist/{chunk-5UM2VJ6D.js.map → chunk-UEY3VB6W.js.map} +0 -0
- /package/dist/{chunk-GIF42EW3.js.map → chunk-UP6MOYCB.js.map} +0 -0
- /package/dist/{chunk-MRILGULB.js.map → chunk-V2RCP53Q.js.map} +0 -0
- /package/dist/{chunk-FSFEQI74.js.map → chunk-W7L6HXUC.js.map} +0 -0
- /package/dist/{chunk-3IQ2TR4N.js.map → chunk-WLEB7WCG.js.map} +0 -0
- /package/dist/{chunk-GL6I6MEQ.js.map → chunk-WSGF57U2.js.map} +0 -0
- /package/dist/{chunk-JA3AK3PT.js.map → chunk-XNLXAWHX.js.map} +0 -0
- /package/dist/{chunk-SIC6U3GZ.js.map → chunk-YHV3KRKS.js.map} +0 -0
- /package/dist/{chunk-VLXA6PI2.js.map → chunk-YQMZ7IH2.js.map} +0 -0
- /package/dist/{contradiction-scan-5A4IDZV5.js.map → contradiction-review-6V2LXXK6.js.map} +0 -0
- /package/dist/{migrate-from-identity-anchor-G27MCD6A.js.map → contradiction-scan-GIRVC4C7.js.map} +0 -0
- /package/dist/{first-start-migration-4MHQEOSD.js.map → first-start-migration-CKTCTCQI.js.map} +0 -0
- /package/dist/{graph-edge-decay-5DI5GUNL.js.map → graph-edge-decay-MUP5J7CC.js.map} +0 -0
- /package/dist/{path-MR5JPYOP.js.map → importers/index.js.map} +0 -0
- /package/dist/{peers-HCVGHMAE.js.map → migrate-from-identity-anchor-EB4XI4Q2.js.map} +0 -0
- /package/dist/{resolution-B7FNQSSP.js.map → path-X2K5XCHL.js.map} +0 -0
- /package/dist/{state-store-VZU2IA53.js.map → peers/index.js.map} +0 -0
- /package/dist/{tier-stats-62ZVDFKS.js.map → tier-stats-SKML2OSF.js.map} +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/source-attribution.ts"],"sourcesContent":["/**\n * Inline Source Attribution Protocol (issue #369)\n *\n * Extracted facts carry provenance inline inside the fact body, so the\n * citation survives hostile memory text, copy/paste, and LLM quoting. This\n * complements — never replaces — the YAML frontmatter provenance stored on\n * disk.\n *\n * Default format (matches issue #369 proposal):\n *\n * The foo service uses Redis for rate limiting. [Source: agent=planner, session=abc123, ts=2026-04-10T14:25:07Z]\n *\n * Key properties:\n * - Inline (part of the body, not metadata).\n * - Compact (typically <80 chars of overhead per fact).\n * - Machine-parseable by a single regex.\n * - Opt-in via `inlineSourceAttributionEnabled` config flag (default off\n * for backwards compatibility with existing downstream consumers).\n * - Legacy facts without a citation remain fully readable.\n *\n * The format template is configurable via `inlineSourceAttributionFormat`\n * with supported placeholders:\n *\n * {agent} — principal / agent identifier\n * {session} — full session key (colon-delimited)\n * {sessionId} — short stable session id (trailing component)\n * {ts} — extraction timestamp (ISO 8601)\n * {date} — extraction date (YYYY-MM-DD)\n *\n * Any privacy-sensitive identifiers should be normalized before being passed\n * to `formatCitation` — the helper treats them as opaque strings.\n */\n\n/** Default citation format template (matches issue #369). */\nexport const DEFAULT_CITATION_FORMAT =\n \"[Source: agent={agent}, session={sessionId}, ts={ts}]\";\n\n/** Sentinel value used when a provenance field is missing. */\nexport const CITATION_UNKNOWN = \"unknown\";\n\nexport interface CitationContext {\n /** Principal / agent identifier (e.g. resolved via resolvePrincipal). */\n agent?: string;\n /** Full session key (e.g. \"agent:planner:main\"). */\n session?: string;\n /**\n * Opaque short session id. Derived from the trailing component of the\n * session key when not provided explicitly. Use this for compact formats\n * that do not need the full colon-delimited session key.\n */\n sessionId?: string;\n /** Extraction timestamp as an ISO 8601 string. */\n ts?: string;\n}\n\nexport interface ParsedCitation {\n /** Agent identifier parsed from the citation (never crashes on malformed input). */\n agent?: string;\n /** Session identifier parsed from the citation. */\n session?: string;\n /** Extraction timestamp parsed from the citation. */\n ts?: string;\n /** The full matched citation substring. */\n raw: string;\n}\n\n/**\n * Regex that matches the default `[Source: agent=X, session=Y, ts=Z]` shape\n * as well as human-edited variants (extra whitespace, reordered fields,\n * subset of fields). Matches non-greedily so it can be anchored anywhere in\n * the text. Kept as a getter factory so callers do not share regex state.\n */\nfunction defaultCitationMatcher(): RegExp {\n return /\\[Source:\\s*([^\\]\\n]+?)\\]/gi;\n}\n\n/**\n * Derive a short session id from a full session key.\n * Falls back to the raw session string if no colon is present.\n */\nexport function deriveSessionId(session: string | undefined): string | undefined {\n if (!session) return undefined;\n const trimmed = session.trim();\n if (trimmed.length === 0) return undefined;\n const parts = trimmed.split(\":\").filter((p) => p.length > 0);\n if (parts.length === 0) return trimmed;\n return parts[parts.length - 1];\n}\n\n/**\n * Format an inline citation tag using the provided template.\n *\n * Missing context fields fall back to {@link CITATION_UNKNOWN} — the caller\n * should always get a non-empty, parseable tag.\n *\n * Uses a single-pass substitution so that values which themselves contain\n * placeholder syntax (e.g. an agent literally named `\"{ts}\"`) cannot be\n * re-interpreted by subsequent replacement steps. Each placeholder slot\n * receives exactly one lookup and the substituted value is treated as\n * terminal text, not template source.\n */\nexport function formatCitation(\n ctx: CitationContext,\n template: string = DEFAULT_CITATION_FORMAT,\n): string {\n const session = ctx.session ?? \"\";\n const sessionId = ctx.sessionId ?? deriveSessionId(session) ?? CITATION_UNKNOWN;\n const ts = ctx.ts ?? CITATION_UNKNOWN;\n const agent = ctx.agent && ctx.agent.trim().length > 0 ? ctx.agent : CITATION_UNKNOWN;\n const date = ts && ts !== CITATION_UNKNOWN ? ts.slice(0, 10) : CITATION_UNKNOWN;\n const sessionForTemplate = session.trim().length > 0 ? session : CITATION_UNKNOWN;\n\n // Map from recognised placeholder names to their resolved value. Unknown\n // placeholder names are left intact (returning the original `{name}`).\n const values: Record<string, string> = {\n agent,\n session: sessionForTemplate,\n sessionId,\n ts,\n date,\n };\n\n // Single-pass scan: replace every recognised `{name}` in one sweep so that\n // substituted values cannot themselves be treated as template source on a\n // subsequent pass. The replacer-function form also guarantees that `$` /\n // `$&` / `$1` sequences inside values are emitted literally.\n return template.replace(/\\{([a-zA-Z_][\\w]*)\\}/g, (match, name: string) => {\n return Object.prototype.hasOwnProperty.call(values, name)\n ? values[name]!\n : match;\n });\n}\n\n/**\n * Returns true if the text already carries at least one citation marker.\n * Safe to call on any string — never throws.\n */\nexport function hasCitation(text: string): boolean {\n if (typeof text !== \"string\" || text.length === 0) return false;\n return defaultCitationMatcher().test(text);\n}\n\n/**\n * Escape a string for use as a regex literal.\n */\nfunction escapeRegExp(s: string): string {\n return s.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\n/**\n * Escape a single character for use INSIDE a character class `[...]`.\n * Special chars inside character classes: `]`, `\\`, `^`, `-`.\n */\nfunction escapeRegExpCharClass(ch: string): string {\n if (ch === \"]\") return \"\\\\]\";\n if (ch === \"\\\\\") return \"\\\\\\\\\";\n if (ch === \"^\") return \"\\\\^\";\n if (ch === \"-\") return \"\\\\-\";\n // Other regex meta chars are NOT special inside [...] but escaping them is safe.\n return escapeRegExp(ch);\n}\n\n/**\n * Build a per-placeholder token pattern that excludes newlines, whitespace,\n * and any punctuation characters used as inner separators in the template.\n *\n * This prevents a placeholder span from consuming separator bytes and\n * matching strings that cross separator boundaries in user-supplied content.\n *\n * @param nonWordSepChars - Set of non-word (non-alphanumeric, non-`_`) chars\n * extracted from the inner separator literals of the template.\n */\nfunction buildTokenPattern(nonWordSepChars: Set<string>): string {\n // Always exclude newlines and whitespace.\n const base = \"\\\\n\\\\s\";\n if (nonWordSepChars.size === 0) {\n // No inner separator punctuation — the placeholder spans the full space\n // between prefix and suffix. Fall back to a generous no-newline match.\n return `[^\\\\n]+?`;\n }\n const escaped = [...nonWordSepChars].map(escapeRegExpCharClass).join(\"\");\n return `[^${base}${escaped}]+?`;\n}\n\n/** Regex that matches a `{placeholder}` token inside a template string. */\nconst PLACEHOLDER_REGEX = /\\{[a-zA-Z_][\\w]*\\}/g;\n\n/**\n * Build a regex that matches a citation produced by the given template.\n *\n * The approach depends on the shape of the template:\n *\n * - **Normal case (non-empty literal prefix or suffix).** Anchor the match\n * on the outer literal frame and reconstruct the interior as\n * `interToken + sep + interToken + sep + ... + sep + lastToken`.\n * All **intermediate** per-placeholder tokens exclude the combined set of\n * non-word separator characters used between any two adjacent placeholders,\n * preventing a value from consuming a separator and crossing placeholder\n * boundaries. The **last** token is only required to avoid newlines because\n * it is terminated by the literal suffix anchor — this lets placeholder\n * values that legitimately contain a separator character be recognised (e.g.\n * an ISO-8601 timestamp `2026-04-10T14:25:07Z` in `[src:{agent}:{ts}]`\n * where `:` is the inter-placeholder separator). A template like\n * `[src:{agent}/{sessionId}@{date}]` emits\n * `\\[src:[^\\n\\s/@]+?\\/[^\\n\\s/@]+?@[^\\n]+?\\]` so that `[src:foo/bar]`\n * is NOT matched (wrong separator count), `[src:foo/bar/extra@2026]`\n * is NOT matched (intermediate token crosses a `/` boundary), and\n * `[src:planner/main@2026-04-10]` IS matched correctly.\n *\n * - **Placeholder-bounded with whitespace separator.** Both prefix and\n * suffix are empty and the separator literal(s) between placeholders\n * contain at least one whitespace character (e.g. `{source}: {content}`,\n * `{agent} {sessionId}`). A whitespace-containing separator produces\n * output that is visually indistinguishable from ordinary prose, so the\n * safe strategy is to require a **hard bracket/paren/angle delimiter** on\n * both sides of the reconstructed match. Prose almost never places\n * `[...]` / `(...)` / `<...>` around a phrase, so this yields clean\n * false-positive rejection.\n *\n * - **Placeholder-bounded with compact (non-whitespace) separator.** Both\n * prefix and suffix are empty and the separator literal(s) contain NO\n * whitespace (e.g. `{agent}:{sessionId}`, `{agent}/{sessionId}`).\n * `formatCitation` emits a compact token like `planner:main` with no\n * surrounding delimiters, so the bracket strategy cannot detect it.\n * Instead, the pattern requires that the entire token is bordered by\n * whitespace or a bracket/paren/angle on each side:\n *\n * `(?:(?<=[\\[\\(\\<])|(?<!\\S))[\\w.-]+<sep>[\\w.-]+(?:(?=[\\]\\)\\>])|(?!\\S))`\n *\n * This accepts `planner:main` when it appears standalone or inside a\n * bracket-wrapped token, and rejects `host:80` embedded inside a URL like\n * `http://host:80` because `host` is immediately preceded by `/`\n * (non-whitespace, non-bracket).\n *\n * - **All-placeholder case (no literals between placeholders either).** No\n * reliable regex can be built — a template like `{agent}{sessionId}`\n * contains no anchor characters. Returns `null`; {@link\n * hasCitationForTemplate} treats this as \"cannot detect\" and returns\n * false, falling back on explicit sentinel/format detection only for the\n * default `[Source: ...]` shape.\n *\n * Returns `null` when the template has no placeholders (fully-literal\n * citation, handled by the string-equality fast path in {@link\n * hasCitationForTemplate}) **or** when the template is entirely placeholder-\n * only with no literal content whatsoever.\n */\nfunction templateMatcher(template: string): RegExp | null {\n // Split around all {placeholder} tokens.\n const parts = template.split(PLACEHOLDER_REGEX);\n if (parts.length <= 1) return null;\n\n const prefix = parts[0] ?? \"\";\n const suffix = parts[parts.length - 1] ?? \"\";\n\n // Normal case: at least one literal frame on the outside.\n // Tighten the per-placeholder token so it cannot consume separator\n // characters and match strings that cross separator boundaries\n // (Finding 3 — Uru3).\n if (prefix.length > 0 || suffix.length > 0) {\n const escapedPrefix = escapeRegExp(prefix);\n const escapedSuffix = escapeRegExp(suffix);\n\n // Inner parts: literal separators that sit between adjacent placeholders.\n // For `[src:{agent}/{sessionId}@{date}]`, parts = [\"[src:\", \"/\", \"@\", \"]\"]\n // so innerParts = [\"/\", \"@\"].\n const innerParts = parts.slice(1, -1);\n\n // Collect only the non-word (punctuation/symbol) characters from each\n // inner separator so alphabetic separator text (unlikely but valid) does\n // not exclude letters from the per-placeholder token pattern.\n const nonWordSepChars = new Set<string>();\n for (const sep of innerParts) {\n for (const ch of sep) {\n if (!/\\w/.test(ch)) {\n nonWordSepChars.add(ch);\n }\n }\n }\n\n // All intermediate tokens (every placeholder except the last) use the\n // combined exclusion so they cannot cross placeholder boundaries.\n //\n // The LAST token is different: it is terminated by the literal suffix anchor\n // (e.g. `\\]`), so it does not need to exclude inner-separator characters.\n // Dropping that restriction lets placeholder values that legitimately contain\n // a separator character (e.g. an ISO-8601 timestamp `2026-04-10T14:25:07Z`\n // in template `[src:{agent}:{ts}]`) be recognised correctly instead of\n // producing false-negative misses that trigger duplicate citation injection.\n //\n // Only the LAST token is relaxed. Intermediate tokens keep the combined\n // exclusion so that cross-boundary false positives are still rejected\n // (e.g. `[src:foo/bar/extra@2026-04-11]` for `[src:{a}/{b}@{c}]`).\n const interToken = buildTokenPattern(nonWordSepChars);\n // Last token: terminated by suffix anchor — exclude only newlines.\n const lastToken = buildTokenPattern(new Set<string>());\n\n // Reconstruct the interior: interToken sep interToken sep ... sep lastToken\n // (or just lastToken when there are no inner separators at all).\n const middle =\n innerParts.length === 0\n ? lastToken\n : interToken +\n innerParts\n .slice(0, -1)\n .map((sep) => escapeRegExp(sep) + interToken)\n .join(\"\") +\n escapeRegExp(innerParts[innerParts.length - 1]!) +\n lastToken;\n\n const pattern = escapedPrefix + middle + escapedSuffix;\n return new RegExp(pattern, \"i\");\n }\n\n // Placeholder-bounded case: prefix and suffix are both empty.\n const middleLiterals = parts.slice(1, -1);\n const hasNonEmptyMiddle = middleLiterals.some((p) => p.length > 0);\n if (!hasNonEmptyMiddle) {\n // All-placeholder template with no literal content. Impossible to anchor\n // reliably without sentinel markers; signal the caller.\n return null;\n }\n\n // Identifier token: one or more word chars, dots, dashes, or colons.\n // Colons are included to allow timestamp values like \"10:30\" or session\n // keys like \"agent:planner:main\" inside compact placeholder-bounded\n // templates. URL-like fragments (`http://host:80`) are still rejected\n // because the lead anchor requires whitespace or a bracket immediately\n // before the first id-token group (`http` is preceded by `/`).\n const idToken = \"[\\\\w.:-]+\";\n const body =\n idToken +\n middleLiterals.map((lit) => escapeRegExp(lit) + idToken).join(\"\");\n\n const separatorText = middleLiterals.join(\"\");\n if (/\\s/.test(separatorText)) {\n // Separator contains whitespace: the emitted citation looks like ordinary\n // prose (e.g. `planner main`). Require a hard bracket/paren/angle\n // delimiter on both sides to prevent false matches on English text.\n const opener = \"[\\\\[\\\\(\\\\<]\";\n const closer = \"[\\\\]\\\\)\\\\>]\";\n return new RegExp(opener + body + closer, \"i\");\n }\n\n // Separator is compact (no whitespace): `formatCitation` emits a token like\n // `planner:main` without surrounding delimiters. The challenge is that the\n // same token shape also matches ordinary hyphenated or slashed prose words\n // (e.g. `long-term`, `docs/setup`), causing `hasCitationForTemplate` to\n // return true on uncited fact bodies and silently suppress citation injection\n // from `attachCitation`.\n //\n // Fix (Finding 1): tighten the trail anchor so a bare compact token is only\n // accepted when it sits at the very end of the string (possibly followed by\n // optional trailing whitespace or a newline). Since `attachCitation` always\n // appends the citation at the trimmed end of the fact body, a real citation\n // token will always appear at the tail. Prose like `\"long-term solution\"`\n // has `long-term` in the middle of the string (followed by ` solution`), so\n // the end-of-string anchor rejects it — no false positive, no silent drop.\n //\n // The lead anchor still accepts either a bracket opener or a whitespace\n // boundary (or start of string), so `\"Fact. planner:main\"` and standalone\n // `\"planner:main\"` are both detected after the first attachment pass.\n //\n // Bracket-wrapped form (e.g. `[planner:main]`) is also accepted via the\n // opener/closer pair — bracket still takes precedence over end-of-string.\n //\n // Example — why `http://host:80` does NOT match:\n // Trying to match `host:80`: the char before `h` is `/` (non-whitespace,\n // non-bracket), so `(?<=[\\[\\(\\<])` and `(?<!\\S)` both fail ⟹ no match.\n // Trying to match `http:...`: after `http:` the next chars are `//` which\n // are not `[\\w.-]+`, so the second id-token group fails ⟹ no match.\n const leadAnchor = \"(?:(?<=[\\\\[\\\\(\\\\<])|(?<!\\\\S))\";\n // Trail: either a bracket closer (for `[token]` shape) or end-of-string\n // optionally preceded by whitespace. The `(?!\\S)` is deliberately removed\n // so that a compact token in the MIDDLE of a sentence does not match.\n const trailAnchor = \"(?:(?=[\\\\]\\\\)\\\\>])|(?=\\\\s*$))\";\n return new RegExp(leadAnchor + body + trailAnchor, \"i\");\n}\n\n/**\n * Returns true if `text` already carries a citation produced by `template`\n * **or** by the default `[Source: ...]` format (for facts that were tagged\n * before a config change).\n *\n * Use this instead of {@link hasCitation} whenever the caller has access to\n * the configured `inlineSourceAttributionFormat`.\n *\n * All-placeholder templates such as `{agent}{sessionId}` have no literal\n * content to anchor on and therefore cannot be reliably detected without\n * dedicated sentinel markers. In that case the function returns `false` —\n * callers that need idempotent dedup for such templates should either adopt\n * a template with literal delimiters (recommended) or rely on the default\n * `[Source: ...]` marker detection which is always available via\n * {@link hasCitation}.\n */\nexport function hasCitationForTemplate(text: string, template: string): boolean {\n if (typeof text !== \"string\" || text.length === 0) return false;\n // Always accept the default format as a fallback so facts tagged before a\n // configuration change are not double-tagged on reprocessing.\n if (hasCitation(text)) return true;\n // If the configured template matches the default, we're done.\n //\n // Known limitation (Thread 2 — Codex P2): this fast path exits without\n // checking whether the content carries a citation from a DIFFERENT custom\n // template that was active before the config was changed back to the default.\n // Such a fact would be detected by `hasCitation` above only if the prior\n // custom template happened to match the default `[Source: ...]` pattern.\n // In practice, template changes mid-stream are rare, and the false-negative\n // (missing an old custom citation) produces a benign duplicate citation rather\n // than data loss. A full fix would require storing the citation template used\n // at write time in the frontmatter and checking that here.\n if (template === DEFAULT_CITATION_FORMAT) return false;\n\n // Fully-literal template (no placeholders): exact inclusion check.\n if (!PLACEHOLDER_REGEX.test(template)) {\n // Reset lastIndex because PLACEHOLDER_REGEX is declared with /g.\n PLACEHOLDER_REGEX.lastIndex = 0;\n return text.includes(template);\n }\n // Reset lastIndex after the .test() probe above.\n PLACEHOLDER_REGEX.lastIndex = 0;\n\n const matcher = templateMatcher(template);\n if (!matcher) {\n // All-placeholder template: cannot build a reliable matcher. See the\n // docstring — callers should not rely on dedup for this shape.\n return false;\n }\n return matcher.test(text);\n}\n\n/**\n * Attach an inline citation to fact text.\n *\n * If the text already has a citation — either the default `[Source: ...]`\n * marker or one produced by the configured template — it is returned unchanged.\n * Existing provenance is respected and never overwritten. Otherwise the\n * citation is appended to the trimmed text with a single space separator,\n * which keeps the marker visually adjacent to the fact body.\n */\nexport function attachCitation(\n text: string,\n ctx: CitationContext,\n template: string = DEFAULT_CITATION_FORMAT,\n): string {\n if (typeof text !== \"string\") return text as unknown as string;\n if (hasCitationForTemplate(text, template)) return text;\n const trimmedEnd = text.replace(/\\s+$/u, \"\");\n if (trimmedEnd.length === 0) return text;\n const citation = formatCitation(ctx, template);\n // Preserve any trailing newline that callers rely on for markdown rendering.\n const trailing = text.slice(trimmedEnd.length);\n return `${trimmedEnd} ${citation}${trailing}`;\n}\n\n/**\n * Parse a single inline citation from a piece of text. Returns the first\n * citation encountered or `null` when none is present. Malformed citations\n * do not throw — fields that cannot be parsed simply remain `undefined`.\n */\nexport function parseCitation(text: string): ParsedCitation | null {\n if (typeof text !== \"string\" || text.length === 0) return null;\n const matcher = defaultCitationMatcher();\n const match = matcher.exec(text);\n if (!match) return null;\n\n const body = match[1] ?? \"\";\n const raw = match[0] ?? \"\";\n const parsed: ParsedCitation = { raw };\n\n const fields = body\n .split(\",\")\n .map((segment) => segment.trim())\n .filter((segment) => segment.length > 0);\n\n for (const field of fields) {\n const eqIdx = field.indexOf(\"=\");\n if (eqIdx <= 0) continue;\n const key = field.slice(0, eqIdx).trim().toLowerCase();\n const value = field.slice(eqIdx + 1).trim();\n if (value.length === 0) continue;\n switch (key) {\n case \"agent\":\n parsed.agent = value;\n break;\n case \"session\":\n case \"sessionid\":\n parsed.session = value;\n break;\n case \"ts\":\n case \"timestamp\":\n parsed.ts = value;\n break;\n default:\n // Unknown fields are ignored defensively so human edits never crash.\n break;\n }\n }\n\n return parsed;\n}\n\n/**\n * Parse every citation embedded in the text. Always returns an array; empty\n * when none are present.\n */\nexport function parseAllCitations(text: string): ParsedCitation[] {\n if (typeof text !== \"string\" || text.length === 0) return [];\n const matcher = defaultCitationMatcher();\n const results: ParsedCitation[] = [];\n let match: RegExpExecArray | null;\n while ((match = matcher.exec(text)) !== null) {\n const parsed = parseCitation(match[0]);\n if (parsed) results.push(parsed);\n }\n return results;\n}\n\n/**\n * Remove all inline citations from a piece of text.\n *\n * Callers that want the raw fact body (for dedup hashing, display, or\n * comparison) should use this helper instead of hand-rolled regexes so the\n * whole codebase agrees on the citation syntax.\n *\n * Finding 2 fix: when the input contains no citation marker, the input is\n * returned byte-for-byte unchanged. When a citation IS removed, whitespace\n * normalization is applied only at each join seam (the single space between\n * the preceding text and where the citation was), rather than across the\n * entire string. This preserves markdown hard-break spacing, aligned text,\n * and code-like snippets in fact bodies that happen to carry a citation.\n *\n * Implementation: each citation match is replaced by its \"seam fix\" — the\n * content before the match has its trailing whitespace trimmed and then a\n * single space is appended if any text remains, collapsing only the gap\n * left by the removed marker. Whitespace elsewhere in the body is untouched.\n */\nexport function stripCitation(text: string): string {\n if (typeof text !== \"string\" || text.length === 0) return text;\n // Early exit: no citation marker present — return the input unchanged so\n // that callers never lose formatting fidelity on uncited strings.\n if (!hasCitation(text)) return text;\n\n // Walk through all citations and slice them out one by one so that we can\n // normalise ONLY the whitespace at each seam rather than the entire string.\n const matcher = defaultCitationMatcher();\n let result = \"\";\n let lastIndex = 0;\n\n let match: RegExpExecArray | null;\n while ((match = matcher.exec(text)) !== null) {\n // Text before this citation. Trim trailing spaces/tabs at the seam only.\n const before = text.slice(lastIndex, match.index).replace(/[ \\t]+$/, \"\");\n result += before;\n lastIndex = match.index + match[0].length;\n }\n\n // Append any trailing text after the last citation. Trim leading\n // spaces/tabs and trailing whitespace at the join seam.\n const after = text.slice(lastIndex).replace(/^[ \\t]+/, \"\");\n if (after.length > 0) {\n if (result.length > 0) result += \" \";\n result += after;\n }\n\n return result.trimEnd();\n}\n\n/**\n * Strip an inline citation from text using a specific template regex.\n *\n * This is the template-aware counterpart to {@link stripCitation}. When the\n * caller holds the configured `inlineSourceAttributionFormat`, use this\n * function to strip citations produced by that template — including custom\n * templates that differ from the default `[Source: ...]` pattern.\n *\n * Behaviour:\n * - If the text has a **default-format** citation, delegates to\n * {@link stripCitation} (always safe).\n * - If the text has a **custom-template** citation detected by\n * `hasCitationForTemplate`, builds the template regex and removes every\n * occurrence (citations are appended at the end of the fact body by\n * {@link attachCitation}).\n * - All-placeholder templates (no literal prefix/suffix/separator) cannot\n * produce a reliable matcher. `hasCitationForTemplate` already returns\n * `false` for such templates, so this function never attempts to strip an\n * undetectable citation. The text is returned unchanged when no citation\n * is detected.\n * - If no citation is detected for the given template, returns the text\n * unchanged.\n *\n * @returns The stripped text (or the original text when no citation is found).\n */\nexport function stripCitationForTemplate(\n text: string,\n template: string,\n): string {\n if (typeof text !== \"string\" || text.length === 0) return text;\n\n // Fast path: default-format citation — delegate to the existing stripper.\n if (hasCitation(text)) return stripCitation(text);\n\n // No default citation; check whether the custom template produced one.\n // hasCitationForTemplate returns false for all-placeholder templates (no\n // reliable matcher), so those pass through unchanged below.\n if (!hasCitationForTemplate(text, template)) return text;\n\n // Build the template matcher. hasCitationForTemplate already returned true,\n // which means templateMatcher produced a non-null result. The null branch\n // here is a defensive fallback only — delegate to stripCitation.\n const matcher = templateMatcher(template);\n if (!matcher) return stripCitation(text);\n\n // The matcher regex was built without the global flag; add it for exec loop.\n const globalMatcher = new RegExp(\n matcher.source,\n matcher.flags.includes(\"g\") ? matcher.flags : matcher.flags + \"g\",\n );\n let result = \"\";\n let lastIndex = 0;\n let match: RegExpExecArray | null;\n\n while ((match = globalMatcher.exec(text)) !== null) {\n const before = text.slice(lastIndex, match.index).replace(/[ \\t]+$/, \"\");\n result += before;\n lastIndex = match.index + match[0].length;\n // Guard against zero-width matches causing an infinite loop.\n if (match[0].length === 0) {\n globalMatcher.lastIndex++;\n }\n }\n\n const after = text.slice(lastIndex).replace(/^[ \\t]+/, \"\");\n if (after.length > 0) {\n if (result.length > 0) result += \" \";\n result += after;\n }\n\n return result.trimEnd();\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/source-attribution.ts"],"sourcesContent":["/**\n * Inline Source Attribution Protocol (issue #369)\n *\n * Extracted facts carry provenance inline inside the fact body, so the\n * citation survives hostile memory text, copy/paste, and LLM quoting. This\n * complements — never replaces — the YAML frontmatter provenance stored on\n * disk.\n *\n * Default format (matches issue #369 proposal):\n *\n * The foo service uses Redis for rate limiting. [Source: agent=planner, session=abc123, ts=2026-04-10T14:25:07Z]\n *\n * Key properties:\n * - Inline (part of the body, not metadata).\n * - Compact (typically <80 chars of overhead per fact).\n * - Machine-parseable by a single regex.\n * - Opt-in via `inlineSourceAttributionEnabled` config flag (default off\n * for backwards compatibility with existing downstream consumers).\n * - Legacy facts without a citation remain fully readable.\n *\n * The format template is configurable via `inlineSourceAttributionFormat`\n * with supported placeholders:\n *\n * {agent} — principal / agent identifier\n * {session} — full session key (colon-delimited)\n * {sessionId} — short stable session id (trailing component)\n * {ts} — extraction timestamp (ISO 8601)\n * {date} — extraction date (YYYY-MM-DD)\n *\n * Any privacy-sensitive identifiers should be normalized before being passed\n * to `formatCitation` — the helper treats them as opaque strings.\n */\n\n/** Default citation format template (matches issue #369). */\nexport const DEFAULT_CITATION_FORMAT =\n \"[Source: agent={agent}, session={sessionId}, ts={ts}]\";\n\n/** Sentinel value used when a provenance field is missing. */\nexport const CITATION_UNKNOWN = \"unknown\";\n\nexport interface CitationContext {\n /** Principal / agent identifier (e.g. resolved via resolvePrincipal). */\n agent?: string;\n /** Full session key (e.g. \"agent:planner:main\"). */\n session?: string;\n /**\n * Opaque short session id. Derived from the trailing component of the\n * session key when not provided explicitly. Use this for compact formats\n * that do not need the full colon-delimited session key.\n */\n sessionId?: string;\n /** Extraction timestamp as an ISO 8601 string. */\n ts?: string;\n}\n\nexport interface ParsedCitation {\n /** Agent identifier parsed from the citation (never crashes on malformed input). */\n agent?: string;\n /** Session identifier parsed from the citation. */\n session?: string;\n /** Extraction timestamp parsed from the citation. */\n ts?: string;\n /** The full matched citation substring. */\n raw: string;\n}\n\n/**\n * Regex that matches the default `[Source: agent=X, session=Y, ts=Z]` shape\n * as well as human-edited variants (extra whitespace, reordered fields,\n * subset of fields). Matches non-greedily so it can be anchored anywhere in\n * the text. Kept as a getter factory so callers do not share regex state.\n */\nfunction defaultCitationMatcher(): RegExp {\n return /\\[Source:\\s*([^\\]\\n]+?)\\]/gi;\n}\n\n/**\n * Derive a short session id from a full session key.\n * Falls back to the raw session string if no colon is present.\n */\nexport function deriveSessionId(session: string | undefined): string | undefined {\n if (!session) return undefined;\n const trimmed = session.trim();\n if (trimmed.length === 0) return undefined;\n const parts = trimmed.split(\":\").filter((p) => p.length > 0);\n if (parts.length === 0) return trimmed;\n return parts[parts.length - 1];\n}\n\n/**\n * Format an inline citation tag using the provided template.\n *\n * Missing context fields fall back to {@link CITATION_UNKNOWN} — the caller\n * should always get a non-empty, parseable tag.\n *\n * Uses a single-pass substitution so that values which themselves contain\n * placeholder syntax (e.g. an agent literally named `\"{ts}\"`) cannot be\n * re-interpreted by subsequent replacement steps. Each placeholder slot\n * receives exactly one lookup and the substituted value is treated as\n * terminal text, not template source.\n */\nexport function formatCitation(\n ctx: CitationContext,\n template: string = DEFAULT_CITATION_FORMAT,\n): string {\n const session = ctx.session ?? \"\";\n const sessionId = ctx.sessionId ?? deriveSessionId(session) ?? CITATION_UNKNOWN;\n const ts = ctx.ts ?? CITATION_UNKNOWN;\n const agent = ctx.agent && ctx.agent.trim().length > 0 ? ctx.agent : CITATION_UNKNOWN;\n const date = ts && ts !== CITATION_UNKNOWN ? ts.slice(0, 10) : CITATION_UNKNOWN;\n const sessionForTemplate = session.trim().length > 0 ? session : CITATION_UNKNOWN;\n\n // Map from recognised placeholder names to their resolved value. Unknown\n // placeholder names are left intact (returning the original `{name}`).\n const values: Record<string, string> = {\n agent,\n session: sessionForTemplate,\n sessionId,\n ts,\n date,\n };\n\n // Single-pass scan: replace every recognised `{name}` in one sweep so that\n // substituted values cannot themselves be treated as template source on a\n // subsequent pass. The replacer-function form also guarantees that `$` /\n // `$&` / `$1` sequences inside values are emitted literally.\n return template.replace(/\\{([a-zA-Z_][\\w]*)\\}/g, (match, name: string) => {\n return Object.prototype.hasOwnProperty.call(values, name)\n ? values[name]!\n : match;\n });\n}\n\n/**\n * Returns true if the text already carries at least one citation marker.\n * Safe to call on any string — never throws.\n */\nexport function hasCitation(text: string): boolean {\n if (typeof text !== \"string\" || text.length === 0) return false;\n return defaultCitationMatcher().test(text);\n}\n\n/**\n * Escape a string for use as a regex literal.\n */\nfunction escapeRegExp(s: string): string {\n return s.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\n/**\n * Escape a single character for use INSIDE a character class `[...]`.\n * Special chars inside character classes: `]`, `\\`, `^`, `-`.\n */\nfunction escapeRegExpCharClass(ch: string): string {\n if (ch === \"]\") return \"\\\\]\";\n if (ch === \"\\\\\") return \"\\\\\\\\\";\n if (ch === \"^\") return \"\\\\^\";\n if (ch === \"-\") return \"\\\\-\";\n // Other regex meta chars are NOT special inside [...] but escaping them is safe.\n return escapeRegExp(ch);\n}\n\n/**\n * Build a per-placeholder token pattern that excludes newlines, whitespace,\n * and any punctuation characters used as inner separators in the template.\n *\n * This prevents a placeholder span from consuming separator bytes and\n * matching strings that cross separator boundaries in user-supplied content.\n *\n * @param nonWordSepChars - Set of non-word (non-alphanumeric, non-`_`) chars\n * extracted from the inner separator literals of the template.\n */\nfunction buildTokenPattern(nonWordSepChars: Set<string>): string {\n // Always exclude newlines and whitespace.\n const base = \"\\\\n\\\\s\";\n if (nonWordSepChars.size === 0) {\n // No inner separator punctuation — the placeholder spans the full space\n // between prefix and suffix. Fall back to a generous no-newline match.\n return `[^\\\\n]+?`;\n }\n const escaped = [...nonWordSepChars].map(escapeRegExpCharClass).join(\"\");\n return `[^${base}${escaped}]+?`;\n}\n\n/** Regex that matches a `{placeholder}` token inside a template string. */\nconst PLACEHOLDER_REGEX = /\\{[a-zA-Z_][\\w]*\\}/g;\n\n/**\n * Build a regex that matches a citation produced by the given template.\n *\n * The approach depends on the shape of the template:\n *\n * - **Normal case (non-empty literal prefix or suffix).** Anchor the match\n * on the outer literal frame and reconstruct the interior as\n * `interToken + sep + interToken + sep + ... + sep + lastToken`.\n * All **intermediate** per-placeholder tokens exclude the combined set of\n * non-word separator characters used between any two adjacent placeholders,\n * preventing a value from consuming a separator and crossing placeholder\n * boundaries. The **last** token is only required to avoid newlines because\n * it is terminated by the literal suffix anchor — this lets placeholder\n * values that legitimately contain a separator character be recognised (e.g.\n * an ISO-8601 timestamp `2026-04-10T14:25:07Z` in `[src:{agent}:{ts}]`\n * where `:` is the inter-placeholder separator). A template like\n * `[src:{agent}/{sessionId}@{date}]` emits\n * `\\[src:[^\\n\\s/@]+?\\/[^\\n\\s/@]+?@[^\\n]+?\\]` so that `[src:foo/bar]`\n * is NOT matched (wrong separator count), `[src:foo/bar/extra@2026]`\n * is NOT matched (intermediate token crosses a `/` boundary), and\n * `[src:planner/main@2026-04-10]` IS matched correctly.\n *\n * - **Placeholder-bounded with whitespace separator.** Both prefix and\n * suffix are empty and the separator literal(s) between placeholders\n * contain at least one whitespace character (e.g. `{source}: {content}`,\n * `{agent} {sessionId}`). A whitespace-containing separator produces\n * output that is visually indistinguishable from ordinary prose, so the\n * safe strategy is to require a **hard bracket/paren/angle delimiter** on\n * both sides of the reconstructed match. Prose almost never places\n * `[...]` / `(...)` / `<...>` around a phrase, so this yields clean\n * false-positive rejection.\n *\n * - **Placeholder-bounded with compact (non-whitespace) separator.** Both\n * prefix and suffix are empty and the separator literal(s) contain NO\n * whitespace (e.g. `{agent}:{sessionId}`, `{agent}/{sessionId}`).\n * `formatCitation` emits a compact token like `planner:main` with no\n * surrounding delimiters, so the bracket strategy cannot detect it.\n * Instead, the pattern requires that the entire token is bordered by\n * whitespace or a bracket/paren/angle on each side:\n *\n * `(?:(?<=[\\[\\(\\<])|(?<!\\S))[\\w.-]+<sep>[\\w.-]+(?:(?=[\\]\\)\\>])|(?!\\S))`\n *\n * This accepts `planner:main` when it appears standalone or inside a\n * bracket-wrapped token, and rejects `host:80` embedded inside a URL like\n * `http://host:80` because `host` is immediately preceded by `/`\n * (non-whitespace, non-bracket).\n *\n * - **All-placeholder case (no literals between placeholders either).** No\n * reliable regex can be built — a template like `{agent}{sessionId}`\n * contains no anchor characters. Returns `null`; {@link\n * hasCitationForTemplate} treats this as \"cannot detect\" and returns\n * false, falling back on explicit sentinel/format detection only for the\n * default `[Source: ...]` shape.\n *\n * Returns `null` when the template has no placeholders (fully-literal\n * citation, handled by the string-equality fast path in {@link\n * hasCitationForTemplate}) **or** when the template is entirely placeholder-\n * only with no literal content whatsoever.\n */\nfunction templateMatcher(template: string): RegExp | null {\n // Split around all {placeholder} tokens.\n const parts = template.split(PLACEHOLDER_REGEX);\n if (parts.length <= 1) return null;\n\n const prefix = parts[0] ?? \"\";\n const suffix = parts[parts.length - 1] ?? \"\";\n\n // Normal case: at least one literal frame on the outside.\n // Tighten the per-placeholder token so it cannot consume separator\n // characters and match strings that cross separator boundaries\n // (Finding 3 — Uru3).\n if (prefix.length > 0 || suffix.length > 0) {\n const escapedPrefix = escapeRegExp(prefix);\n const escapedSuffix = escapeRegExp(suffix);\n\n // Inner parts: literal separators that sit between adjacent placeholders.\n // For `[src:{agent}/{sessionId}@{date}]`, parts = [\"[src:\", \"/\", \"@\", \"]\"]\n // so innerParts = [\"/\", \"@\"].\n const innerParts = parts.slice(1, -1);\n\n // Collect only the non-word (punctuation/symbol) characters from each\n // inner separator so alphabetic separator text (unlikely but valid) does\n // not exclude letters from the per-placeholder token pattern.\n const nonWordSepChars = new Set<string>();\n for (const sep of innerParts) {\n for (const ch of sep) {\n if (!/\\w/.test(ch)) {\n nonWordSepChars.add(ch);\n }\n }\n }\n\n // All intermediate tokens (every placeholder except the last) use the\n // combined exclusion so they cannot cross placeholder boundaries.\n //\n // The LAST token is different: it is terminated by the literal suffix anchor\n // (e.g. `\\]`), so it does not need to exclude inner-separator characters.\n // Dropping that restriction lets placeholder values that legitimately contain\n // a separator character (e.g. an ISO-8601 timestamp `2026-04-10T14:25:07Z`\n // in template `[src:{agent}:{ts}]`) be recognised correctly instead of\n // producing false-negative misses that trigger duplicate citation injection.\n //\n // Only the LAST token is relaxed. Intermediate tokens keep the combined\n // exclusion so that cross-boundary false positives are still rejected\n // (e.g. `[src:foo/bar/extra@2026-04-11]` for `[src:{a}/{b}@{c}]`).\n const interToken = buildTokenPattern(nonWordSepChars);\n // Last token: terminated by suffix anchor — exclude only newlines.\n const lastToken = buildTokenPattern(new Set<string>());\n\n // Reconstruct the interior: interToken sep interToken sep ... sep lastToken\n // (or just lastToken when there are no inner separators at all).\n const middle =\n innerParts.length === 0\n ? lastToken\n : interToken +\n innerParts\n .slice(0, -1)\n .map((sep) => escapeRegExp(sep) + interToken)\n .join(\"\") +\n escapeRegExp(innerParts[innerParts.length - 1]!) +\n lastToken;\n\n const pattern = escapedPrefix + middle + escapedSuffix;\n return new RegExp(pattern, \"i\");\n }\n\n // Placeholder-bounded case: prefix and suffix are both empty.\n const middleLiterals = parts.slice(1, -1);\n const hasNonEmptyMiddle = middleLiterals.some((p) => p.length > 0);\n if (!hasNonEmptyMiddle) {\n // All-placeholder template with no literal content. Impossible to anchor\n // reliably without sentinel markers; signal the caller.\n return null;\n }\n\n // Identifier token: one or more word chars, dots, dashes, or colons.\n // Colons are included to allow timestamp values like \"10:30\" or session\n // keys like \"agent:planner:main\" inside compact placeholder-bounded\n // templates. URL-like fragments (`http://host:80`) are still rejected\n // because the lead anchor requires whitespace or a bracket immediately\n // before the first id-token group (`http` is preceded by `/`).\n const idToken = \"[\\\\w.:-]+\";\n const body =\n idToken +\n middleLiterals.map((lit) => escapeRegExp(lit) + idToken).join(\"\");\n\n const separatorText = middleLiterals.join(\"\");\n if (/\\s/.test(separatorText)) {\n // Separator contains whitespace: the emitted citation looks like ordinary\n // prose (e.g. `planner main`). Require a hard bracket/paren/angle\n // delimiter on both sides to prevent false matches on English text.\n const opener = \"[\\\\[\\\\(\\\\<]\";\n const closer = \"[\\\\]\\\\)\\\\>]\";\n return new RegExp(opener + body + closer, \"i\");\n }\n\n // Separator is compact (no whitespace): `formatCitation` emits a token like\n // `planner:main` without surrounding delimiters. The challenge is that the\n // same token shape also matches ordinary hyphenated or slashed prose words\n // (e.g. `long-term`, `docs/setup`), causing `hasCitationForTemplate` to\n // return true on uncited fact bodies and silently suppress citation injection\n // from `attachCitation`.\n //\n // Fix (Finding 1): tighten the trail anchor so a bare compact token is only\n // accepted when it sits at the very end of the string (possibly followed by\n // optional trailing whitespace or a newline). Since `attachCitation` always\n // appends the citation at the trimmed end of the fact body, a real citation\n // token will always appear at the tail. Prose like `\"long-term solution\"`\n // has `long-term` in the middle of the string (followed by ` solution`), so\n // the end-of-string anchor rejects it — no false positive, no silent drop.\n //\n // The lead anchor still accepts either a bracket opener or a whitespace\n // boundary (or start of string), so `\"Fact. planner:main\"` and standalone\n // `\"planner:main\"` are both detected after the first attachment pass.\n //\n // Bracket-wrapped form (e.g. `[planner:main]`) is also accepted via the\n // opener/closer pair — bracket still takes precedence over end-of-string.\n //\n // Example — why `http://host:80` does NOT match:\n // Trying to match `host:80`: the char before `h` is `/` (non-whitespace,\n // non-bracket), so `(?<=[\\[\\(\\<])` and `(?<!\\S)` both fail ⟹ no match.\n // Trying to match `http:...`: after `http:` the next chars are `//` which\n // are not `[\\w.-]+`, so the second id-token group fails ⟹ no match.\n const leadAnchor = \"(?:(?<=[\\\\[\\\\(\\\\<])|(?<!\\\\S))\";\n // Trail: either a bracket closer (for `[token]` shape) or end-of-string\n // optionally preceded by whitespace. The `(?!\\S)` is deliberately removed\n // so that a compact token in the MIDDLE of a sentence does not match.\n const trailAnchor = \"(?:(?=[\\\\]\\\\)\\\\>])|(?=\\\\s*$))\";\n return new RegExp(leadAnchor + body + trailAnchor, \"i\");\n}\n\n/**\n * Returns true if `text` already carries a citation produced by `template`\n * **or** by the default `[Source: ...]` format (for facts that were tagged\n * before a config change).\n *\n * Use this instead of {@link hasCitation} whenever the caller has access to\n * the configured `inlineSourceAttributionFormat`.\n *\n * All-placeholder templates such as `{agent}{sessionId}` have no literal\n * content to anchor on and therefore cannot be reliably detected without\n * dedicated sentinel markers. In that case the function returns `false` —\n * callers that need idempotent dedup for such templates should either adopt\n * a template with literal delimiters (recommended) or rely on the default\n * `[Source: ...]` marker detection which is always available via\n * {@link hasCitation}.\n */\nexport function hasCitationForTemplate(text: string, template: string): boolean {\n if (typeof text !== \"string\" || text.length === 0) return false;\n // Always accept the default format as a fallback so facts tagged before a\n // configuration change are not double-tagged on reprocessing.\n if (hasCitation(text)) return true;\n // If the configured template matches the default, we're done.\n //\n // Known limitation (Thread 2 — Codex P2): this fast path exits without\n // checking whether the content carries a citation from a DIFFERENT custom\n // template that was active before the config was changed back to the default.\n // Such a fact would be detected by `hasCitation` above only if the prior\n // custom template happened to match the default `[Source: ...]` pattern.\n // In practice, template changes mid-stream are rare, and the false-negative\n // (missing an old custom citation) produces a benign duplicate citation rather\n // than data loss. A full fix would require storing the citation template used\n // at write time in the frontmatter and checking that here.\n if (template === DEFAULT_CITATION_FORMAT) return false;\n\n // Fully-literal template (no placeholders): exact inclusion check.\n if (!PLACEHOLDER_REGEX.test(template)) {\n // Reset lastIndex because PLACEHOLDER_REGEX is declared with /g.\n PLACEHOLDER_REGEX.lastIndex = 0;\n return text.includes(template);\n }\n // Reset lastIndex after the .test() probe above.\n PLACEHOLDER_REGEX.lastIndex = 0;\n\n const matcher = templateMatcher(template);\n if (!matcher) {\n // All-placeholder template: cannot build a reliable matcher. See the\n // docstring — callers should not rely on dedup for this shape.\n return false;\n }\n return matcher.test(text);\n}\n\n/**\n * Attach an inline citation to fact text.\n *\n * If the text already has a citation — either the default `[Source: ...]`\n * marker or one produced by the configured template — it is returned unchanged.\n * Existing provenance is respected and never overwritten. Otherwise the\n * citation is appended to the trimmed text with a single space separator,\n * which keeps the marker visually adjacent to the fact body.\n */\nexport function attachCitation(\n text: string,\n ctx: CitationContext,\n template: string = DEFAULT_CITATION_FORMAT,\n): string {\n if (typeof text !== \"string\") return text as unknown as string;\n if (hasCitationForTemplate(text, template)) return text;\n const trimmedEnd = text.replace(/\\s+$/u, \"\");\n if (trimmedEnd.length === 0) return text;\n const citation = formatCitation(ctx, template);\n // Preserve any trailing newline that callers rely on for markdown rendering.\n const trailing = text.slice(trimmedEnd.length);\n return `${trimmedEnd} ${citation}${trailing}`;\n}\n\n/**\n * Parse a single inline citation from a piece of text. Returns the first\n * citation encountered or `null` when none is present. Malformed citations\n * do not throw — fields that cannot be parsed simply remain `undefined`.\n */\nexport function parseCitation(text: string): ParsedCitation | null {\n if (typeof text !== \"string\" || text.length === 0) return null;\n const matcher = defaultCitationMatcher();\n const match = matcher.exec(text);\n if (!match) return null;\n\n const body = match[1] ?? \"\";\n const raw = match[0] ?? \"\";\n const parsed: ParsedCitation = { raw };\n\n const fields = body\n .split(\",\")\n .map((segment) => segment.trim())\n .filter((segment) => segment.length > 0);\n\n for (const field of fields) {\n const eqIdx = field.indexOf(\"=\");\n if (eqIdx <= 0) continue;\n const key = field.slice(0, eqIdx).trim().toLowerCase();\n const value = field.slice(eqIdx + 1).trim();\n if (value.length === 0) continue;\n switch (key) {\n case \"agent\":\n parsed.agent = value;\n break;\n case \"session\":\n case \"sessionid\":\n parsed.session = value;\n break;\n case \"ts\":\n case \"timestamp\":\n parsed.ts = value;\n break;\n default:\n // Unknown fields are ignored defensively so human edits never crash.\n break;\n }\n }\n\n return parsed;\n}\n\n/**\n * Parse every citation embedded in the text. Always returns an array; empty\n * when none are present.\n */\nexport function parseAllCitations(text: string): ParsedCitation[] {\n if (typeof text !== \"string\" || text.length === 0) return [];\n const matcher = defaultCitationMatcher();\n const results: ParsedCitation[] = [];\n let match: RegExpExecArray | null;\n while ((match = matcher.exec(text)) !== null) {\n const parsed = parseCitation(match[0]);\n if (parsed) results.push(parsed);\n }\n return results;\n}\n\n/**\n * Remove all inline citations from a piece of text.\n *\n * Callers that want the raw fact body (for dedup hashing, display, or\n * comparison) should use this helper instead of hand-rolled regexes so the\n * whole codebase agrees on the citation syntax.\n *\n * Finding 2 fix: when the input contains no citation marker, the input is\n * returned byte-for-byte unchanged. When a citation IS removed, whitespace\n * normalization is applied only at each join seam (the single space between\n * the preceding text and where the citation was), rather than across the\n * entire string. This preserves markdown hard-break spacing, aligned text,\n * and code-like snippets in fact bodies that happen to carry a citation.\n *\n * Implementation: each citation match is replaced by its \"seam fix\" — the\n * content before the match has its trailing whitespace trimmed and then a\n * single space is appended if any text remains, collapsing only the gap\n * left by the removed marker. Whitespace elsewhere in the body is untouched.\n */\nexport function stripCitation(text: string): string {\n if (typeof text !== \"string\" || text.length === 0) return text;\n // Early exit: no citation marker present — return the input unchanged so\n // that callers never lose formatting fidelity on uncited strings.\n if (!hasCitation(text)) return text;\n\n // Walk through all citations and slice them out one by one so that we can\n // normalise ONLY the whitespace at each seam rather than the entire string.\n const matcher = defaultCitationMatcher();\n let result = \"\";\n let lastIndex = 0;\n\n let match: RegExpExecArray | null;\n while ((match = matcher.exec(text)) !== null) {\n // Text before this citation. Trim trailing spaces/tabs at the seam only.\n const before = text.slice(lastIndex, match.index).replace(/[ \\t]+$/, \"\");\n result += before;\n lastIndex = match.index + match[0].length;\n }\n\n // Append any trailing text after the last citation. Trim leading\n // spaces/tabs and trailing whitespace at the join seam.\n const after = text.slice(lastIndex).replace(/^[ \\t]+/, \"\");\n if (after.length > 0) {\n if (result.length > 0) result += \" \";\n result += after;\n }\n\n return result.trimEnd();\n}\n\n/**\n * Strip an inline citation from text using a specific template regex.\n *\n * This is the template-aware counterpart to {@link stripCitation}. When the\n * caller holds the configured `inlineSourceAttributionFormat`, use this\n * function to strip citations produced by that template — including custom\n * templates that differ from the default `[Source: ...]` pattern.\n *\n * Behaviour:\n * - If the text has a **default-format** citation, delegates to\n * {@link stripCitation} (always safe).\n * - If the text has a **custom-template** citation detected by\n * `hasCitationForTemplate`, builds the template regex and removes every\n * occurrence (citations are appended at the end of the fact body by\n * {@link attachCitation}).\n * - All-placeholder templates (no literal prefix/suffix/separator) cannot\n * produce a reliable matcher. `hasCitationForTemplate` already returns\n * `false` for such templates, so this function never attempts to strip an\n * undetectable citation. The text is returned unchanged when no citation\n * is detected.\n * - If no citation is detected for the given template, returns the text\n * unchanged.\n *\n * @returns The stripped text (or the original text when no citation is found).\n */\nexport function stripCitationForTemplate(\n text: string,\n template: string,\n): string {\n if (typeof text !== \"string\" || text.length === 0) return text;\n\n // Fast path: default-format citation — delegate to the existing stripper.\n if (hasCitation(text)) return stripCitation(text);\n\n // No default citation; check whether the custom template produced one.\n // hasCitationForTemplate returns false for all-placeholder templates (no\n // reliable matcher), so those pass through unchanged below.\n if (!hasCitationForTemplate(text, template)) return text;\n\n // Build the template matcher. hasCitationForTemplate already returned true,\n // which means templateMatcher produced a non-null result. The null branch\n // here is a defensive fallback only — delegate to stripCitation.\n const matcher = templateMatcher(template);\n if (!matcher) return stripCitation(text);\n\n // The matcher regex was built without the global flag; add it for exec loop.\n const globalMatcher = new RegExp(\n matcher.source,\n matcher.flags.includes(\"g\") ? matcher.flags : matcher.flags + \"g\",\n );\n let result = \"\";\n let lastIndex = 0;\n let match: RegExpExecArray | null;\n\n while ((match = globalMatcher.exec(text)) !== null) {\n const matchEnd = match.index + match[0].length;\n const enclosure = enclosingDelimiterRange(text, match.index, matchEnd);\n const removalStart = enclosure?.start ?? match.index;\n const removalEnd = enclosure?.end ?? matchEnd;\n const before = text.slice(lastIndex, removalStart).replace(/[ \\t]+$/, \"\");\n result += before;\n lastIndex = removalEnd;\n // Guard against zero-width matches causing an infinite loop.\n if (match[0].length === 0) {\n globalMatcher.lastIndex++;\n }\n }\n\n const after = text.slice(lastIndex).replace(/^[ \\t]+/, \"\");\n if (after.length > 0) {\n if (result.length > 0) result += \" \";\n result += after;\n }\n\n return result.trimEnd();\n}\n\nfunction enclosingDelimiterRange(\n text: string,\n start: number,\n end: number,\n): { start: number; end: number } | undefined {\n if (start <= 0 || end >= text.length) return undefined;\n const opener = text[start - 1];\n const closer = text[end];\n if (\n (opener === \"[\" && closer === \"]\") ||\n (opener === \"(\" && closer === \")\") ||\n (opener === \"<\" && closer === \">\")\n ) {\n return { start: start - 1, end: end + 1 };\n }\n return undefined;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
parseMessageParts
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-XKIQZXUB.js";
|
|
4
4
|
import {
|
|
5
5
|
log
|
|
6
6
|
} from "./chunk-2ODBA7MQ.js";
|
|
@@ -9,6 +9,16 @@ import {
|
|
|
9
9
|
function estimateTokens(text) {
|
|
10
10
|
return Math.ceil(text.length / 4);
|
|
11
11
|
}
|
|
12
|
+
var PUBLIC_LCM_SEARCH_LIMIT = 100;
|
|
13
|
+
function normalizeSearchLimit(limit, fallback = 10, max = PUBLIC_LCM_SEARCH_LIMIT) {
|
|
14
|
+
if (typeof limit !== "number" || !Number.isFinite(limit)) return fallback;
|
|
15
|
+
const normalized = Math.floor(limit);
|
|
16
|
+
if (normalized <= 0) return 0;
|
|
17
|
+
return Math.min(max, normalized);
|
|
18
|
+
}
|
|
19
|
+
function escapeSqlLike(value) {
|
|
20
|
+
return value.replace(/[\\%_]/g, (match) => `\\${match}`);
|
|
21
|
+
}
|
|
12
22
|
var LcmArchive = class {
|
|
13
23
|
constructor(db) {
|
|
14
24
|
this.db = db;
|
|
@@ -100,10 +110,12 @@ var LcmArchive = class {
|
|
|
100
110
|
).all(sessionId, lastSummarized);
|
|
101
111
|
}
|
|
102
112
|
/** Full-text search across all messages. */
|
|
103
|
-
search(query, limit, sessionId) {
|
|
113
|
+
search(query, limit, sessionId, sessionPrefix) {
|
|
104
114
|
try {
|
|
105
115
|
const ftsQuery = sanitizeFtsQuery(query);
|
|
106
116
|
if (!ftsQuery) return [];
|
|
117
|
+
const cappedLimit = normalizeSearchLimit(limit);
|
|
118
|
+
if (cappedLimit === 0) return [];
|
|
107
119
|
let sql;
|
|
108
120
|
const params = [ftsQuery];
|
|
109
121
|
if (sessionId) {
|
|
@@ -117,7 +129,19 @@ var LcmArchive = class {
|
|
|
117
129
|
ORDER BY rank
|
|
118
130
|
LIMIT ?
|
|
119
131
|
`;
|
|
120
|
-
params.push(sessionId,
|
|
132
|
+
params.push(sessionId, cappedLimit);
|
|
133
|
+
} else if (sessionPrefix) {
|
|
134
|
+
sql = `
|
|
135
|
+
SELECT m.turn_index, m.role, snippet(lcm_messages_fts, 0, '>>>', '<<<', '...', 48) as snippet,
|
|
136
|
+
m.session_id, rank
|
|
137
|
+
FROM lcm_messages_fts f
|
|
138
|
+
JOIN lcm_messages m ON m.id = f.rowid
|
|
139
|
+
WHERE lcm_messages_fts MATCH ?
|
|
140
|
+
AND m.session_id LIKE ? ESCAPE '\\'
|
|
141
|
+
ORDER BY rank
|
|
142
|
+
LIMIT ?
|
|
143
|
+
`;
|
|
144
|
+
params.push(`${escapeSqlLike(sessionPrefix)}%`, cappedLimit);
|
|
121
145
|
} else {
|
|
122
146
|
sql = `
|
|
123
147
|
SELECT m.turn_index, m.role, snippet(lcm_messages_fts, 0, '>>>', '<<<', '...', 48) as snippet,
|
|
@@ -128,7 +152,7 @@ var LcmArchive = class {
|
|
|
128
152
|
ORDER BY rank
|
|
129
153
|
LIMIT ?
|
|
130
154
|
`;
|
|
131
|
-
params.push(
|
|
155
|
+
params.push(cappedLimit);
|
|
132
156
|
}
|
|
133
157
|
const rows = this.db.prepare(sql).all(...params);
|
|
134
158
|
return rows.map((r) => ({
|
|
@@ -149,10 +173,12 @@ var LcmArchive = class {
|
|
|
149
173
|
* Returns ~1000-char windows centered on query term matches.
|
|
150
174
|
* Deduplicates by message id and returns results sorted by FTS rank.
|
|
151
175
|
*/
|
|
152
|
-
searchWithContent(query, limit, sessionId, excerptChars = 1e3) {
|
|
176
|
+
searchWithContent(query, limit, sessionId, excerptChars = 1e3, sessionPrefix) {
|
|
153
177
|
try {
|
|
154
178
|
const ftsQuery = sanitizeFtsQuery(query);
|
|
155
179
|
if (!ftsQuery) return [];
|
|
180
|
+
const cappedLimit = normalizeSearchLimit(limit);
|
|
181
|
+
if (cappedLimit === 0) return [];
|
|
156
182
|
const queryWords = query.replace(/[^\w\s]/g, " ").split(/\s+/).filter((w) => w.length > 1 && !STOPWORDS.has(w.toLowerCase())).map((w) => w.toLowerCase());
|
|
157
183
|
let sql;
|
|
158
184
|
const params = [ftsQuery];
|
|
@@ -166,7 +192,18 @@ var LcmArchive = class {
|
|
|
166
192
|
ORDER BY rank
|
|
167
193
|
LIMIT ?
|
|
168
194
|
`;
|
|
169
|
-
params.push(sessionId,
|
|
195
|
+
params.push(sessionId, cappedLimit);
|
|
196
|
+
} else if (sessionPrefix) {
|
|
197
|
+
sql = `
|
|
198
|
+
SELECT m.id, m.turn_index, m.role, m.content, m.session_id, rank
|
|
199
|
+
FROM lcm_messages_fts f
|
|
200
|
+
JOIN lcm_messages m ON m.id = f.rowid
|
|
201
|
+
WHERE lcm_messages_fts MATCH ?
|
|
202
|
+
AND m.session_id LIKE ? ESCAPE '\\'
|
|
203
|
+
ORDER BY rank
|
|
204
|
+
LIMIT ?
|
|
205
|
+
`;
|
|
206
|
+
params.push(`${escapeSqlLike(sessionPrefix)}%`, cappedLimit);
|
|
170
207
|
} else {
|
|
171
208
|
sql = `
|
|
172
209
|
SELECT m.id, m.turn_index, m.role, m.content, m.session_id, rank
|
|
@@ -176,7 +213,7 @@ var LcmArchive = class {
|
|
|
176
213
|
ORDER BY rank
|
|
177
214
|
LIMIT ?
|
|
178
215
|
`;
|
|
179
|
-
params.push(
|
|
216
|
+
params.push(cappedLimit);
|
|
180
217
|
}
|
|
181
218
|
const rows = this.db.prepare(sql).all(...params);
|
|
182
219
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -285,11 +322,17 @@ var LcmArchive = class {
|
|
|
285
322
|
/** Prune messages older than retentionDays. */
|
|
286
323
|
pruneOldMessages(retentionDays) {
|
|
287
324
|
const cutoff = new Date(Date.now() - retentionDays * 864e5).toISOString();
|
|
288
|
-
this.db.
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
325
|
+
const txn = this.db.transaction(() => {
|
|
326
|
+
this.db.prepare(
|
|
327
|
+
"DELETE FROM lcm_messages_fts WHERE rowid IN (SELECT id FROM lcm_messages WHERE created_at < ?)"
|
|
328
|
+
).run(cutoff);
|
|
329
|
+
this.db.prepare(
|
|
330
|
+
"DELETE FROM lcm_message_parts WHERE message_id IN (SELECT id FROM lcm_messages WHERE created_at < ?)"
|
|
331
|
+
).run(cutoff);
|
|
332
|
+
const result = this.db.prepare("DELETE FROM lcm_messages WHERE created_at < ?").run(cutoff);
|
|
333
|
+
return result.changes;
|
|
334
|
+
});
|
|
335
|
+
return txn();
|
|
293
336
|
}
|
|
294
337
|
};
|
|
295
338
|
function extractExcerpt(content, queryWords, excerptChars) {
|
|
@@ -503,4 +546,4 @@ export {
|
|
|
503
546
|
estimateTokens,
|
|
504
547
|
LcmArchive
|
|
505
548
|
};
|
|
506
|
-
//# sourceMappingURL=chunk-
|
|
549
|
+
//# sourceMappingURL=chunk-4HP7HIE3.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/lcm/archive.ts"],"sourcesContent":["import type Database from \"better-sqlite3\";\nimport { log } from \"../logger.js\";\nimport {\n parseMessageParts,\n type LcmMessagePartInput,\n type MessagePartSourceFormat,\n} from \"../message-parts/index.js\";\n\nexport interface LcmMessage {\n id: number;\n session_id: string;\n turn_index: number;\n role: string;\n content: string;\n token_count: number;\n created_at: string;\n metadata: string | null;\n}\n\nexport interface LcmSearchResult {\n turn_index: number;\n role: string;\n snippet: string;\n session_id: string;\n score: number;\n}\n\nexport interface LcmSearchWithContentResult {\n id: number;\n turn_index: number;\n role: string;\n content: string;\n session_id: string;\n score: number;\n}\n\nexport interface LcmStructuredRecallMatch {\n part_id: number;\n message_id: number;\n turn_index: number;\n role: string;\n content: string;\n session_id: string;\n kind: string;\n tool_name: string | null;\n file_path: string | null;\n payload: string;\n score: number;\n}\n\n/** Rough token count: ~4 chars per token. */\nexport function estimateTokens(text: string): number {\n return Math.ceil(text.length / 4);\n}\n\nconst PUBLIC_LCM_SEARCH_LIMIT = 100;\n\nfunction normalizeSearchLimit(limit: number, fallback = 10, max = PUBLIC_LCM_SEARCH_LIMIT): number {\n if (typeof limit !== \"number\" || !Number.isFinite(limit)) return fallback;\n const normalized = Math.floor(limit);\n if (normalized <= 0) return 0;\n return Math.min(max, normalized);\n}\n\nfunction escapeSqlLike(value: string): string {\n return value.replace(/[\\\\%_]/g, (match) => `\\\\${match}`);\n}\n\nexport class LcmArchive {\n constructor(private readonly db: Database.Database) {}\n\n /** Append a message to the archive. Returns the row id. */\n appendMessage(\n sessionId: string,\n turnIndex: number,\n role: string,\n content: string,\n metadata?: Record<string, unknown>,\n parts?: LcmMessagePartInput[],\n ): number {\n const tokenCount = estimateTokens(content);\n const now = new Date().toISOString();\n const metaJson = metadata ? JSON.stringify(metadata) : null;\n\n const stmt = this.db.prepare(`\n INSERT INTO lcm_messages (session_id, turn_index, role, content, token_count, created_at, metadata)\n VALUES (?, ?, ?, ?, ?, ?, ?)\n `);\n const result = stmt.run(sessionId, turnIndex, role, content, tokenCount, now, metaJson);\n const rowId = Number(result.lastInsertRowid);\n\n // Keep FTS in sync\n this.db\n .prepare(\"INSERT INTO lcm_messages_fts (rowid, content) VALUES (?, ?)\")\n .run(rowId, content);\n\n if (parts && parts.length > 0) {\n this.insertMessageParts(rowId, parts, now);\n }\n\n return rowId;\n }\n\n /** Append multiple messages in a single transaction. */\n appendMessages(\n sessionId: string,\n messages: Array<{\n turnIndex: number;\n role: string;\n content: string;\n metadata?: Record<string, unknown>;\n parts?: LcmMessagePartInput[];\n rawContent?: unknown;\n sourceFormat?: MessagePartSourceFormat;\n }>,\n options: { messagePartsEnabled?: boolean } = {},\n ): void {\n if (messages.length === 0) return;\n const captureMessageParts = options.messagePartsEnabled !== false;\n\n const txn = this.db.transaction(() => {\n for (const msg of messages) {\n const explicitParts =\n msg.parts && msg.parts.length > 0 ? msg.parts : undefined;\n const rawContent = msg.rawContent ?? msg.content;\n const parts =\n captureMessageParts\n ? explicitParts ??\n parseMessageParts(rawContent, {\n sourceFormat: msg.sourceFormat,\n renderedContent: msg.content,\n })\n : undefined;\n this.appendMessage(\n sessionId,\n msg.turnIndex,\n msg.role,\n msg.content,\n msg.metadata,\n parts,\n );\n }\n });\n txn();\n }\n\n insertMessageParts(\n messageId: number,\n parts: LcmMessagePartInput[],\n fallbackCreatedAt: string,\n ): void {\n if (parts.length === 0) return;\n const stmt = this.db.prepare(`\n INSERT INTO lcm_message_parts (message_id, ordinal, kind, payload, tool_name, file_path, created_at)\n VALUES (?, ?, ?, ?, ?, ?, ?)\n `);\n for (let index = 0; index < parts.length; index += 1) {\n const part = parts[index]!;\n const rawPart = part as unknown as Record<string, unknown>;\n const toolName = part.toolName ?? asNullableString(rawPart.tool_name);\n const filePath = part.filePath ?? asNullableString(rawPart.file_path);\n const createdAt = part.createdAt ?? asNullableString(rawPart.created_at);\n stmt.run(\n messageId,\n part.ordinal ?? index,\n part.kind,\n JSON.stringify(part.payload ?? {}),\n toolName ?? null,\n filePath ?? null,\n createdAt ?? fallbackCreatedAt,\n );\n }\n }\n\n /** Get the highest turn_index for a session, or -1 if none. */\n getMaxTurnIndex(sessionId: string): number {\n const row = this.db\n .prepare(\"SELECT MAX(turn_index) as max_turn FROM lcm_messages WHERE session_id = ?\")\n .get(sessionId) as { max_turn: number | null } | undefined;\n return row?.max_turn ?? -1;\n }\n\n /** Retrieve messages in a turn range (inclusive). */\n getMessages(sessionId: string, fromTurn: number, toTurn: number): LcmMessage[] {\n return this.db\n .prepare(\n \"SELECT * FROM lcm_messages WHERE session_id = ? AND turn_index >= ? AND turn_index <= ? ORDER BY turn_index\",\n )\n .all(sessionId, fromTurn, toTurn) as LcmMessage[];\n }\n\n /** Retrieve unsummarized messages (after last leaf summary). */\n getUnsummarizedMessages(sessionId: string): LcmMessage[] {\n const lastLeafEnd = this.db\n .prepare(\n \"SELECT MAX(msg_end) as last_end FROM lcm_summary_nodes WHERE session_id = ? AND depth = 0\",\n )\n .get(sessionId) as { last_end: number | null } | undefined;\n\n const lastSummarized = lastLeafEnd?.last_end ?? -1;\n return this.db\n .prepare(\n \"SELECT * FROM lcm_messages WHERE session_id = ? AND turn_index > ? ORDER BY turn_index\",\n )\n .all(sessionId, lastSummarized) as LcmMessage[];\n }\n\n /** Full-text search across all messages. */\n search(query: string, limit: number, sessionId?: string, sessionPrefix?: string): LcmSearchResult[] {\n try {\n const ftsQuery = sanitizeFtsQuery(query);\n if (!ftsQuery) return [];\n const cappedLimit = normalizeSearchLimit(limit);\n if (cappedLimit === 0) return [];\n\n let sql: string;\n const params: unknown[] = [ftsQuery];\n\n if (sessionId) {\n sql = `\n SELECT m.turn_index, m.role, snippet(lcm_messages_fts, 0, '>>>', '<<<', '...', 48) as snippet,\n m.session_id, rank\n FROM lcm_messages_fts f\n JOIN lcm_messages m ON m.id = f.rowid\n WHERE lcm_messages_fts MATCH ?\n AND m.session_id = ?\n ORDER BY rank\n LIMIT ?\n `;\n params.push(sessionId, cappedLimit);\n } else if (sessionPrefix) {\n sql = `\n SELECT m.turn_index, m.role, snippet(lcm_messages_fts, 0, '>>>', '<<<', '...', 48) as snippet,\n m.session_id, rank\n FROM lcm_messages_fts f\n JOIN lcm_messages m ON m.id = f.rowid\n WHERE lcm_messages_fts MATCH ?\n AND m.session_id LIKE ? ESCAPE '\\\\'\n ORDER BY rank\n LIMIT ?\n `;\n params.push(`${escapeSqlLike(sessionPrefix)}%`, cappedLimit);\n } else {\n sql = `\n SELECT m.turn_index, m.role, snippet(lcm_messages_fts, 0, '>>>', '<<<', '...', 48) as snippet,\n m.session_id, rank\n FROM lcm_messages_fts f\n JOIN lcm_messages m ON m.id = f.rowid\n WHERE lcm_messages_fts MATCH ?\n ORDER BY rank\n LIMIT ?\n `;\n params.push(cappedLimit);\n }\n\n const rows = this.db.prepare(sql).all(...params) as Array<{\n turn_index: number;\n role: string;\n snippet: string;\n session_id: string;\n rank: number;\n }>;\n\n return rows.map((r) => ({\n turn_index: r.turn_index,\n role: r.role,\n snippet: r.snippet,\n session_id: r.session_id,\n score: -r.rank, // FTS5 rank is negative; negate for ascending score\n }));\n } catch (err) {\n log.debug(`LCM FTS search error: ${err}`);\n return [];\n }\n }\n\n /**\n * Full-text search returning focused excerpts around matching terms.\n * Returns ~1000-char windows centered on query term matches.\n * Deduplicates by message id and returns results sorted by FTS rank.\n */\n searchWithContent(query: string, limit: number, sessionId?: string, excerptChars = 1000, sessionPrefix?: string): LcmSearchWithContentResult[] {\n try {\n const ftsQuery = sanitizeFtsQuery(query);\n if (!ftsQuery) return [];\n const cappedLimit = normalizeSearchLimit(limit);\n if (cappedLimit === 0) return [];\n\n // Extract content words from query for excerpt windowing\n const queryWords = query\n .replace(/[^\\w\\s]/g, \" \")\n .split(/\\s+/)\n .filter((w) => w.length > 1 && !STOPWORDS.has(w.toLowerCase()))\n .map((w) => w.toLowerCase());\n\n let sql: string;\n const params: unknown[] = [ftsQuery];\n\n if (sessionId) {\n sql = `\n SELECT m.id, m.turn_index, m.role, m.content, m.session_id, rank\n FROM lcm_messages_fts f\n JOIN lcm_messages m ON m.id = f.rowid\n WHERE lcm_messages_fts MATCH ?\n AND m.session_id = ?\n ORDER BY rank\n LIMIT ?\n `;\n params.push(sessionId, cappedLimit);\n } else if (sessionPrefix) {\n sql = `\n SELECT m.id, m.turn_index, m.role, m.content, m.session_id, rank\n FROM lcm_messages_fts f\n JOIN lcm_messages m ON m.id = f.rowid\n WHERE lcm_messages_fts MATCH ?\n AND m.session_id LIKE ? ESCAPE '\\\\'\n ORDER BY rank\n LIMIT ?\n `;\n params.push(`${escapeSqlLike(sessionPrefix)}%`, cappedLimit);\n } else {\n sql = `\n SELECT m.id, m.turn_index, m.role, m.content, m.session_id, rank\n FROM lcm_messages_fts f\n JOIN lcm_messages m ON m.id = f.rowid\n WHERE lcm_messages_fts MATCH ?\n ORDER BY rank\n LIMIT ?\n `;\n params.push(cappedLimit);\n }\n\n const rows = this.db.prepare(sql).all(...params) as Array<{\n id: number;\n turn_index: number;\n role: string;\n content: string;\n session_id: string;\n rank: number;\n }>;\n\n // Deduplicate by message id (same message may match multiple terms)\n const seen = new Set<number>();\n const results: LcmSearchWithContentResult[] = [];\n for (const r of rows) {\n if (seen.has(r.id)) continue;\n seen.add(r.id);\n results.push({\n id: r.id,\n turn_index: r.turn_index,\n role: r.role,\n content: extractExcerpt(r.content, queryWords, excerptChars),\n session_id: r.session_id,\n score: -r.rank,\n });\n }\n return results;\n } catch (err) {\n log.debug(`LCM FTS searchWithContent error: ${err}`);\n return [];\n }\n }\n\n searchStructuredParts(\n query: string,\n limit: number,\n sessionId?: string,\n ): LcmStructuredRecallMatch[] {\n const cappedLimit = Math.max(0, Math.min(20, Math.floor(limit)));\n if (cappedLimit === 0) return [];\n\n const fileTerms = extractStructuredFileTerms(query);\n const toolTerms = extractStructuredToolTerms(query);\n if (fileTerms.length === 0 && toolTerms.length === 0) return [];\n\n const matchWhere: string[] = [];\n const whereParams: unknown[] = [];\n for (const term of fileTerms) {\n matchWhere.push(\"(p.file_path = ? OR p.file_path LIKE ? ESCAPE '\\\\')\");\n whereParams.push(term, `%${escapeLike(term)}%`);\n }\n for (const term of toolTerms) {\n matchWhere.push(\"p.tool_name LIKE ? ESCAPE '\\\\'\");\n whereParams.push(`%${escapeLike(term)}%`);\n }\n const where = [`(${matchWhere.join(\" OR \")})`];\n if (sessionId) {\n where.push(\"m.session_id = ?\");\n whereParams.push(sessionId);\n }\n const exactFileScoreParams = [...fileTerms];\n const sqlParams = [...exactFileScoreParams, ...whereParams, cappedLimit];\n\n const rows = this.db.prepare(`\n SELECT\n p.id AS part_id,\n p.message_id AS message_id,\n m.turn_index AS turn_index,\n m.role AS role,\n m.content AS content,\n m.session_id AS session_id,\n p.kind AS kind,\n p.tool_name AS tool_name,\n p.file_path AS file_path,\n p.payload AS payload,\n CASE\n WHEN p.file_path IN (${fileTerms.map(() => \"?\").join(\",\") || \"NULL\"}) THEN 3\n WHEN p.file_path IS NOT NULL THEN 2\n WHEN p.tool_name IS NOT NULL THEN 1\n ELSE 0\n END AS score\n FROM lcm_message_parts p\n JOIN lcm_messages m ON m.id = p.message_id\n WHERE ${where.join(\" AND \")}\n ORDER BY score DESC, m.turn_index DESC, p.ordinal ASC\n LIMIT ?\n `).all(...sqlParams) as LcmStructuredRecallMatch[];\n\n return rows;\n }\n\n /** Get total message count for a session. */\n getMessageCount(sessionId: string): number {\n const row = this.db\n .prepare(\"SELECT COUNT(*) as cnt FROM lcm_messages WHERE session_id = ?\")\n .get(sessionId) as { cnt: number };\n return row.cnt;\n }\n\n /** Get total message count across all sessions. */\n getTotalMessageCount(): number {\n const row = this.db\n .prepare(\"SELECT COUNT(*) as cnt FROM lcm_messages\")\n .get() as { cnt: number };\n return row.cnt;\n }\n\n /** Delete all archived messages for one session. */\n deleteSession(sessionId: string): number {\n const txn = this.db.transaction(() => {\n this.db\n .prepare(\n \"DELETE FROM lcm_messages_fts WHERE rowid IN (SELECT id FROM lcm_messages WHERE session_id = ?)\",\n )\n .run(sessionId);\n this.db\n .prepare(\n \"DELETE FROM lcm_message_parts WHERE message_id IN (SELECT id FROM lcm_messages WHERE session_id = ?)\",\n )\n .run(sessionId);\n const result = this.db\n .prepare(\"DELETE FROM lcm_messages WHERE session_id = ?\")\n .run(sessionId);\n return result.changes;\n });\n return txn();\n }\n\n /** Delete all archived messages. */\n deleteAll(): number {\n const txn = this.db.transaction(() => {\n this.db.prepare(\"DELETE FROM lcm_messages_fts\").run();\n this.db.prepare(\"DELETE FROM lcm_message_parts\").run();\n const result = this.db.prepare(\"DELETE FROM lcm_messages\").run();\n return result.changes;\n });\n return txn();\n }\n\n /** Prune messages older than retentionDays. */\n pruneOldMessages(retentionDays: number): number {\n const cutoff = new Date(Date.now() - retentionDays * 86400_000).toISOString();\n\n const txn = this.db.transaction(() => {\n // Delete from FTS and child tables first. Some handles may not have\n // foreign_keys enabled, so do not rely on ON DELETE CASCADE for parts.\n this.db\n .prepare(\n \"DELETE FROM lcm_messages_fts WHERE rowid IN (SELECT id FROM lcm_messages WHERE created_at < ?)\",\n )\n .run(cutoff);\n this.db\n .prepare(\n \"DELETE FROM lcm_message_parts WHERE message_id IN (SELECT id FROM lcm_messages WHERE created_at < ?)\",\n )\n .run(cutoff);\n\n const result = this.db\n .prepare(\"DELETE FROM lcm_messages WHERE created_at < ?\")\n .run(cutoff);\n return result.changes;\n });\n return txn();\n }\n}\n\n/**\n * Extract a focused excerpt from content centered on query term matches.\n * Returns a window of ~excerptChars around the first matching term.\n * If content is shorter than excerptChars, returns the full content.\n */\nfunction extractExcerpt(content: string, queryWords: string[], excerptChars: number): string {\n if (content.length <= excerptChars) return content;\n\n // Find the earliest position of any query word in the content\n const contentLower = content.toLowerCase();\n let bestPos = -1;\n for (const word of queryWords) {\n const pos = contentLower.indexOf(word);\n if (pos !== -1 && (bestPos === -1 || pos < bestPos)) {\n bestPos = pos;\n }\n }\n\n // If no match found (shouldn't happen for FTS results), return start\n if (bestPos === -1) {\n return content.slice(0, excerptChars) + \"...\";\n }\n\n // Center the window around the match\n const halfWindow = Math.floor(excerptChars / 2);\n let start = Math.max(0, bestPos - halfWindow);\n let end = Math.min(content.length, start + excerptChars);\n\n // Adjust start if we hit the end\n if (end === content.length) {\n start = Math.max(0, end - excerptChars);\n }\n\n // Extend to sentence boundaries if possible\n if (start > 0) {\n const sentenceStart = content.lastIndexOf(\". \", start);\n if (sentenceStart !== -1 && start - sentenceStart < 200) {\n start = sentenceStart + 2;\n }\n }\n if (end < content.length) {\n const sentenceEnd = content.indexOf(\". \", end - 1);\n if (sentenceEnd !== -1 && sentenceEnd - end < 200) {\n end = sentenceEnd + 1;\n }\n }\n\n const prefix = start > 0 ? \"...\" : \"\";\n const suffix = end < content.length ? \"...\" : \"\";\n return prefix + content.slice(start, end) + suffix;\n}\n\nconst STOPWORDS = new Set([\n \"a\", \"an\", \"the\", \"is\", \"are\", \"was\", \"were\", \"be\", \"been\", \"being\",\n \"have\", \"has\", \"had\", \"do\", \"does\", \"did\", \"will\", \"would\", \"could\",\n \"should\", \"may\", \"might\", \"shall\", \"can\", \"to\", \"of\", \"in\", \"for\",\n \"on\", \"with\", \"at\", \"by\", \"from\", \"as\", \"into\", \"about\", \"between\",\n \"through\", \"during\", \"before\", \"after\", \"and\", \"but\", \"or\", \"nor\",\n \"not\", \"so\", \"if\", \"then\", \"than\", \"that\", \"this\", \"it\", \"its\",\n \"what\", \"which\", \"who\", \"whom\", \"how\", \"when\", \"where\", \"why\",\n \"all\", \"each\", \"every\", \"both\", \"few\", \"more\", \"most\", \"other\",\n \"some\", \"such\", \"no\", \"only\", \"own\", \"same\", \"just\", \"very\",\n \"my\", \"your\", \"his\", \"her\", \"our\", \"their\", \"me\", \"him\", \"us\", \"them\",\n \"i\", \"you\", \"he\", \"she\", \"we\", \"they\",\n]);\n\nfunction extractStructuredFileTerms(query: string): string[] {\n const terms = new Set<string>();\n for (const raw of splitQueryTerms(query)) {\n const cleaned = trimStructuredQueryTerm(raw);\n if (\n cleaned.includes(\"/\") ||\n hasStructuredFileExtension(cleaned)\n ) {\n terms.add(cleaned);\n const basename = cleaned.split(\"/\").pop();\n if (basename && basename !== cleaned) terms.add(basename);\n }\n }\n return [...terms].filter((term) => term.length > 1).slice(0, 12);\n}\n\nfunction splitQueryTerms(query: string): string[] {\n const terms: string[] = [];\n let term = \"\";\n for (const char of query.slice(0, 20_000)) {\n if (char === \" \" || char === \"\\n\" || char === \"\\r\" || char === \"\\t\") {\n if (term.length > 0) terms.push(term);\n term = \"\";\n continue;\n }\n term += char;\n if (term.length > 512) {\n terms.push(term);\n term = \"\";\n }\n }\n if (term.length > 0) terms.push(term);\n return terms;\n}\n\nfunction trimStructuredQueryTerm(raw: string): string {\n const leading = new Set([\"`\", \"'\", \"\\\"\", \"(\", \"[\", \"{\"]);\n const trailing = new Set([\"`\", \"'\", \"\\\"\", \",\", \".\", \"?\", \"!\", \":\", \";\", \")\", \"]\", \"}\"]);\n let start = 0;\n let end = raw.length;\n while (start < end && leading.has(raw[start]!)) start += 1;\n while (end > start && trailing.has(raw[end - 1]!)) end -= 1;\n return raw.slice(start, end);\n}\n\nfunction hasStructuredFileExtension(value: string): boolean {\n const slash = value.lastIndexOf(\"/\");\n const basename = value.slice(slash + 1);\n const dot = basename.lastIndexOf(\".\");\n if (dot <= 0 || dot === basename.length - 1) return false;\n const ext = basename.slice(dot + 1);\n if (ext.length < 1 || ext.length > 12) return false;\n for (const char of ext) {\n const code = char.charCodeAt(0);\n const valid =\n (code >= 48 && code <= 57) ||\n (code >= 65 && code <= 90) ||\n (code >= 97 && code <= 122) ||\n char === \"_\" ||\n char === \"+\" ||\n char === \"-\";\n if (!valid) return false;\n }\n return true;\n}\n\nfunction extractStructuredToolTerms(query: string): string[] {\n const lower = query.toLowerCase();\n if (!/\\b(tool|command|invocation|called|used|ran|read|write|patch|edit|grep|search)\\b/.test(lower)) {\n return [];\n }\n return query\n .replace(/[^\\w.-]/g, \" \")\n .split(/\\s+/)\n .filter((term) => term.length > 2 && !STOPWORDS.has(term.toLowerCase()))\n .slice(0, 8);\n}\n\nfunction escapeLike(value: string): string {\n return value.replace(/[\\\\%_]/g, (char) => `\\\\${char}`);\n}\n\nfunction asNullableString(value: unknown): string | null {\n return typeof value === \"string\" && value.trim().length > 0 ? value : null;\n}\n\n/**\n * Sanitize a query for FTS5 MATCH.\n * Uses OR logic so partial matches rank higher than no matches.\n * Filters stopwords to focus on content words.\n */\nfunction sanitizeFtsQuery(raw: string): string {\n const words = raw\n .replace(/[^\\w\\s]/g, \" \")\n .split(/\\s+/)\n .filter((w) => w.length > 1 && !STOPWORDS.has(w.toLowerCase()));\n if (words.length === 0) {\n // If all words were stopwords, fall back to using all words\n const allWords = raw.replace(/[^\\w\\s]/g, \" \").split(/\\s+/).filter((w) => w.length > 1);\n if (allWords.length === 0) return \"\";\n return allWords.map((w) => `\"${w}\"`).join(\" OR \");\n }\n return words.map((w) => `\"${w}\"`).join(\" OR \");\n}\n"],"mappings":"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","names":[]}
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
} from "./chunk-2LSZVONP.js";
|
|
4
4
|
import {
|
|
5
5
|
emitGraphEvent
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-DEUNUKTD.js";
|
|
7
7
|
|
|
8
8
|
// src/graph.ts
|
|
9
9
|
import { mkdir, appendFile, readFile } from "fs/promises";
|
|
@@ -59,13 +59,16 @@ async function appendEdge(memoryDir, edge) {
|
|
|
59
59
|
function isNodeError(err) {
|
|
60
60
|
return typeof err === "object" && err !== null && "code" in err;
|
|
61
61
|
}
|
|
62
|
-
function parseEdgesJsonl(raw) {
|
|
62
|
+
function parseEdgesJsonl(raw, expectedType) {
|
|
63
63
|
const edges = [];
|
|
64
64
|
for (const line of raw.split("\n")) {
|
|
65
65
|
const trimmed = line.trim();
|
|
66
66
|
if (!trimmed) continue;
|
|
67
67
|
try {
|
|
68
|
-
|
|
68
|
+
const parsed = JSON.parse(trimmed);
|
|
69
|
+
if (isValidGraphEdge(parsed, expectedType)) {
|
|
70
|
+
edges.push(parsed);
|
|
71
|
+
}
|
|
69
72
|
} catch {
|
|
70
73
|
}
|
|
71
74
|
}
|
|
@@ -75,7 +78,7 @@ async function readEdges(memoryDir, type) {
|
|
|
75
78
|
const filePath = graphFilePath(memoryDir, type);
|
|
76
79
|
try {
|
|
77
80
|
const raw = await readFile(filePath, "utf8");
|
|
78
|
-
return parseEdgesJsonl(raw);
|
|
81
|
+
return parseEdgesJsonl(raw, type);
|
|
79
82
|
} catch {
|
|
80
83
|
return [];
|
|
81
84
|
}
|
|
@@ -84,7 +87,7 @@ async function readEdgesStrict(memoryDir, type) {
|
|
|
84
87
|
const filePath = graphFilePath(memoryDir, type);
|
|
85
88
|
try {
|
|
86
89
|
const raw = await readFile(filePath, "utf8");
|
|
87
|
-
return parseEdgesJsonl(raw);
|
|
90
|
+
return parseEdgesJsonl(raw, type);
|
|
88
91
|
} catch (err) {
|
|
89
92
|
if (isNodeError(err) && err.code === "ENOENT") {
|
|
90
93
|
return [];
|
|
@@ -489,4 +492,4 @@ export {
|
|
|
489
492
|
applyPageRankRefinement,
|
|
490
493
|
applyLateralInhibition
|
|
491
494
|
};
|
|
492
|
-
//# sourceMappingURL=chunk-
|
|
495
|
+
//# sourceMappingURL=chunk-4JRRISLU.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/graph.ts"],"sourcesContent":["/**\n * Multi-Graph Memory (MAGMA/SYNAPSE-inspired, v8.2)\n *\n * Maintains three typed edge graphs:\n * entity.jsonl — memories sharing a named entity (entityRef)\n * time.jsonl — consecutive memories in the same thread/session\n * causal.jsonl — memories linked by causal language heuristics\n *\n * Stored under `<memoryDir>/state/graphs/`.\n * All writes are fail-open: errors are caught/logged, never thrown.\n */\n\nimport { mkdir, appendFile, readFile } from \"node:fs/promises\";\nimport * as path from \"path\";\n\nimport { readEdgeConfidence } from \"./graph-edge-reinforcement.js\";\nimport { emitGraphEvent } from \"./graph-events.js\";\n\nexport type GraphType = \"entity\" | \"time\" | \"causal\";\n\nexport interface GraphEdge {\n from: string; // relative memory path (e.g. \"facts/2026-02-22/abc.md\")\n to: string; // relative memory path\n type: GraphType;\n weight: number; // 1.0 default, decay applied during traversal\n label: string; // entity name, threadId, or matched causal phrase\n ts: string; // ISO timestamp of edge creation\n\n // Issue #681 — edge confidence + reinforcement (PR 1/3: schema + primitive only).\n // Both fields are optional so existing edges without confidence still validate.\n // Treat a missing `confidence` as 1.0 (legacy behavior) at read sites.\n // PR 2/3 wires the maintenance decay job; PR 3/3 weights PageRank traversal by confidence.\n confidence?: number; // [0, 1]; missing = 1.0\n lastReinforcedAt?: string; // ISO timestamp of most recent reinforcement\n}\n\nexport interface GraphConfig {\n multiGraphMemoryEnabled: boolean;\n entityGraphEnabled: boolean;\n timeGraphEnabled: boolean;\n causalGraphEnabled: boolean;\n maxGraphTraversalSteps: number;\n graphActivationDecay: number;\n maxEntityGraphEdgesPerMemory: number;\n graphLateralInhibitionEnabled: boolean;\n graphLateralInhibitionBeta: number;\n graphLateralInhibitionTopM: number;\n /**\n * Issue #681 PR 3/3 — minimum edge confidence required for traversal.\n * Edges with confidence below this floor are pruned. Legacy edges\n * (no `confidence` field) are treated as 1.0 and always pass.\n * Range `[0, 1]`. Default 0.2.\n */\n graphTraversalConfidenceFloor: number;\n /**\n * Issue #681 PR 3/3 — number of PageRank-style refinement iterations\n * applied on top of BFS activation. Set to 0 to disable refinement\n * and return raw BFS scores. Default 8.\n */\n graphTraversalPageRankIterations: number;\n}\n\n/** Default minimum edge confidence required for traversal (issue #681 PR 3/3). */\nexport const DEFAULT_GRAPH_TRAVERSAL_CONFIDENCE_FLOOR = 0.2;\n/** Default PageRank-style refinement iteration count (issue #681 PR 3/3). */\nexport const DEFAULT_GRAPH_TRAVERSAL_PAGERANK_ITERATIONS = 8;\n\n// Causal signal phrases — order matters (most specific first)\nexport const CAUSAL_PHRASES = [\n \"as a result\",\n \"led to\",\n \"because of\",\n \"therefore\",\n \"caused\",\n \"because\",\n];\n\nexport function graphsDir(memoryDir: string): string {\n return path.join(memoryDir, \"state\", \"graphs\");\n}\n\nexport function graphFilePath(memoryDir: string, type: GraphType): string {\n return path.join(graphsDir(memoryDir), `${type}.jsonl`);\n}\n\nexport async function ensureGraphsDir(memoryDir: string): Promise<void> {\n await mkdir(graphsDir(memoryDir), { recursive: true });\n}\n\n// ---------------------------------------------------------------------------\n// Per-graph-file write lock (gotcha #40 promise-chain pattern).\n//\n// Both the append path (`appendEdge`) and the rewrite path used by the\n// decay maintenance job must serialize on the same lock keyed by the\n// JSONL file path. Without this, an extraction can append a new edge\n// between the decay job's read-snapshot and rewrite, silently dropping\n// the appended edge during active traffic (issue #729 / Codex P1).\n// ---------------------------------------------------------------------------\nconst graphWriteLocks = new Map<string, Promise<void>>();\n\n/**\n * Run `fn` while holding the write lock for the given graph JSONL file.\n *\n * The lock is keyed by absolute file path so concurrent writes to\n * different graph types proceed independently. The chain recovers from\n * rejection (gotcha #40) so a single I/O failure does not poison all\n * future writers, but the original error is still surfaced to the\n * caller of `withGraphWriteLock`.\n */\nexport function withGraphWriteLock<T>(filePath: string, fn: () => Promise<T>): Promise<T> {\n const prev = graphWriteLocks.get(filePath) ?? Promise.resolve();\n const next = prev.then(fn, fn);\n graphWriteLocks.set(\n filePath,\n next.then(\n () => undefined,\n () => undefined,\n ),\n );\n return next;\n}\n\nexport async function appendEdge(memoryDir: string, edge: GraphEdge): Promise<void> {\n await ensureGraphsDir(memoryDir);\n const filePath = graphFilePath(memoryDir, edge.type);\n const line = JSON.stringify(edge) + \"\\n\";\n await withGraphWriteLock(filePath, async () => {\n await appendFile(filePath, line, \"utf8\");\n });\n // Emit edge-added event for SSE subscribers (issue #691 PR 5/5).\n // Fail-open: emitGraphEvent catches listener errors so a bad SSE client\n // can never surface into the extraction pipeline.\n emitGraphEvent(memoryDir, \"edge-added\", {\n source: edge.from,\n target: edge.to,\n kind: edge.type,\n weight: edge.weight,\n label: edge.label,\n confidence: typeof edge.confidence === \"number\" ? edge.confidence : 1.0,\n });\n}\n\nfunction isNodeError(err: unknown): err is NodeJS.ErrnoException {\n return typeof err === \"object\" && err !== null && \"code\" in err;\n}\n\nfunction parseEdgesJsonl(raw: string, expectedType: GraphType): GraphEdge[] {\n const edges: GraphEdge[] = [];\n for (const line of raw.split(\"\\n\")) {\n const trimmed = line.trim();\n if (!trimmed) continue;\n try {\n const parsed = JSON.parse(trimmed) as unknown;\n if (isValidGraphEdge(parsed, expectedType)) {\n edges.push(parsed);\n }\n } catch {\n // skip corrupt lines — fail-open for partial JSONL recovery\n }\n }\n return edges;\n}\n\n/**\n * Read all edges of a given type from the JSONL file.\n * Returns [] if the file doesn't exist or any read error occurs (fail-open).\n *\n * Production traversal callers (recall/PageRank) depend on this fail-open\n * posture so a temporarily missing or unreadable graph file never blocks\n * a recall. Maintenance jobs that need to distinguish ENOENT from real\n * I/O failures must use {@link readEdgesStrict} instead.\n */\nexport async function readEdges(memoryDir: string, type: GraphType): Promise<GraphEdge[]> {\n const filePath = graphFilePath(memoryDir, type);\n try {\n const raw = await readFile(filePath, \"utf8\");\n return parseEdgesJsonl(raw, type);\n } catch {\n return [];\n }\n}\n\n/**\n * Same as {@link readEdges} but only swallows `ENOENT`; all other read\n * errors (`EACCES`, `EIO`, …) are propagated. Used by the graph-edge\n * decay maintenance job so I/O outages surface as a failed run instead\n * of being silently reported as \"no edges to decay\" (issue #729 /\n * Codex P1, line 120).\n */\nexport async function readEdgesStrict(memoryDir: string, type: GraphType): Promise<GraphEdge[]> {\n const filePath = graphFilePath(memoryDir, type);\n try {\n const raw = await readFile(filePath, \"utf8\");\n return parseEdgesJsonl(raw, type);\n } catch (err) {\n if (isNodeError(err) && err.code === \"ENOENT\") {\n return [];\n }\n throw err;\n }\n}\n\n/**\n * Read edges from all enabled graph types.\n */\nexport async function readAllEdges(\n memoryDir: string,\n config: Pick<GraphConfig, \"entityGraphEnabled\" | \"timeGraphEnabled\" | \"causalGraphEnabled\">,\n): Promise<GraphEdge[]> {\n const parts: GraphEdge[][] = await Promise.all([\n config.entityGraphEnabled ? readEdges(memoryDir, \"entity\") : Promise.resolve([]),\n config.timeGraphEnabled ? readEdges(memoryDir, \"time\") : Promise.resolve([]),\n config.causalGraphEnabled ? readEdges(memoryDir, \"causal\") : Promise.resolve([]),\n ]);\n return parts.flat();\n}\n\nexport interface GraphHealthFileStats {\n type: GraphType;\n filePath: string;\n exists: boolean;\n totalLines: number;\n validEdges: number;\n corruptLines: number;\n uniqueNodes: number;\n}\n\nexport interface GraphHealthReport {\n generatedAt: string;\n enabledTypes: GraphType[];\n totals: {\n totalLines: number;\n validEdges: number;\n corruptLines: number;\n uniqueNodes: number;\n };\n files: GraphHealthFileStats[];\n repairGuidance?: string[];\n}\n\nfunction isValidGraphEdge(raw: unknown, expectedType: GraphType): raw is GraphEdge {\n if (!raw || typeof raw !== \"object\") return false;\n const edge = raw as Record<string, unknown>;\n return (\n edge.type === expectedType &&\n typeof edge.from === \"string\" && edge.from.length > 0 &&\n typeof edge.to === \"string\" && edge.to.length > 0 &&\n typeof edge.weight === \"number\" && Number.isFinite(edge.weight) &&\n typeof edge.label === \"string\" &&\n typeof edge.ts === \"string\"\n );\n}\n\nexport async function analyzeGraphHealth(\n memoryDir: string,\n options?: {\n entityGraphEnabled?: boolean;\n timeGraphEnabled?: boolean;\n causalGraphEnabled?: boolean;\n includeRepairGuidance?: boolean;\n },\n): Promise<GraphHealthReport> {\n const enabledTypes: GraphType[] = [];\n if (options?.entityGraphEnabled !== false) enabledTypes.push(\"entity\");\n if (options?.timeGraphEnabled !== false) enabledTypes.push(\"time\");\n if (options?.causalGraphEnabled !== false) enabledTypes.push(\"causal\");\n\n const files: GraphHealthFileStats[] = [];\n const globalNodes = new Set<string>();\n\n for (const type of enabledTypes) {\n const filePath = graphFilePath(memoryDir, type);\n let exists = true;\n let totalLines = 0;\n let validEdges = 0;\n let corruptLines = 0;\n const nodes = new Set<string>();\n\n try {\n const raw = await readFile(filePath, \"utf8\");\n for (const line of raw.split(\"\\n\")) {\n const trimmed = line.trim();\n if (!trimmed) continue;\n totalLines += 1;\n try {\n const parsed = JSON.parse(trimmed) as unknown;\n if (!isValidGraphEdge(parsed, type)) {\n corruptLines += 1;\n continue;\n }\n validEdges += 1;\n nodes.add(parsed.from);\n nodes.add(parsed.to);\n globalNodes.add(parsed.from);\n globalNodes.add(parsed.to);\n } catch {\n corruptLines += 1;\n }\n }\n } catch {\n exists = false;\n }\n\n files.push({\n type,\n filePath,\n exists,\n totalLines,\n validEdges,\n corruptLines,\n uniqueNodes: nodes.size,\n });\n }\n\n const totals = files.reduce(\n (acc, item) => {\n acc.totalLines += item.totalLines;\n acc.validEdges += item.validEdges;\n acc.corruptLines += item.corruptLines;\n return acc;\n },\n {\n totalLines: 0,\n validEdges: 0,\n corruptLines: 0,\n uniqueNodes: globalNodes.size,\n },\n );\n totals.uniqueNodes = globalNodes.size;\n\n const report: GraphHealthReport = {\n generatedAt: new Date().toISOString(),\n enabledTypes,\n totals,\n files,\n };\n\n if (options?.includeRepairGuidance === true) {\n const guidance: string[] = [];\n if (totals.corruptLines > 0) {\n guidance.push(\"Corrupt graph lines detected: back up memory/state/graphs, then rebuild graphs from clean memory replay/extraction runs.\");\n }\n if (totals.validEdges === 0) {\n guidance.push(\"No valid edges detected yet: run normal extraction traffic (or replay ingestion) to seed graph files.\");\n }\n if (guidance.length > 0) report.repairGuidance = guidance;\n }\n\n return report;\n}\n\n/**\n * Detect causal signal phrases in text. Returns the first matched phrase, or null.\n */\nexport function detectCausalPhrase(text: string): string | null {\n const lower = text.toLowerCase();\n for (const phrase of CAUSAL_PHRASES) {\n if (lower.includes(phrase)) return phrase;\n }\n return null;\n}\n\n/**\n * GraphIndex — builds and updates the three memory graphs.\n *\n * Usage (orchestrator):\n * this.graphIndex = new GraphIndex(config.memoryDir, config);\n *\n * // After each memory write:\n * await this.graphIndex.onMemoryWritten(memoryPath, frontmatter, threadId, recentInThread);\n */\nexport class GraphIndex {\n private readonly memoryDir: string;\n private readonly cfg: GraphConfig;\n\n // Cache for readAllEdges() result. With 30k+ entity edges (6 MB JSONL) the\n // file read + JSON parse takes 2-4 s per call. This instance-level cache\n // eliminates that overhead on every spreadingActivation() call; it is\n // invalidated (set to null) in onMemoryWritten() so new edges appear promptly.\n private edgeCache: { allEdges: GraphEdge[]; loadedAt: number } | null = null;\n private static readonly EDGE_CACHE_TTL_MS = 300_000; // 5 minutes\n\n constructor(memoryDir: string, cfg: GraphConfig) {\n this.memoryDir = memoryDir;\n this.cfg = cfg;\n }\n\n /** Clear the edge cache so the next spreadingActivation() re-reads from disk.\n * Call after any code path that appends edges outside of onMemoryWritten(). */\n invalidateEdgeCache(): void {\n this.edgeCache = null;\n }\n\n private async loadEdgesCached(): Promise<GraphEdge[]> {\n if (\n this.edgeCache &&\n Date.now() - this.edgeCache.loadedAt < GraphIndex.EDGE_CACHE_TTL_MS\n ) {\n return this.edgeCache.allEdges;\n }\n const allEdges = await readAllEdges(this.memoryDir, {\n entityGraphEnabled: this.cfg.entityGraphEnabled,\n timeGraphEnabled: this.cfg.timeGraphEnabled,\n causalGraphEnabled: this.cfg.causalGraphEnabled,\n });\n this.edgeCache = { allEdges, loadedAt: Date.now() };\n return allEdges;\n }\n\n /**\n * Called after a memory is written to disk.\n *\n * @param memoryPath - relative path from memoryDir (e.g. \"facts/2026-02-22/abc.md\")\n * @param entityRef - entityRef frontmatter field (if any)\n * @param content - full memory text (for causal detection)\n * @param created - ISO timestamp of this memory\n * @param threadId - current thread ID (for time graph)\n * @param recentInThread - paths of the N most-recent memories in this thread (for time graph)\n * @param entitySiblings - paths of other memories that share the same entityRef (for entity graph)\n */\n async onMemoryWritten(opts: {\n memoryPath: string;\n entityRef?: string;\n content: string;\n created: string;\n threadId?: string;\n recentInThread?: string[];\n entitySiblings?: string[];\n causalPredecessor?: string;\n }): Promise<void> {\n if (!this.cfg.multiGraphMemoryEnabled) return;\n const ts = new Date().toISOString();\n\n try {\n // Entity graph\n if (this.cfg.entityGraphEnabled && opts.entityRef && opts.entitySiblings?.length) {\n const siblings = opts.entitySiblings.slice(0, this.cfg.maxEntityGraphEdgesPerMemory);\n for (const sibling of siblings) {\n await appendEdge(this.memoryDir, {\n from: opts.memoryPath,\n to: sibling,\n type: \"entity\",\n weight: 1.0,\n label: opts.entityRef,\n ts,\n });\n }\n }\n\n // Time graph — link to most recent memory in same thread\n if (this.cfg.timeGraphEnabled && opts.threadId && opts.recentInThread?.length) {\n const predecessor = opts.recentInThread[opts.recentInThread.length - 1];\n if (predecessor && predecessor !== opts.memoryPath) {\n await appendEdge(this.memoryDir, {\n from: predecessor,\n to: opts.memoryPath,\n type: \"time\",\n weight: 1.0,\n label: opts.threadId,\n ts,\n });\n }\n }\n\n // Causal graph\n if (this.cfg.causalGraphEnabled && opts.causalPredecessor) {\n const phrase = detectCausalPhrase(opts.content);\n if (phrase) {\n await appendEdge(this.memoryDir, {\n from: opts.causalPredecessor,\n to: opts.memoryPath,\n type: \"causal\",\n weight: 1.0,\n label: phrase,\n ts,\n });\n }\n }\n } catch (err) {\n // Fail-open: graph write errors must never surface to caller\n const { log } = await import(\"./logger.js\");\n log.warn(`[graph] onMemoryWritten error: ${err}`);\n } finally {\n // Invalidate edge cache so spreadingActivation() picks up new edges.\n // In `finally` so the cache is cleared even on partial write failure.\n this.edgeCache = null;\n }\n }\n\n /**\n * Spreading activation BFS (SYNAPSE-inspired).\n *\n * Starting from `seeds`, traverse the combined graph for up to `maxSteps` hops.\n * Each candidate gets an activation score = edge.weight × edgeConfidence × decay^hop.\n *\n * Issue #681 PR 3/3 — confidence-aware traversal:\n * - Each edge's `weight` is multiplied by its `confidence` (legacy edges\n * missing `confidence` are treated as 1.0, preserving prior behavior).\n * - Edges with `confidence < graphTraversalConfidenceFloor` are pruned and\n * contribute neither activation nor downstream neighbors.\n * - When `graphTraversalPageRankIterations > 0`, an additional PageRank-\n * style refinement pass redistributes activation along confidence-weighted\n * edges, sharpening the ranking among multi-hop candidates.\n * - Per-result provenance includes the highest-confidence edge that landed\n * on each candidate, so the X-ray surface can attribute pruning and\n * ranking decisions back to specific edges.\n *\n * @param seeds - initial memory paths to expand from (e.g. QMD top results)\n * @param maxSteps - max BFS hops (from config: maxGraphTraversalSteps)\n * @returns Array of {path, score, edgeConfidence, ...} sorted descending, not including seed paths\n */\n async spreadingActivation(\n seeds: string[],\n maxSteps?: number,\n opts?: {\n /**\n * Issue #681 — when `true`, bypasses the configured\n * `graphTraversalConfidenceFloor` and includes low-confidence\n * edges in traversal. Equivalent to forcing the floor to `0`.\n * Default `false` (floor from config is applied).\n */\n includeLowConfidence?: boolean;\n },\n ): Promise<Array<{\n path: string;\n score: number;\n seed: string;\n hopDepth: number;\n decayedWeight: number;\n graphType: \"entity\" | \"time\" | \"causal\";\n /**\n * Confidence of the edge that produced this candidate's recorded\n * provenance (the strongest edge along the chosen entry path).\n * In `[0, 1]`. Legacy edges without `confidence` surface as 1.0.\n */\n edgeConfidence: number;\n }>> {\n if (!this.cfg.multiGraphMemoryEnabled) return [];\n const steps = maxSteps ?? this.cfg.maxGraphTraversalSteps;\n const decay = this.cfg.graphActivationDecay;\n // When `includeLowConfidence` is set, use floor=0 so all edges\n // participate in traversal regardless of their decay state.\n // Otherwise clamp the configured floor into [0, 1] so misconfiguration\n // cannot (a) admit edges with negative confidence or (b) reject every\n // edge.\n const floor = opts?.includeLowConfidence === true\n ? 0\n : clampConfidenceFloor(this.cfg.graphTraversalConfidenceFloor);\n const iterations = clampPageRankIterations(\n this.cfg.graphTraversalPageRankIterations,\n );\n\n try {\n const allEdges = await this.loadEdgesCached();\n\n // Build adjacency index: from → edges, to → edges (bidirectional for entity/time, directional for causal).\n // Edges below the confidence floor are pruned at index time so neither\n // direct activation nor downstream BFS expansion can re-introduce them.\n const adj = new Map<string, GraphEdge[]>();\n for (const edge of allEdges) {\n const conf = readEdgeConfidence(edge);\n if (conf < floor) continue;\n if (!adj.has(edge.from)) adj.set(edge.from, []);\n adj.get(edge.from)!.push(edge);\n // Entity and time edges are bidirectional\n if (edge.type !== \"causal\") {\n if (!adj.has(edge.to)) adj.set(edge.to, []);\n adj.get(edge.to)!.push({ ...edge, from: edge.to, to: edge.from });\n }\n }\n\n const seedSet = new Set(seeds);\n const scores = new Map<string, number>(); // candidate path → accumulated activation score\n const provenance = new Map<\n string,\n {\n seed: string;\n hopDepth: number;\n decayedWeight: number;\n graphType: \"entity\" | \"time\" | \"causal\";\n edgeConfidence: number;\n }\n >();\n const visited = new Set<string>(seeds);\n\n // BFS queue: [nodePath, hop, seedPath]\n const queue: Array<[string, number, string]> = seeds.map((s) => [s, 0, s]);\n\n while (queue.length > 0) {\n const [node, hop, sourceSeed] = queue.shift()!;\n if (hop >= steps) continue;\n\n const edges = adj.get(node) ?? [];\n for (const edge of edges) {\n const neighbor = edge.to === node ? edge.from : edge.to;\n const conf = readEdgeConfidence(edge);\n // Defense in depth: the adjacency build already drops sub-floor\n // edges, but if a synthesized reverse edge ever bypassed that\n // path, this guard keeps spreading activation honest.\n if (conf < floor) continue;\n const score = edge.weight * conf * Math.pow(decay, hop + 1);\n\n if (!seedSet.has(neighbor)) {\n const existing = scores.get(neighbor) ?? 0;\n scores.set(neighbor, existing + score);\n\n const prev = provenance.get(neighbor);\n if (\n !prev ||\n hop + 1 < prev.hopDepth ||\n (hop + 1 === prev.hopDepth && score > prev.decayedWeight)\n ) {\n provenance.set(neighbor, {\n seed: sourceSeed,\n hopDepth: hop + 1,\n decayedWeight: score,\n graphType: edge.type,\n edgeConfidence: conf,\n });\n }\n }\n\n if (!visited.has(neighbor)) {\n visited.add(neighbor);\n queue.push([neighbor, hop + 1, sourceSeed]);\n }\n }\n }\n\n // Issue #681 PR 3/3 — optional PageRank-style refinement.\n // Redistributes a node's accumulated activation along its outgoing\n // edges, weighted by edge confidence. Damping is fixed at the\n // canonical 0.85 so the ranking stays comparable across queries;\n // the `iterations` knob bounds compute, not behavior shape.\n if (iterations > 0 && scores.size > 1) {\n applyPageRankRefinement(scores, adj, {\n iterations,\n floor,\n damping: 0.85,\n });\n }\n\n // Apply lateral inhibition if enabled (Synapse-inspired competitive suppression)\n if (this.cfg.graphLateralInhibitionEnabled && scores.size > 1) {\n const inhibited = applyLateralInhibition(scores, {\n beta: this.cfg.graphLateralInhibitionBeta,\n topM: this.cfg.graphLateralInhibitionTopM,\n });\n for (const [k, v] of inhibited) {\n scores.set(k, v);\n }\n }\n\n return Array.from(scores.entries())\n .map(([p, score]) => ({\n path: p,\n score,\n seed: provenance.get(p)?.seed ?? \"\",\n hopDepth: provenance.get(p)?.hopDepth ?? 0,\n decayedWeight: provenance.get(p)?.decayedWeight ?? 0,\n graphType: provenance.get(p)?.graphType ?? \"entity\",\n edgeConfidence: provenance.get(p)?.edgeConfidence ?? 1,\n }))\n .sort((a, b) => b.score - a.score);\n } catch (err) {\n const { log } = await import(\"./logger.js\");\n log.warn(`[graph] spreadingActivation error: ${err}`);\n return [];\n }\n }\n}\n\n/**\n * Clamp `graphTraversalConfidenceFloor` into the legal range `[0, 1]`.\n * Non-finite or non-numeric values fall back to the documented default\n * so misconfiguration cannot silently disable the floor or reject every edge.\n *\n * Exported for tests; call sites in `spreadingActivation` use it to make\n * the contract explicit at every boundary.\n */\nexport function clampConfidenceFloor(raw: unknown): number {\n if (typeof raw !== \"number\" || !Number.isFinite(raw)) {\n return DEFAULT_GRAPH_TRAVERSAL_CONFIDENCE_FLOOR;\n }\n if (raw < 0) return 0;\n if (raw > 1) return 1;\n return raw;\n}\n\n/**\n * Clamp `graphTraversalPageRankIterations` into a non-negative integer.\n * Negative or non-finite values fall back to 0 (disable refinement) so\n * misconfiguration cannot stall recall in an unbounded loop.\n */\nexport function clampPageRankIterations(raw: unknown): number {\n if (typeof raw !== \"number\" || !Number.isFinite(raw)) return 0;\n if (raw <= 0) return 0;\n return Math.floor(raw);\n}\n\n/**\n * PageRank-style refinement on top of the BFS activation map.\n *\n * Each iteration redistributes a fraction of every node's score along\n * its outgoing edges, scaled by edge confidence. Confidence below\n * `floor` is filtered out before redistribution, mirroring the BFS\n * pruning rule. Mutates `scores` in place.\n *\n * Exported for tests; in production, call sites pass the same adjacency\n * map already used by BFS so behavior stays consistent.\n */\nexport function applyPageRankRefinement(\n scores: Map<string, number>,\n adj: Map<string, GraphEdge[]>,\n opts: { iterations: number; floor: number; damping: number },\n): void {\n const { iterations, floor, damping } = opts;\n if (iterations <= 0 || scores.size === 0) return;\n const safeDamping = Math.min(1, Math.max(0, damping));\n\n // Pre-compute confidence-weighted out-edge totals for normalization.\n // Done once per refinement, not per iteration, since adjacency is\n // immutable inside the loop.\n //\n // Codex P1 (#735): the denominator MUST be computed over the same\n // eligible-neighbor set the iteration redistributes into — i.e.\n // edges whose neighbor is in `scores`. Counting edges-to-seeds (or\n // edges-to-unseen-nodes) in the denominator while dropping their\n // flow during iteration leaks `safeDamping × score` every pass and\n // collapses leaf candidates' scores instead of just re-ranking them.\n const eligible = (edge: GraphEdge, fromNode: string): boolean => {\n if (readEdgeConfidence(edge) < floor) return false;\n const neighbor = edge.to === fromNode ? edge.from : edge.to;\n return scores.has(neighbor);\n };\n const outboundTotal = new Map<string, number>();\n for (const [node, edges] of adj.entries()) {\n if (!scores.has(node)) continue; // only candidate nodes redistribute\n let sum = 0;\n for (const edge of edges) {\n if (!eligible(edge, node)) continue;\n sum += readEdgeConfidence(edge) * edge.weight;\n }\n if (sum > 0) outboundTotal.set(node, sum);\n }\n\n for (let i = 0; i < iterations; i += 1) {\n const next = new Map<string, number>();\n // Teleport / damping floor: every node retains `(1 - damping) * score`\n // of its current activation so dangling nodes do not bleed to zero.\n for (const [node, score] of scores) {\n next.set(node, (1 - safeDamping) * score);\n }\n for (const [node, score] of scores) {\n const outEdges = adj.get(node);\n const total = outboundTotal.get(node);\n // Dangling-node fallback: when a candidate has zero eligible\n // outflow (no in-scores neighbors above the floor), the\n // `safeDamping × score` portion would otherwise evaporate. Keep\n // it on `node` so total mass is conserved and the score reflects\n // the candidate's standing rather than its in-degree topology.\n if (!outEdges || outEdges.length === 0 || !total || total <= 0) {\n next.set(node, (next.get(node) ?? 0) + safeDamping * score);\n continue;\n }\n for (const edge of outEdges) {\n if (!eligible(edge, node)) continue;\n const conf = readEdgeConfidence(edge);\n const neighbor = edge.to === node ? edge.from : edge.to;\n const flow = safeDamping * score * ((conf * edge.weight) / total);\n next.set(neighbor, (next.get(neighbor) ?? 0) + flow);\n }\n }\n for (const [node, score] of next) {\n scores.set(node, score);\n }\n }\n}\n\n/**\n * Lateral inhibition (Synapse-inspired).\n *\n * For each node, the top-M higher-activation competitors exert inhibition\n * proportional to their activation difference. Output is clamped to [0, ∞).\n *\n * No sigmoid is applied here — downstream `normalizeGraphActivationScore`\n * already applies x/(1+x) soft squash, so adding a sigmoid would double-\n * normalize and cap graph influence at ~50%.\n *\n * Formula: u_hat_i = max(0, u_i - beta * sum_{k in top-M where u_k > u_i}(u_k - u_i))\n *\n * When beta=0 or topM=0, returns original scores unchanged (no-op).\n */\nexport function applyLateralInhibition(\n scores: Map<string, number>,\n opts: { beta: number; topM: number },\n): Map<string, number> {\n const { beta, topM } = opts;\n if (beta === 0 || topM === 0) return new Map(scores);\n\n const sorted = Array.from(scores.entries()).sort((a, b) => b[1] - a[1]);\n const topCompetitors = sorted.slice(0, topM);\n\n const result = new Map<string, number>();\n for (const [node, u] of scores) {\n let inhibition = 0;\n for (const [, uK] of topCompetitors) {\n if (uK > u) {\n inhibition += uK - u;\n }\n }\n result.set(node, Math.max(0, u - beta * inhibition));\n }\n\n return result;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import {
|
|
2
2
|
DEFAULT_TRANSFER_EXCLUDE_DIRS
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-UI3NYK34.js";
|
|
4
4
|
import {
|
|
5
5
|
prepareSafeArchiveRoot,
|
|
6
6
|
resolveSafeArchiveTarget,
|
|
7
7
|
sha256Bytes,
|
|
8
8
|
validateArchiveRelativePath
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-GCGJW34D.js";
|
|
10
10
|
import {
|
|
11
11
|
MAGIC_HEADER_SIZE,
|
|
12
12
|
isEncryptedFile
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-KJMYHC7K.js";
|
|
14
14
|
import {
|
|
15
15
|
parseFlexibleIsoTimestamp
|
|
16
16
|
} from "./chunk-P7FMDTKL.js";
|
|
@@ -378,12 +378,11 @@ async function readPlainFileContentChunk(options) {
|
|
|
378
378
|
await handle.close();
|
|
379
379
|
}
|
|
380
380
|
}
|
|
381
|
-
async function
|
|
381
|
+
async function* iterateOfflineSyncSnapshotFileRecords(options) {
|
|
382
382
|
const rootAbs = path.resolve(options.root);
|
|
383
|
-
const root = await prepareSafeArchiveRoot(rootAbs, "
|
|
383
|
+
const root = await prepareSafeArchiveRoot(rootAbs, "iterateOfflineSyncSnapshotFileRecords", "root");
|
|
384
384
|
const includeTranscripts = options.includeTranscripts !== false;
|
|
385
|
-
|
|
386
|
-
async function walk(dirAbs) {
|
|
385
|
+
async function* walk(dirAbs) {
|
|
387
386
|
let entries = await readdir(dirAbs, { withFileTypes: true });
|
|
388
387
|
entries = entries.sort((left, right) => left.name.localeCompare(right.name));
|
|
389
388
|
for (const entry of entries) {
|
|
@@ -392,21 +391,26 @@ async function buildOfflineSyncSnapshot(options) {
|
|
|
392
391
|
if (shouldExcludeRelPath(relPosix, includeTranscripts)) continue;
|
|
393
392
|
if (entry.isSymbolicLink()) continue;
|
|
394
393
|
if (entry.isDirectory()) {
|
|
395
|
-
|
|
394
|
+
yield* walk(abs);
|
|
396
395
|
continue;
|
|
397
396
|
}
|
|
398
397
|
if (!entry.isFile()) continue;
|
|
399
|
-
|
|
398
|
+
yield await readOfflineSyncFileRecord({
|
|
400
399
|
root,
|
|
401
400
|
relPath: relPosix,
|
|
402
401
|
filePath: abs,
|
|
403
402
|
includeContent: options.includeContent === true,
|
|
404
403
|
readFile: options.readFile,
|
|
405
404
|
readFileDigest: options.readFileDigest
|
|
406
|
-
})
|
|
405
|
+
});
|
|
407
406
|
}
|
|
408
407
|
}
|
|
409
|
-
|
|
408
|
+
yield* walk(root.abs);
|
|
409
|
+
}
|
|
410
|
+
async function buildOfflineSyncSnapshot(options) {
|
|
411
|
+
const includeTranscripts = options.includeTranscripts !== false;
|
|
412
|
+
const files = [];
|
|
413
|
+
for await (const file of iterateOfflineSyncSnapshotFileRecords(options)) files.push(file);
|
|
410
414
|
return {
|
|
411
415
|
format: OFFLINE_SYNC_SNAPSHOT_FORMAT,
|
|
412
416
|
schemaVersion: 1,
|
|
@@ -1575,6 +1579,7 @@ export {
|
|
|
1575
1579
|
normalizeOfflineSyncSnapshot,
|
|
1576
1580
|
normalizeOfflineSyncChangeset,
|
|
1577
1581
|
shouldPreferIncomingOfflineRuntimeFile,
|
|
1582
|
+
iterateOfflineSyncSnapshotFileRecords,
|
|
1578
1583
|
buildOfflineSyncSnapshot,
|
|
1579
1584
|
buildOfflineSyncSnapshotFromBase,
|
|
1580
1585
|
buildOfflineSyncSnapshotForPaths,
|
|
@@ -1594,4 +1599,4 @@ export {
|
|
|
1594
1599
|
normalizeOfflineSyncState,
|
|
1595
1600
|
fileStatesFromSnapshot
|
|
1596
1601
|
};
|
|
1597
|
-
//# sourceMappingURL=chunk-
|
|
1602
|
+
//# sourceMappingURL=chunk-4Q73JBSM.js.map
|