@remnic/core 1.1.12 → 1.1.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/access-cli.d.ts +2 -1
- package/dist/access-cli.js +263 -82
- package/dist/access-cli.js.map +1 -1
- package/dist/access-http.d.ts +26 -60
- package/dist/access-http.js +43 -29
- package/dist/access-mcp.d.ts +24 -6
- package/dist/access-mcp.js +35 -28
- package/dist/access-schema.d.ts +9 -6
- package/dist/access-schema.js +7 -5
- package/dist/access-service-DcCDmNYC.d.ts +1542 -0
- package/dist/access-service.d.ts +25 -7
- package/dist/access-service.js +33 -26
- package/dist/active-memory-bridge.js +2 -2
- package/dist/active-recall.js +11 -3
- package/dist/active-recall.js.map +1 -1
- package/dist/adapters/claude-code.d.ts +24 -0
- package/dist/adapters/claude-code.js +9 -0
- package/dist/adapters/codex.d.ts +25 -0
- package/dist/adapters/codex.js +9 -0
- package/dist/adapters/hermes.d.ts +35 -0
- package/dist/adapters/hermes.js +9 -0
- package/dist/adapters/index.d.ts +6 -0
- package/dist/adapters/index.js +26 -0
- package/dist/adapters/registry.d.ts +20 -0
- package/dist/adapters/registry.js +13 -0
- package/dist/adapters/replit.d.ts +28 -0
- package/dist/adapters/replit.js +9 -0
- package/dist/adapters/types.d.ts +43 -0
- package/dist/adapters/types.js +8 -0
- package/dist/bootstrap.d.ts +20 -5
- package/dist/boxes.d.ts +7 -0
- package/dist/boxes.js +1 -1
- package/dist/briefing.d.ts +5 -3
- package/dist/briefing.js +9 -6
- package/dist/buffer-surprise-report.js +1 -1
- package/dist/buffer.d.ts +18 -4
- package/dist/buffer.js +1 -1
- package/dist/calibration.js +4 -4
- package/dist/capsule-cli.d.ts +4 -4
- package/dist/capsule-cli.js +1 -1
- package/dist/capsule-crypto-5CYAGVC5.js +18 -0
- package/dist/capsule-merge-4MGKE7C5.js +189 -0
- package/dist/causal-behavior.d.ts +8 -28
- package/dist/causal-behavior.js +6 -3
- package/dist/causal-behavior.js.map +1 -1
- package/dist/causal-chain.js +3 -2
- package/dist/causal-consolidation.d.ts +1 -1
- package/dist/causal-consolidation.js +24 -13
- package/dist/causal-consolidation.js.map +1 -1
- package/dist/causal-retrieval.js +3 -3
- package/dist/causal-trajectory.js +1 -1
- package/dist/chunk-25MQ7IHJ.js +427 -0
- package/dist/chunk-25MQ7IHJ.js.map +1 -0
- package/dist/chunk-2F2W355T.js +256 -0
- package/dist/chunk-2F2W355T.js.map +1 -0
- package/dist/chunk-2KI4QFHU.js +228 -0
- package/dist/chunk-2KI4QFHU.js.map +1 -0
- package/dist/chunk-2PRQG7PV.js +86 -0
- package/dist/chunk-2PRQG7PV.js.map +1 -0
- package/dist/chunk-2QR3XXIC.js +2272 -0
- package/dist/chunk-2QR3XXIC.js.map +1 -0
- package/dist/chunk-2WWLHTZY.js +121 -0
- package/dist/chunk-326G7DJK.js +2185 -0
- package/dist/chunk-326G7DJK.js.map +1 -0
- package/dist/chunk-34DQE4KF.js +174 -0
- package/dist/chunk-34DQE4KF.js.map +1 -0
- package/dist/chunk-3APJ5EVB.js +601 -0
- package/dist/chunk-3APJ5EVB.js.map +1 -0
- package/dist/chunk-3HPAPHUK.js +51 -0
- package/dist/chunk-3HPAPHUK.js.map +1 -0
- package/dist/chunk-3JXBXXM2.js +69 -0
- package/dist/chunk-3JXBXXM2.js.map +1 -0
- package/dist/chunk-3KW65B36.js +681 -0
- package/dist/chunk-3KW65B36.js.map +1 -0
- package/dist/chunk-3UXOZBHV.js +20 -0
- package/dist/chunk-3UXOZBHV.js.map +1 -0
- package/dist/chunk-3VAL7ZL2.js +266 -0
- package/dist/chunk-3VAL7ZL2.js.map +1 -0
- package/dist/chunk-3Y4P7RXM.js +31 -0
- package/dist/chunk-3Y4P7RXM.js.map +1 -0
- package/dist/chunk-47VWKCAF.js +273 -0
- package/dist/chunk-47VWKCAF.js.map +1 -0
- package/dist/chunk-4CRG46BG.js +271 -0
- package/dist/chunk-5375UYTQ.js +914 -0
- package/dist/chunk-5375UYTQ.js.map +1 -0
- package/dist/chunk-56K5QLHX.js +506 -0
- package/dist/chunk-56K5QLHX.js.map +1 -0
- package/dist/chunk-5RGLBDQF.js +596 -0
- package/dist/chunk-5RGLBDQF.js.map +1 -0
- package/dist/chunk-5UZXUTVO.js +9 -0
- package/dist/chunk-5UZXUTVO.js.map +1 -0
- package/dist/chunk-65PG43EQ.js +105 -0
- package/dist/chunk-65PG43EQ.js.map +1 -0
- package/dist/chunk-66DHUKLO.js +57 -0
- package/dist/chunk-66DHUKLO.js.map +1 -0
- package/dist/chunk-6FC5EGNV.js +46 -0
- package/dist/chunk-6FC5EGNV.js.map +1 -0
- package/dist/chunk-6H2TESSP.js +62 -0
- package/dist/chunk-6H2TESSP.js.map +1 -0
- package/dist/chunk-6LVVDPJ4.js +32 -0
- package/dist/chunk-6LVVDPJ4.js.map +1 -0
- package/dist/chunk-6RVI47ZR.js +159 -0
- package/dist/chunk-6RVI47ZR.js.map +1 -0
- package/dist/chunk-7AAT6G4Q.js +5117 -0
- package/dist/chunk-7AAT6G4Q.js.map +1 -0
- package/dist/chunk-7DTASS5T.js +29 -0
- package/dist/chunk-7DTASS5T.js.map +1 -0
- package/dist/chunk-7IASACLB.js +596 -0
- package/dist/chunk-7MNMYOFP.js +32 -0
- package/dist/chunk-7MNMYOFP.js.map +1 -0
- package/dist/chunk-7N4KAIGN.js +133 -0
- package/dist/chunk-7N4KAIGN.js.map +1 -0
- package/dist/chunk-7OZ53EXP.js +101 -0
- package/dist/chunk-7OZ53EXP.js.map +1 -0
- package/dist/chunk-7XYTQGCC.js +134 -0
- package/dist/chunk-7XYTQGCC.js.map +1 -0
- package/dist/chunk-A2XUIMJ3.js +341 -0
- package/dist/chunk-A2XUIMJ3.js.map +1 -0
- package/dist/chunk-AGZQD76C.js +201 -0
- package/dist/chunk-AGZQD76C.js.map +1 -0
- package/dist/chunk-APO3DCMU.js +361 -0
- package/dist/chunk-APO3DCMU.js.map +1 -0
- package/dist/chunk-BFBF3XEF.js +283 -0
- package/dist/chunk-BFBF3XEF.js.map +1 -0
- package/dist/chunk-BJ3KMYTB.js +1974 -0
- package/dist/chunk-BJ3KMYTB.js.map +1 -0
- package/dist/chunk-CHEL3SKB.js +6758 -0
- package/dist/chunk-CHEL3SKB.js.map +1 -0
- package/dist/chunk-CQZRLNMV.js +1491 -0
- package/dist/chunk-CQZRLNMV.js.map +1 -0
- package/dist/chunk-D46YSIYX.js +892 -0
- package/dist/chunk-D46YSIYX.js.map +1 -0
- package/dist/chunk-DINWEURR.js +648 -0
- package/dist/chunk-DINWEURR.js.map +1 -0
- package/dist/chunk-DK5LDEQM.js +530 -0
- package/dist/chunk-DK5LDEQM.js.map +1 -0
- package/dist/chunk-DOM4GKSW.js +34 -0
- package/dist/chunk-DOM4GKSW.js.map +1 -0
- package/dist/chunk-EDTHC6UD.js +1075 -0
- package/dist/chunk-EFJ3MQ4V.js +721 -0
- package/dist/chunk-EHRTFRWW.js +89 -0
- package/dist/chunk-EHRTFRWW.js.map +1 -0
- package/dist/chunk-FAJ7FZYM.js +11 -0
- package/dist/chunk-FAJ7FZYM.js.map +1 -0
- package/dist/chunk-FBYESMQ2.js +570 -0
- package/dist/chunk-FDU6HUUL.js +147 -0
- package/dist/chunk-FF4KLI5W.js +99 -0
- package/dist/chunk-FF4KLI5W.js.map +1 -0
- package/dist/chunk-FIT6DMX6.js +310 -0
- package/dist/chunk-FIT6DMX6.js.map +1 -0
- package/dist/chunk-FJ43PRLT.js +272 -0
- package/dist/chunk-FJ43PRLT.js.map +1 -0
- package/dist/chunk-FKFMOY3N.js +32 -0
- package/dist/chunk-FKFMOY3N.js.map +1 -0
- package/dist/chunk-FLTNHQK6.js +262 -0
- package/dist/chunk-FLTNHQK6.js.map +1 -0
- package/dist/chunk-GA454ALV.js +12436 -0
- package/dist/chunk-GA454ALV.js.map +1 -0
- package/dist/chunk-GGKRUQOO.js +228 -0
- package/dist/chunk-GIF42EW3.js +63 -0
- package/dist/chunk-GIF42EW3.js.map +1 -0
- package/dist/chunk-GL6I6MEQ.js +647 -0
- package/dist/chunk-H3ME6L6D.js +709 -0
- package/dist/chunk-H3ME6L6D.js.map +1 -0
- package/dist/chunk-HHLLAQGZ.js +1 -0
- package/dist/chunk-HXXBL2KD.js +2040 -0
- package/dist/chunk-I5V2VDIW.js +219 -0
- package/dist/chunk-I5V2VDIW.js.map +1 -0
- package/dist/chunk-I6K5FBRQ.js +35 -0
- package/dist/chunk-I6K5FBRQ.js.map +1 -0
- package/dist/chunk-ICRIXAP2.js +121 -0
- package/dist/chunk-ICRIXAP2.js.map +1 -0
- package/dist/chunk-J4EB7DNW.js +11 -0
- package/dist/chunk-J4EB7DNW.js.map +1 -0
- package/dist/chunk-JLFA7DQG.js +62 -0
- package/dist/chunk-JLFA7DQG.js.map +1 -0
- package/dist/chunk-KJTKLXTH.js +9 -0
- package/dist/chunk-KJTKLXTH.js.map +1 -0
- package/dist/chunk-KLAO5DGL.js +917 -0
- package/dist/chunk-KLAO5DGL.js.map +1 -0
- package/dist/chunk-KNKUID7G.js +183 -0
- package/dist/chunk-KOSORCJG.js +624 -0
- package/dist/chunk-KOSORCJG.js.map +1 -0
- package/dist/chunk-KUJVMMZQ.js +1262 -0
- package/dist/chunk-KUJVMMZQ.js.map +1 -0
- package/dist/chunk-LCR46JY5.js +123 -0
- package/dist/chunk-LCR46JY5.js.map +1 -0
- package/dist/chunk-LLQ2LLWF.js +148 -0
- package/dist/chunk-LLQ2LLWF.js.map +1 -0
- package/dist/chunk-LPMVBPA3.js +236 -0
- package/dist/chunk-LT3NLYSI.js +50 -0
- package/dist/chunk-LT3NLYSI.js.map +1 -0
- package/dist/chunk-LUDTDZLK.js +287 -0
- package/dist/chunk-LUDTDZLK.js.map +1 -0
- package/dist/chunk-M23FSH32.js +3963 -0
- package/dist/chunk-M23FSH32.js.map +1 -0
- package/dist/chunk-MC26UJIM.js +118 -0
- package/dist/chunk-ME6ESPZU.js +119 -0
- package/dist/chunk-ME6ESPZU.js.map +1 -0
- package/dist/chunk-MGKYQQYF.js +272 -0
- package/dist/chunk-MJFNCJXV.js +66 -0
- package/dist/chunk-MJFNCJXV.js.map +1 -0
- package/dist/chunk-MSWG7JI6.js +237 -0
- package/dist/chunk-MSWG7JI6.js.map +1 -0
- package/dist/chunk-MT25YHYH.js +141 -0
- package/dist/chunk-MT25YHYH.js.map +1 -0
- package/dist/chunk-MT4HVDUZ.js +53 -0
- package/dist/chunk-MY6TPVXW.js +219 -0
- package/dist/chunk-N2D6GXBM.js +267 -0
- package/dist/chunk-N2D6GXBM.js.map +1 -0
- package/dist/chunk-NJ3MJQZX.js +46 -0
- package/dist/chunk-NJ3MJQZX.js.map +1 -0
- package/dist/chunk-NMZY542O.js +335 -0
- package/dist/chunk-NMZY542O.js.map +1 -0
- package/dist/chunk-NNVTUXEB.js +23 -0
- package/dist/chunk-NZL6GGQE.js +375 -0
- package/dist/chunk-NZL6GGQE.js.map +1 -0
- package/dist/chunk-P4NEIHUT.js +108 -0
- package/dist/chunk-P7FMDTKL.js +103 -0
- package/dist/chunk-P7FMDTKL.js.map +1 -0
- package/dist/chunk-PHK3HARR.js +32 -0
- package/dist/chunk-PHK3HARR.js.map +1 -0
- package/dist/chunk-PIRJPV5T.js +98 -0
- package/dist/chunk-PIRJPV5T.js.map +1 -0
- package/dist/chunk-PK7H5L6Y.js +159 -0
- package/dist/chunk-PK7H5L6Y.js.map +1 -0
- package/dist/chunk-PR5FBTFU.js +233 -0
- package/dist/chunk-PR5FBTFU.js.map +1 -0
- package/dist/chunk-PU63GXWS.js +174 -0
- package/dist/chunk-PU63GXWS.js.map +1 -0
- package/dist/chunk-PZIAX57I.js +124 -0
- package/dist/chunk-PZIAX57I.js.map +1 -0
- package/dist/chunk-Q7P4WJDP.js +26 -0
- package/dist/chunk-Q7P4WJDP.js.map +1 -0
- package/dist/chunk-QQUAB63I.js +63 -0
- package/dist/chunk-QQUAB63I.js.map +1 -0
- package/dist/chunk-QRNI5JBH.js +18 -0
- package/dist/chunk-RHY3HH7P.js +601 -0
- package/dist/chunk-RHY3HH7P.js.map +1 -0
- package/dist/chunk-RRF5UOBJ.js +91 -0
- package/dist/chunk-RXDLTSWT.js +124 -0
- package/dist/chunk-RXDLTSWT.js.map +1 -0
- package/dist/chunk-RYED3SPJ.js +42 -0
- package/dist/chunk-RYED3SPJ.js.map +1 -0
- package/dist/chunk-S7KDBTWT.js +106 -0
- package/dist/chunk-S7KDBTWT.js.map +1 -0
- package/dist/chunk-SEDEKFYQ.js +1 -0
- package/dist/chunk-TECVW3JP.js +36 -0
- package/dist/chunk-TECVW3JP.js.map +1 -0
- package/dist/chunk-TFO23QT4.js +88 -0
- package/dist/chunk-TFO23QT4.js.map +1 -0
- package/dist/chunk-TK4UEOSK.js +76 -0
- package/dist/chunk-TK4UEOSK.js.map +1 -0
- package/dist/chunk-TKWGAOLV.js +122 -0
- package/dist/chunk-TKWGAOLV.js.map +1 -0
- package/dist/chunk-TMM4S4IJ.js +597 -0
- package/dist/chunk-TMM4S4IJ.js.map +1 -0
- package/dist/chunk-TMQLARTH.js +188 -0
- package/dist/chunk-TMQLARTH.js.map +1 -0
- package/dist/chunk-TPDBFYEG.js +130 -0
- package/dist/chunk-TPDBFYEG.js.map +1 -0
- package/dist/chunk-TPMQ3G6Z.js +145 -0
- package/dist/chunk-TPMQ3G6Z.js.map +1 -0
- package/dist/chunk-TZOLIGIG.js +61 -0
- package/dist/chunk-TZOLIGIG.js.map +1 -0
- package/dist/chunk-U3PN77QT.js +113 -0
- package/dist/chunk-U3WSW6PZ.js +277 -0
- package/dist/chunk-U4SCL7B7.js +640 -0
- package/dist/chunk-U4SCL7B7.js.map +1 -0
- package/dist/chunk-UWK5OXUJ.js +156 -0
- package/dist/chunk-UWK5OXUJ.js.map +1 -0
- package/dist/chunk-UWVJF25J.js +74 -0
- package/dist/chunk-UXHQAFNA.js +1317 -0
- package/dist/chunk-UXHQAFNA.js.map +1 -0
- package/dist/chunk-V5OCT34X.js +1 -0
- package/dist/chunk-VLXA6PI2.js +304 -0
- package/dist/chunk-VLXA6PI2.js.map +1 -0
- package/dist/chunk-VNO6ZJ35.js +500 -0
- package/dist/chunk-VNO6ZJ35.js.map +1 -0
- package/dist/chunk-VW676BEI.js +827 -0
- package/dist/chunk-VW676BEI.js.map +1 -0
- package/dist/chunk-W3LR522O.js +2296 -0
- package/dist/chunk-W4L6CZKA.js +96 -0
- package/dist/chunk-W4L6CZKA.js.map +1 -0
- package/dist/chunk-W4RVMTHR.js +372 -0
- package/dist/chunk-W4RVMTHR.js.map +1 -0
- package/dist/chunk-WEHSQBFR.js +188 -0
- package/dist/chunk-WEHSQBFR.js.map +1 -0
- package/dist/chunk-WELDCG6C.js +380 -0
- package/dist/chunk-WELDCG6C.js.map +1 -0
- package/dist/chunk-WZYKANL3.js +2800 -0
- package/dist/chunk-WZYKANL3.js.map +1 -0
- package/dist/chunk-XIG5PDM7.js +48 -0
- package/dist/chunk-XJNBEDFE.js +193 -0
- package/dist/chunk-XJNBEDFE.js.map +1 -0
- package/dist/chunk-XVVIG67A.js +291 -0
- package/dist/chunk-XVVIG67A.js.map +1 -0
- package/dist/chunk-XVZ7B3HG.js +135 -0
- package/dist/chunk-YBPYIAA5.js +73 -0
- package/dist/chunk-YBPYIAA5.js.map +1 -0
- package/dist/chunk-Z734BLO3.js +21 -0
- package/dist/chunk-Z734BLO3.js.map +1 -0
- package/dist/chunk-ZKSK55RC.js +269 -0
- package/dist/chunk-ZKSK55RC.js.map +1 -0
- package/dist/chunk-ZTFCYYEZ.js +69 -0
- package/dist/chunk-ZTFCYYEZ.js.map +1 -0
- package/dist/chunk-ZY2MNJR6.js +329 -0
- package/dist/chunk-ZY2MNJR6.js.map +1 -0
- package/dist/cli-D3VpkVwB.d.ts +1136 -0
- package/dist/cli.d.ts +39 -10
- package/dist/cli.js +108 -49
- package/dist/commitment-ledger.js +1 -1
- package/dist/compat/checks.d.ts +5 -0
- package/dist/compat/checks.js +11 -0
- package/dist/compat/checks.js.map +1 -0
- package/dist/compat/types.d.ts +30 -0
- package/dist/compat/types.js +1 -0
- package/dist/compat/types.js.map +1 -0
- package/dist/compounding/engine.d.ts +221 -0
- package/dist/compounding/engine.js +32 -0
- package/dist/compounding/engine.js.map +1 -0
- package/dist/compounding/preference-consolidator.d.ts +92 -0
- package/dist/compounding/preference-consolidator.js +553 -0
- package/dist/compounding/preference-consolidator.js.map +1 -0
- package/dist/config.d.ts +4 -2
- package/dist/config.js +9 -4
- package/dist/conflict-policy-DyJ2wd-h.d.ts +4 -0
- package/dist/connectors/codex-materialize-runner.d.ts +64 -0
- package/dist/connectors/codex-materialize-runner.js +33 -0
- package/dist/connectors/codex-materialize-runner.js.map +1 -0
- package/dist/connectors/codex-materialize.d.ts +195 -0
- package/dist/connectors/codex-materialize.js +38 -0
- package/dist/connectors/codex-materialize.js.map +1 -0
- package/dist/connectors/index.d.ts +444 -0
- package/dist/connectors/index.js +115 -0
- package/dist/connectors/index.js.map +1 -0
- package/dist/connectors-cli-CwbyjGR7.d.ts +257 -0
- package/dist/connectors-cli.d.ts +1 -1
- package/dist/consolidation-provenance-check.d.ts +3 -1
- package/dist/consolidation-undo.d.ts +3 -1
- package/dist/contradiction/index.d.ts +258 -0
- package/dist/contradiction/index.js +43 -0
- package/dist/contradiction/index.js.map +1 -0
- package/dist/contradiction-review-ATP4S6IC.js +30 -0
- package/dist/contradiction-review-ATP4S6IC.js.map +1 -0
- package/dist/contradiction-scan-5A4IDZV5.js +13 -0
- package/dist/contradiction-scan-5A4IDZV5.js.map +1 -0
- package/dist/conversation-index/backend.d.ts +97 -0
- package/dist/conversation-index/backend.js +13 -0
- package/dist/conversation-index/backend.js.map +1 -0
- package/dist/conversation-index/chunker.d.ts +16 -0
- package/dist/conversation-index/chunker.js +8 -0
- package/dist/conversation-index/chunker.js.map +1 -0
- package/dist/conversation-index/cleanup.d.ts +11 -0
- package/dist/conversation-index/cleanup.js +9 -0
- package/dist/conversation-index/cleanup.js.map +1 -0
- package/dist/conversation-index/faiss-adapter.d.ts +6 -0
- package/dist/conversation-index/faiss-adapter.js +16 -0
- package/dist/conversation-index/faiss-adapter.js.map +1 -0
- package/dist/conversation-index/indexer.d.ts +23 -0
- package/dist/conversation-index/indexer.js +15 -0
- package/dist/conversation-index/indexer.js.map +1 -0
- package/dist/conversation-index/search.d.ts +6 -0
- package/dist/conversation-index/search.js +11 -0
- package/dist/conversation-index/search.js.map +1 -0
- package/dist/embedding-fallback.js +2 -2
- package/dist/enrichment/index.d.ts +163 -0
- package/dist/enrichment/index.js +18 -0
- package/dist/enrichment/index.js.map +1 -0
- package/dist/entity-retrieval.d.ts +4 -2
- package/dist/entity-retrieval.js +8 -5
- package/dist/evals.js +1 -1
- package/dist/explicit-capture.d.ts +20 -5
- package/dist/explicit-capture.js +2 -2
- package/dist/extraction-judge-training.js +1 -1
- package/dist/extraction.js +8 -8
- package/dist/faiss-adapter-CzPghc4C.d.ts +70 -0
- package/dist/fallback-llm.d.ts +2 -0
- package/dist/fallback-llm.js +4 -4
- package/dist/graph-edge-decay-5DI5GUNL.js +207 -0
- package/dist/index.d.ts +66 -711
- package/dist/index.js +556 -2680
- package/dist/index.js.map +1 -1
- package/dist/lcm/archive.d.ts +89 -0
- package/dist/lcm/archive.js +12 -0
- package/dist/lcm/archive.js.map +1 -0
- package/dist/lcm/dag.d.ts +48 -0
- package/dist/lcm/dag.js +8 -0
- package/dist/lcm/dag.js.map +1 -0
- package/dist/lcm/engine.d.ts +116 -0
- package/dist/lcm/engine.js +20 -0
- package/dist/lcm/engine.js.map +1 -0
- package/dist/lcm/index.d.ts +12 -0
- package/dist/lcm/index.js +44 -0
- package/dist/lcm/index.js.map +1 -0
- package/dist/lcm/queue.d.ts +62 -0
- package/dist/lcm/queue.js +8 -0
- package/dist/lcm/queue.js.map +1 -0
- package/dist/lcm/recall.d.ts +20 -0
- package/dist/lcm/recall.js +8 -0
- package/dist/lcm/recall.js.map +1 -0
- package/dist/lcm/schema.d.ts +16 -0
- package/dist/lcm/schema.js +14 -0
- package/dist/lcm/schema.js.map +1 -0
- package/dist/lcm/summarizer.d.ts +38 -0
- package/dist/lcm/summarizer.js +12 -0
- package/dist/lcm/summarizer.js.map +1 -0
- package/dist/lcm/tools.d.ts +29 -0
- package/dist/lcm/tools.js +8 -0
- package/dist/lcm/tools.js.map +1 -0
- package/dist/live-connectors-runner.js +5 -5
- package/dist/local-llm.js +3 -3
- package/dist/maintenance/archive-observations.d.ts +18 -0
- package/dist/maintenance/archive-observations.js +8 -0
- package/dist/maintenance/archive-observations.js.map +1 -0
- package/dist/maintenance/backup-stamp.d.ts +3 -0
- package/dist/maintenance/backup-stamp.js +8 -0
- package/dist/maintenance/backup-stamp.js.map +1 -0
- package/dist/maintenance/memory-governance-cron.d.ts +85 -0
- package/dist/maintenance/memory-governance-cron.js +22 -0
- package/dist/maintenance/memory-governance-cron.js.map +1 -0
- package/dist/maintenance/memory-governance.d.ts +137 -0
- package/dist/maintenance/memory-governance.js +40 -0
- package/dist/maintenance/memory-governance.js.map +1 -0
- package/dist/maintenance/migrate-observations.d.ts +18 -0
- package/dist/maintenance/migrate-observations.js +9 -0
- package/dist/maintenance/migrate-observations.js.map +1 -0
- package/dist/maintenance/observation-ledger-utils.d.ts +10 -0
- package/dist/maintenance/observation-ledger-utils.js +10 -0
- package/dist/maintenance/observation-ledger-utils.js.map +1 -0
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.d.ts +15 -0
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +28 -0
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js.map +1 -0
- package/dist/maintenance/rebuild-memory-projection.d.ts +77 -0
- package/dist/maintenance/rebuild-memory-projection.js +35 -0
- package/dist/maintenance/rebuild-memory-projection.js.map +1 -0
- package/dist/maintenance/rebuild-observations.d.ts +17 -0
- package/dist/maintenance/rebuild-observations.js +9 -0
- package/dist/maintenance/rebuild-observations.js.map +1 -0
- package/dist/mcp-memory-inspector-app.d.ts +24 -6
- package/dist/memory-projection-store.d.ts +108 -3
- package/dist/memory-projection-store.js +2 -1
- package/dist/memory-worth-outcomes.d.ts +4 -2
- package/dist/migrate/from-engram.d.ts +24 -0
- package/dist/migrate/from-engram.js +12 -0
- package/dist/migrate/from-engram.js.map +1 -0
- package/dist/namespaces/migrate.d.ts +50 -0
- package/dist/namespaces/migrate.js +50 -0
- package/dist/namespaces/migrate.js.map +1 -0
- package/dist/namespaces/principal.d.ts +17 -0
- package/dist/namespaces/principal.js +16 -0
- package/dist/namespaces/principal.js.map +1 -0
- package/dist/namespaces/search.d.ts +46 -0
- package/dist/namespaces/search.js +28 -0
- package/dist/namespaces/search.js.map +1 -0
- package/dist/namespaces/storage.d.ts +32 -0
- package/dist/namespaces/storage.js +28 -0
- package/dist/namespaces/storage.js.map +1 -0
- package/dist/network/tailscale.d.ts +41 -0
- package/dist/network/tailscale.js +9 -0
- package/dist/network/tailscale.js.map +1 -0
- package/dist/network/webdav.d.ts +39 -0
- package/dist/network/webdav.js +10 -0
- package/dist/network/webdav.js.map +1 -0
- package/dist/objective-state-writers.js +2 -2
- package/dist/operator-toolkit.d.ts +4 -2
- package/dist/operator-toolkit.js +32 -14
- package/dist/opik-exporter.js +2 -2
- package/dist/opik-exporter.js.map +1 -1
- package/dist/orchestrator-DuWl9Hwx.d.ts +1244 -0
- package/dist/orchestrator.d.ts +22 -7
- package/dist/orchestrator.js +79 -44
- package/dist/path-MR5JPYOP.js +9 -0
- package/dist/path-MR5JPYOP.js.map +1 -0
- package/dist/qmd-recall-cache.d.ts +1 -1
- package/dist/qmd.d.ts +102 -3
- package/dist/qmd.js +23 -5
- package/dist/recall-explain-renderer.js +3 -3
- package/dist/recall-xray-cli.js +4 -4
- package/dist/recall-xray-renderer.js +3 -3
- package/dist/recall-xray.js +2 -2
- package/dist/replay/normalizers/chatgpt.d.ts +6 -0
- package/dist/replay/normalizers/chatgpt.js +11 -0
- package/dist/replay/normalizers/chatgpt.js.map +1 -0
- package/dist/replay/normalizers/claude.d.ts +6 -0
- package/dist/replay/normalizers/claude.js +11 -0
- package/dist/replay/normalizers/claude.js.map +1 -0
- package/dist/replay/normalizers/openclaw.d.ts +6 -0
- package/dist/replay/normalizers/openclaw.js +11 -0
- package/dist/replay/normalizers/openclaw.js.map +1 -0
- package/dist/replay/normalizers/shared.d.ts +16 -0
- package/dist/replay/normalizers/shared.js +14 -0
- package/dist/replay/normalizers/shared.js.map +1 -0
- package/dist/replay/runner.d.ts +35 -0
- package/dist/replay/runner.js +16 -0
- package/dist/replay/runner.js.map +1 -0
- package/dist/replay/types.d.ts +57 -0
- package/dist/replay/types.js +19 -0
- package/dist/replay/types.js.map +1 -0
- package/dist/resolution-B7FNQSSP.js +12 -0
- package/dist/resolution-B7FNQSSP.js.map +1 -0
- package/dist/resolve-provider-secret.js +2 -2
- package/dist/resume-bundles.js +8 -6
- package/dist/retrieval-agents.d.ts +1 -1
- package/dist/routing/engine.d.ts +35 -0
- package/dist/routing/engine.js +16 -0
- package/dist/routing/engine.js.map +1 -0
- package/dist/routing/store.d.ts +27 -0
- package/dist/routing/store.js +10 -0
- package/dist/routing/store.js.map +1 -0
- package/dist/runtime/better-sqlite.d.ts +8 -0
- package/dist/runtime/better-sqlite.js +10 -0
- package/dist/runtime/better-sqlite.js.map +1 -0
- package/dist/runtime/child-process.d.ts +32 -0
- package/dist/runtime/child-process.js +10 -0
- package/dist/runtime/child-process.js.map +1 -0
- package/dist/runtime/env.d.ts +5 -0
- package/dist/runtime/env.js +12 -0
- package/dist/runtime/env.js.map +1 -0
- package/dist/schemas.d.ts +22 -22
- package/dist/sdk-compat.js +1 -1
- package/dist/search/document-scanner.d.ts +22 -0
- package/dist/search/document-scanner.js +8 -0
- package/dist/search/document-scanner.js.map +1 -0
- package/dist/search/embed-helper.d.ts +35 -0
- package/dist/search/embed-helper.js +9 -0
- package/dist/search/embed-helper.js.map +1 -0
- package/dist/search/factory.d.ts +32 -0
- package/dist/search/factory.js +29 -0
- package/dist/search/factory.js.map +1 -0
- package/dist/search/index.d.ts +15 -0
- package/dist/search/index.js +50 -0
- package/dist/search/index.js.map +1 -0
- package/dist/search/lancedb-backend.d.ts +51 -0
- package/dist/search/lancedb-backend.js +10 -0
- package/dist/search/lancedb-backend.js.map +1 -0
- package/dist/search/meilisearch-backend.d.ts +48 -0
- package/dist/search/meilisearch-backend.js +10 -0
- package/dist/search/meilisearch-backend.js.map +1 -0
- package/dist/search/noop-backend.d.ts +26 -0
- package/dist/search/noop-backend.js +8 -0
- package/dist/search/noop-backend.js.map +1 -0
- package/dist/search/orama-backend.d.ts +53 -0
- package/dist/search/orama-backend.js +10 -0
- package/dist/search/orama-backend.js.map +1 -0
- package/dist/search/port.d.ts +61 -0
- package/dist/search/port.js +1 -0
- package/dist/search/port.js.map +1 -0
- package/dist/search/remote-backend.d.ts +39 -0
- package/dist/search/remote-backend.js +9 -0
- package/dist/search/remote-backend.js.map +1 -0
- package/dist/secure-store/index.d.ts +890 -0
- package/dist/secure-store/index.js +156 -0
- package/dist/secure-store/index.js.map +1 -0
- package/dist/semantic-VwGI14Ok.d.ts +69 -0
- package/dist/semantic-consolidation-4HkHWgeI.d.ts +180 -0
- package/dist/semantic-consolidation.d.ts +2 -2
- package/dist/semantic-consolidation.js +13 -6
- package/dist/semantic-rule-promotion.js +8 -5
- package/dist/semantic-rule-verifier.js +8 -5
- package/dist/shared-context/manager.d.ts +131 -0
- package/dist/shared-context/manager.js +15 -0
- package/dist/shared-context/manager.js.map +1 -0
- package/dist/skills-registry.js +13 -1
- package/dist/skills-registry.js.map +1 -1
- package/dist/state-store-VZU2IA53.js +16 -0
- package/dist/state-store-VZU2IA53.js.map +1 -0
- package/dist/storage-paths.d.ts +9 -0
- package/dist/storage-paths.js +20 -0
- package/dist/storage-paths.js.map +1 -0
- package/dist/storage.d.ts +3 -1
- package/dist/storage.js +7 -4
- package/dist/summarizer.d.ts +5 -0
- package/dist/summarizer.js +9 -8
- package/dist/summary-snapshot.js +2 -1
- package/dist/surfaces/dreams.d.ts +16 -0
- package/dist/surfaces/dreams.js +282 -0
- package/dist/surfaces/dreams.js.map +1 -0
- package/dist/surfaces/heartbeat.d.ts +17 -0
- package/dist/surfaces/heartbeat.js +265 -0
- package/dist/surfaces/heartbeat.js.map +1 -0
- package/dist/temporal-supersession.d.ts +3 -1
- package/dist/threading.d.ts +5 -0
- package/dist/threading.js +2 -1
- package/dist/tier-migration.d.ts +4 -2
- package/dist/tokens.js +2 -2
- package/dist/transcript.d.ts +15 -1
- package/dist/transcript.js +2 -1
- package/dist/transfer/autodetect.d.ts +4 -0
- package/dist/transfer/autodetect.js +15 -0
- package/dist/transfer/autodetect.js.map +1 -0
- package/dist/transfer/backup.d.ts +21 -0
- package/dist/transfer/backup.js +17 -0
- package/dist/transfer/backup.js.map +1 -0
- package/dist/transfer/capsule-export.d.ts +113 -0
- package/dist/transfer/capsule-export.js +19 -0
- package/dist/transfer/capsule-export.js.map +1 -0
- package/dist/transfer/capsule-import.d.ts +124 -0
- package/dist/transfer/capsule-import.js +16 -0
- package/dist/transfer/capsule-import.js.map +1 -0
- package/dist/transfer/constants.d.ts +13 -0
- package/dist/transfer/constants.js +12 -0
- package/dist/transfer/constants.js.map +1 -0
- package/dist/transfer/export-json.d.ts +11 -0
- package/dist/transfer/export-json.js +11 -0
- package/dist/transfer/export-json.js.map +1 -0
- package/dist/transfer/export-md.d.ts +10 -0
- package/dist/transfer/export-md.js +13 -0
- package/dist/transfer/export-md.js.map +1 -0
- package/dist/transfer/export-sqlite.d.ts +9 -0
- package/dist/transfer/export-sqlite.js +12 -0
- package/dist/transfer/export-sqlite.js.map +1 -0
- package/dist/transfer/fs-utils.d.ts +61 -0
- package/dist/transfer/fs-utils.js +40 -0
- package/dist/transfer/fs-utils.js.map +1 -0
- package/dist/transfer/import-json.d.ts +16 -0
- package/dist/transfer/import-json.js +13 -0
- package/dist/transfer/import-json.js.map +1 -0
- package/dist/transfer/import-md.d.ts +14 -0
- package/dist/transfer/import-md.js +11 -0
- package/dist/transfer/import-md.js.map +1 -0
- package/dist/transfer/import-sqlite.d.ts +14 -0
- package/dist/transfer/import-sqlite.js +12 -0
- package/dist/transfer/import-sqlite.js.map +1 -0
- package/dist/transfer/sqlite-schema.d.ts +4 -0
- package/dist/transfer/sqlite-schema.js +10 -0
- package/dist/transfer/sqlite-schema.js.map +1 -0
- package/dist/transfer/types.d.ts +916 -0
- package/dist/transfer/types.js +30 -0
- package/dist/transfer/types.js.map +1 -0
- package/dist/types.d.ts +28 -1
- package/dist/types.js +1 -1
- package/dist/verified-recall.js +9 -6
- package/dist/work/board.d.ts +43 -0
- package/dist/work/board.js +14 -0
- package/dist/work/board.js.map +1 -0
- package/dist/work/boundary.d.ts +8 -0
- package/dist/work/boundary.js +14 -0
- package/dist/work/boundary.js.map +1 -0
- package/dist/work/storage.d.ts +39 -0
- package/dist/work/storage.js +11 -0
- package/dist/work/storage.js.map +1 -0
- package/dist/work/types.d.ts +75 -0
- package/dist/work/types.js +1 -0
- package/dist/work/types.js.map +1 -0
- package/package.json +2767 -6
- package/scripts/faiss_index.py +816 -0
- package/scripts/faiss_requirements.txt +3 -0
- package/skills/remnic-entities/SKILL.md +51 -0
- package/skills/remnic-memory-workflow/SKILL.md +61 -0
- package/skills/remnic-recall/SKILL.md +51 -0
- package/skills/remnic-remember/SKILL.md +56 -0
- package/skills/remnic-search/SKILL.md +51 -0
- package/skills/remnic-status/SKILL.md +51 -0
- package/src/abort-error.test.ts +49 -0
- package/src/abort-error.ts +46 -0
- package/src/abstraction-nodes.ts +162 -0
- package/src/access-audit.test.ts +178 -0
- package/src/access-audit.ts +125 -0
- package/src/access-cli.test.ts +439 -0
- package/src/access-cli.ts +438 -0
- package/src/access-http.test.ts +225 -0
- package/src/access-http.ts +1899 -0
- package/src/access-idempotency.ts +232 -0
- package/src/access-mcp.test.ts +568 -0
- package/src/access-mcp.ts +3056 -0
- package/src/access-schema-pi.test.ts +60 -0
- package/src/access-schema.ts +522 -0
- package/src/access-service-namespace.test.ts +123 -0
- package/src/access-service.ts +5629 -0
- package/src/action-confidence.test.ts +206 -0
- package/src/action-confidence.ts +466 -0
- package/src/active-memory-bridge.test.ts +285 -0
- package/src/active-memory-bridge.ts +217 -0
- package/src/active-recall.test.ts +484 -0
- package/src/active-recall.ts +459 -0
- package/src/adapters/claude-code.ts +56 -0
- package/src/adapters/codex.ts +57 -0
- package/src/adapters/hermes.ts +64 -0
- package/src/adapters/index.ts +6 -0
- package/src/adapters/registry.ts +41 -0
- package/src/adapters/replit.ts +55 -0
- package/src/adapters/types.ts +51 -0
- package/src/behavior-learner.ts +144 -0
- package/src/behavior-signals.ts +73 -0
- package/src/binary-lifecycle/backend.ts +117 -0
- package/src/binary-lifecycle/index.ts +35 -0
- package/src/binary-lifecycle/manifest.ts +79 -0
- package/src/binary-lifecycle/pipeline.ts +352 -0
- package/src/binary-lifecycle/scanner.ts +89 -0
- package/src/binary-lifecycle/types.ts +89 -0
- package/src/bootstrap.ts +178 -0
- package/src/boxes.ts +521 -0
- package/src/briefing.test.ts +1535 -0
- package/src/briefing.ts +1382 -0
- package/src/buffer-session.test.ts +443 -0
- package/src/buffer-surprise-report.ts +176 -0
- package/src/buffer-surprise-telemetry.test.ts +606 -0
- package/src/buffer-surprise-trigger.test.ts +766 -0
- package/src/buffer-surprise.test.ts +339 -0
- package/src/buffer-surprise.ts +203 -0
- package/src/buffer.ts +900 -0
- package/src/bulk-import/cli-command.test.ts +204 -0
- package/src/bulk-import/index.ts +34 -0
- package/src/bulk-import/pipeline.test.ts +445 -0
- package/src/bulk-import/pipeline.ts +178 -0
- package/src/bulk-import/registry.test.ts +151 -0
- package/src/bulk-import/registry.ts +72 -0
- package/src/bulk-import/types.test.ts +272 -0
- package/src/bulk-import/types.ts +145 -0
- package/src/calibration.ts +394 -0
- package/src/capsule-cli.test.ts +398 -0
- package/src/capsule-cli.ts +565 -0
- package/src/causal-behavior.ts +308 -0
- package/src/causal-chain.ts +419 -0
- package/src/causal-consolidation.ts +370 -0
- package/src/causal-retrieval.ts +286 -0
- package/src/causal-trajectory-graph.ts +60 -0
- package/src/causal-trajectory.ts +303 -0
- package/src/chunking.ts +220 -0
- package/src/citations.ts +232 -0
- package/src/cli.ts +9403 -0
- package/src/codex-cli-fallback.ts +162 -0
- package/src/codex-thread-key.ts +1 -0
- package/src/coding/access-coding-context.test.ts +197 -0
- package/src/coding/coding-branch-scope.test.ts +281 -0
- package/src/coding/coding-namespace.test.ts +360 -0
- package/src/coding/coding-namespace.ts +412 -0
- package/src/coding/coding-orchestrator.test.ts +249 -0
- package/src/coding/git-context.test.ts +507 -0
- package/src/coding/git-context.ts +336 -0
- package/src/coding/mcp-set-coding-context.test.ts +174 -0
- package/src/coding/review-context.test.ts +316 -0
- package/src/coding/review-context.ts +349 -0
- package/src/coding/wire-coding-context.test.ts +468 -0
- package/src/commitment-ledger.test.ts +78 -0
- package/src/commitment-ledger.ts +337 -0
- package/src/compat/checks.test.ts +206 -0
- package/src/compat/checks.ts +716 -0
- package/src/compat/types.ts +33 -0
- package/src/compounding/engine.ts +1686 -0
- package/src/compounding/preference-consolidator.ts +778 -0
- package/src/compression-optimizer.ts +312 -0
- package/src/config.test.ts +930 -0
- package/src/config.ts +3807 -0
- package/src/connectors/codex/instructions.md +160 -0
- package/src/connectors/codex/resources/namespace-cheatsheet.md +48 -0
- package/src/connectors/codex-marketplace.ts +500 -0
- package/src/connectors/codex-materialize-runner.ts +212 -0
- package/src/connectors/codex-materialize.ts +983 -0
- package/src/connectors/coerce.ts +62 -0
- package/src/connectors/index.test.ts +1570 -0
- package/src/connectors/index.ts +3222 -0
- package/src/connectors/live/framework.ts +164 -0
- package/src/connectors/live/github.test.ts +1218 -0
- package/src/connectors/live/github.ts +1068 -0
- package/src/connectors/live/gmail.test.ts +1706 -0
- package/src/connectors/live/gmail.ts +1293 -0
- package/src/connectors/live/google-drive.test.ts +696 -0
- package/src/connectors/live/google-drive.ts +724 -0
- package/src/connectors/live/index.ts +101 -0
- package/src/connectors/live/live-connectors.test.ts +689 -0
- package/src/connectors/live/notion.test.ts +1109 -0
- package/src/connectors/live/notion.ts +978 -0
- package/src/connectors/live/registry.ts +103 -0
- package/src/connectors/live/state-store.ts +399 -0
- package/src/connectors/live/transient-errors.ts +150 -0
- package/src/connectors/weclone-installer.test.ts +850 -0
- package/src/connectors-cli.ts +513 -0
- package/src/console/state.test.ts +224 -0
- package/src/console/state.ts +514 -0
- package/src/console/trace.test.ts +813 -0
- package/src/console/trace.ts +603 -0
- package/src/console/tui.test.ts +582 -0
- package/src/console/tui.ts +508 -0
- package/src/consolidation-operator.ts +182 -0
- package/src/consolidation-provenance-check.ts +551 -0
- package/src/consolidation-undo.ts +718 -0
- package/src/contradiction/contradiction-judge.test.ts +189 -0
- package/src/contradiction/contradiction-judge.ts +333 -0
- package/src/contradiction/contradiction-review.ts +574 -0
- package/src/contradiction/contradiction-scan.ts +504 -0
- package/src/contradiction/contradiction.test.ts +2230 -0
- package/src/contradiction/index.ts +37 -0
- package/src/contradiction/resolution.ts +383 -0
- package/src/conversation-index/backend.ts +323 -0
- package/src/conversation-index/chunker.ts +47 -0
- package/src/conversation-index/cleanup.ts +53 -0
- package/src/conversation-index/faiss-adapter.ts +384 -0
- package/src/conversation-index/indexer.test.ts +164 -0
- package/src/conversation-index/indexer.ts +192 -0
- package/src/conversation-index/search.ts +37 -0
- package/src/cross-namespace-budget.test.ts +275 -0
- package/src/cross-namespace-budget.ts +365 -0
- package/src/cue-anchors.ts +163 -0
- package/src/curation/index.ts +544 -0
- package/src/dashboard-runtime.ts +337 -0
- package/src/day-summary.ts +122 -0
- package/src/dedup/index.ts +330 -0
- package/src/dedup/semantic.test.ts +1577 -0
- package/src/dedup/semantic.ts +148 -0
- package/src/delinearize.ts +193 -0
- package/src/direct-answer-wiring.test.ts +473 -0
- package/src/direct-answer-wiring.ts +180 -0
- package/src/direct-answer.test.ts +484 -0
- package/src/direct-answer.ts +273 -0
- package/src/embedding-fallback.ts +565 -0
- package/src/enrichment/audit.ts +89 -0
- package/src/enrichment/index.ts +27 -0
- package/src/enrichment/pipeline.ts +197 -0
- package/src/enrichment/provider-registry.ts +85 -0
- package/src/enrichment/types.ts +100 -0
- package/src/enrichment/web-search-provider.ts +63 -0
- package/src/entity-retrieval.ts +774 -0
- package/src/entity-schema.ts +239 -0
- package/src/evals.ts +1312 -0
- package/src/event-order-recall.test.ts +4164 -0
- package/src/event-order-recall.ts +2802 -0
- package/src/evidence-pack.test.ts +89 -0
- package/src/evidence-pack.ts +388 -0
- package/src/explicit-capture.ts +530 -0
- package/src/explicit-cue-recall.test.ts +3019 -0
- package/src/explicit-cue-recall.ts +5545 -0
- package/src/extraction-judge-telemetry.ts +234 -0
- package/src/extraction-judge-training.ts +221 -0
- package/src/extraction-judge.ts +846 -0
- package/src/extraction-timeout.test.ts +265 -0
- package/src/extraction.ts +2719 -0
- package/src/fallback-llm.test.ts +1060 -0
- package/src/fallback-llm.ts +918 -0
- package/src/focused-list-recall.test.ts +734 -0
- package/src/focused-list-recall.ts +1160 -0
- package/src/graph-dashboard-diff.ts +35 -0
- package/src/graph-dashboard-key.ts +5 -0
- package/src/graph-dashboard-parser.ts +104 -0
- package/src/graph-edge-reinforcement.ts +192 -0
- package/src/graph-events.ts +151 -0
- package/src/graph-recall.test.ts +164 -0
- package/src/graph-recall.ts +189 -0
- package/src/graph-retrieval.test.ts +809 -0
- package/src/graph-retrieval.ts +823 -0
- package/src/graph-snapshot.ts +329 -0
- package/src/graph.ts +813 -0
- package/src/harmonic-retrieval.ts +223 -0
- package/src/himem.ts +154 -0
- package/src/hygiene.ts +87 -0
- package/src/identity-continuity.ts +333 -0
- package/src/importance.ts +328 -0
- package/src/importers/base.test.ts +294 -0
- package/src/importers/base.ts +436 -0
- package/src/importers/index.ts +21 -0
- package/src/index.ts +1204 -0
- package/src/intent.ts +154 -0
- package/src/json-extract.ts +85 -0
- package/src/json-store.ts +42 -0
- package/src/lcm/archive.ts +617 -0
- package/src/lcm/dag.ts +199 -0
- package/src/lcm/engine.ts +645 -0
- package/src/lcm/index.ts +7 -0
- package/src/lcm/queue.test.ts +178 -0
- package/src/lcm/queue.ts +200 -0
- package/src/lcm/recall.ts +117 -0
- package/src/lcm/schema.ts +154 -0
- package/src/lcm/summarizer.ts +235 -0
- package/src/lcm/tools.ts +191 -0
- package/src/lcm-engine.test.ts +660 -0
- package/src/legacy-hook-compat.test.ts +20 -0
- package/src/legacy-hook-compat.ts +45 -0
- package/src/lifecycle.ts +289 -0
- package/src/live-connectors-runner.ts +385 -0
- package/src/local-llm-qos.test.ts +303 -0
- package/src/local-llm-thinking.test.ts +292 -0
- package/src/local-llm.ts +1464 -0
- package/src/logger.ts +49 -0
- package/src/maintenance/archive-observations.ts +147 -0
- package/src/maintenance/backup-stamp.ts +3 -0
- package/src/maintenance/dreams-ledger.ts +516 -0
- package/src/maintenance/first-start-migration.ts +362 -0
- package/src/maintenance/forget.test.ts +206 -0
- package/src/maintenance/forget.ts +126 -0
- package/src/maintenance/graph-edge-decay.test.ts +409 -0
- package/src/maintenance/graph-edge-decay.ts +394 -0
- package/src/maintenance/memory-governance-cron.ts +447 -0
- package/src/maintenance/memory-governance.ts +1039 -0
- package/src/maintenance/migrate-observations.ts +216 -0
- package/src/maintenance/observation-ledger-utils.ts +54 -0
- package/src/maintenance/pattern-reinforcement.test.ts +875 -0
- package/src/maintenance/pattern-reinforcement.ts +369 -0
- package/src/maintenance/purge.ts +334 -0
- package/src/maintenance/rebuild-memory-lifecycle-ledger.ts +78 -0
- package/src/maintenance/rebuild-memory-projection.ts +1234 -0
- package/src/maintenance/rebuild-observations.ts +178 -0
- package/src/maintenance/tier-stats.test.ts +378 -0
- package/src/maintenance/tier-stats.ts +222 -0
- package/src/mcp-memory-inspector-app.ts +421 -0
- package/src/memory-action-policy.ts +80 -0
- package/src/memory-cache.ts +208 -0
- package/src/memory-extension/claude-code-publisher.ts +51 -0
- package/src/memory-extension/codex-publisher.ts +149 -0
- package/src/memory-extension/hermes-publisher.ts +51 -0
- package/src/memory-extension/index.ts +100 -0
- package/src/memory-extension/shared-instructions.ts +133 -0
- package/src/memory-extension/types.ts +86 -0
- package/src/memory-extension-host/host-discovery.ts +276 -0
- package/src/memory-extension-host/index.ts +14 -0
- package/src/memory-extension-host/render-extensions-block.ts +73 -0
- package/src/memory-extension-host/types.ts +21 -0
- package/src/memory-lifecycle-ledger-utils.ts +116 -0
- package/src/memory-projection-format.ts +11 -0
- package/src/memory-projection-store.ts +951 -0
- package/src/memory-provenance.test.ts +196 -0
- package/src/memory-provenance.ts +484 -0
- package/src/memory-worth-bench.test.ts +71 -0
- package/src/memory-worth-bench.ts +265 -0
- package/src/memory-worth-filter.test.ts +209 -0
- package/src/memory-worth-filter.ts +204 -0
- package/src/memory-worth-frontmatter.test.ts +311 -0
- package/src/memory-worth-outcomes.test.ts +316 -0
- package/src/memory-worth-outcomes.ts +286 -0
- package/src/memory-worth.test.ts +317 -0
- package/src/memory-worth.ts +215 -0
- package/src/message-parts/index.ts +806 -0
- package/src/message-parts/message-parts.test.ts +421 -0
- package/src/migrate/from-engram.ts +789 -0
- package/src/model-registry.ts +313 -0
- package/src/models-json.ts +76 -0
- package/src/namespaces/migrate.ts +187 -0
- package/src/namespaces/path.ts +25 -0
- package/src/namespaces/principal.test.ts +195 -0
- package/src/namespaces/principal.ts +86 -0
- package/src/namespaces/search.test.ts +105 -0
- package/src/namespaces/search.ts +233 -0
- package/src/namespaces/storage.ts +74 -0
- package/src/native-knowledge.ts +1823 -0
- package/src/negative.ts +72 -0
- package/src/network/tailscale.ts +179 -0
- package/src/network/webdav.ts +385 -0
- package/src/objective-state-writers.ts +951 -0
- package/src/objective-state.ts +320 -0
- package/src/onboarding/index.ts +529 -0
- package/src/openai-chat-compat.ts +56 -0
- package/src/operator-toolkit.ts +2132 -0
- package/src/opik-exporter.test.ts +72 -0
- package/src/opik-exporter.ts +587 -0
- package/src/orchestrator-extraction-queue.test.ts +197 -0
- package/src/orchestrator-flush.test.ts +1171 -0
- package/src/orchestrator-pattern-reinforcement.test.ts +128 -0
- package/src/orchestrator-source-attribution.test.ts +701 -0
- package/src/orchestrator.ts +16368 -0
- package/src/page-versioning.ts +450 -0
- package/src/patterns-cli.ts +574 -0
- package/src/peers/index.ts +54 -0
- package/src/peers/migrate-from-identity-anchor.test.ts +291 -0
- package/src/peers/migrate-from-identity-anchor.ts +350 -0
- package/src/peers/peers.test.ts +419 -0
- package/src/peers/profile-reasoner.ts +694 -0
- package/src/peers/storage.ts +1350 -0
- package/src/peers/types.ts +138 -0
- package/src/plugin-id.ts +84 -0
- package/src/policy-runtime.ts +209 -0
- package/src/procedural/procedure-miner.ts +150 -0
- package/src/procedural/procedure-recall.ts +93 -0
- package/src/procedural/procedure-stats.ts +213 -0
- package/src/procedural/procedure-types.ts +132 -0
- package/src/procedural/reinforcement-core.test.ts +132 -0
- package/src/procedural/reinforcement-core.ts +73 -0
- package/src/profiling.test.ts +263 -0
- package/src/profiling.ts +435 -0
- package/src/projection/index.ts +398 -0
- package/src/qmd-recall-cache.test.ts +138 -0
- package/src/qmd-recall-cache.ts +111 -0
- package/src/qmd.test.ts +257 -0
- package/src/qmd.ts +2614 -0
- package/src/reasoning-trace-recall.ts +201 -0
- package/src/reasoning-trace-types.ts +235 -0
- package/src/recall-audit-anomaly.test.ts +246 -0
- package/src/recall-audit-anomaly.ts +297 -0
- package/src/recall-audit.test.ts +51 -0
- package/src/recall-audit.ts +72 -0
- package/src/recall-budget-config.test.ts +87 -0
- package/src/recall-disclosure-escalation.test.ts +196 -0
- package/src/recall-disclosure-escalation.ts +158 -0
- package/src/recall-disclosure-shaping.test.ts +146 -0
- package/src/recall-disclosure.test.ts +214 -0
- package/src/recall-explain-renderer.test.ts +140 -0
- package/src/recall-explain-renderer.ts +356 -0
- package/src/recall-mmr.test.ts +808 -0
- package/src/recall-mmr.ts +607 -0
- package/src/recall-qos.test.ts +85 -0
- package/src/recall-qos.ts +82 -0
- package/src/recall-query-policy.ts +221 -0
- package/src/recall-state.test.ts +233 -0
- package/src/recall-state.ts +456 -0
- package/src/recall-tag-filter.ts +143 -0
- package/src/recall-tokenization.ts +35 -0
- package/src/recall-xray-cli.test.ts +118 -0
- package/src/recall-xray-cli.ts +100 -0
- package/src/recall-xray-disclosure-telemetry.test.ts +183 -0
- package/src/recall-xray-renderer.test.ts +539 -0
- package/src/recall-xray-renderer.ts +487 -0
- package/src/recall-xray.test.ts +503 -0
- package/src/recall-xray.ts +621 -0
- package/src/reconstruct.ts +41 -0
- package/src/release-changelog.ts +35 -0
- package/src/relevance.ts +67 -0
- package/src/replay/normalizers/chatgpt.ts +133 -0
- package/src/replay/normalizers/claude.ts +102 -0
- package/src/replay/normalizers/openclaw.ts +119 -0
- package/src/replay/normalizers/shared.ts +69 -0
- package/src/replay/runner.ts +197 -0
- package/src/replay/types.ts +143 -0
- package/src/rerank.test.ts +48 -0
- package/src/rerank.ts +176 -0
- package/src/resolve-auth-token.test.ts +226 -0
- package/src/resolve-auth-token.ts +151 -0
- package/src/resolve-provider-secret.test.ts +187 -0
- package/src/resolve-provider-secret.ts +410 -0
- package/src/response-guidance-recall.test.ts +3952 -0
- package/src/response-guidance-recall.ts +4431 -0
- package/src/resume-bundles.ts +415 -0
- package/src/retrieval-agents.ts +623 -0
- package/src/retrieval-tiers.ts +25 -0
- package/src/retrieval.ts +104 -0
- package/src/review/index.test.ts +201 -0
- package/src/review/index.ts +536 -0
- package/src/routing/engine.ts +162 -0
- package/src/routing/store.ts +321 -0
- package/src/runtime/better-sqlite.test.ts +32 -0
- package/src/runtime/better-sqlite.ts +76 -0
- package/src/runtime/child-process.ts +67 -0
- package/src/runtime/env.ts +48 -0
- package/src/sanitize.ts +58 -0
- package/src/schemas.ts +449 -0
- package/src/sdk-compat.ts +87 -0
- package/src/search/document-scanner.ts +96 -0
- package/src/search/embed-helper.ts +142 -0
- package/src/search/factory.ts +189 -0
- package/src/search/index.ts +10 -0
- package/src/search/lancedb-backend.ts +342 -0
- package/src/search/meilisearch-backend.ts +232 -0
- package/src/search/noop-backend.ts +57 -0
- package/src/search/orama-backend.ts +358 -0
- package/src/search/port.ts +86 -0
- package/src/search/remote-backend.ts +124 -0
- package/src/secure-store/cipher.ts +271 -0
- package/src/secure-store/cli-handlers.ts +355 -0
- package/src/secure-store/cli-renderer.ts +131 -0
- package/src/secure-store/header.ts +373 -0
- package/src/secure-store/index.ts +137 -0
- package/src/secure-store/kdf.ts +263 -0
- package/src/secure-store/keyring.ts +106 -0
- package/src/secure-store/metadata.ts +394 -0
- package/src/secure-store/passphrase-reader.ts +252 -0
- package/src/secure-store/secure-fs.ts +571 -0
- package/src/secure-store/secure-store.test.ts +755 -0
- package/src/semantic-chunking.ts +545 -0
- package/src/semantic-consolidation.test.ts +182 -0
- package/src/semantic-consolidation.ts +432 -0
- package/src/semantic-rule-promotion.ts +183 -0
- package/src/semantic-rule-verifier.ts +160 -0
- package/src/session-integrity.ts +569 -0
- package/src/session-observer-bands.ts +11 -0
- package/src/session-observer-state.ts +346 -0
- package/src/session-toggles.test.ts +96 -0
- package/src/session-toggles.ts +159 -0
- package/src/shared-context/manager.ts +810 -0
- package/src/signal.ts +84 -0
- package/src/skills-registry.test.ts +277 -0
- package/src/skills-registry.ts +120 -0
- package/src/source-attribution-roundtrip.test.ts +215 -0
- package/src/source-attribution.test.ts +1425 -0
- package/src/source-attribution.ts +639 -0
- package/src/spaces/index.ts +627 -0
- package/src/storage-paths.ts +117 -0
- package/src/storage.ts +6657 -0
- package/src/store-contract.ts +55 -0
- package/src/summarizer.ts +844 -0
- package/src/summary-snapshot.test.ts +681 -0
- package/src/summary-snapshot.ts +238 -0
- package/src/surfaces/dreams.test.ts +394 -0
- package/src/surfaces/dreams.ts +346 -0
- package/src/surfaces/heartbeat.test.ts +415 -0
- package/src/surfaces/heartbeat.ts +325 -0
- package/src/sync/index.ts +308 -0
- package/src/targeted-fact-recall.test.ts +1694 -0
- package/src/targeted-fact-recall.ts +2905 -0
- package/src/taxonomy/default-taxonomy.ts +87 -0
- package/src/taxonomy/index.ts +26 -0
- package/src/taxonomy/resolver-doc-generator.ts +57 -0
- package/src/taxonomy/resolver.ts +184 -0
- package/src/taxonomy/taxonomy-loader.ts +186 -0
- package/src/taxonomy/types.ts +48 -0
- package/src/telemetry-transcript.ts +70 -0
- package/src/temporal-index.ts +890 -0
- package/src/temporal-supersession.test.ts +2703 -0
- package/src/temporal-supersession.ts +493 -0
- package/src/temporal-validity.test.ts +448 -0
- package/src/temporal-validity.ts +123 -0
- package/src/threading.ts +395 -0
- package/src/tier-migration.ts +124 -0
- package/src/tier-routing.ts +102 -0
- package/src/tmt.ts +462 -0
- package/src/tokens.test.ts +178 -0
- package/src/tokens.ts +279 -0
- package/src/topics.ts +147 -0
- package/src/training-export/cli-date-validation.test.ts +258 -0
- package/src/training-export/converter.test.ts +452 -0
- package/src/training-export/converter.ts +319 -0
- package/src/training-export/date-parse.ts +117 -0
- package/src/training-export/index.ts +26 -0
- package/src/training-export/registry.test.ts +85 -0
- package/src/training-export/registry.ts +57 -0
- package/src/training-export/types.ts +31 -0
- package/src/transcript.ts +1179 -0
- package/src/transfer/autodetect.ts +30 -0
- package/src/transfer/backup.ts +138 -0
- package/src/transfer/capsule-crypto.ts +485 -0
- package/src/transfer/capsule-encrypt.test.ts +690 -0
- package/src/transfer/capsule-export.ts +543 -0
- package/src/transfer/capsule-fork.ts +375 -0
- package/src/transfer/capsule-import.ts +564 -0
- package/src/transfer/capsule-merge.ts +433 -0
- package/src/transfer/conflict-policy.ts +16 -0
- package/src/transfer/constants.ts +13 -0
- package/src/transfer/exclusions.ts +37 -0
- package/src/transfer/export-json.ts +65 -0
- package/src/transfer/export-md.ts +59 -0
- package/src/transfer/export-sqlite.ts +52 -0
- package/src/transfer/fs-utils.ts +269 -0
- package/src/transfer/import-json.ts +108 -0
- package/src/transfer/import-md.ts +84 -0
- package/src/transfer/import-sqlite.ts +100 -0
- package/src/transfer/integrity.ts +71 -0
- package/src/transfer/sqlite-schema.ts +16 -0
- package/src/transfer/types.ts +297 -0
- package/src/trust-zones.ts +1186 -0
- package/src/types.ts +3074 -0
- package/src/user-model.test.ts +124 -0
- package/src/user-model.ts +162 -0
- package/src/utility-learner.ts +353 -0
- package/src/utility-runtime.ts +88 -0
- package/src/utility-telemetry.ts +215 -0
- package/src/utils/category-dir.ts +44 -0
- package/src/utils/errno.ts +6 -0
- package/src/utils/iso-timestamp.test.ts +37 -0
- package/src/utils/iso-timestamp.ts +164 -0
- package/src/utils/path.ts +26 -0
- package/src/verified-recall.ts +138 -0
- package/src/version-utils.test.ts +10 -0
- package/src/version-utils.ts +9 -0
- package/src/whitespace.ts +10 -0
- package/src/work/board.ts +359 -0
- package/src/work/boundary.ts +107 -0
- package/src/work/storage.ts +436 -0
- package/src/work/types.ts +82 -0
- package/src/work-product-ledger.ts +265 -0
- package/dist/access-service-DDjzFALq.d.ts +0 -2088
- package/dist/capsule-crypto-SJS5VVAP.js +0 -18
- package/dist/capsule-export-7QNCBZOQ.js +0 -17
- package/dist/capsule-import-EPBHD2EN.js +0 -16
- package/dist/capsule-merge-DI7PNQ2H.js +0 -189
- package/dist/chunk-23ZZK64Y.js +0 -26
- package/dist/chunk-23ZZK64Y.js.map +0 -1
- package/dist/chunk-242S3I2A.js +0 -647
- package/dist/chunk-2LGMW3DJ.js +0 -111
- package/dist/chunk-3B6KIRBH.js +0 -5213
- package/dist/chunk-3B6KIRBH.js.map +0 -1
- package/dist/chunk-457A4P3L.js +0 -119
- package/dist/chunk-457A4P3L.js.map +0 -1
- package/dist/chunk-4IS4SXIQ.js +0 -2040
- package/dist/chunk-4YM32CRU.js +0 -721
- package/dist/chunk-6TBWYBJ3.js +0 -236
- package/dist/chunk-74EMIVE4.js +0 -329
- package/dist/chunk-74EMIVE4.js.map +0 -1
- package/dist/chunk-767ODGE6.js +0 -183
- package/dist/chunk-7V22HTMD.js +0 -623
- package/dist/chunk-7V22HTMD.js.map +0 -1
- package/dist/chunk-7ZM3BFKK.js +0 -9705
- package/dist/chunk-7ZM3BFKK.js.map +0 -1
- package/dist/chunk-AQJNPMOA.js +0 -643
- package/dist/chunk-AQJNPMOA.js.map +0 -1
- package/dist/chunk-ASAITVLA.js +0 -64
- package/dist/chunk-ASAITVLA.js.map +0 -1
- package/dist/chunk-BBE34QBJ.js +0 -275
- package/dist/chunk-BBE34QBJ.js.map +0 -1
- package/dist/chunk-BZSQEPRW.js +0 -14710
- package/dist/chunk-BZSQEPRW.js.map +0 -1
- package/dist/chunk-CPKTBRS2.js +0 -891
- package/dist/chunk-CPKTBRS2.js.map +0 -1
- package/dist/chunk-D4GAOFF6.js +0 -562
- package/dist/chunk-D4GAOFF6.js.map +0 -1
- package/dist/chunk-D54LZC5L.js +0 -147
- package/dist/chunk-DF3RVK3X.js +0 -119
- package/dist/chunk-DF3RVK3X.js.map +0 -1
- package/dist/chunk-DZZPC36E.js +0 -1451
- package/dist/chunk-DZZPC36E.js.map +0 -1
- package/dist/chunk-E2UCDP5S.js +0 -570
- package/dist/chunk-E6K4NIEU.js +0 -747
- package/dist/chunk-E6K4NIEU.js.map +0 -1
- package/dist/chunk-EEQLFRUM.js +0 -89
- package/dist/chunk-ETOW6ACV.js +0 -158
- package/dist/chunk-ETOW6ACV.js.map +0 -1
- package/dist/chunk-FMEBPEAO.js +0 -347
- package/dist/chunk-FMEBPEAO.js.map +0 -1
- package/dist/chunk-FQDPCE3I.js +0 -1837
- package/dist/chunk-FQDPCE3I.js.map +0 -1
- package/dist/chunk-FYIYMQ5N.js +0 -221
- package/dist/chunk-FYIYMQ5N.js.map +0 -1
- package/dist/chunk-G2WADRQ3.js +0 -219
- package/dist/chunk-G4SK7DSQ.js +0 -121
- package/dist/chunk-GVPWB7EY.js +0 -390
- package/dist/chunk-GVPWB7EY.js.map +0 -1
- package/dist/chunk-HELQZFZO.js +0 -1075
- package/dist/chunk-HL5LRPNA.js +0 -1914
- package/dist/chunk-HL5LRPNA.js.map +0 -1
- package/dist/chunk-HQZVVSVB.js +0 -147
- package/dist/chunk-HQZVVSVB.js.map +0 -1
- package/dist/chunk-HY3L4WKC.js +0 -2195
- package/dist/chunk-HY3L4WKC.js.map +0 -1
- package/dist/chunk-IB3BFHGN.js +0 -228
- package/dist/chunk-IXEJRKCZ.js +0 -18
- package/dist/chunk-JBMSGZEQ.js +0 -441
- package/dist/chunk-JBMSGZEQ.js.map +0 -1
- package/dist/chunk-JESOB2HO.js +0 -108
- package/dist/chunk-JKDVIE52.js +0 -272
- package/dist/chunk-JRNQ3RNA.js +0 -284
- package/dist/chunk-JRNQ3RNA.js.map +0 -1
- package/dist/chunk-K6WK37A6.js +0 -865
- package/dist/chunk-K6WK37A6.js.map +0 -1
- package/dist/chunk-MARWOCVP.js +0 -48
- package/dist/chunk-MNU6ZBWT.js +0 -4454
- package/dist/chunk-MNU6ZBWT.js.map +0 -1
- package/dist/chunk-N5AKDXAI.js +0 -74
- package/dist/chunk-OA3L7BFR.js +0 -183
- package/dist/chunk-OA3L7BFR.js.map +0 -1
- package/dist/chunk-OR64ZGRZ.js +0 -23
- package/dist/chunk-P77UEOU2.js +0 -1521
- package/dist/chunk-P77UEOU2.js.map +0 -1
- package/dist/chunk-PH4C2U43.js +0 -239
- package/dist/chunk-PH4C2U43.js.map +0 -1
- package/dist/chunk-RVPLBATS.js +0 -1586
- package/dist/chunk-RVPLBATS.js.map +0 -1
- package/dist/chunk-U5JMRGKX.js +0 -340
- package/dist/chunk-U5JMRGKX.js.map +0 -1
- package/dist/chunk-URB2WSKZ.js +0 -350
- package/dist/chunk-URB2WSKZ.js.map +0 -1
- package/dist/chunk-UVMUAWVT.js +0 -596
- package/dist/chunk-WEJG4TB5.js +0 -118
- package/dist/chunk-X7HPGUVG.js +0 -271
- package/dist/chunk-XAMBKFQS.js +0 -2777
- package/dist/chunk-XAMBKFQS.js.map +0 -1
- package/dist/chunk-XJKFSSDW.js +0 -726
- package/dist/chunk-XJKFSSDW.js.map +0 -1
- package/dist/chunk-XMHBH5H6.js +0 -283
- package/dist/chunk-XMHBH5H6.js.map +0 -1
- package/dist/chunk-XMVFHBHT.js +0 -277
- package/dist/chunk-Y3VMVTYX.js +0 -53
- package/dist/chunk-YNB73F22.js +0 -137
- package/dist/chunk-YNB73F22.js.map +0 -1
- package/dist/chunk-Z2E7VW55.js +0 -335
- package/dist/chunk-Z2E7VW55.js.map +0 -1
- package/dist/chunk-ZG7PTKBK.js +0 -2296
- package/dist/chunk-ZNQN6ZTA.js +0 -135
- package/dist/chunk-ZVTKDVVM.js +0 -827
- package/dist/chunk-ZVTKDVVM.js.map +0 -1
- package/dist/cli-BR8KpIU0.d.ts +0 -1259
- package/dist/codex-materialize-CQlLTzke.d.ts +0 -139
- package/dist/connectors-cli-DFGtY2DB.d.ts +0 -257
- package/dist/contradiction-review-5LTTVDQV.js +0 -22
- package/dist/contradiction-scan-QTXAMBUA.js +0 -414
- package/dist/contradiction-scan-QTXAMBUA.js.map +0 -1
- package/dist/engine-35M5BKQ7.js +0 -28
- package/dist/fs-utils-IRVUFB6G.js +0 -30
- package/dist/graph-edge-decay-PWB63GRE.js +0 -207
- package/dist/memory-governance-IMPQZXFC.js +0 -37
- package/dist/memory-projection-store-CY8TU40w.d.ts +0 -222
- package/dist/orchestrator-DDMPqU6R.d.ts +0 -1792
- package/dist/path-RMTY5Y5A.js +0 -9
- package/dist/port-B6VEDIkC.d.ts +0 -53
- package/dist/resolution-YGIBORXI.js +0 -101
- package/dist/resolution-YGIBORXI.js.map +0 -1
- package/dist/secure-store-4R2GSO7S.js +0 -156
- package/dist/semantic-consolidation-ByBXb-sf.d.ts +0 -180
- package/dist/state-store-3EH7HYIN.js +0 -16
- package/dist/types-V3FJ26TF.js +0 -30
- /package/dist/{capsule-crypto-SJS5VVAP.js.map → adapters/claude-code.js.map} +0 -0
- /package/dist/{capsule-export-7QNCBZOQ.js.map → adapters/codex.js.map} +0 -0
- /package/dist/{capsule-import-EPBHD2EN.js.map → adapters/hermes.js.map} +0 -0
- /package/dist/{contradiction-review-5LTTVDQV.js.map → adapters/index.js.map} +0 -0
- /package/dist/{engine-35M5BKQ7.js.map → adapters/registry.js.map} +0 -0
- /package/dist/{fs-utils-IRVUFB6G.js.map → adapters/replit.js.map} +0 -0
- /package/dist/{memory-governance-IMPQZXFC.js.map → adapters/types.js.map} +0 -0
- /package/dist/{path-RMTY5Y5A.js.map → capsule-crypto-5CYAGVC5.js.map} +0 -0
- /package/dist/{capsule-merge-DI7PNQ2H.js.map → capsule-merge-4MGKE7C5.js.map} +0 -0
- /package/dist/{chunk-G4SK7DSQ.js.map → chunk-2WWLHTZY.js.map} +0 -0
- /package/dist/{chunk-X7HPGUVG.js.map → chunk-4CRG46BG.js.map} +0 -0
- /package/dist/{chunk-UVMUAWVT.js.map → chunk-7IASACLB.js.map} +0 -0
- /package/dist/{chunk-HELQZFZO.js.map → chunk-EDTHC6UD.js.map} +0 -0
- /package/dist/{chunk-4YM32CRU.js.map → chunk-EFJ3MQ4V.js.map} +0 -0
- /package/dist/{chunk-E2UCDP5S.js.map → chunk-FBYESMQ2.js.map} +0 -0
- /package/dist/{chunk-D54LZC5L.js.map → chunk-FDU6HUUL.js.map} +0 -0
- /package/dist/{chunk-IB3BFHGN.js.map → chunk-GGKRUQOO.js.map} +0 -0
- /package/dist/{chunk-242S3I2A.js.map → chunk-GL6I6MEQ.js.map} +0 -0
- /package/dist/{secure-store-4R2GSO7S.js.map → chunk-HHLLAQGZ.js.map} +0 -0
- /package/dist/{chunk-4IS4SXIQ.js.map → chunk-HXXBL2KD.js.map} +0 -0
- /package/dist/{chunk-767ODGE6.js.map → chunk-KNKUID7G.js.map} +0 -0
- /package/dist/{chunk-6TBWYBJ3.js.map → chunk-LPMVBPA3.js.map} +0 -0
- /package/dist/{chunk-WEJG4TB5.js.map → chunk-MC26UJIM.js.map} +0 -0
- /package/dist/{chunk-JKDVIE52.js.map → chunk-MGKYQQYF.js.map} +0 -0
- /package/dist/{chunk-Y3VMVTYX.js.map → chunk-MT4HVDUZ.js.map} +0 -0
- /package/dist/{chunk-G2WADRQ3.js.map → chunk-MY6TPVXW.js.map} +0 -0
- /package/dist/{chunk-OR64ZGRZ.js.map → chunk-NNVTUXEB.js.map} +0 -0
- /package/dist/{chunk-JESOB2HO.js.map → chunk-P4NEIHUT.js.map} +0 -0
- /package/dist/{chunk-IXEJRKCZ.js.map → chunk-QRNI5JBH.js.map} +0 -0
- /package/dist/{chunk-EEQLFRUM.js.map → chunk-RRF5UOBJ.js.map} +0 -0
- /package/dist/{state-store-3EH7HYIN.js.map → chunk-SEDEKFYQ.js.map} +0 -0
- /package/dist/{chunk-2LGMW3DJ.js.map → chunk-U3PN77QT.js.map} +0 -0
- /package/dist/{chunk-XMVFHBHT.js.map → chunk-U3WSW6PZ.js.map} +0 -0
- /package/dist/{chunk-N5AKDXAI.js.map → chunk-UWVJF25J.js.map} +0 -0
- /package/dist/{types-V3FJ26TF.js.map → chunk-V5OCT34X.js.map} +0 -0
- /package/dist/{chunk-ZG7PTKBK.js.map → chunk-W3LR522O.js.map} +0 -0
- /package/dist/{chunk-MARWOCVP.js.map → chunk-XIG5PDM7.js.map} +0 -0
- /package/dist/{chunk-ZNQN6ZTA.js.map → chunk-XVZ7B3HG.js.map} +0 -0
- /package/dist/{graph-edge-decay-PWB63GRE.js.map → graph-edge-decay-5DI5GUNL.js.map} +0 -0
|
@@ -0,0 +1,1218 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import test from "node:test";
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
GITHUB_CONNECTOR_ID,
|
|
6
|
+
GITHUB_CURSOR_KIND,
|
|
7
|
+
GITHUB_DEFAULT_POLL_INTERVAL_MS,
|
|
8
|
+
createGitHubConnector,
|
|
9
|
+
isTransientGitHubError,
|
|
10
|
+
validateGitHubConfig,
|
|
11
|
+
type GitHubComment,
|
|
12
|
+
type GitHubFetchFn,
|
|
13
|
+
type GitHubSyncResult,
|
|
14
|
+
} from "./github.js";
|
|
15
|
+
import type { ConnectorCursor } from "./framework.js";
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Tests for the GitHub connector (#683 PR 5/6). All GitHub API calls are
|
|
19
|
+
* stubbed via the `fetchFn` test hook — the suite never touches the network.
|
|
20
|
+
*
|
|
21
|
+
* Per CLAUDE.md privacy rules: no real tokens, no real usernames, no real
|
|
22
|
+
* repo names. All inputs are obviously synthetic.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
// Synthetic test data
|
|
27
|
+
// ---------------------------------------------------------------------------
|
|
28
|
+
|
|
29
|
+
const SYNTHETIC_TOKEN = "ghp_synthetic_token_DO_NOT_USE_0000000000000000";
|
|
30
|
+
const SYNTHETIC_LOGIN = "synthetic-user";
|
|
31
|
+
const REPO_A = "synthetic-org/repo-alpha";
|
|
32
|
+
const REPO_B = "synthetic-org/repo-beta";
|
|
33
|
+
|
|
34
|
+
const SYNTHETIC_CONFIG = Object.freeze({
|
|
35
|
+
token: SYNTHETIC_TOKEN,
|
|
36
|
+
userLogin: SYNTHETIC_LOGIN,
|
|
37
|
+
repos: [REPO_A],
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
function makeComment(
|
|
41
|
+
id: number,
|
|
42
|
+
login: string,
|
|
43
|
+
body: string,
|
|
44
|
+
updatedAt: string,
|
|
45
|
+
htmlUrl?: string,
|
|
46
|
+
): GitHubComment {
|
|
47
|
+
return {
|
|
48
|
+
id,
|
|
49
|
+
body,
|
|
50
|
+
user: { login },
|
|
51
|
+
created_at: updatedAt,
|
|
52
|
+
updated_at: updatedAt,
|
|
53
|
+
html_url: htmlUrl ?? `https://github.com/${REPO_A}/issues/1#issuecomment-${id}`,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// ---------------------------------------------------------------------------
|
|
58
|
+
// Mock fetch builder
|
|
59
|
+
// ---------------------------------------------------------------------------
|
|
60
|
+
|
|
61
|
+
type HandlerEntry = {
|
|
62
|
+
match: (url: string) => boolean;
|
|
63
|
+
respond: (url: string) => { status: number; data: unknown };
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
function makeFetch(handlers: HandlerEntry[]): GitHubFetchFn {
|
|
67
|
+
return async (url) => {
|
|
68
|
+
for (const handler of handlers) {
|
|
69
|
+
if (handler.match(url)) {
|
|
70
|
+
const { status, data } = handler.respond(url);
|
|
71
|
+
return {
|
|
72
|
+
ok: status >= 200 && status < 300,
|
|
73
|
+
status,
|
|
74
|
+
headers: {
|
|
75
|
+
get: (_name: string) => null,
|
|
76
|
+
},
|
|
77
|
+
json: async () => data,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
throw new Error(`fetch stub: no handler for ${url}`);
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** Returns an empty array for all API calls. */
|
|
86
|
+
function emptyFetch(): GitHubFetchFn {
|
|
87
|
+
return makeFetch([
|
|
88
|
+
{
|
|
89
|
+
match: () => true,
|
|
90
|
+
respond: () => ({ status: 200, data: [] }),
|
|
91
|
+
},
|
|
92
|
+
]);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function makeGitHubCursor(watermarks: Record<string, string>): ConnectorCursor {
|
|
96
|
+
return {
|
|
97
|
+
kind: GITHUB_CURSOR_KIND,
|
|
98
|
+
value: JSON.stringify({ watermarks }),
|
|
99
|
+
updatedAt: "2026-04-25T00:00:00.000Z",
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// ---------------------------------------------------------------------------
|
|
104
|
+
// Config validation
|
|
105
|
+
// ---------------------------------------------------------------------------
|
|
106
|
+
|
|
107
|
+
test("validateGitHubConfig accepts a minimal valid config", () => {
|
|
108
|
+
const cfg = validateGitHubConfig({
|
|
109
|
+
token: SYNTHETIC_TOKEN,
|
|
110
|
+
userLogin: SYNTHETIC_LOGIN,
|
|
111
|
+
});
|
|
112
|
+
assert.equal(cfg.token, SYNTHETIC_TOKEN);
|
|
113
|
+
assert.equal(cfg.userLogin, SYNTHETIC_LOGIN);
|
|
114
|
+
assert.deepEqual([...cfg.repos], []);
|
|
115
|
+
assert.equal(cfg.pollIntervalMs, GITHUB_DEFAULT_POLL_INTERVAL_MS);
|
|
116
|
+
assert.equal(cfg.includeDiscussions, false);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
test("validateGitHubConfig rejects non-object input", () => {
|
|
120
|
+
assert.throws(() => validateGitHubConfig(null), /must be an object/);
|
|
121
|
+
assert.throws(() => validateGitHubConfig([]), /must be an object/);
|
|
122
|
+
assert.throws(() => validateGitHubConfig("nope"), /must be an object/);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
test("validateGitHubConfig rejects missing or empty token", () => {
|
|
126
|
+
assert.throws(() => validateGitHubConfig({ userLogin: SYNTHETIC_LOGIN }), /token must be a string/);
|
|
127
|
+
assert.throws(
|
|
128
|
+
() => validateGitHubConfig({ token: "", userLogin: SYNTHETIC_LOGIN }),
|
|
129
|
+
/token must be non-empty/,
|
|
130
|
+
);
|
|
131
|
+
assert.throws(
|
|
132
|
+
() => validateGitHubConfig({ token: " ", userLogin: SYNTHETIC_LOGIN }),
|
|
133
|
+
/token must be non-empty/,
|
|
134
|
+
);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
test("validateGitHubConfig rejects missing or empty userLogin", () => {
|
|
138
|
+
assert.throws(
|
|
139
|
+
() => validateGitHubConfig({ token: SYNTHETIC_TOKEN }),
|
|
140
|
+
/userLogin must be a string/,
|
|
141
|
+
);
|
|
142
|
+
assert.throws(
|
|
143
|
+
() => validateGitHubConfig({ token: SYNTHETIC_TOKEN, userLogin: "" }),
|
|
144
|
+
/userLogin must be non-empty/,
|
|
145
|
+
);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
test("validateGitHubConfig rejects malformed pollIntervalMs", () => {
|
|
149
|
+
assert.throws(
|
|
150
|
+
() =>
|
|
151
|
+
validateGitHubConfig({ token: SYNTHETIC_TOKEN, userLogin: SYNTHETIC_LOGIN, pollIntervalMs: "300000" }),
|
|
152
|
+
/pollIntervalMs/,
|
|
153
|
+
);
|
|
154
|
+
assert.throws(
|
|
155
|
+
() =>
|
|
156
|
+
validateGitHubConfig({ token: SYNTHETIC_TOKEN, userLogin: SYNTHETIC_LOGIN, pollIntervalMs: 50 }),
|
|
157
|
+
/≥1000/,
|
|
158
|
+
);
|
|
159
|
+
assert.throws(
|
|
160
|
+
() =>
|
|
161
|
+
validateGitHubConfig({
|
|
162
|
+
token: SYNTHETIC_TOKEN,
|
|
163
|
+
userLogin: SYNTHETIC_LOGIN,
|
|
164
|
+
pollIntervalMs: 25 * 60 * 60 * 1000,
|
|
165
|
+
}),
|
|
166
|
+
/≤/,
|
|
167
|
+
);
|
|
168
|
+
assert.throws(
|
|
169
|
+
() =>
|
|
170
|
+
validateGitHubConfig({
|
|
171
|
+
token: SYNTHETIC_TOKEN,
|
|
172
|
+
userLogin: SYNTHETIC_LOGIN,
|
|
173
|
+
pollIntervalMs: 3000.5,
|
|
174
|
+
}),
|
|
175
|
+
/integer/,
|
|
176
|
+
);
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
test("validateGitHubConfig accepts valid repos in owner/repo format", () => {
|
|
180
|
+
const cfg = validateGitHubConfig({
|
|
181
|
+
token: SYNTHETIC_TOKEN,
|
|
182
|
+
userLogin: SYNTHETIC_LOGIN,
|
|
183
|
+
repos: [REPO_A, REPO_B],
|
|
184
|
+
});
|
|
185
|
+
assert.deepEqual([...cfg.repos], [REPO_A, REPO_B]);
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
test("validateGitHubConfig rejects malformed repo slugs", () => {
|
|
189
|
+
assert.throws(
|
|
190
|
+
() =>
|
|
191
|
+
validateGitHubConfig({
|
|
192
|
+
token: SYNTHETIC_TOKEN,
|
|
193
|
+
userLogin: SYNTHETIC_LOGIN,
|
|
194
|
+
repos: ["no-slash"],
|
|
195
|
+
}),
|
|
196
|
+
/owner\/repo/,
|
|
197
|
+
);
|
|
198
|
+
assert.throws(
|
|
199
|
+
() =>
|
|
200
|
+
validateGitHubConfig({
|
|
201
|
+
token: SYNTHETIC_TOKEN,
|
|
202
|
+
userLogin: SYNTHETIC_LOGIN,
|
|
203
|
+
repos: ["../../../etc/passwd"],
|
|
204
|
+
}),
|
|
205
|
+
/owner\/repo/,
|
|
206
|
+
);
|
|
207
|
+
assert.throws(
|
|
208
|
+
() =>
|
|
209
|
+
validateGitHubConfig({
|
|
210
|
+
token: SYNTHETIC_TOKEN,
|
|
211
|
+
userLogin: SYNTHETIC_LOGIN,
|
|
212
|
+
repos: [42 as unknown as string],
|
|
213
|
+
}),
|
|
214
|
+
/repos entries must be strings/,
|
|
215
|
+
);
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
test("validateGitHubConfig deduplicates repos", () => {
|
|
219
|
+
const cfg = validateGitHubConfig({
|
|
220
|
+
token: SYNTHETIC_TOKEN,
|
|
221
|
+
userLogin: SYNTHETIC_LOGIN,
|
|
222
|
+
repos: [REPO_A, REPO_A, REPO_B],
|
|
223
|
+
});
|
|
224
|
+
assert.deepEqual([...cfg.repos], [REPO_A, REPO_B]);
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
test("validateGitHubConfig rejects non-boolean includeDiscussions", () => {
|
|
228
|
+
assert.throws(
|
|
229
|
+
() =>
|
|
230
|
+
validateGitHubConfig({
|
|
231
|
+
token: SYNTHETIC_TOKEN,
|
|
232
|
+
userLogin: SYNTHETIC_LOGIN,
|
|
233
|
+
includeDiscussions: "yes" as unknown as boolean,
|
|
234
|
+
}),
|
|
235
|
+
/includeDiscussions must be a boolean/,
|
|
236
|
+
);
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
test("validateGitHubConfig accepts includeDiscussions: true", () => {
|
|
240
|
+
const cfg = validateGitHubConfig({
|
|
241
|
+
token: SYNTHETIC_TOKEN,
|
|
242
|
+
userLogin: SYNTHETIC_LOGIN,
|
|
243
|
+
includeDiscussions: true,
|
|
244
|
+
});
|
|
245
|
+
assert.equal(cfg.includeDiscussions, true);
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
// ---------------------------------------------------------------------------
|
|
249
|
+
// Connector identity
|
|
250
|
+
// ---------------------------------------------------------------------------
|
|
251
|
+
|
|
252
|
+
test("createGitHubConnector exposes the documented id and display name", () => {
|
|
253
|
+
const connector = createGitHubConnector({ fetchFn: emptyFetch() });
|
|
254
|
+
assert.equal(connector.id, GITHUB_CONNECTOR_ID);
|
|
255
|
+
assert.equal(connector.id, "github");
|
|
256
|
+
assert.equal(connector.displayName, "GitHub");
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
// ---------------------------------------------------------------------------
|
|
260
|
+
// No-op when repos is empty
|
|
261
|
+
// ---------------------------------------------------------------------------
|
|
262
|
+
|
|
263
|
+
test("syncIncremental is a no-op when repos is empty", async () => {
|
|
264
|
+
let fetchCalled = false;
|
|
265
|
+
const fetchFn: GitHubFetchFn = async () => {
|
|
266
|
+
fetchCalled = true;
|
|
267
|
+
throw new Error("should not be called");
|
|
268
|
+
};
|
|
269
|
+
const connector = createGitHubConnector({ fetchFn });
|
|
270
|
+
const config = connector.validateConfig({ token: SYNTHETIC_TOKEN, userLogin: SYNTHETIC_LOGIN });
|
|
271
|
+
|
|
272
|
+
const r1 = (await connector.syncIncremental({ cursor: null, config })) as GitHubSyncResult;
|
|
273
|
+
assert.deepEqual(r1.newDocs, []);
|
|
274
|
+
assert.equal(r1.nextCursor.kind, GITHUB_CURSOR_KIND);
|
|
275
|
+
assert.equal(fetchCalled, false);
|
|
276
|
+
|
|
277
|
+
const r2 = (await connector.syncIncremental({
|
|
278
|
+
cursor: r1.nextCursor,
|
|
279
|
+
config,
|
|
280
|
+
})) as GitHubSyncResult;
|
|
281
|
+
assert.deepEqual(r2.newDocs, []);
|
|
282
|
+
assert.equal(fetchCalled, false);
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
// ---------------------------------------------------------------------------
|
|
286
|
+
// First-sync bootstrap
|
|
287
|
+
// ---------------------------------------------------------------------------
|
|
288
|
+
|
|
289
|
+
test("first sync (cursor=null) seeds watermark and returns no docs", async () => {
|
|
290
|
+
const comment = makeComment(1, SYNTHETIC_LOGIN, "Hello", "2026-04-25T10:00:00.000Z");
|
|
291
|
+
|
|
292
|
+
const fetchFn = makeFetch([
|
|
293
|
+
{
|
|
294
|
+
// issue comments seed
|
|
295
|
+
match: (url) => url.includes("/issues/comments"),
|
|
296
|
+
respond: () => ({ status: 200, data: [comment] }),
|
|
297
|
+
},
|
|
298
|
+
{
|
|
299
|
+
// PR review comments seed
|
|
300
|
+
match: (url) => url.includes("/pulls/comments"),
|
|
301
|
+
respond: () => ({ status: 200, data: [] }),
|
|
302
|
+
},
|
|
303
|
+
]);
|
|
304
|
+
|
|
305
|
+
const connector = createGitHubConnector({ fetchFn });
|
|
306
|
+
const config = connector.validateConfig({ ...SYNTHETIC_CONFIG });
|
|
307
|
+
|
|
308
|
+
const result = (await connector.syncIncremental({ cursor: null, config })) as GitHubSyncResult;
|
|
309
|
+
assert.deepEqual(result.newDocs, []);
|
|
310
|
+
assert.equal(result.nextCursor.kind, GITHUB_CURSOR_KIND);
|
|
311
|
+
|
|
312
|
+
// The cursor must record the watermark from the seeded comment.
|
|
313
|
+
const payload = JSON.parse(result.nextCursor.value) as { watermarks: Record<string, string> };
|
|
314
|
+
assert.equal(
|
|
315
|
+
payload.watermarks[`${REPO_A}/issue-comment`],
|
|
316
|
+
"2026-04-25T10:00:00.000Z",
|
|
317
|
+
);
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
test("first sync with empty API responses seeds empty cursor", async () => {
|
|
321
|
+
const connector = createGitHubConnector({ fetchFn: emptyFetch() });
|
|
322
|
+
const config = connector.validateConfig({ ...SYNTHETIC_CONFIG });
|
|
323
|
+
|
|
324
|
+
const result = await connector.syncIncremental({ cursor: null, config });
|
|
325
|
+
assert.deepEqual(result.newDocs, []);
|
|
326
|
+
assert.equal(result.nextCursor.kind, GITHUB_CURSOR_KIND);
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
// ---------------------------------------------------------------------------
|
|
330
|
+
// Incremental sync: basic happy path
|
|
331
|
+
// ---------------------------------------------------------------------------
|
|
332
|
+
|
|
333
|
+
test("incremental sync emits ConnectorDocument for matching issue comments", async () => {
|
|
334
|
+
const comment = makeComment(
|
|
335
|
+
42,
|
|
336
|
+
SYNTHETIC_LOGIN,
|
|
337
|
+
"This is my note",
|
|
338
|
+
"2026-04-26T09:00:00.000Z",
|
|
339
|
+
`https://github.com/${REPO_A}/issues/10#issuecomment-42`,
|
|
340
|
+
);
|
|
341
|
+
|
|
342
|
+
const fetchFn = makeFetch([
|
|
343
|
+
{
|
|
344
|
+
match: (url) => url.includes("/issues/comments"),
|
|
345
|
+
respond: () => ({ status: 200, data: [comment] }),
|
|
346
|
+
},
|
|
347
|
+
{
|
|
348
|
+
match: (url) => url.includes("/pulls/comments"),
|
|
349
|
+
respond: () => ({ status: 200, data: [] }),
|
|
350
|
+
},
|
|
351
|
+
]);
|
|
352
|
+
|
|
353
|
+
const connector = createGitHubConnector({ fetchFn });
|
|
354
|
+
const config = connector.validateConfig({ ...SYNTHETIC_CONFIG });
|
|
355
|
+
const cursor = makeGitHubCursor({
|
|
356
|
+
[`${REPO_A}/issue-comment`]: "2026-04-25T00:00:00.000Z",
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
const result = (await connector.syncIncremental({ cursor, config })) as GitHubSyncResult;
|
|
360
|
+
|
|
361
|
+
assert.equal(result.newDocs.length, 1);
|
|
362
|
+
const doc = result.newDocs[0];
|
|
363
|
+
assert.equal(doc.source.connector, GITHUB_CONNECTOR_ID);
|
|
364
|
+
assert.equal(doc.source.externalId, `${REPO_A}/issue-comment/42`);
|
|
365
|
+
assert.equal(doc.source.externalRevision, "2026-04-26T09:00:00.000Z");
|
|
366
|
+
assert.equal(doc.source.externalUrl, `https://github.com/${REPO_A}/issues/10#issuecomment-42`);
|
|
367
|
+
assert.equal(doc.content, "This is my note");
|
|
368
|
+
assert.ok(doc.title?.includes("Issue comment"));
|
|
369
|
+
assert.ok(doc.title?.includes(REPO_A));
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
test("incremental sync emits ConnectorDocument for matching PR review comments", async () => {
|
|
373
|
+
const comment = makeComment(99, SYNTHETIC_LOGIN, "PR note here", "2026-04-26T10:00:00.000Z");
|
|
374
|
+
|
|
375
|
+
const fetchFn = makeFetch([
|
|
376
|
+
{
|
|
377
|
+
match: (url) => url.includes("/issues/comments"),
|
|
378
|
+
respond: () => ({ status: 200, data: [] }),
|
|
379
|
+
},
|
|
380
|
+
{
|
|
381
|
+
match: (url) => url.includes("/pulls/comments"),
|
|
382
|
+
respond: () => ({ status: 200, data: [comment] }),
|
|
383
|
+
},
|
|
384
|
+
]);
|
|
385
|
+
|
|
386
|
+
const connector = createGitHubConnector({ fetchFn });
|
|
387
|
+
const config = connector.validateConfig({ ...SYNTHETIC_CONFIG });
|
|
388
|
+
const cursor = makeGitHubCursor({});
|
|
389
|
+
|
|
390
|
+
const result = (await connector.syncIncremental({ cursor, config })) as GitHubSyncResult;
|
|
391
|
+
|
|
392
|
+
assert.equal(result.newDocs.length, 1);
|
|
393
|
+
const doc = result.newDocs[0];
|
|
394
|
+
assert.equal(doc.source.externalId, `${REPO_A}/pr-review-comment/99`);
|
|
395
|
+
assert.ok(doc.title?.includes("PR review comment"));
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
// ---------------------------------------------------------------------------
|
|
399
|
+
// Author filtering
|
|
400
|
+
// ---------------------------------------------------------------------------
|
|
401
|
+
|
|
402
|
+
test("incremental sync skips comments authored by a different user", async () => {
|
|
403
|
+
const myComment = makeComment(1, SYNTHETIC_LOGIN, "Mine", "2026-04-26T09:00:00.000Z");
|
|
404
|
+
const otherComment = makeComment(2, "other-user", "Not mine", "2026-04-26T09:01:00.000Z");
|
|
405
|
+
|
|
406
|
+
const fetchFn = makeFetch([
|
|
407
|
+
{
|
|
408
|
+
match: (url) => url.includes("/issues/comments"),
|
|
409
|
+
respond: () => ({ status: 200, data: [myComment, otherComment] }),
|
|
410
|
+
},
|
|
411
|
+
{
|
|
412
|
+
match: (url) => url.includes("/pulls/comments"),
|
|
413
|
+
respond: () => ({ status: 200, data: [] }),
|
|
414
|
+
},
|
|
415
|
+
]);
|
|
416
|
+
|
|
417
|
+
const connector = createGitHubConnector({ fetchFn });
|
|
418
|
+
const config = connector.validateConfig({ ...SYNTHETIC_CONFIG });
|
|
419
|
+
const cursor = makeGitHubCursor({});
|
|
420
|
+
|
|
421
|
+
const result = (await connector.syncIncremental({ cursor, config })) as GitHubSyncResult;
|
|
422
|
+
|
|
423
|
+
assert.equal(result.newDocs.length, 1);
|
|
424
|
+
assert.equal(result.newDocs[0].source.externalId, `${REPO_A}/issue-comment/1`);
|
|
425
|
+
assert.equal(result.skippedOtherAuthor, 1);
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
// ---------------------------------------------------------------------------
|
|
429
|
+
// Empty / too-large bodies
|
|
430
|
+
// ---------------------------------------------------------------------------
|
|
431
|
+
|
|
432
|
+
test("incremental sync skips comments with empty body", async () => {
|
|
433
|
+
const emptyComment = makeComment(5, SYNTHETIC_LOGIN, "", "2026-04-26T09:00:00.000Z");
|
|
434
|
+
const whitespaceComment = makeComment(6, SYNTHETIC_LOGIN, " \n\t ", "2026-04-26T09:01:00.000Z");
|
|
435
|
+
|
|
436
|
+
const fetchFn = makeFetch([
|
|
437
|
+
{
|
|
438
|
+
match: (url) => url.includes("/issues/comments"),
|
|
439
|
+
respond: () => ({ status: 200, data: [emptyComment, whitespaceComment] }),
|
|
440
|
+
},
|
|
441
|
+
{
|
|
442
|
+
match: (url) => url.includes("/pulls/comments"),
|
|
443
|
+
respond: () => ({ status: 200, data: [] }),
|
|
444
|
+
},
|
|
445
|
+
]);
|
|
446
|
+
|
|
447
|
+
const connector = createGitHubConnector({ fetchFn });
|
|
448
|
+
const config = connector.validateConfig({ ...SYNTHETIC_CONFIG });
|
|
449
|
+
const cursor = makeGitHubCursor({});
|
|
450
|
+
|
|
451
|
+
const result = (await connector.syncIncremental({ cursor, config })) as GitHubSyncResult;
|
|
452
|
+
assert.deepEqual(result.newDocs, []);
|
|
453
|
+
assert.equal(result.skippedEmpty, 2);
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
// ---------------------------------------------------------------------------
|
|
457
|
+
// Watermark advancement
|
|
458
|
+
// ---------------------------------------------------------------------------
|
|
459
|
+
|
|
460
|
+
test("incremental sync advances watermark after importing comments", async () => {
|
|
461
|
+
const c1 = makeComment(10, SYNTHETIC_LOGIN, "first", "2026-04-26T09:00:00.000Z");
|
|
462
|
+
const c2 = makeComment(11, SYNTHETIC_LOGIN, "second", "2026-04-26T10:00:00.000Z");
|
|
463
|
+
|
|
464
|
+
const fetchFn = makeFetch([
|
|
465
|
+
{
|
|
466
|
+
match: (url) => url.includes("/issues/comments"),
|
|
467
|
+
respond: () => ({ status: 200, data: [c1, c2] }),
|
|
468
|
+
},
|
|
469
|
+
{
|
|
470
|
+
match: (url) => url.includes("/pulls/comments"),
|
|
471
|
+
respond: () => ({ status: 200, data: [] }),
|
|
472
|
+
},
|
|
473
|
+
]);
|
|
474
|
+
|
|
475
|
+
const connector = createGitHubConnector({ fetchFn });
|
|
476
|
+
const config = connector.validateConfig({ ...SYNTHETIC_CONFIG });
|
|
477
|
+
const cursor = makeGitHubCursor({
|
|
478
|
+
[`${REPO_A}/issue-comment`]: "2026-04-25T00:00:00.000Z",
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
const result = (await connector.syncIncremental({ cursor, config })) as GitHubSyncResult;
|
|
482
|
+
assert.equal(result.newDocs.length, 2);
|
|
483
|
+
|
|
484
|
+
const payload = JSON.parse(result.nextCursor.value) as { watermarks: Record<string, string> };
|
|
485
|
+
// Watermark must advance to the latest comment's updated_at.
|
|
486
|
+
assert.equal(payload.watermarks[`${REPO_A}/issue-comment`], "2026-04-26T10:00:00.000Z");
|
|
487
|
+
});
|
|
488
|
+
|
|
489
|
+
test("incremental sync skips items strictly before the watermark", async () => {
|
|
490
|
+
// Only comments whose updated_at is STRICTLY before the watermark are
|
|
491
|
+
// dropped by the filter. A comment AT the watermark second passes through
|
|
492
|
+
// to the seenIds check (and, if not in seenIds, is ingested). A comment
|
|
493
|
+
// one second after the watermark is always ingested.
|
|
494
|
+
//
|
|
495
|
+
// "beforeComment" has a timestamp 1 second before the watermark → skipped.
|
|
496
|
+
// "atComment" is AT the watermark second and NOT in seenIds → ingested.
|
|
497
|
+
// "afterComment" is after the watermark → ingested.
|
|
498
|
+
const watermark = "2026-04-25T00:00:01.000Z";
|
|
499
|
+
const beforeComment = makeComment(1, SYNTHETIC_LOGIN, "before", "2026-04-25T00:00:00.000Z");
|
|
500
|
+
const atComment = makeComment(2, SYNTHETIC_LOGIN, "at watermark", watermark);
|
|
501
|
+
const afterComment = makeComment(3, SYNTHETIC_LOGIN, "after", "2026-04-25T00:00:02.000Z");
|
|
502
|
+
|
|
503
|
+
const fetchFn = makeFetch([
|
|
504
|
+
{
|
|
505
|
+
match: (url) => url.includes("/issues/comments"),
|
|
506
|
+
respond: () => ({ status: 200, data: [beforeComment, atComment, afterComment] }),
|
|
507
|
+
},
|
|
508
|
+
{
|
|
509
|
+
match: (url) => url.includes("/pulls/comments"),
|
|
510
|
+
respond: () => ({ status: 200, data: [] }),
|
|
511
|
+
},
|
|
512
|
+
]);
|
|
513
|
+
|
|
514
|
+
const connector = createGitHubConnector({ fetchFn });
|
|
515
|
+
const config = connector.validateConfig({ ...SYNTHETIC_CONFIG });
|
|
516
|
+
// Cursor watermark at exactly the "at" second; seenIds is empty.
|
|
517
|
+
const cursor: import("./framework.js").ConnectorCursor = {
|
|
518
|
+
kind: GITHUB_CURSOR_KIND,
|
|
519
|
+
value: JSON.stringify({
|
|
520
|
+
watermarks: { [`${REPO_A}/issue-comment`]: watermark },
|
|
521
|
+
seenIds: {},
|
|
522
|
+
}),
|
|
523
|
+
updatedAt: watermark,
|
|
524
|
+
};
|
|
525
|
+
|
|
526
|
+
const result = (await connector.syncIncremental({ cursor, config })) as GitHubSyncResult;
|
|
527
|
+
// beforeComment is strictly before the watermark → skipped.
|
|
528
|
+
// atComment is AT the watermark and not in seenIds → ingested.
|
|
529
|
+
// afterComment is after the watermark → ingested.
|
|
530
|
+
assert.equal(result.newDocs.length, 2);
|
|
531
|
+
const ids = result.newDocs.map((d) => d.source.externalId);
|
|
532
|
+
assert.ok(ids.includes(`${REPO_A}/issue-comment/2`), "atComment must be ingested");
|
|
533
|
+
assert.ok(ids.includes(`${REPO_A}/issue-comment/3`), "afterComment must be ingested");
|
|
534
|
+
});
|
|
535
|
+
|
|
536
|
+
// ---------------------------------------------------------------------------
|
|
537
|
+
// Multiple repos
|
|
538
|
+
// ---------------------------------------------------------------------------
|
|
539
|
+
|
|
540
|
+
test("incremental sync handles multiple repos independently", async () => {
|
|
541
|
+
const commentA = makeComment(1, SYNTHETIC_LOGIN, "In A", "2026-04-26T09:00:00.000Z");
|
|
542
|
+
const commentB = makeComment(2, SYNTHETIC_LOGIN, "In B", "2026-04-26T09:00:00.000Z");
|
|
543
|
+
|
|
544
|
+
const fetchFn = makeFetch([
|
|
545
|
+
{
|
|
546
|
+
match: (url) => url.includes(`${REPO_A}/issues/comments`),
|
|
547
|
+
respond: () => ({ status: 200, data: [commentA] }),
|
|
548
|
+
},
|
|
549
|
+
{
|
|
550
|
+
match: (url) => url.includes(`${REPO_B}/issues/comments`),
|
|
551
|
+
respond: () => ({ status: 200, data: [commentB] }),
|
|
552
|
+
},
|
|
553
|
+
{
|
|
554
|
+
match: (url) => url.includes("/pulls/comments"),
|
|
555
|
+
respond: () => ({ status: 200, data: [] }),
|
|
556
|
+
},
|
|
557
|
+
]);
|
|
558
|
+
|
|
559
|
+
const connector = createGitHubConnector({ fetchFn });
|
|
560
|
+
const config = connector.validateConfig({
|
|
561
|
+
token: SYNTHETIC_TOKEN,
|
|
562
|
+
userLogin: SYNTHETIC_LOGIN,
|
|
563
|
+
repos: [REPO_A, REPO_B],
|
|
564
|
+
});
|
|
565
|
+
const cursor = makeGitHubCursor({});
|
|
566
|
+
|
|
567
|
+
const result = (await connector.syncIncremental({ cursor, config })) as GitHubSyncResult;
|
|
568
|
+
assert.equal(result.newDocs.length, 2);
|
|
569
|
+
const ids = result.newDocs.map((d) => d.source.externalId).sort();
|
|
570
|
+
assert.deepEqual(ids, [
|
|
571
|
+
`${REPO_A}/issue-comment/1`,
|
|
572
|
+
`${REPO_B}/issue-comment/2`,
|
|
573
|
+
].sort());
|
|
574
|
+
});
|
|
575
|
+
|
|
576
|
+
// ---------------------------------------------------------------------------
|
|
577
|
+
// Discussion comments (opt-in)
|
|
578
|
+
// ---------------------------------------------------------------------------
|
|
579
|
+
|
|
580
|
+
test("discussion comments are not fetched unless includeDiscussions is true", async () => {
|
|
581
|
+
let discussionFetched = false;
|
|
582
|
+
const fetchFn = makeFetch([
|
|
583
|
+
{
|
|
584
|
+
match: (url) => url.includes("/discussions"),
|
|
585
|
+
respond: () => {
|
|
586
|
+
discussionFetched = true;
|
|
587
|
+
return { status: 200, data: [] };
|
|
588
|
+
},
|
|
589
|
+
},
|
|
590
|
+
{
|
|
591
|
+
match: () => true,
|
|
592
|
+
respond: () => ({ status: 200, data: [] }),
|
|
593
|
+
},
|
|
594
|
+
]);
|
|
595
|
+
|
|
596
|
+
const connector = createGitHubConnector({ fetchFn });
|
|
597
|
+
const config = connector.validateConfig({
|
|
598
|
+
...SYNTHETIC_CONFIG,
|
|
599
|
+
includeDiscussions: false,
|
|
600
|
+
});
|
|
601
|
+
const cursor = makeGitHubCursor({});
|
|
602
|
+
|
|
603
|
+
await connector.syncIncremental({ cursor, config });
|
|
604
|
+
assert.equal(discussionFetched, false);
|
|
605
|
+
});
|
|
606
|
+
|
|
607
|
+
test("discussion comments are fetched when includeDiscussions is true", async () => {
|
|
608
|
+
let discussionFetched = false;
|
|
609
|
+
const fetchFn = makeFetch([
|
|
610
|
+
{
|
|
611
|
+
match: (url) => url.includes("/discussions"),
|
|
612
|
+
respond: () => {
|
|
613
|
+
discussionFetched = true;
|
|
614
|
+
return { status: 200, data: [] };
|
|
615
|
+
},
|
|
616
|
+
},
|
|
617
|
+
{
|
|
618
|
+
match: () => true,
|
|
619
|
+
respond: () => ({ status: 200, data: [] }),
|
|
620
|
+
},
|
|
621
|
+
]);
|
|
622
|
+
|
|
623
|
+
const connector = createGitHubConnector({ fetchFn });
|
|
624
|
+
const config = connector.validateConfig({
|
|
625
|
+
...SYNTHETIC_CONFIG,
|
|
626
|
+
includeDiscussions: true,
|
|
627
|
+
});
|
|
628
|
+
const cursor = makeGitHubCursor({});
|
|
629
|
+
|
|
630
|
+
await connector.syncIncremental({ cursor, config });
|
|
631
|
+
assert.equal(discussionFetched, true);
|
|
632
|
+
});
|
|
633
|
+
|
|
634
|
+
// ---------------------------------------------------------------------------
|
|
635
|
+
// Error classification
|
|
636
|
+
// ---------------------------------------------------------------------------
|
|
637
|
+
|
|
638
|
+
test("isTransientGitHubError classifies common error shapes", () => {
|
|
639
|
+
// Terminal — skip-and-continue.
|
|
640
|
+
assert.equal(isTransientGitHubError({ githubStatus: 404 }), false);
|
|
641
|
+
assert.equal(isTransientGitHubError({ githubStatus: 403 }), false);
|
|
642
|
+
assert.equal(isTransientGitHubError({ githubStatus: 400 }), false);
|
|
643
|
+
assert.equal(isTransientGitHubError({ status: 410 }), false);
|
|
644
|
+
// Transient — re-throw.
|
|
645
|
+
assert.equal(isTransientGitHubError({ githubStatus: 429 }), true);
|
|
646
|
+
assert.equal(isTransientGitHubError({ githubStatus: 500 }), true);
|
|
647
|
+
assert.equal(isTransientGitHubError({ githubStatus: 503 }), true);
|
|
648
|
+
assert.equal(isTransientGitHubError({ status: 504 }), true);
|
|
649
|
+
// Network errors.
|
|
650
|
+
assert.equal(isTransientGitHubError({ code: "ECONNRESET" }), true);
|
|
651
|
+
assert.equal(isTransientGitHubError({ code: "ETIMEDOUT" }), true);
|
|
652
|
+
assert.equal(isTransientGitHubError({ code: "ENOTFOUND" }), true);
|
|
653
|
+
assert.equal(isTransientGitHubError({ code: "EAI_AGAIN" }), true);
|
|
654
|
+
// AbortError.
|
|
655
|
+
assert.equal(isTransientGitHubError({ name: "AbortError" }), true);
|
|
656
|
+
// Bare Error with no metadata — conservatively transient.
|
|
657
|
+
assert.equal(isTransientGitHubError(new Error("unknown")), true);
|
|
658
|
+
// Non-objects.
|
|
659
|
+
assert.equal(isTransientGitHubError(null), false);
|
|
660
|
+
assert.equal(isTransientGitHubError(undefined), false);
|
|
661
|
+
assert.equal(isTransientGitHubError("oops"), false);
|
|
662
|
+
});
|
|
663
|
+
|
|
664
|
+
// ---------------------------------------------------------------------------
|
|
665
|
+
// HTTP error handling
|
|
666
|
+
// ---------------------------------------------------------------------------
|
|
667
|
+
|
|
668
|
+
test("a 429 on issue comments re-throws (transient) and cursor does NOT advance", async () => {
|
|
669
|
+
const fetchFn = makeFetch([
|
|
670
|
+
{
|
|
671
|
+
match: (url) => url.includes("/issues/comments"),
|
|
672
|
+
respond: () => ({
|
|
673
|
+
status: 429,
|
|
674
|
+
data: { message: "API rate limit exceeded" },
|
|
675
|
+
}),
|
|
676
|
+
},
|
|
677
|
+
]);
|
|
678
|
+
|
|
679
|
+
const connector = createGitHubConnector({ fetchFn });
|
|
680
|
+
const config = connector.validateConfig({ ...SYNTHETIC_CONFIG });
|
|
681
|
+
const cursor = makeGitHubCursor({
|
|
682
|
+
[`${REPO_A}/issue-comment`]: "2026-04-25T00:00:00.000Z",
|
|
683
|
+
});
|
|
684
|
+
|
|
685
|
+
await assert.rejects(
|
|
686
|
+
connector.syncIncremental({ cursor, config }),
|
|
687
|
+
/rate limit/i,
|
|
688
|
+
);
|
|
689
|
+
});
|
|
690
|
+
|
|
691
|
+
test("a 503 on issue comments re-throws (transient)", async () => {
|
|
692
|
+
const fetchFn = makeFetch([
|
|
693
|
+
{
|
|
694
|
+
match: (url) => url.includes("/issues/comments"),
|
|
695
|
+
respond: () => ({
|
|
696
|
+
status: 503,
|
|
697
|
+
data: { message: "Service Unavailable" },
|
|
698
|
+
}),
|
|
699
|
+
},
|
|
700
|
+
]);
|
|
701
|
+
|
|
702
|
+
const connector = createGitHubConnector({ fetchFn });
|
|
703
|
+
const config = connector.validateConfig({ ...SYNTHETIC_CONFIG });
|
|
704
|
+
const cursor = makeGitHubCursor({});
|
|
705
|
+
|
|
706
|
+
await assert.rejects(
|
|
707
|
+
connector.syncIncremental({ cursor, config }),
|
|
708
|
+
/503/,
|
|
709
|
+
);
|
|
710
|
+
});
|
|
711
|
+
|
|
712
|
+
test("a 404 on issue comments is terminal (skip repo resource, continue)", async () => {
|
|
713
|
+
// Issue comments returns 404 → skipped.
|
|
714
|
+
// PR review comments returns a valid comment → should still be imported.
|
|
715
|
+
const prComment = makeComment(77, SYNTHETIC_LOGIN, "PR note", "2026-04-26T09:00:00.000Z");
|
|
716
|
+
|
|
717
|
+
const fetchFn = makeFetch([
|
|
718
|
+
{
|
|
719
|
+
match: (url) => url.includes("/issues/comments"),
|
|
720
|
+
respond: () => ({
|
|
721
|
+
status: 404,
|
|
722
|
+
data: { message: "Not Found" },
|
|
723
|
+
}),
|
|
724
|
+
},
|
|
725
|
+
{
|
|
726
|
+
match: (url) => url.includes("/pulls/comments"),
|
|
727
|
+
respond: () => ({ status: 200, data: [prComment] }),
|
|
728
|
+
},
|
|
729
|
+
]);
|
|
730
|
+
|
|
731
|
+
const connector = createGitHubConnector({ fetchFn });
|
|
732
|
+
const config = connector.validateConfig({ ...SYNTHETIC_CONFIG });
|
|
733
|
+
const cursor = makeGitHubCursor({});
|
|
734
|
+
|
|
735
|
+
const result = (await connector.syncIncremental({ cursor, config })) as GitHubSyncResult;
|
|
736
|
+
// The 404 should be swallowed (terminal), and the PR comment should be imported.
|
|
737
|
+
assert.equal(result.newDocs.length, 1);
|
|
738
|
+
assert.equal(result.newDocs[0].source.externalId, `${REPO_A}/pr-review-comment/77`);
|
|
739
|
+
});
|
|
740
|
+
|
|
741
|
+
test("a 403 on issue comments is terminal (skip, continue to PR comments)", async () => {
|
|
742
|
+
const prComment = makeComment(88, SYNTHETIC_LOGIN, "Another PR note", "2026-04-26T09:00:00.000Z");
|
|
743
|
+
|
|
744
|
+
const fetchFn = makeFetch([
|
|
745
|
+
{
|
|
746
|
+
match: (url) => url.includes("/issues/comments"),
|
|
747
|
+
respond: () => ({
|
|
748
|
+
status: 403,
|
|
749
|
+
data: { message: "Forbidden" },
|
|
750
|
+
}),
|
|
751
|
+
},
|
|
752
|
+
{
|
|
753
|
+
match: (url) => url.includes("/pulls/comments"),
|
|
754
|
+
respond: () => ({ status: 200, data: [prComment] }),
|
|
755
|
+
},
|
|
756
|
+
]);
|
|
757
|
+
|
|
758
|
+
const connector = createGitHubConnector({ fetchFn });
|
|
759
|
+
const config = connector.validateConfig({ ...SYNTHETIC_CONFIG });
|
|
760
|
+
const cursor = makeGitHubCursor({});
|
|
761
|
+
|
|
762
|
+
const result = (await connector.syncIncremental({ cursor, config })) as GitHubSyncResult;
|
|
763
|
+
assert.equal(result.newDocs.length, 1);
|
|
764
|
+
});
|
|
765
|
+
|
|
766
|
+
// ---------------------------------------------------------------------------
|
|
767
|
+
// AbortSignal
|
|
768
|
+
// ---------------------------------------------------------------------------
|
|
769
|
+
|
|
770
|
+
test("syncIncremental honors abortSignal", async () => {
|
|
771
|
+
const controller = new AbortController();
|
|
772
|
+
let callCount = 0;
|
|
773
|
+
|
|
774
|
+
const fetchFn: GitHubFetchFn = async () => {
|
|
775
|
+
callCount++;
|
|
776
|
+
if (callCount === 1) controller.abort();
|
|
777
|
+
return {
|
|
778
|
+
ok: true,
|
|
779
|
+
status: 200,
|
|
780
|
+
headers: { get: () => null },
|
|
781
|
+
json: async () => [],
|
|
782
|
+
};
|
|
783
|
+
};
|
|
784
|
+
|
|
785
|
+
const connector = createGitHubConnector({ fetchFn });
|
|
786
|
+
const config = connector.validateConfig({ ...SYNTHETIC_CONFIG });
|
|
787
|
+
const cursor = makeGitHubCursor({});
|
|
788
|
+
|
|
789
|
+
await assert.rejects(
|
|
790
|
+
connector.syncIncremental({ cursor, config, abortSignal: controller.signal }),
|
|
791
|
+
/aborted/,
|
|
792
|
+
);
|
|
793
|
+
});
|
|
794
|
+
|
|
795
|
+
// ---------------------------------------------------------------------------
|
|
796
|
+
// Cursor validation
|
|
797
|
+
// ---------------------------------------------------------------------------
|
|
798
|
+
|
|
799
|
+
test("syncIncremental rejects a cursor of an unexpected kind", async () => {
|
|
800
|
+
const connector = createGitHubConnector({ fetchFn: emptyFetch() });
|
|
801
|
+
const config = connector.validateConfig({ ...SYNTHETIC_CONFIG });
|
|
802
|
+
const badCursor: ConnectorCursor = {
|
|
803
|
+
kind: "wrong-kind",
|
|
804
|
+
value: "{}",
|
|
805
|
+
updatedAt: "2026-04-25T00:00:00.000Z",
|
|
806
|
+
};
|
|
807
|
+
|
|
808
|
+
await assert.rejects(
|
|
809
|
+
connector.syncIncremental({ cursor: badCursor, config }),
|
|
810
|
+
/unexpected cursor kind/,
|
|
811
|
+
);
|
|
812
|
+
});
|
|
813
|
+
|
|
814
|
+
test("syncIncremental rejects a cursor with invalid JSON", async () => {
|
|
815
|
+
const connector = createGitHubConnector({ fetchFn: emptyFetch() });
|
|
816
|
+
const config = connector.validateConfig({ ...SYNTHETIC_CONFIG });
|
|
817
|
+
const badCursor: ConnectorCursor = {
|
|
818
|
+
kind: GITHUB_CURSOR_KIND,
|
|
819
|
+
value: "{ not valid json",
|
|
820
|
+
updatedAt: "2026-04-25T00:00:00.000Z",
|
|
821
|
+
};
|
|
822
|
+
|
|
823
|
+
await assert.rejects(
|
|
824
|
+
connector.syncIncremental({ cursor: badCursor, config }),
|
|
825
|
+
/not valid JSON/,
|
|
826
|
+
);
|
|
827
|
+
});
|
|
828
|
+
|
|
829
|
+
test("validateConfig is enforced again on every sync pass", async () => {
|
|
830
|
+
const connector = createGitHubConnector({ fetchFn: emptyFetch() });
|
|
831
|
+
const badConfig = { token: SYNTHETIC_TOKEN } as unknown as import("./framework.js").ConnectorConfig;
|
|
832
|
+
const cursor = makeGitHubCursor({});
|
|
833
|
+
|
|
834
|
+
await assert.rejects(
|
|
835
|
+
connector.syncIncremental({ cursor, config: badConfig }),
|
|
836
|
+
/userLogin/,
|
|
837
|
+
);
|
|
838
|
+
});
|
|
839
|
+
|
|
840
|
+
// ---------------------------------------------------------------------------
|
|
841
|
+
// Network-layer transient error
|
|
842
|
+
// ---------------------------------------------------------------------------
|
|
843
|
+
|
|
844
|
+
test("a network ECONNRESET on issue comments re-throws as transient", async () => {
|
|
845
|
+
const fetchFn: GitHubFetchFn = async () => {
|
|
846
|
+
throw Object.assign(new Error("socket hang up"), { code: "ECONNRESET" });
|
|
847
|
+
};
|
|
848
|
+
|
|
849
|
+
const connector = createGitHubConnector({ fetchFn });
|
|
850
|
+
const config = connector.validateConfig({ ...SYNTHETIC_CONFIG });
|
|
851
|
+
const cursor = makeGitHubCursor({});
|
|
852
|
+
|
|
853
|
+
await assert.rejects(
|
|
854
|
+
connector.syncIncremental({ cursor, config }),
|
|
855
|
+
/socket hang up/,
|
|
856
|
+
);
|
|
857
|
+
});
|
|
858
|
+
|
|
859
|
+
// ---------------------------------------------------------------------------
|
|
860
|
+
// Non-matching author watermark advancement (CLAUDE.md gotcha #44 regression)
|
|
861
|
+
// ---------------------------------------------------------------------------
|
|
862
|
+
|
|
863
|
+
test("watermark advances for non-matching-author and empty comments so they aren't re-fetched", async () => {
|
|
864
|
+
// A comment from a different user. The watermark must still advance so we
|
|
865
|
+
// don't fetch the same item on every subsequent poll.
|
|
866
|
+
const otherComment = makeComment(200, "other-user", "Not mine", "2026-04-26T09:00:00.000Z");
|
|
867
|
+
|
|
868
|
+
const fetchFn = makeFetch([
|
|
869
|
+
{
|
|
870
|
+
match: (url) => url.includes("/issues/comments"),
|
|
871
|
+
respond: () => ({ status: 200, data: [otherComment] }),
|
|
872
|
+
},
|
|
873
|
+
{
|
|
874
|
+
match: (url) => url.includes("/pulls/comments"),
|
|
875
|
+
respond: () => ({ status: 200, data: [] }),
|
|
876
|
+
},
|
|
877
|
+
]);
|
|
878
|
+
|
|
879
|
+
const connector = createGitHubConnector({ fetchFn });
|
|
880
|
+
const config = connector.validateConfig({ ...SYNTHETIC_CONFIG });
|
|
881
|
+
const cursor = makeGitHubCursor({});
|
|
882
|
+
|
|
883
|
+
const result = (await connector.syncIncremental({ cursor, config })) as GitHubSyncResult;
|
|
884
|
+
assert.deepEqual(result.newDocs, []);
|
|
885
|
+
assert.equal(result.skippedOtherAuthor, 1);
|
|
886
|
+
|
|
887
|
+
// The watermark must have advanced past the other-author comment so the
|
|
888
|
+
// next incremental pass skips it via the `since` filter.
|
|
889
|
+
const payload = JSON.parse(result.nextCursor.value) as { watermarks: Record<string, string> };
|
|
890
|
+
assert.equal(payload.watermarks[`${REPO_A}/issue-comment`], "2026-04-26T09:00:00.000Z");
|
|
891
|
+
});
|
|
892
|
+
|
|
893
|
+
// ---------------------------------------------------------------------------
|
|
894
|
+
// P1 fix: Same-timestamp dedup via seenIds (PRRT_kwDORJXyws59sfBq)
|
|
895
|
+
// ---------------------------------------------------------------------------
|
|
896
|
+
|
|
897
|
+
test("seenIds are stored in cursor for ingested comments at the watermark second", async () => {
|
|
898
|
+
// Two comments with the same second-level timestamp. Both are new (above watermark).
|
|
899
|
+
const c1 = makeComment(10, SYNTHETIC_LOGIN, "first comment", "2026-04-26T10:00:00.000Z");
|
|
900
|
+
const c2 = makeComment(11, SYNTHETIC_LOGIN, "second comment", "2026-04-26T10:00:00.500Z");
|
|
901
|
+
|
|
902
|
+
const fetchFn = makeFetch([
|
|
903
|
+
{
|
|
904
|
+
match: (url) => url.includes("/issues/comments"),
|
|
905
|
+
respond: () => ({ status: 200, data: [c1, c2] }),
|
|
906
|
+
},
|
|
907
|
+
{
|
|
908
|
+
match: (url) => url.includes("/pulls/comments"),
|
|
909
|
+
respond: () => ({ status: 200, data: [] }),
|
|
910
|
+
},
|
|
911
|
+
]);
|
|
912
|
+
|
|
913
|
+
const connector = createGitHubConnector({ fetchFn });
|
|
914
|
+
const config = connector.validateConfig({ ...SYNTHETIC_CONFIG });
|
|
915
|
+
const cursor = makeGitHubCursor({
|
|
916
|
+
[`${REPO_A}/issue-comment`]: "2026-04-26T09:00:00.000Z",
|
|
917
|
+
});
|
|
918
|
+
|
|
919
|
+
const result = (await connector.syncIncremental({ cursor, config })) as GitHubSyncResult;
|
|
920
|
+
assert.equal(result.newDocs.length, 2);
|
|
921
|
+
|
|
922
|
+
const payload = JSON.parse(result.nextCursor.value) as {
|
|
923
|
+
watermarks: Record<string, string>;
|
|
924
|
+
seenIds: Record<string, string>;
|
|
925
|
+
};
|
|
926
|
+
// seenIds must contain both ingested comment ids.
|
|
927
|
+
assert.ok(
|
|
928
|
+
`${REPO_A}/issue-comment/10` in payload.seenIds,
|
|
929
|
+
"seenIds must include comment 10",
|
|
930
|
+
);
|
|
931
|
+
assert.ok(
|
|
932
|
+
`${REPO_A}/issue-comment/11` in payload.seenIds,
|
|
933
|
+
"seenIds must include comment 11",
|
|
934
|
+
);
|
|
935
|
+
});
|
|
936
|
+
|
|
937
|
+
test("seenIds prevent re-importing same-second comments on the next poll", async () => {
|
|
938
|
+
// Scenario: c1 was ingested on the previous pass and is recorded in seenIds.
|
|
939
|
+
// On this poll GitHub re-returns it (inclusive `since=` at the watermark
|
|
940
|
+
// second). It must be skipped without emitting a new document.
|
|
941
|
+
const ts = "2026-04-26T10:00:00.000Z";
|
|
942
|
+
const c1 = makeComment(10, SYNTHETIC_LOGIN, "already imported", ts);
|
|
943
|
+
const c2 = makeComment(12, SYNTHETIC_LOGIN, "new this poll", "2026-04-26T10:00:01.000Z");
|
|
944
|
+
|
|
945
|
+
const fetchFn = makeFetch([
|
|
946
|
+
{
|
|
947
|
+
match: (url) => url.includes("/issues/comments"),
|
|
948
|
+
respond: () => ({ status: 200, data: [c1, c2] }),
|
|
949
|
+
},
|
|
950
|
+
{
|
|
951
|
+
match: (url) => url.includes("/pulls/comments"),
|
|
952
|
+
respond: () => ({ status: 200, data: [] }),
|
|
953
|
+
},
|
|
954
|
+
]);
|
|
955
|
+
|
|
956
|
+
const connector = createGitHubConnector({ fetchFn });
|
|
957
|
+
const config = connector.validateConfig({ ...SYNTHETIC_CONFIG });
|
|
958
|
+
// Cursor encodes the watermark at c1's timestamp AND c1 already in seenIds.
|
|
959
|
+
const cursor: import("./framework.js").ConnectorCursor = {
|
|
960
|
+
kind: GITHUB_CURSOR_KIND,
|
|
961
|
+
value: JSON.stringify({
|
|
962
|
+
watermarks: { [`${REPO_A}/issue-comment`]: ts },
|
|
963
|
+
seenIds: { [`${REPO_A}/issue-comment/10`]: ts },
|
|
964
|
+
}),
|
|
965
|
+
updatedAt: "2026-04-26T10:00:00.000Z",
|
|
966
|
+
};
|
|
967
|
+
|
|
968
|
+
const result = (await connector.syncIncremental({ cursor, config })) as GitHubSyncResult;
|
|
969
|
+
// c1 must be skipped (in seenIds); only c2 should be imported.
|
|
970
|
+
assert.equal(result.newDocs.length, 1);
|
|
971
|
+
assert.equal(result.newDocs[0].source.externalId, `${REPO_A}/issue-comment/12`);
|
|
972
|
+
});
|
|
973
|
+
|
|
974
|
+
test("seenIds are cleared when the watermark advances past a second boundary", async () => {
|
|
975
|
+
// Previous cursor has seenIds entries at the old second.
|
|
976
|
+
// After ingesting c2 which is in a new second, seenIds must be cleared.
|
|
977
|
+
const oldTs = "2026-04-26T10:00:00.000Z";
|
|
978
|
+
const newTs = "2026-04-26T10:00:01.000Z";
|
|
979
|
+
const c1Old = makeComment(10, SYNTHETIC_LOGIN, "old second item", oldTs);
|
|
980
|
+
const c2New = makeComment(20, SYNTHETIC_LOGIN, "new second item", newTs);
|
|
981
|
+
|
|
982
|
+
const fetchFn = makeFetch([
|
|
983
|
+
{
|
|
984
|
+
match: (url) => url.includes("/issues/comments"),
|
|
985
|
+
respond: () => ({ status: 200, data: [c1Old, c2New] }),
|
|
986
|
+
},
|
|
987
|
+
{
|
|
988
|
+
match: (url) => url.includes("/pulls/comments"),
|
|
989
|
+
respond: () => ({ status: 200, data: [] }),
|
|
990
|
+
},
|
|
991
|
+
]);
|
|
992
|
+
|
|
993
|
+
const connector = createGitHubConnector({ fetchFn });
|
|
994
|
+
const config = connector.validateConfig({ ...SYNTHETIC_CONFIG });
|
|
995
|
+
// Cursor: watermark is at oldTs, seenIds contains c1Old's id.
|
|
996
|
+
const cursor: import("./framework.js").ConnectorCursor = {
|
|
997
|
+
kind: GITHUB_CURSOR_KIND,
|
|
998
|
+
value: JSON.stringify({
|
|
999
|
+
watermarks: { [`${REPO_A}/issue-comment`]: oldTs },
|
|
1000
|
+
seenIds: { [`${REPO_A}/issue-comment/10`]: oldTs },
|
|
1001
|
+
}),
|
|
1002
|
+
updatedAt: oldTs,
|
|
1003
|
+
};
|
|
1004
|
+
|
|
1005
|
+
const result = (await connector.syncIncremental({ cursor, config })) as GitHubSyncResult;
|
|
1006
|
+
// c1Old is in seenIds → skipped. c2New (above watermark) → imported.
|
|
1007
|
+
assert.equal(result.newDocs.length, 1);
|
|
1008
|
+
assert.equal(result.newDocs[0].source.externalId, `${REPO_A}/issue-comment/20`);
|
|
1009
|
+
|
|
1010
|
+
const payload = JSON.parse(result.nextCursor.value) as {
|
|
1011
|
+
watermarks: Record<string, string>;
|
|
1012
|
+
seenIds: Record<string, string>;
|
|
1013
|
+
};
|
|
1014
|
+
// Watermark must have advanced to the new second.
|
|
1015
|
+
assert.equal(payload.watermarks[`${REPO_A}/issue-comment`], newTs);
|
|
1016
|
+
// seenIds for c1Old must be cleared (watermark crossed the second boundary).
|
|
1017
|
+
assert.ok(
|
|
1018
|
+
!(`${REPO_A}/issue-comment/10` in payload.seenIds),
|
|
1019
|
+
"stale seenId from old second must be cleared",
|
|
1020
|
+
);
|
|
1021
|
+
// c2New must be in the fresh seenIds.
|
|
1022
|
+
assert.ok(
|
|
1023
|
+
`${REPO_A}/issue-comment/20` in payload.seenIds,
|
|
1024
|
+
"new-second comment must be in seenIds",
|
|
1025
|
+
);
|
|
1026
|
+
});
|
|
1027
|
+
|
|
1028
|
+
// ---------------------------------------------------------------------------
|
|
1029
|
+
// P1 fix: Skip-aware budget — skipped records don't consume the cap
|
|
1030
|
+
// (PRRT_kwDORJXyws59sfBs)
|
|
1031
|
+
// ---------------------------------------------------------------------------
|
|
1032
|
+
|
|
1033
|
+
test("skipped (wrong-author) records do not consume the per-pass budget", async () => {
|
|
1034
|
+
// Build MAX_ITEMS_PER_PASS wrong-author comments followed by one valid one.
|
|
1035
|
+
// The budget should NOT be exhausted by the skipped ones, so the valid
|
|
1036
|
+
// comment at the end must still be ingested.
|
|
1037
|
+
const MAX = 200; // matches MAX_ITEMS_PER_PASS constant
|
|
1038
|
+
const skippedComments = Array.from({ length: MAX }, (_, i) =>
|
|
1039
|
+
makeComment(1000 + i, "other-user", `not mine ${i}`, `2026-04-26T09:00:${String(i).padStart(2, "0")}.000Z`),
|
|
1040
|
+
);
|
|
1041
|
+
const validComment = makeComment(9999, SYNTHETIC_LOGIN, "mine", "2026-04-26T09:05:00.000Z");
|
|
1042
|
+
|
|
1043
|
+
// All comments returned on first page (we return MAX+1 items so pagination
|
|
1044
|
+
// doesn't trigger — but since GITHUB_PAGE_SIZE=100 we need to simulate
|
|
1045
|
+
// multiple pages). Simpler: just return one page with all items (>100),
|
|
1046
|
+
// but since the page-size check caps at 100 we'll use a single page of
|
|
1047
|
+
// exactly the right set. Instead use a simpler approach: return the skipped
|
|
1048
|
+
// comments as 2 pages of 100, then the valid comment on a "short" 3rd page.
|
|
1049
|
+
let callCount = 0;
|
|
1050
|
+
const issuePages: (typeof skippedComments)[] = [
|
|
1051
|
+
skippedComments.slice(0, 100),
|
|
1052
|
+
skippedComments.slice(100, 200),
|
|
1053
|
+
[validComment],
|
|
1054
|
+
];
|
|
1055
|
+
|
|
1056
|
+
const fetchFn = makeFetch([
|
|
1057
|
+
{
|
|
1058
|
+
match: (url) => url.includes("/issues/comments"),
|
|
1059
|
+
respond: () => {
|
|
1060
|
+
const page = issuePages[callCount] ?? [];
|
|
1061
|
+
callCount++;
|
|
1062
|
+
return { status: 200, data: page };
|
|
1063
|
+
},
|
|
1064
|
+
},
|
|
1065
|
+
{
|
|
1066
|
+
match: (url) => url.includes("/pulls/comments"),
|
|
1067
|
+
respond: () => ({ status: 200, data: [] }),
|
|
1068
|
+
},
|
|
1069
|
+
]);
|
|
1070
|
+
|
|
1071
|
+
const connector = createGitHubConnector({ fetchFn });
|
|
1072
|
+
const config = connector.validateConfig({ ...SYNTHETIC_CONFIG });
|
|
1073
|
+
const cursor = makeGitHubCursor({});
|
|
1074
|
+
|
|
1075
|
+
const result = (await connector.syncIncremental({ cursor, config })) as GitHubSyncResult;
|
|
1076
|
+
|
|
1077
|
+
// The valid comment must be imported despite all the skipped ones.
|
|
1078
|
+
assert.equal(
|
|
1079
|
+
result.newDocs.length,
|
|
1080
|
+
1,
|
|
1081
|
+
"the valid comment must not be starved by budget-consuming skipped items",
|
|
1082
|
+
);
|
|
1083
|
+
assert.equal(result.newDocs[0].source.externalId, `${REPO_A}/issue-comment/9999`);
|
|
1084
|
+
assert.equal(result.skippedOtherAuthor, MAX);
|
|
1085
|
+
});
|
|
1086
|
+
|
|
1087
|
+
test("skipped (empty-body) records do not consume the per-pass budget", async () => {
|
|
1088
|
+
// 5 empty-body comments followed by 3 valid ones within a budget of 3.
|
|
1089
|
+
const emptyComments = Array.from({ length: 5 }, (_, i) =>
|
|
1090
|
+
makeComment(200 + i, SYNTHETIC_LOGIN, "", `2026-04-26T09:00:0${i}.000Z`),
|
|
1091
|
+
);
|
|
1092
|
+
const validComments = Array.from({ length: 3 }, (_, i) =>
|
|
1093
|
+
makeComment(300 + i, SYNTHETIC_LOGIN, `valid ${i}`, `2026-04-26T09:01:0${i}.000Z`),
|
|
1094
|
+
);
|
|
1095
|
+
|
|
1096
|
+
const fetchFn = makeFetch([
|
|
1097
|
+
{
|
|
1098
|
+
match: (url) => url.includes("/issues/comments"),
|
|
1099
|
+
respond: () => ({ status: 200, data: [...emptyComments, ...validComments] }),
|
|
1100
|
+
},
|
|
1101
|
+
{
|
|
1102
|
+
match: (url) => url.includes("/pulls/comments"),
|
|
1103
|
+
respond: () => ({ status: 200, data: [] }),
|
|
1104
|
+
},
|
|
1105
|
+
]);
|
|
1106
|
+
|
|
1107
|
+
const connector = createGitHubConnector({ fetchFn });
|
|
1108
|
+
const config = connector.validateConfig({ ...SYNTHETIC_CONFIG });
|
|
1109
|
+
const cursor = makeGitHubCursor({});
|
|
1110
|
+
|
|
1111
|
+
// Use a budget of 3. With the old (buggy) code, the 5 empty comments would
|
|
1112
|
+
// exhaust the budget before reaching the valid ones.
|
|
1113
|
+
// We can't set MAX_ITEMS_PER_PASS directly; instead verify that with the
|
|
1114
|
+
// default budget all 3 valid comments are imported.
|
|
1115
|
+
const result = (await connector.syncIncremental({ cursor, config })) as GitHubSyncResult;
|
|
1116
|
+
|
|
1117
|
+
assert.equal(result.newDocs.length, 3, "all 3 valid comments must be imported");
|
|
1118
|
+
assert.equal(result.skippedEmpty, 5);
|
|
1119
|
+
});
|
|
1120
|
+
|
|
1121
|
+
// ---------------------------------------------------------------------------
|
|
1122
|
+
// Regression: strict watermark filter (<, not <=) + timestamp-gated seenIds
|
|
1123
|
+
// Fixes: Cursor High finding (watermark boundary), Codex P1 finding (seenIds).
|
|
1124
|
+
// ---------------------------------------------------------------------------
|
|
1125
|
+
|
|
1126
|
+
test("watermark uses strict < so boundary-second comments pass through to seenIds", async () => {
|
|
1127
|
+
// Scenario: The cursor watermark is set to T. On the next poll GitHub's
|
|
1128
|
+
// `since=T` filter re-returns any comment whose updated_at equals T
|
|
1129
|
+
// (GitHub's `since` parameter is inclusive). With the old `<=` filter these
|
|
1130
|
+
// were silently dropped before seenIds could distinguish them. This test
|
|
1131
|
+
// verifies the `<` fix: a comment AT exactly the watermark second that is
|
|
1132
|
+
// NOT in seenIds must be ingested.
|
|
1133
|
+
const ts = "2026-04-26T12:00:00.000Z";
|
|
1134
|
+
// commentAtTs is at the exact watermark second but was NOT ingested before.
|
|
1135
|
+
const commentAtTs = makeComment(42, SYNTHETIC_LOGIN, "exactly at watermark", ts);
|
|
1136
|
+
|
|
1137
|
+
const fetchFn = makeFetch([
|
|
1138
|
+
{
|
|
1139
|
+
match: (url) => url.includes("/issues/comments"),
|
|
1140
|
+
respond: () => ({ status: 200, data: [commentAtTs] }),
|
|
1141
|
+
},
|
|
1142
|
+
{
|
|
1143
|
+
match: (url) => url.includes("/pulls/comments"),
|
|
1144
|
+
respond: () => ({ status: 200, data: [] }),
|
|
1145
|
+
},
|
|
1146
|
+
]);
|
|
1147
|
+
|
|
1148
|
+
const connector = createGitHubConnector({ fetchFn });
|
|
1149
|
+
const config = connector.validateConfig({ ...SYNTHETIC_CONFIG });
|
|
1150
|
+
// Cursor watermark is AT ts; seenIds is empty (comment was NOT previously seen).
|
|
1151
|
+
const cursor: import("./framework.js").ConnectorCursor = {
|
|
1152
|
+
kind: GITHUB_CURSOR_KIND,
|
|
1153
|
+
value: JSON.stringify({
|
|
1154
|
+
watermarks: { [`${REPO_A}/issue-comment`]: ts },
|
|
1155
|
+
seenIds: {},
|
|
1156
|
+
}),
|
|
1157
|
+
updatedAt: ts,
|
|
1158
|
+
};
|
|
1159
|
+
|
|
1160
|
+
const result = (await connector.syncIncremental({ cursor, config })) as GitHubSyncResult;
|
|
1161
|
+
// With `<` fix: commentAtTs is NOT before the watermark, NOT in seenIds →
|
|
1162
|
+
// must be ingested. With old `<=` it would have been silently dropped.
|
|
1163
|
+
assert.equal(
|
|
1164
|
+
result.newDocs.length,
|
|
1165
|
+
1,
|
|
1166
|
+
"comment exactly at watermark second must be ingested when not in seenIds",
|
|
1167
|
+
);
|
|
1168
|
+
assert.equal(result.newDocs[0].source.externalId, `${REPO_A}/issue-comment/42`);
|
|
1169
|
+
});
|
|
1170
|
+
|
|
1171
|
+
test("seenIds check gates on (id + timestamp), not id alone", async () => {
|
|
1172
|
+
// Scenario: A comment was previously imported at ts1. It is then edited and
|
|
1173
|
+
// re-fetched at ts2 (same id, newer updated_at). With the old `!== undefined`
|
|
1174
|
+
// check the edited version would be silently dropped. With the new
|
|
1175
|
+
// `=== comment.updated_at` check only the exact (id, ts) pair is suppressed.
|
|
1176
|
+
const ts1 = "2026-04-26T13:00:00.000Z";
|
|
1177
|
+
const ts2 = "2026-04-26T13:00:01.000Z";
|
|
1178
|
+
// Same id as the previously-ingested comment, but updated_at has advanced.
|
|
1179
|
+
const editedComment = makeComment(77, SYNTHETIC_LOGIN, "edited body", ts2);
|
|
1180
|
+
|
|
1181
|
+
const fetchFn = makeFetch([
|
|
1182
|
+
{
|
|
1183
|
+
match: (url) => url.includes("/issues/comments"),
|
|
1184
|
+
respond: () => ({ status: 200, data: [editedComment] }),
|
|
1185
|
+
},
|
|
1186
|
+
{
|
|
1187
|
+
match: (url) => url.includes("/pulls/comments"),
|
|
1188
|
+
respond: () => ({ status: 200, data: [] }),
|
|
1189
|
+
},
|
|
1190
|
+
]);
|
|
1191
|
+
|
|
1192
|
+
const connector = createGitHubConnector({ fetchFn });
|
|
1193
|
+
const config = connector.validateConfig({ ...SYNTHETIC_CONFIG });
|
|
1194
|
+
// Cursor records id 77 with ts1 in seenIds (the old ingested version).
|
|
1195
|
+
// Watermark is at ts1 so the edited comment (ts2 > ts1) is above it.
|
|
1196
|
+
const cursor: import("./framework.js").ConnectorCursor = {
|
|
1197
|
+
kind: GITHUB_CURSOR_KIND,
|
|
1198
|
+
value: JSON.stringify({
|
|
1199
|
+
watermarks: { [`${REPO_A}/issue-comment`]: ts1 },
|
|
1200
|
+
seenIds: { [`${REPO_A}/issue-comment/77`]: ts1 },
|
|
1201
|
+
}),
|
|
1202
|
+
updatedAt: ts1,
|
|
1203
|
+
};
|
|
1204
|
+
|
|
1205
|
+
const result = (await connector.syncIncremental({ cursor, config })) as GitHubSyncResult;
|
|
1206
|
+
// The edited comment (same id, newer ts) must be ingested — seenIds only
|
|
1207
|
+
// suppresses the (id, ts1) pair, not (id, ts2).
|
|
1208
|
+
assert.equal(
|
|
1209
|
+
result.newDocs.length,
|
|
1210
|
+
1,
|
|
1211
|
+
"edited comment with newer updated_at must not be suppressed by seenIds",
|
|
1212
|
+
);
|
|
1213
|
+
assert.equal(result.newDocs[0].source.externalId, `${REPO_A}/issue-comment/77`);
|
|
1214
|
+
assert.ok(
|
|
1215
|
+
result.newDocs[0].content.includes("edited body"),
|
|
1216
|
+
"imported document must contain the new body",
|
|
1217
|
+
);
|
|
1218
|
+
});
|