@remnic/core 1.0.2 → 1.1.0
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/LICENSE +21 -0
- package/README.md +1 -0
- package/dist/abort-error.d.ts +32 -0
- package/dist/abort-error.js +11 -0
- package/dist/access-cli.d.ts +13 -3
- package/dist/access-cli.js +96 -80
- package/dist/access-cli.js.map +1 -1
- package/dist/access-http.d.ts +12 -4
- package/dist/access-http.js +25 -18
- package/dist/access-mcp.d.ts +32 -4
- package/dist/access-mcp.js +16 -1
- package/dist/access-schema.d.ts +28 -28
- package/dist/access-schema.js +1 -1
- package/dist/access-service-HmO1Trrx.d.ts +732 -0
- package/dist/access-service.d.ts +15 -601
- package/dist/access-service.js +21 -15
- package/dist/active-memory-bridge.d.ts +66 -0
- package/dist/active-memory-bridge.js +11 -0
- package/dist/active-memory-bridge.js.map +1 -0
- package/dist/active-recall.d.ts +96 -0
- package/dist/active-recall.js +308 -0
- package/dist/active-recall.js.map +1 -0
- package/dist/behavior-learner.js +1 -1
- package/dist/bootstrap.d.ts +6 -3
- package/dist/bootstrap.js +2 -2
- package/dist/boxes.js +2 -2
- package/dist/briefing.d.ts +169 -0
- package/dist/briefing.js +52 -0
- package/dist/briefing.js.map +1 -0
- package/dist/buffer.d.ts +19 -5
- package/dist/buffer.js +2 -2
- package/dist/calibration.js +6 -6
- package/dist/causal-behavior.js +5 -5
- package/dist/causal-chain.js +3 -3
- package/dist/causal-consolidation.d.ts +22 -2
- package/dist/causal-consolidation.js +36 -9
- package/dist/causal-consolidation.js.map +1 -1
- package/dist/causal-retrieval.js +6 -6
- package/dist/causal-trajectory-graph.js +1 -1
- package/dist/causal-trajectory.d.ts +14 -1
- package/dist/causal-trajectory.js +5 -1
- package/dist/{chunk-KWBU5S5U.js → chunk-2ODBA7MQ.js} +9 -3
- package/dist/chunk-2ODBA7MQ.js.map +1 -0
- package/dist/{chunk-ZJLY4QSU.js → chunk-37UIFYWO.js} +130 -6
- package/dist/chunk-37UIFYWO.js.map +1 -0
- package/dist/chunk-3PG3H5TD.js +7 -0
- package/dist/chunk-3PG3H5TD.js.map +1 -0
- package/dist/{chunk-NTTLPF7F.js → chunk-3QFQGRHO.js} +5 -5
- package/dist/{chunk-QDOSNLB4.js → chunk-3QHL5ABG.js} +17 -15
- package/dist/chunk-3QHL5ABG.js.map +1 -0
- package/dist/{chunk-6UJQNRIO.js → chunk-3SV6CQHO.js} +92 -33
- package/dist/chunk-3SV6CQHO.js.map +1 -0
- package/dist/{chunk-U4PV25RD.js → chunk-3WHVNEN7.js} +1 -1
- package/dist/chunk-3WHVNEN7.js.map +1 -0
- package/dist/{chunk-XUHI52HK.js → chunk-44ICJRF3.js} +98 -10
- package/dist/chunk-44ICJRF3.js.map +1 -0
- package/dist/{chunk-HG2NKWR2.js → chunk-47UU5PU2.js} +49 -10
- package/dist/chunk-47UU5PU2.js.map +1 -0
- package/dist/chunk-4DJQYKMN.js +187 -0
- package/dist/chunk-4DJQYKMN.js.map +1 -0
- package/dist/chunk-4KAN3GZ3.js +225 -0
- package/dist/chunk-4KAN3GZ3.js.map +1 -0
- package/dist/chunk-4LACOVZX.js +813 -0
- package/dist/chunk-4LACOVZX.js.map +1 -0
- package/dist/{chunk-ORZMT74A.js → chunk-4NRAJUDS.js} +11 -1
- package/dist/chunk-4NRAJUDS.js.map +1 -0
- package/dist/{chunk-B7LOFDVE.js → chunk-4WMCPJWX.js} +8 -3
- package/dist/chunk-4WMCPJWX.js.map +1 -0
- package/dist/{chunk-G3AG3KZN.js → chunk-5IZL4DCV.js} +2 -2
- package/dist/{chunk-BRK4ODMI.js → chunk-5NPGSAVB.js} +2 -2
- package/dist/{chunk-QANCTXQF.js → chunk-6LX5ORAS.js} +3 -3
- package/dist/chunk-6MKAMLQL.js +16 -0
- package/dist/chunk-6MKAMLQL.js.map +1 -0
- package/dist/{chunk-ESSMF2FR.js → chunk-6PFRXT4K.js} +15 -6
- package/dist/chunk-6PFRXT4K.js.map +1 -0
- package/dist/{chunk-UIYZ5T3I.js → chunk-6UJ47TVX.js} +8 -8
- package/dist/chunk-6ZH4TU6I.js +245 -0
- package/dist/chunk-6ZH4TU6I.js.map +1 -0
- package/dist/{chunk-L5RPWGFK.js → chunk-7DHTMOND.js} +2 -2
- package/dist/{chunk-L7WO3MZ4.js → chunk-7ECD5ATE.js} +2 -2
- package/dist/{chunk-Q6FETXJA.js → chunk-7SEAZFFB.js} +2 -2
- package/dist/{chunk-V4YC4LUK.js → chunk-7WQ6SLIE.js} +175 -63
- package/dist/chunk-7WQ6SLIE.js.map +1 -0
- package/dist/chunk-ALXMCZEU.js +332 -0
- package/dist/chunk-ALXMCZEU.js.map +1 -0
- package/dist/{chunk-TVVVQQAK.js → chunk-BLKTA7MM.js} +58 -24
- package/dist/chunk-BLKTA7MM.js.map +1 -0
- package/dist/{chunk-SCHEKPYH.js → chunk-C2EFFULQ.js} +1 -1
- package/dist/{chunk-GJR6D6KC.js → chunk-D654IBA6.js} +2 -2
- package/dist/{chunk-OTFNI3OO.js → chunk-DEPL3635.js} +1828 -401
- package/dist/chunk-DEPL3635.js.map +1 -0
- package/dist/{chunk-UYSKNO6E.js → chunk-DHHP2Z4X.js} +15 -4
- package/dist/chunk-DHHP2Z4X.js.map +1 -0
- package/dist/{chunk-UV2FO7J4.js → chunk-E6K4NIEU.js} +2 -2
- package/dist/{chunk-T4WRIV2C.js → chunk-EABGC2TL.js} +2 -2
- package/dist/chunk-EJI5XIBB.js +232 -0
- package/dist/chunk-EJI5XIBB.js.map +1 -0
- package/dist/{chunk-ONRU4L2N.js → chunk-FEMOX5AD.js} +2 -2
- package/dist/{chunk-IFFFR3MR.js → chunk-FSFEQI74.js} +3 -3
- package/dist/chunk-G4SK7DSQ.js +121 -0
- package/dist/chunk-G4SK7DSQ.js.map +1 -0
- package/dist/{chunk-WWIQTB2Y.js → chunk-GGD5W7TB.js} +9 -2
- package/dist/chunk-GGD5W7TB.js.map +1 -0
- package/dist/{chunk-QWUUMMIK.js → chunk-GV6NLQ4X.js} +1355 -80
- package/dist/chunk-GV6NLQ4X.js.map +1 -0
- package/dist/{chunk-2PO5ZRKV.js → chunk-GZCUW5IC.js} +16 -3
- package/dist/chunk-GZCUW5IC.js.map +1 -0
- package/dist/{chunk-AAI7JARD.js → chunk-HMDCOMYU.js} +8 -11
- package/dist/chunk-HMDCOMYU.js.map +1 -0
- package/dist/chunk-IQT3XTKW.js +121 -0
- package/dist/chunk-IQT3XTKW.js.map +1 -0
- package/dist/{chunk-J3BT33K7.js → chunk-ITRLGI2T.js} +5 -5
- package/dist/{chunk-BDFZXRSO.js → chunk-J4IYOZZ5.js} +15 -2
- package/dist/chunk-J4IYOZZ5.js.map +1 -0
- package/dist/{chunk-J47FNDR7.js → chunk-JIU55F3X.js} +7 -7
- package/dist/{chunk-MDDAA2AO.js → chunk-JL2PU6AI.js} +17 -6
- package/dist/chunk-JL2PU6AI.js.map +1 -0
- package/dist/{chunk-ZKYI7UVO.js → chunk-JR4ZC3G4.js} +2 -2
- package/dist/{chunk-UCYSTFZR.js → chunk-JRNQ3RNA.js} +2 -2
- package/dist/{chunk-GPGBSNKM.js → chunk-K4FLSOR5.js} +2 -2
- package/dist/chunk-KVE7R4CG.js +320 -0
- package/dist/chunk-KVE7R4CG.js.map +1 -0
- package/dist/chunk-LAYN4LDC.js +267 -0
- package/dist/chunk-LAYN4LDC.js.map +1 -0
- package/dist/{chunk-ISY75RLM.js → chunk-MBJHSA7F.js} +344 -7
- package/dist/chunk-MBJHSA7F.js.map +1 -0
- package/dist/{chunk-PGK3VUHN.js → chunk-MTLYEMJB.js} +3 -2
- package/dist/chunk-MTLYEMJB.js.map +1 -0
- package/dist/{chunk-QY2BHY5O.js → chunk-MVTHXUBX.js} +297 -34
- package/dist/chunk-MVTHXUBX.js.map +1 -0
- package/dist/{chunk-LP47L3ZX.js → chunk-N42IWANG.js} +5 -5
- package/dist/{chunk-YNI4S5WT.js → chunk-N53K2EXC.js} +2 -2
- package/dist/{chunk-763GUIOU.js → chunk-NBNN5GOB.js} +2 -2
- package/dist/{chunk-CXWFUJR2.js → chunk-NQEVYWX6.js} +195 -5
- package/dist/chunk-NQEVYWX6.js.map +1 -0
- package/dist/{chunk-KL4CP4SB.js → chunk-O5ETUNBT.js} +17 -5
- package/dist/chunk-O5ETUNBT.js.map +1 -0
- package/dist/{chunk-OOSWAUYB.js → chunk-ODWDQNRE.js} +2 -2
- package/dist/chunk-OIT5QGG4.js +80 -0
- package/dist/chunk-OIT5QGG4.js.map +1 -0
- package/dist/{chunk-HLBYLYRD.js → chunk-PAORGQRI.js} +70 -13
- package/dist/chunk-PAORGQRI.js.map +1 -0
- package/dist/chunk-PVGDJXVK.js +21 -0
- package/dist/chunk-PVGDJXVK.js.map +1 -0
- package/dist/{chunk-OTAVQCSF.js → chunk-PYXS46O7.js} +2 -2
- package/dist/chunk-QDW3E4RD.js +108 -0
- package/dist/chunk-QDW3E4RD.js.map +1 -0
- package/dist/{chunk-YNCQ7E4M.js → chunk-QDYXG4CS.js} +4 -3
- package/dist/chunk-QDYXG4CS.js.map +1 -0
- package/dist/{chunk-HLXVTBF3.js → chunk-QNJMBKFK.js} +3 -2
- package/dist/chunk-QNJMBKFK.js.map +1 -0
- package/dist/{chunk-4A24LIM2.js → chunk-S75M5ZRK.js} +2 -2
- package/dist/chunk-SYUK3VLY.js +789 -0
- package/dist/chunk-SYUK3VLY.js.map +1 -0
- package/dist/{chunk-QCCCQT3O.js → chunk-TBBDFYXW.js} +2 -2
- package/dist/chunk-TBBDFYXW.js.map +1 -0
- package/dist/chunk-U66YHYC7.js +31 -0
- package/dist/chunk-U66YHYC7.js.map +1 -0
- package/dist/{chunk-MWGVGUIS.js → chunk-UEYA6UC7.js} +36 -4
- package/dist/chunk-UEYA6UC7.js.map +1 -0
- package/dist/{chunk-M5KEYE5E.js → chunk-URB2WSKZ.js} +2 -2
- package/dist/chunk-UVJFDP7P.js +202 -0
- package/dist/chunk-UVJFDP7P.js.map +1 -0
- package/dist/chunk-W6SL7OFG.js +180 -0
- package/dist/chunk-W6SL7OFG.js.map +1 -0
- package/dist/chunk-WBSAYXVI.js +7945 -0
- package/dist/chunk-WBSAYXVI.js.map +1 -0
- package/dist/{chunk-M5ZBBBJI.js → chunk-XZ2TIKGC.js} +39 -9
- package/dist/chunk-XZ2TIKGC.js.map +1 -0
- package/dist/chunk-Y4FHOFJ2.js +140 -0
- package/dist/chunk-Y4FHOFJ2.js.map +1 -0
- package/dist/chunk-YDBIWGNI.js +298 -0
- package/dist/chunk-YDBIWGNI.js.map +1 -0
- package/dist/chunk-YNB73F22.js +137 -0
- package/dist/chunk-YNB73F22.js.map +1 -0
- package/dist/{chunk-IZME7KW2.js → chunk-ZVBB3T7V.js} +31 -12
- package/dist/chunk-ZVBB3T7V.js.map +1 -0
- package/dist/chunking.js +1 -1
- package/dist/citations.d.ts +67 -0
- package/dist/citations.js +13 -0
- package/dist/citations.js.map +1 -0
- package/dist/cli-BneVIEvh.d.ts +1240 -0
- package/dist/cli.d.ts +32 -1147
- package/dist/cli.js +150 -7092
- package/dist/cli.js.map +1 -1
- package/dist/codex-materialize-CQlLTzke.d.ts +139 -0
- package/dist/codex-thread-key.d.ts +3 -0
- package/dist/codex-thread-key.js +7 -0
- package/dist/codex-thread-key.js.map +1 -0
- package/dist/config.js +3 -2
- package/dist/connectors/codex/instructions.md +160 -0
- package/dist/connectors/codex/resources/namespace-cheatsheet.md +48 -0
- package/dist/contradiction-review-WIUBAR52.js +21 -0
- package/dist/contradiction-review-WIUBAR52.js.map +1 -0
- package/dist/contradiction-scan-GR33PONM.js +376 -0
- package/dist/contradiction-scan-GR33PONM.js.map +1 -0
- package/dist/day-summary.d.ts +7 -2
- package/dist/day-summary.js +5 -2
- package/dist/direct-answer-wiring.d.ts +77 -0
- package/dist/direct-answer-wiring.js +75 -0
- package/dist/direct-answer-wiring.js.map +1 -0
- package/dist/direct-answer.d.ts +106 -0
- package/dist/direct-answer.js +10 -0
- package/dist/direct-answer.js.map +1 -0
- package/dist/embedding-fallback.d.ts +96 -2
- package/dist/embedding-fallback.js +6 -4
- package/dist/{engine-2A6J4XEX.js → engine-5TIQBYZR.js} +10 -7
- package/dist/engine-5TIQBYZR.js.map +1 -0
- package/dist/entity-retrieval.d.ts +3 -2
- package/dist/entity-retrieval.js +10 -7
- package/dist/entity-schema.d.ts +11 -0
- package/dist/entity-schema.js +19 -0
- package/dist/entity-schema.js.map +1 -0
- package/dist/explicit-capture.d.ts +6 -3
- package/dist/explicit-capture.js +2 -2
- package/dist/extraction-judge.d.ts +66 -0
- package/dist/extraction-judge.js +18 -0
- package/dist/extraction-judge.js.map +1 -0
- package/dist/extraction.d.ts +1 -0
- package/dist/extraction.js +12 -10
- package/dist/fallback-llm.d.ts +11 -2
- package/dist/fallback-llm.js +4 -4
- package/dist/graph.js +1 -1
- package/dist/harmonic-retrieval.js +2 -1
- package/dist/importance.d.ts +11 -1
- package/dist/importance.js +3 -1
- package/dist/index.d.ts +1027 -9
- package/dist/index.js +3303 -349
- package/dist/index.js.map +1 -1
- package/dist/intent.d.ts +2 -1
- package/dist/intent.js +3 -1
- package/dist/lifecycle.js +1 -1
- package/dist/local-llm.d.ts +10 -3
- package/dist/local-llm.js +2 -2
- package/dist/logger.d.ts +1 -1
- package/dist/logger.js +1 -1
- package/dist/memory-cache.d.ts +2 -2
- package/dist/memory-cache.js +1 -1
- package/dist/{memory-projection-store-NxMkbocT.d.ts → memory-projection-store-DeSXPh1j.d.ts} +1 -1
- package/dist/memory-projection-store.d.ts +1 -1
- package/dist/model-registry.js +2 -2
- package/dist/models-json.js +2 -2
- package/dist/native-knowledge.js +2 -2
- package/dist/negative.js +2 -2
- package/dist/operator-toolkit.js +20 -15
- package/dist/{orchestrator-zTa-Qo-1.d.ts → orchestrator-DRYA6_lW.d.ts} +273 -9
- package/dist/orchestrator.d.ts +6 -3
- package/dist/orchestrator.js +76 -63
- package/dist/page-versioning.d.ts +77 -0
- package/dist/page-versioning.js +15 -0
- package/dist/page-versioning.js.map +1 -0
- package/dist/plugin-id.d.ts +37 -0
- package/dist/plugin-id.js +11 -0
- package/dist/plugin-id.js.map +1 -0
- package/dist/policy-runtime.js +2 -2
- package/dist/profiling.js +2 -2
- package/dist/qmd.d.ts +5 -2
- package/dist/qmd.js +4 -3
- package/dist/recall-audit.d.ts +20 -0
- package/dist/recall-audit.js +50 -0
- package/dist/recall-audit.js.map +1 -0
- package/dist/recall-mmr.d.ts +152 -0
- package/dist/recall-mmr.js +17 -0
- package/dist/recall-mmr.js.map +1 -0
- package/dist/recall-qos.js +2 -2
- package/dist/recall-state.d.ts +28 -1
- package/dist/recall-state.js +2 -2
- package/dist/relevance.js +2 -2
- package/dist/resolution-QBTDHTG7.js +100 -0
- package/dist/resolution-QBTDHTG7.js.map +1 -0
- package/dist/resolve-provider-secret.d.ts +24 -1
- package/dist/resolve-provider-secret.js +4 -2
- package/dist/resume-bundles.js +6 -5
- package/dist/retrieval-agents.js +2 -2
- package/dist/retrieval.js +2 -2
- package/dist/schemas.d.ts +412 -54
- package/dist/schemas.js +3 -1
- package/dist/sdk-compat.d.ts +2 -0
- package/dist/sdk-compat.js +6 -3
- package/dist/sdk-compat.js.map +1 -1
- package/dist/semantic-chunking.d.ts +87 -0
- package/dist/semantic-chunking.js +20 -0
- package/dist/semantic-chunking.js.map +1 -0
- package/dist/semantic-consolidation-DrvSYRdB.d.ts +119 -0
- package/dist/semantic-consolidation.d.ts +4 -42
- package/dist/semantic-consolidation.js +23 -2
- package/dist/semantic-rule-promotion.js +9 -6
- package/dist/semantic-rule-verifier.js +10 -7
- package/dist/session-observer-state.js +2 -2
- package/dist/session-toggles.d.ts +22 -0
- package/dist/session-toggles.js +116 -0
- package/dist/session-toggles.js.map +1 -0
- package/dist/skills-registry.d.ts +47 -0
- package/dist/skills-registry.js +48 -0
- package/dist/skills-registry.js.map +1 -0
- package/dist/source-attribution.d.ts +169 -0
- package/dist/source-attribution.js +27 -0
- package/dist/source-attribution.js.map +1 -0
- package/dist/storage.d.ts +171 -10
- package/dist/storage.js +16 -5
- package/dist/summarizer.js +7 -7
- package/dist/temporal-supersession.d.ts +127 -0
- package/dist/temporal-supersession.js +20 -0
- package/dist/temporal-supersession.js.map +1 -0
- package/dist/threading.js +2 -2
- package/dist/tier-migration.d.ts +2 -1
- package/dist/tier-routing.js +2 -2
- package/dist/tokens.d.ts +21 -1
- package/dist/tokens.js +5 -1
- package/dist/transcript.js +2 -2
- package/dist/types-DJhqDJUV.d.ts +50 -0
- package/dist/types.d.ts +529 -3
- package/dist/types.js +1 -1
- package/dist/utility-learner.js +2 -2
- package/dist/utility-runtime.js +3 -3
- package/dist/verified-recall.js +11 -8
- package/dist/whitespace.d.ts +4 -0
- package/dist/whitespace.js +9 -0
- package/dist/whitespace.js.map +1 -0
- package/package.json +14 -8
- package/dist/chunk-2CJCWDMR.js +0 -87
- package/dist/chunk-2CJCWDMR.js.map +0 -1
- package/dist/chunk-2PO5ZRKV.js.map +0 -1
- package/dist/chunk-6UJQNRIO.js.map +0 -1
- package/dist/chunk-AAI7JARD.js.map +0 -1
- package/dist/chunk-B7LOFDVE.js.map +0 -1
- package/dist/chunk-BDFZXRSO.js.map +0 -1
- package/dist/chunk-CXWFUJR2.js.map +0 -1
- package/dist/chunk-DORBM6OB.js +0 -81
- package/dist/chunk-DORBM6OB.js.map +0 -1
- package/dist/chunk-ESSMF2FR.js.map +0 -1
- package/dist/chunk-HG2NKWR2.js.map +0 -1
- package/dist/chunk-HLBYLYRD.js.map +0 -1
- package/dist/chunk-HLXVTBF3.js.map +0 -1
- package/dist/chunk-ISY75RLM.js.map +0 -1
- package/dist/chunk-IZME7KW2.js.map +0 -1
- package/dist/chunk-KL4CP4SB.js.map +0 -1
- package/dist/chunk-KWBU5S5U.js.map +0 -1
- package/dist/chunk-M5ZBBBJI.js.map +0 -1
- package/dist/chunk-MDDAA2AO.js.map +0 -1
- package/dist/chunk-MWGVGUIS.js.map +0 -1
- package/dist/chunk-ORZMT74A.js.map +0 -1
- package/dist/chunk-OTFNI3OO.js.map +0 -1
- package/dist/chunk-PGK3VUHN.js.map +0 -1
- package/dist/chunk-QCCCQT3O.js.map +0 -1
- package/dist/chunk-QDOSNLB4.js.map +0 -1
- package/dist/chunk-QPKFPHOO.js +0 -178
- package/dist/chunk-QPKFPHOO.js.map +0 -1
- package/dist/chunk-QWUUMMIK.js.map +0 -1
- package/dist/chunk-QY2BHY5O.js.map +0 -1
- package/dist/chunk-TVVVQQAK.js.map +0 -1
- package/dist/chunk-U4PV25RD.js.map +0 -1
- package/dist/chunk-UYSKNO6E.js.map +0 -1
- package/dist/chunk-V4YC4LUK.js.map +0 -1
- package/dist/chunk-WWIQTB2Y.js.map +0 -1
- package/dist/chunk-XUHI52HK.js.map +0 -1
- package/dist/chunk-YNCQ7E4M.js.map +0 -1
- package/dist/chunk-ZJLY4QSU.js.map +0 -1
- /package/dist/{engine-2A6J4XEX.js.map → abort-error.js.map} +0 -0
- /package/dist/{chunk-NTTLPF7F.js.map → chunk-3QFQGRHO.js.map} +0 -0
- /package/dist/{chunk-G3AG3KZN.js.map → chunk-5IZL4DCV.js.map} +0 -0
- /package/dist/{chunk-BRK4ODMI.js.map → chunk-5NPGSAVB.js.map} +0 -0
- /package/dist/{chunk-QANCTXQF.js.map → chunk-6LX5ORAS.js.map} +0 -0
- /package/dist/{chunk-UIYZ5T3I.js.map → chunk-6UJ47TVX.js.map} +0 -0
- /package/dist/{chunk-L5RPWGFK.js.map → chunk-7DHTMOND.js.map} +0 -0
- /package/dist/{chunk-L7WO3MZ4.js.map → chunk-7ECD5ATE.js.map} +0 -0
- /package/dist/{chunk-Q6FETXJA.js.map → chunk-7SEAZFFB.js.map} +0 -0
- /package/dist/{chunk-SCHEKPYH.js.map → chunk-C2EFFULQ.js.map} +0 -0
- /package/dist/{chunk-GJR6D6KC.js.map → chunk-D654IBA6.js.map} +0 -0
- /package/dist/{chunk-UV2FO7J4.js.map → chunk-E6K4NIEU.js.map} +0 -0
- /package/dist/{chunk-T4WRIV2C.js.map → chunk-EABGC2TL.js.map} +0 -0
- /package/dist/{chunk-ONRU4L2N.js.map → chunk-FEMOX5AD.js.map} +0 -0
- /package/dist/{chunk-IFFFR3MR.js.map → chunk-FSFEQI74.js.map} +0 -0
- /package/dist/{chunk-J3BT33K7.js.map → chunk-ITRLGI2T.js.map} +0 -0
- /package/dist/{chunk-J47FNDR7.js.map → chunk-JIU55F3X.js.map} +0 -0
- /package/dist/{chunk-ZKYI7UVO.js.map → chunk-JR4ZC3G4.js.map} +0 -0
- /package/dist/{chunk-UCYSTFZR.js.map → chunk-JRNQ3RNA.js.map} +0 -0
- /package/dist/{chunk-GPGBSNKM.js.map → chunk-K4FLSOR5.js.map} +0 -0
- /package/dist/{chunk-LP47L3ZX.js.map → chunk-N42IWANG.js.map} +0 -0
- /package/dist/{chunk-YNI4S5WT.js.map → chunk-N53K2EXC.js.map} +0 -0
- /package/dist/{chunk-763GUIOU.js.map → chunk-NBNN5GOB.js.map} +0 -0
- /package/dist/{chunk-OOSWAUYB.js.map → chunk-ODWDQNRE.js.map} +0 -0
- /package/dist/{chunk-OTAVQCSF.js.map → chunk-PYXS46O7.js.map} +0 -0
- /package/dist/{chunk-4A24LIM2.js.map → chunk-S75M5ZRK.js.map} +0 -0
- /package/dist/{chunk-M5KEYE5E.js.map → chunk-URB2WSKZ.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/recall-mmr.ts"],"sourcesContent":["/**\n * Maximal Marginal Relevance (MMR) re-selection for recall candidates.\n *\n * After the reranker produces its ordered candidate list, we run an MMR pass\n * over the top N candidates (per-section) so that a cluster of near-duplicate\n * high-scoring facts cannot dominate the injected context.\n *\n * MMR(d) = λ * sim(d, query) − (1 − λ) * max_{d' ∈ selected} sim(d, d')\n *\n * - λ defaults to 0.7 (tilted toward relevance, with meaningful diversity).\n * - Similarity uses cosine over pre-computed embeddings when available, and\n * falls back to Jaccard over normalized tokens (lowercased alphanumerics)\n * when embeddings are missing.\n * - Per-section application is the caller's responsibility: pass each\n * section's ordered candidate slice independently so one cluster in one\n * section cannot starve another section.\n *\n * Pure, deterministic, no IO. Input arrays are never mutated.\n */\n\n/** Minimal candidate shape used by MMR. Callers may wrap their own records. */\nexport interface MmrCandidate {\n /** Stable identifier for the candidate. Only used for tie-breaking. */\n id: string;\n /** Text content used for the Jaccard similarity fallback. */\n content: string;\n /**\n * Relevance score from the upstream ranker (e.g. rerank score or RRF).\n * Used as the `sim(d, query)` term when no query embedding is available.\n */\n score: number;\n /** Optional pre-computed embedding vector for cosine similarity. */\n embedding?: readonly number[] | null;\n}\n\nexport interface ApplyMmrOptions<C extends MmrCandidate> {\n /** Ordered candidate list (most relevant first). */\n candidates: readonly C[];\n /** Optional query embedding. If provided and candidates carry embeddings,\n * relevance is measured by cosine similarity to the query. Otherwise\n * candidate `score` is normalized and used. */\n queryEmbedding?: readonly number[] | null;\n /** λ ∈ [0, 1]. 1 = pure relevance, 0 = pure diversity. Default 0.7. */\n lambda?: number;\n /** Apply MMR only over the top N candidates. Default 40. */\n topN?: number;\n /** Maximum number of candidates to select. Default = candidates length. */\n budget?: number;\n}\n\nexport interface MmrDiversityReport {\n /**\n * Total number of candidates MMR considered (the full input pool, pre-MMR).\n * Previously this mirrored `kept` and was therefore uninformative; it now\n * reflects the true pool size so `kept/considered` logs carry signal even\n * though MMR is reorder-only.\n */\n considered: number;\n /**\n * Number of candidates present in the MMR output. With the current\n * orchestrator pipeline MMR is reorder-only (no drops), so this is the\n * same as `considered`. It's still reported separately so a future\n * drop-mode MMR can distinguish them without another schema change.\n */\n kept: number;\n /**\n * Head-of-list positions whose candidate identity changed between the\n * pre-MMR and post-MMR slices. This is the *actionable* diversity signal:\n * it tells the caller how many of the top `sampleSize` results MMR\n * promoted or demoted. Zero means MMR had no head-of-list effect; a value\n * greater than zero means at least one diverse candidate was swapped in.\n */\n headReorderCount: number;\n /** Average pairwise similarity of the head-of-list input slice (pre-MMR). */\n avgPairwiseSimBefore: number;\n /** Average pairwise similarity of the head-of-list MMR output slice. */\n avgPairwiseSimAfter: number;\n}\n\nexport const DEFAULT_LAMBDA = 0.7;\nexport const DEFAULT_TOP_N = 40;\n/**\n * Default number of head-of-list candidates compared by\n * {@link summarizeMmrDiversity}. Small on purpose: we want to measure whether\n * MMR actually *changed* the head of the list. Comparing the full top-N slice\n * is meaningless when `budget >= candidates.length` because both slices\n * contain the same set (just reordered), making pairwise-similarity\n * order-independent and identical before vs after.\n */\nconst DEFAULT_DIVERSITY_SAMPLE_SIZE = 10;\n\n/**\n * Pure MMR re-selection over an ordered candidate list.\n *\n * Returns a new array — the input is never mutated. When `candidates.length`\n * is `<= 1` or `budget <= 0`, a defensive copy is returned as a no-op.\n */\nexport function applyMmrToCandidates<C extends MmrCandidate>(\n opts: ApplyMmrOptions<C>,\n): C[] {\n const candidates = Array.isArray(opts.candidates) ? opts.candidates : [];\n if (candidates.length === 0) return [];\n\n const lambda = clampLambda(opts.lambda);\n const topN = clampPositiveInt(opts.topN, DEFAULT_TOP_N);\n const budget = clampPositiveInt(opts.budget, candidates.length);\n\n if (budget <= 0) return [];\n if (candidates.length === 1) return [candidates[0]!];\n\n // Only reorder the top-N slice; anything past it keeps its original position\n // appended at the end (but will not be selected unless budget > topN).\n const pool = candidates.slice(0, topN);\n const tail = candidates.slice(topN);\n\n // Relevance scores as `sim(d, query)`. Prefer cosine to the query embedding\n // when we can compute it; otherwise fall back to the candidate's own score\n // normalized into [0, 1] across the pool.\n const relevance = computeRelevanceScores(pool, opts.queryEmbedding);\n\n // Pre-compute pairwise candidate-candidate similarity lazily as needed.\n const pairSim = new Map<string, number>();\n const pairKey = (i: number, j: number): string =>\n i < j ? `${i}:${j}` : `${j}:${i}`;\n const sim = (i: number, j: number): number => {\n if (i === j) return 1;\n const key = pairKey(i, j);\n const cached = pairSim.get(key);\n if (cached !== undefined) return cached;\n const a = pool[i]!;\n const b = pool[j]!;\n const s = similarity(a, b);\n pairSim.set(key, s);\n return s;\n };\n\n const selectedIdx: number[] = [];\n const remaining = new Set<number>();\n for (let i = 0; i < pool.length; i += 1) remaining.add(i);\n\n while (selectedIdx.length < budget && remaining.size > 0) {\n let bestIdx = -1;\n let bestMmr = Number.NEGATIVE_INFINITY;\n for (const idx of remaining) {\n const rel = relevance[idx] ?? 0;\n let maxSimToSelected = 0;\n if (selectedIdx.length > 0) {\n for (const s of selectedIdx) {\n const pairwise = sim(idx, s);\n if (pairwise > maxSimToSelected) maxSimToSelected = pairwise;\n }\n }\n const mmr = lambda * rel - (1 - lambda) * maxSimToSelected;\n if (\n mmr > bestMmr ||\n // Stable tie-breaker: prefer the earlier original position.\n (mmr === bestMmr && (bestIdx < 0 || idx < bestIdx))\n ) {\n bestMmr = mmr;\n bestIdx = idx;\n }\n }\n if (bestIdx < 0) break;\n selectedIdx.push(bestIdx);\n remaining.delete(bestIdx);\n }\n\n const selected: C[] = selectedIdx.map((i) => pool[i]!);\n\n // If the caller set a budget larger than topN, append tail candidates in\n // their original order until budget is filled. This keeps MMR scoped to the\n // top-N slice while still respecting the caller's requested size.\n if (selected.length < budget && tail.length > 0) {\n for (const c of tail) {\n if (selected.length >= budget) break;\n selected.push(c);\n }\n }\n\n return selected;\n}\n\n/**\n * Summarize how much MMR reshuffled the head of the candidate list, for\n * logging. Optional — callers can skip this if they don't care about metrics.\n *\n * IMPORTANT: `before` should be the score-ordered input (the same ordering the\n * upstream reranker emitted, *not* the MMR output), and `after` should be the\n * MMR-reordered list. Both are truncated to `sampleSize` before their pairwise\n * similarity is averaged.\n *\n * The sample size intentionally defaults to a small number\n * ({@link DEFAULT_DIVERSITY_SAMPLE_SIZE}) so that when the caller uses\n * `budget >= candidates.length` (i.e. MMR only reorders without dropping), the\n * head-of-list comparison still reflects whether MMR promoted diverse\n * candidates. Passing a sample size `>= candidates.length` in that situation\n * makes `avgPairwiseSimBefore` and `avgPairwiseSimAfter` trivially equal\n * because pairwise similarity is order-independent.\n */\nexport function summarizeMmrDiversity<C extends MmrCandidate>(\n before: readonly C[],\n after: readonly C[],\n sampleSize: number = DEFAULT_DIVERSITY_SAMPLE_SIZE,\n): MmrDiversityReport {\n const n = clampPositiveInt(sampleSize, DEFAULT_DIVERSITY_SAMPLE_SIZE);\n const beforeSlice = before.slice(0, n);\n const afterSlice = after.slice(0, n);\n // Count how many head-of-list positions MMR actually changed. A zero value\n // means MMR left the head untouched; a positive value is the actionable\n // \"MMR promoted N diverse candidates\" signal the previous metric lacked.\n let headReorderCount = 0;\n const compareLength = Math.min(beforeSlice.length, afterSlice.length);\n for (let i = 0; i < compareLength; i += 1) {\n if (beforeSlice[i]!.id !== afterSlice[i]!.id) headReorderCount += 1;\n }\n return {\n considered: before.length,\n kept: after.length,\n headReorderCount,\n avgPairwiseSimBefore: averagePairwiseSimilarity(beforeSlice),\n avgPairwiseSimAfter: averagePairwiseSimilarity(afterSlice),\n };\n}\n\n// -----------------------------------------------------------------------------\n// Internals\n// -----------------------------------------------------------------------------\n\nfunction clampLambda(value: number | undefined): number {\n if (typeof value !== \"number\" || !Number.isFinite(value)) return DEFAULT_LAMBDA;\n if (value < 0) return 0;\n if (value > 1) return 1;\n return value;\n}\n\nfunction clampPositiveInt(\n value: number | undefined,\n fallback: number,\n): number {\n if (typeof value !== \"number\" || !Number.isFinite(value)) return fallback;\n if (value <= 0) return 0;\n return Math.floor(value);\n}\n\nfunction computeRelevanceScores<C extends MmrCandidate>(\n pool: readonly C[],\n queryEmbedding: readonly number[] | null | undefined,\n): number[] {\n // If we have a query embedding, use cosine against each candidate embedding\n // where possible. Candidates without embeddings fall back to normalized score.\n const canUseQueryEmbedding =\n Array.isArray(queryEmbedding) && queryEmbedding.length > 0;\n\n if (canUseQueryEmbedding) {\n const scores: number[] = [];\n for (const c of pool) {\n if (Array.isArray(c.embedding) && c.embedding.length > 0) {\n scores.push(cosineSimilarity(queryEmbedding!, c.embedding));\n } else {\n // Fall back to the upstream relevance score for this one candidate.\n scores.push(normalizeFinite(c.score));\n }\n }\n return normalizeVector(scores);\n }\n\n // No query embedding — use normalized candidate scores.\n const raw = pool.map((c) => normalizeFinite(c.score));\n return normalizeVector(raw);\n}\n\nfunction similarity<C extends MmrCandidate>(a: C, b: C): number {\n if (\n Array.isArray(a.embedding) &&\n a.embedding.length > 0 &&\n Array.isArray(b.embedding) &&\n b.embedding.length > 0 &&\n a.embedding.length === b.embedding.length\n ) {\n return cosineSimilarity(a.embedding, b.embedding);\n }\n return jaccardSimilarity(a.content ?? \"\", b.content ?? \"\");\n}\n\nfunction cosineSimilarity(\n a: readonly number[],\n b: readonly number[],\n): number {\n if (a.length === 0 || b.length === 0) return 0;\n const len = Math.min(a.length, b.length);\n let dot = 0;\n let na = 0;\n let nb = 0;\n for (let i = 0; i < len; i += 1) {\n const av = a[i] ?? 0;\n const bv = b[i] ?? 0;\n dot += av * bv;\n na += av * av;\n nb += bv * bv;\n }\n if (na === 0 || nb === 0) return 0;\n const cos = dot / (Math.sqrt(na) * Math.sqrt(nb));\n // Clamp to [0, 1]. Negative cosines (opposite directions) are treated as 0\n // for MMR purposes — two embeddings pointing opposite ways should be\n // \"maximally diverse\", not discouraged further.\n if (cos < 0) return 0;\n if (cos > 1) return 1;\n return cos;\n}\n\nexport function normalizeTokens(text: string): Set<string> {\n if (!text) return new Set();\n // Unicode-aware normalization. We lowercase and replace any character that\n // is NOT a Unicode letter or number with a space, then split on whitespace.\n // This preserves non-Latin scripts (Cyrillic, Greek, Hebrew, Arabic, etc.)\n // so the Jaccard fallback still detects near-duplicates in multilingual\n // snippets. Without this fix, `[^a-z0-9]+` strips all non-ASCII and two\n // identical Chinese/Japanese/Cyrillic snippets both collapse to the empty\n // set, returning similarity 0 and letting duplicates dominate recall.\n const cleaned = text\n .toLowerCase()\n .replace(/[^\\p{L}\\p{N}]+/gu, \" \")\n .trim();\n if (cleaned.length === 0) {\n // CJK fallback: scripts like Chinese/Japanese/Korean do not use word\n // breaks, so after the Unicode strip the above split-on-whitespace yields\n // one big token. The Latin-style split is a bad match for these scripts;\n // use per-codepoint character tokens instead, which approximates a\n // unigram shingle and is accurate enough for near-duplicate detection.\n const chars = new Set<string>();\n for (const ch of text.toLowerCase()) {\n if (/\\s/.test(ch)) continue;\n chars.add(ch);\n }\n return chars;\n }\n const tokens = new Set<string>();\n for (const token of cleaned.split(/\\s+/)) {\n if (!token) continue;\n // A single \"token\" that is actually a run of CJK codepoints (no spaces in\n // the source) should still be split into per-character tokens so the\n // Jaccard overlap works. Latin/Cyrillic/etc. keep their word-level tokens.\n if (token.length >= 2 && hasUnsegmentableScript(token)) {\n for (const ch of token) tokens.add(ch);\n } else {\n tokens.add(token);\n }\n }\n return tokens;\n}\n\n/**\n * Returns true when `token` contains at least one codepoint in a script that\n * does not use whitespace for word segmentation (CJK Unified Ideographs,\n * Hiragana, Katakana, Hangul). These are tokenized per-character so Jaccard\n * similarity can still detect near-duplicate snippets.\n */\nfunction hasUnsegmentableScript(token: string): boolean {\n return /[\\p{Script=Han}\\p{Script=Hiragana}\\p{Script=Katakana}\\p{Script=Hangul}]/u.test(\n token,\n );\n}\n\nfunction jaccardSimilarity(a: string, b: string): number {\n const ta = normalizeTokens(a);\n const tb = normalizeTokens(b);\n if (ta.size === 0 && tb.size === 0) return 0;\n let intersection = 0;\n for (const t of ta) if (tb.has(t)) intersection += 1;\n const union = ta.size + tb.size - intersection;\n if (union === 0) return 0;\n return intersection / union;\n}\n\nfunction normalizeFinite(value: number): number {\n if (typeof value !== \"number\" || !Number.isFinite(value)) return 0;\n return value;\n}\n\nfunction normalizeVector(values: number[]): number[] {\n if (values.length === 0) return values;\n // Intentionally *not* min-max normalized. Min-max maps the lowest score to\n // 0 and the highest to 1, which amplifies tiny relevance gaps (e.g. a tight\n // reranker cluster like [0.93, 0.94, 0.95]) into the full [0, 1] range.\n // That lets a near-duplicate at the top permanently beat a diverse\n // candidate at the bottom because `lambda * 0 - (1 - lambda) * 0 = 0` is\n // always worse than `lambda * 1 - (1 - lambda) * maxSim` for any\n // maxSim < lambda / (1 - lambda). MMR is supposed to escape exactly that\n // scenario; min-max defeats it.\n //\n // Instead we preserve the relative score gaps by scaling only if the\n // scores leave the MMR-friendly `[0, 1]` range. Negative or out-of-range\n // scores are clamped to `[0, 1]` by dividing by the max absolute value; if\n // everything is already inside `[0, 1]`, we pass through untouched so\n // tight clusters stay tight and MMR can promote a diverse candidate from\n // the bottom of the cluster.\n let max = 0;\n let min = Number.POSITIVE_INFINITY;\n for (const v of values) {\n if (!Number.isFinite(v)) continue;\n const a = Math.abs(v);\n if (a > max) max = a;\n if (v < min) min = v;\n }\n if (!Number.isFinite(min)) {\n return values.map(() => 0);\n }\n if (max === 0) {\n // All scores are zero — give everyone 1 so diversity fully drives\n // selection (otherwise every candidate has `lambda * 0 = 0` and the\n // first-found wins trivially, hiding any diversity benefit).\n return values.map(() => 1);\n }\n // If scores already live in [0, 1], pass them through so tight clusters\n // (e.g. [0.93, 0.94, 0.95]) stay tight and the (1 - lambda) * maxSim term\n // can actually outweigh a 0.01 relevance gap, which is the whole point of\n // MMR. Otherwise divide by `max` to bring the top score to 1 while\n // preserving *relative* gaps (e.g. [100, 200, 300] -> [0.33, 0.67, 1.0]).\n if (min >= 0 && max <= 1) {\n return values.map((v) =>\n Number.isFinite(v) ? (v < 0 ? 0 : v > 1 ? 1 : v) : 0,\n );\n }\n return values.map((v) => {\n if (!Number.isFinite(v)) return 0;\n const scaled = v / max;\n if (scaled < 0) return 0;\n if (scaled > 1) return 1;\n return scaled;\n });\n}\n\nfunction averagePairwiseSimilarity<C extends MmrCandidate>(\n candidates: readonly C[],\n): number {\n if (candidates.length < 2) return 0;\n let sum = 0;\n let count = 0;\n for (let i = 0; i < candidates.length; i += 1) {\n for (let j = i + 1; j < candidates.length; j += 1) {\n sum += similarity(candidates[i]!, candidates[j]!);\n count += 1;\n }\n }\n if (count === 0) return 0;\n return sum / count;\n}\n\n// -----------------------------------------------------------------------------\n// Orchestration helper: MMR for recall results keyed by path-first\n// -----------------------------------------------------------------------------\n\n/**\n * Minimum shape the recall-MMR orchestration helper expects from a result.\n * Any richer result type (e.g. {@link QmdSearchResult}) satisfies this.\n */\nexport interface MmrRecallResult {\n readonly docid?: string;\n readonly path?: string;\n readonly snippet?: string;\n readonly score?: number;\n}\n\nexport interface ReorderRecallResultsOptions {\n readonly lambda?: number;\n readonly topN?: number;\n /**\n * Head-of-list sample size used by the diversity metric. Defaults to\n * {@link DEFAULT_DIVERSITY_SAMPLE_SIZE}. Intentionally small so the metric\n * reflects head-of-list changes even when `budget >= results.length`.\n */\n readonly diversitySampleSize?: number;\n}\n\nexport interface ReorderRecallResultsOutcome<R extends MmrRecallResult> {\n readonly reordered: R[];\n readonly diversity: MmrDiversityReport;\n readonly lambda: number;\n}\n\n/**\n * Apply MMR to an ordered list of recall results and return the reordered\n * list plus a head-of-list diversity report.\n *\n * This helper is the single source of truth for the orchestrator's\n * per-section MMR pass. It is pure and deterministic so it can be unit\n * tested without constructing an Orchestrator.\n *\n * Key invariants:\n * 1. **No silent drops.** Candidates are keyed by a stable unique key derived\n * from `path` first, falling back to `docid`, and always suffixed with the\n * candidate's original index. Two results that share a basename-style\n * docid but differ in path are treated as distinct candidates and both\n * survive the reorder.\n * 2. **No mutation.** The input array is never mutated; a new array is\n * returned.\n * 3. **Diversity metric is meaningful.** The report compares the *head of\n * list* before and after MMR using a small sample size, so it reflects\n * whether MMR promoted diverse candidates even when\n * `budget >= results.length`.\n */\nexport function reorderRecallResultsWithMmr<R extends MmrRecallResult>(\n results: readonly R[],\n options: ReorderRecallResultsOptions = {},\n): ReorderRecallResultsOutcome<R> {\n const emptyReport: MmrDiversityReport = {\n considered: 0,\n kept: 0,\n headReorderCount: 0,\n avgPairwiseSimBefore: 0,\n avgPairwiseSimAfter: 0,\n };\n const lambda = clampLambda(options.lambda);\n\n if (!Array.isArray(results) || results.length === 0) {\n return { reordered: [], diversity: emptyReport, lambda };\n }\n if (results.length < 2) {\n // Single-element input: MMR is a trivial no-op but the diversity report\n // must still reflect the real pool size so external callers see\n // `considered=1 kept=1` rather than the `0/0` sentinel used for the\n // empty case. This is also the exact path the orchestrator short-circuits\n // past, but the helper is a public export and external callers cannot\n // rely on that invariant.\n return {\n reordered: [results[0]!],\n diversity: {\n considered: 1,\n kept: 1,\n headReorderCount: 0,\n avgPairwiseSimBefore: 0,\n avgPairwiseSimAfter: 0,\n },\n lambda,\n };\n }\n\n const topN = clampPositiveInt(options.topN, DEFAULT_TOP_N);\n\n // Build a per-result *unique* key so distinct results with colliding\n // docids or paths are never silently collapsed by id-based lookups.\n // Keys are always suffixed with the original input index so two results\n // that share the same `path` (or share the same `docid` when `path` is\n // empty) still get distinct identities.\n const candidates: MmrCandidate[] = results.map((r, index) => ({\n id: makeRecallKey(r, index),\n content: r.snippet ?? \"\",\n score: typeof r.score === \"number\" ? r.score : 0,\n embedding: null,\n }));\n // Index lookup by id so we can map MMR-selected candidates back to their\n // *original* position (and therefore their original `candidates[i]` object,\n // which preserves its `id` across the reorder). Reusing those original\n // candidate objects in the diversity comparison means\n // `headReorderCount` works correctly even when two results in the input\n // share the same base key: a swap at the same head position shows up\n // because their original-index-suffixed ids still differ.\n const indexById = new Map<string, number>();\n for (let i = 0; i < candidates.length; i += 1) {\n indexById.set(candidates[i]!.id, i);\n }\n\n const selectedMmr = applyMmrToCandidates({\n candidates,\n lambda,\n topN,\n budget: results.length,\n });\n\n const reordered: R[] = [];\n // Parallel array of the *original* candidate objects in the new (post-MMR)\n // order. Used for a faithful diversity comparison that preserves each\n // candidate's original identity.\n const reorderedCandidates: MmrCandidate[] = [];\n const seen = new Set<string>();\n for (const c of selectedMmr) {\n if (seen.has(c.id)) continue;\n const origIndex = indexById.get(c.id);\n if (origIndex === undefined) continue;\n seen.add(c.id);\n reordered.push(results[origIndex]!);\n reorderedCandidates.push(candidates[origIndex]!);\n }\n // Safety: append any candidates MMR did not select so nothing is dropped.\n if (reordered.length < results.length) {\n for (let i = 0; i < results.length; i += 1) {\n const origCandidate = candidates[i]!;\n if (seen.has(origCandidate.id)) continue;\n seen.add(origCandidate.id);\n reordered.push(results[i]!);\n reorderedCandidates.push(origCandidate);\n }\n }\n\n const diversity = summarizeMmrDiversity(\n candidates,\n reorderedCandidates,\n options.diversitySampleSize,\n );\n\n return { reordered, diversity, lambda };\n}\n\nfunction makeRecallKey(r: MmrRecallResult, index: number): string {\n const baseKey = r.path || r.docid || \"\";\n return `${baseKey}::${index}`;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
// src/contradiction/contradiction-review.ts
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import { createHash } from "crypto";
|
|
5
|
+
function computePairId(memoryIdA, memoryIdB) {
|
|
6
|
+
const sorted = [memoryIdA, memoryIdB].sort();
|
|
7
|
+
return createHash("sha256").update(sorted.join("::")).digest("hex").slice(0, 24);
|
|
8
|
+
}
|
|
9
|
+
function reviewDir(memoryDir) {
|
|
10
|
+
return path.join(memoryDir, ".review", "contradictions");
|
|
11
|
+
}
|
|
12
|
+
function pairPath(memoryDir, pairId) {
|
|
13
|
+
if (pairId.includes("/") || pairId.includes("\\") || pairId.includes("..")) {
|
|
14
|
+
throw new Error(`Invalid pairId: ${pairId}`);
|
|
15
|
+
}
|
|
16
|
+
return path.join(reviewDir(memoryDir), `${pairId}.json`);
|
|
17
|
+
}
|
|
18
|
+
function ensureDir(memoryDir) {
|
|
19
|
+
const dir = reviewDir(memoryDir);
|
|
20
|
+
if (!fs.existsSync(dir)) {
|
|
21
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function writePair(memoryDir, pair) {
|
|
25
|
+
ensureDir(memoryDir);
|
|
26
|
+
const pairId = computePairId(pair.memoryIds[0], pair.memoryIds[1]);
|
|
27
|
+
const existing = readPair(memoryDir, pairId);
|
|
28
|
+
if (existing?.resolution) {
|
|
29
|
+
return existing;
|
|
30
|
+
}
|
|
31
|
+
if (existing && existing.confidence >= pair.confidence) {
|
|
32
|
+
return existing;
|
|
33
|
+
}
|
|
34
|
+
const full = {
|
|
35
|
+
...pair,
|
|
36
|
+
pairId,
|
|
37
|
+
lastReviewedAt: existing?.lastReviewedAt,
|
|
38
|
+
resolution: existing?.resolution
|
|
39
|
+
};
|
|
40
|
+
const filePath = pairPath(memoryDir, pairId);
|
|
41
|
+
const tmpPath = `${filePath}.tmp`;
|
|
42
|
+
fs.writeFileSync(tmpPath, JSON.stringify(full, null, 2), "utf-8");
|
|
43
|
+
fs.renameSync(tmpPath, filePath);
|
|
44
|
+
return full;
|
|
45
|
+
}
|
|
46
|
+
function writePairs(memoryDir, pairs) {
|
|
47
|
+
const seen = /* @__PURE__ */ new Set();
|
|
48
|
+
const results = [];
|
|
49
|
+
for (const pair of pairs) {
|
|
50
|
+
const key = computePairId(pair.memoryIds[0], pair.memoryIds[1]);
|
|
51
|
+
if (seen.has(key)) continue;
|
|
52
|
+
seen.add(key);
|
|
53
|
+
results.push(writePair(memoryDir, pair));
|
|
54
|
+
}
|
|
55
|
+
return results;
|
|
56
|
+
}
|
|
57
|
+
function readPair(memoryDir, pairId) {
|
|
58
|
+
const filePath = pairPath(memoryDir, pairId);
|
|
59
|
+
try {
|
|
60
|
+
const raw = fs.readFileSync(filePath, "utf-8");
|
|
61
|
+
const parsed = JSON.parse(raw);
|
|
62
|
+
if (typeof parsed === "object" && parsed !== null && Array.isArray(parsed.memoryIds)) {
|
|
63
|
+
return parsed;
|
|
64
|
+
}
|
|
65
|
+
return null;
|
|
66
|
+
} catch {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
function listPairs(memoryDir, options) {
|
|
71
|
+
const startTime = Date.now();
|
|
72
|
+
const dir = reviewDir(memoryDir);
|
|
73
|
+
const { filter = "all", namespace, limit = 50 } = options ?? {};
|
|
74
|
+
const pairs = [];
|
|
75
|
+
let total = 0;
|
|
76
|
+
if (!fs.existsSync(dir)) {
|
|
77
|
+
return { pairs: [], total: 0, durationMs: Date.now() - startTime };
|
|
78
|
+
}
|
|
79
|
+
for (const entry of fs.readdirSync(dir)) {
|
|
80
|
+
if (!entry.endsWith(".json")) continue;
|
|
81
|
+
try {
|
|
82
|
+
const raw = fs.readFileSync(path.join(dir, entry), "utf-8");
|
|
83
|
+
const pair = JSON.parse(raw);
|
|
84
|
+
if (typeof pair !== "object" || pair === null) continue;
|
|
85
|
+
if (!Array.isArray(pair.memoryIds)) continue;
|
|
86
|
+
if (namespace && pair.namespace !== namespace) continue;
|
|
87
|
+
if (filter === "unresolved") {
|
|
88
|
+
if (pair.resolution) continue;
|
|
89
|
+
if (pair.verdict === "independent") continue;
|
|
90
|
+
} else if (filter !== "all" && pair.verdict !== filter) {
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
total++;
|
|
94
|
+
if (pairs.length < limit) pairs.push(pair);
|
|
95
|
+
} catch {
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return { pairs, total, durationMs: Date.now() - startTime };
|
|
100
|
+
}
|
|
101
|
+
function isCoolingDown(pair, cooldownDays) {
|
|
102
|
+
if (cooldownDays <= 0) return false;
|
|
103
|
+
if (!pair.lastReviewedAt) return false;
|
|
104
|
+
const lastReviewed = new Date(pair.lastReviewedAt).getTime();
|
|
105
|
+
if (!Number.isFinite(lastReviewed)) return false;
|
|
106
|
+
const cooldownMs = cooldownDays * 24 * 60 * 60 * 1e3;
|
|
107
|
+
return Date.now() < lastReviewed + cooldownMs;
|
|
108
|
+
}
|
|
109
|
+
function resolvePair(memoryDir, pairId, verb) {
|
|
110
|
+
const existing = readPair(memoryDir, pairId);
|
|
111
|
+
if (!existing) return null;
|
|
112
|
+
const updated = {
|
|
113
|
+
...existing,
|
|
114
|
+
lastReviewedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
115
|
+
resolution: verb
|
|
116
|
+
};
|
|
117
|
+
const filePath = pairPath(memoryDir, pairId);
|
|
118
|
+
const tmpPath = `${filePath}.tmp`;
|
|
119
|
+
fs.writeFileSync(tmpPath, JSON.stringify(updated, null, 2), "utf-8");
|
|
120
|
+
fs.renameSync(tmpPath, filePath);
|
|
121
|
+
return updated;
|
|
122
|
+
}
|
|
123
|
+
function memoryHashesChanged(_memoryDir, _pair, _getCurrentHash) {
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export {
|
|
128
|
+
computePairId,
|
|
129
|
+
writePair,
|
|
130
|
+
writePairs,
|
|
131
|
+
readPair,
|
|
132
|
+
listPairs,
|
|
133
|
+
isCoolingDown,
|
|
134
|
+
resolvePair,
|
|
135
|
+
memoryHashesChanged
|
|
136
|
+
};
|
|
137
|
+
//# sourceMappingURL=chunk-YNB73F22.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/contradiction/contradiction-review.ts"],"sourcesContent":["/**\n * Contradiction Review Queue — storage for detected contradiction pairs (issue #520).\n *\n * Stores candidate pairs as JSON files under `memoryDir/.review/contradictions/`.\n * Pair IDs are deterministic (sha256 of sorted memory IDs) so reruns are idempotent.\n *\n * Lifecycle:\n * - `contradicts` → awaiting user review\n * - `duplicates` → auto-flagged for dedup (still needs user approval)\n * - `independent` / `both-valid` → dormant with cooldown\n */\n\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { createHash } from \"node:crypto\";\nimport { log } from \"../logger.js\";\nimport type { ContradictionVerdict } from \"./contradiction-judge.js\";\n\n// ── Types ──────────────────────────────────────────────────────────────────────\n\nexport type ResolutionVerb = \"keep-a\" | \"keep-b\" | \"merge\" | \"both-valid\" | \"needs-more-context\";\n\nexport interface ContradictionPair {\n /** Deterministic pair ID: sha256(sorted(memoryIdA, memoryIdB)). */\n pairId: string;\n /** Memory IDs (sorted). */\n memoryIds: [string, string];\n /** Judge verdict. */\n verdict: ContradictionVerdict;\n /** Judge rationale. */\n rationale: string;\n /** Judge confidence in [0, 1]. */\n confidence: number;\n /** ISO timestamp when detected. */\n detectedAt: string;\n /** ISO timestamp when last reviewed by user. */\n lastReviewedAt?: string;\n /** Resolution verb applied by user. */\n resolution?: ResolutionVerb;\n /** Namespace scope. */\n namespace?: string;\n}\n\nexport interface ContradictionListResult {\n pairs: ContradictionPair[];\n total: number;\n durationMs: number;\n}\n\nexport type ContradictionFilter = ContradictionVerdict | \"all\" | \"unresolved\";\n\n// ── Helpers ────────────────────────────────────────────────────────────────────\n\nexport function computePairId(memoryIdA: string, memoryIdB: string): string {\n const sorted = [memoryIdA, memoryIdB].sort();\n return createHash(\"sha256\").update(sorted.join(\"::\")).digest(\"hex\").slice(0, 24);\n}\n\nfunction reviewDir(memoryDir: string): string {\n return path.join(memoryDir, \".review\", \"contradictions\");\n}\n\nfunction pairPath(memoryDir: string, pairId: string): string {\n if (pairId.includes(\"/\") || pairId.includes(\"\\\\\") || pairId.includes(\"..\")) {\n throw new Error(`Invalid pairId: ${pairId}`);\n }\n return path.join(reviewDir(memoryDir), `${pairId}.json`);\n}\n\nfunction ensureDir(memoryDir: string): void {\n const dir = reviewDir(memoryDir);\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true });\n }\n}\n\n// ── Write ──────────────────────────────────────────────────────────────────────\n\n/**\n * Write a contradiction pair to the review queue.\n * Idempotent: if the pair already exists with a higher or equal confidence,\n * the existing entry is preserved.\n */\nexport function writePair(memoryDir: string, pair: Omit<ContradictionPair, \"pairId\"> & { memoryIds: [string, string] }): ContradictionPair {\n ensureDir(memoryDir);\n const pairId = computePairId(pair.memoryIds[0], pair.memoryIds[1]);\n const existing = readPair(memoryDir, pairId);\n\n // Preserve user resolution if already reviewed\n if (existing?.resolution) {\n return existing;\n }\n\n // Preserve cooldown: don't overwrite a cooled-down entry with lower confidence\n if (existing && existing.confidence >= pair.confidence) {\n return existing;\n }\n\n const full: ContradictionPair = {\n ...pair,\n pairId,\n lastReviewedAt: existing?.lastReviewedAt,\n resolution: existing?.resolution,\n };\n\n const filePath = pairPath(memoryDir, pairId);\n const tmpPath = `${filePath}.tmp`;\n\n // Atomic write: temp then rename (rule 54)\n fs.writeFileSync(tmpPath, JSON.stringify(full, null, 2), \"utf-8\");\n fs.renameSync(tmpPath, filePath);\n\n return full;\n}\n\n/**\n * Write multiple pairs, deduplicating inputs first (rule 49).\n */\nexport function writePairs(memoryDir: string, pairs: Array<Omit<ContradictionPair, \"pairId\"> & { memoryIds: [string, string] }>): ContradictionPair[] {\n const seen = new Set<string>();\n const results: ContradictionPair[] = [];\n\n for (const pair of pairs) {\n const key = computePairId(pair.memoryIds[0], pair.memoryIds[1]);\n if (seen.has(key)) continue;\n seen.add(key);\n results.push(writePair(memoryDir, pair));\n }\n\n return results;\n}\n\n// ── Read ───────────────────────────────────────────────────────────────────────\n\n/**\n * Read a single pair by ID. Returns null if not found.\n */\nexport function readPair(memoryDir: string, pairId: string): ContradictionPair | null {\n const filePath = pairPath(memoryDir, pairId);\n try {\n const raw = fs.readFileSync(filePath, \"utf-8\");\n const parsed = JSON.parse(raw);\n if (typeof parsed === \"object\" && parsed !== null && Array.isArray(parsed.memoryIds)) {\n return parsed as ContradictionPair;\n }\n return null;\n } catch {\n return null;\n }\n}\n\n/**\n * List pairs in the review queue, optionally filtered by verdict.\n */\nexport function listPairs(\n memoryDir: string,\n options?: {\n filter?: ContradictionFilter;\n namespace?: string;\n limit?: number;\n },\n): ContradictionListResult {\n const startTime = Date.now();\n const dir = reviewDir(memoryDir);\n const { filter = \"all\", namespace, limit = 50 } = options ?? {};\n const pairs: ContradictionPair[] = [];\n let total = 0;\n\n if (!fs.existsSync(dir)) {\n return { pairs: [], total: 0, durationMs: Date.now() - startTime };\n }\n\n for (const entry of fs.readdirSync(dir)) {\n if (!entry.endsWith(\".json\")) continue;\n\n try {\n const raw = fs.readFileSync(path.join(dir, entry), \"utf-8\");\n const pair = JSON.parse(raw) as ContradictionPair;\n\n if (typeof pair !== \"object\" || pair === null) continue;\n if (!Array.isArray(pair.memoryIds)) continue;\n\n // Namespace filter\n if (namespace && pair.namespace !== namespace) continue;\n\n // Verdict filter\n if (filter === \"unresolved\") {\n if (pair.resolution) continue;\n if (pair.verdict === \"independent\") continue;\n } else if (filter !== \"all\" && pair.verdict !== filter) {\n continue;\n }\n\n total++;\n if (pairs.length < limit) pairs.push(pair);\n } catch {\n continue;\n }\n }\n\n return { pairs, total, durationMs: Date.now() - startTime };\n}\n\n// ── Cooldown ───────────────────────────────────────────────────────────────────\n\n/**\n * Check if a pair is within its cooldown window.\n * Returns true if the pair should be SKIPPED (still cooling down).\n */\nexport function isCoolingDown(pair: ContradictionPair, cooldownDays: number): boolean {\n if (cooldownDays <= 0) return false; // rule 27: guard against 0\n if (!pair.lastReviewedAt) return false;\n\n const lastReviewed = new Date(pair.lastReviewedAt).getTime();\n if (!Number.isFinite(lastReviewed)) return false;\n\n const cooldownMs = cooldownDays * 24 * 60 * 60 * 1000;\n return Date.now() < lastReviewed + cooldownMs;\n}\n\n/**\n * Mark a pair as reviewed (sets lastReviewedAt and resolution).\n */\nexport function resolvePair(\n memoryDir: string,\n pairId: string,\n verb: ResolutionVerb,\n): ContradictionPair | null {\n const existing = readPair(memoryDir, pairId);\n if (!existing) return null;\n\n const updated: ContradictionPair = {\n ...existing,\n lastReviewedAt: new Date().toISOString(),\n resolution: verb,\n };\n\n const filePath = pairPath(memoryDir, pairId);\n const tmpPath = `${filePath}.tmp`;\n\n fs.writeFileSync(tmpPath, JSON.stringify(updated, null, 2), \"utf-8\");\n fs.renameSync(tmpPath, filePath);\n\n return updated;\n}\n\n/**\n * Check whether a pair's referenced memories have changed since detection,\n * which should override cooldown.\n */\nexport function memoryHashesChanged(\n _memoryDir: string,\n _pair: ContradictionPair,\n _getCurrentHash: (memoryId: string) => string | null,\n): boolean {\n // Intentionally a stub for now — the full implementation would compare\n // content hashes stored at detection time with current hashes.\n return false;\n}\n"],"mappings":";AAYA,OAAO,QAAQ;AACf,OAAO,UAAU;AACjB,SAAS,kBAAkB;AAuCpB,SAAS,cAAc,WAAmB,WAA2B;AAC1E,QAAM,SAAS,CAAC,WAAW,SAAS,EAAE,KAAK;AAC3C,SAAO,WAAW,QAAQ,EAAE,OAAO,OAAO,KAAK,IAAI,CAAC,EAAE,OAAO,KAAK,EAAE,MAAM,GAAG,EAAE;AACjF;AAEA,SAAS,UAAU,WAA2B;AAC5C,SAAO,KAAK,KAAK,WAAW,WAAW,gBAAgB;AACzD;AAEA,SAAS,SAAS,WAAmB,QAAwB;AAC3D,MAAI,OAAO,SAAS,GAAG,KAAK,OAAO,SAAS,IAAI,KAAK,OAAO,SAAS,IAAI,GAAG;AAC1E,UAAM,IAAI,MAAM,mBAAmB,MAAM,EAAE;AAAA,EAC7C;AACA,SAAO,KAAK,KAAK,UAAU,SAAS,GAAG,GAAG,MAAM,OAAO;AACzD;AAEA,SAAS,UAAU,WAAyB;AAC1C,QAAM,MAAM,UAAU,SAAS;AAC/B,MAAI,CAAC,GAAG,WAAW,GAAG,GAAG;AACvB,OAAG,UAAU,KAAK,EAAE,WAAW,KAAK,CAAC;AAAA,EACvC;AACF;AASO,SAAS,UAAU,WAAmB,MAA8F;AACzI,YAAU,SAAS;AACnB,QAAM,SAAS,cAAc,KAAK,UAAU,CAAC,GAAG,KAAK,UAAU,CAAC,CAAC;AACjE,QAAM,WAAW,SAAS,WAAW,MAAM;AAG3C,MAAI,UAAU,YAAY;AACxB,WAAO;AAAA,EACT;AAGA,MAAI,YAAY,SAAS,cAAc,KAAK,YAAY;AACtD,WAAO;AAAA,EACT;AAEA,QAAM,OAA0B;AAAA,IAC9B,GAAG;AAAA,IACH;AAAA,IACA,gBAAgB,UAAU;AAAA,IAC1B,YAAY,UAAU;AAAA,EACxB;AAEA,QAAM,WAAW,SAAS,WAAW,MAAM;AAC3C,QAAM,UAAU,GAAG,QAAQ;AAG3B,KAAG,cAAc,SAAS,KAAK,UAAU,MAAM,MAAM,CAAC,GAAG,OAAO;AAChE,KAAG,WAAW,SAAS,QAAQ;AAE/B,SAAO;AACT;AAKO,SAAS,WAAW,WAAmB,OAAwG;AACpJ,QAAM,OAAO,oBAAI,IAAY;AAC7B,QAAM,UAA+B,CAAC;AAEtC,aAAW,QAAQ,OAAO;AACxB,UAAM,MAAM,cAAc,KAAK,UAAU,CAAC,GAAG,KAAK,UAAU,CAAC,CAAC;AAC9D,QAAI,KAAK,IAAI,GAAG,EAAG;AACnB,SAAK,IAAI,GAAG;AACZ,YAAQ,KAAK,UAAU,WAAW,IAAI,CAAC;AAAA,EACzC;AAEA,SAAO;AACT;AAOO,SAAS,SAAS,WAAmB,QAA0C;AACpF,QAAM,WAAW,SAAS,WAAW,MAAM;AAC3C,MAAI;AACF,UAAM,MAAM,GAAG,aAAa,UAAU,OAAO;AAC7C,UAAM,SAAS,KAAK,MAAM,GAAG;AAC7B,QAAI,OAAO,WAAW,YAAY,WAAW,QAAQ,MAAM,QAAQ,OAAO,SAAS,GAAG;AACpF,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAKO,SAAS,UACd,WACA,SAKyB;AACzB,QAAM,YAAY,KAAK,IAAI;AAC3B,QAAM,MAAM,UAAU,SAAS;AAC/B,QAAM,EAAE,SAAS,OAAO,WAAW,QAAQ,GAAG,IAAI,WAAW,CAAC;AAC9D,QAAM,QAA6B,CAAC;AACpC,MAAI,QAAQ;AAEZ,MAAI,CAAC,GAAG,WAAW,GAAG,GAAG;AACvB,WAAO,EAAE,OAAO,CAAC,GAAG,OAAO,GAAG,YAAY,KAAK,IAAI,IAAI,UAAU;AAAA,EACnE;AAEA,aAAW,SAAS,GAAG,YAAY,GAAG,GAAG;AACvC,QAAI,CAAC,MAAM,SAAS,OAAO,EAAG;AAE9B,QAAI;AACF,YAAM,MAAM,GAAG,aAAa,KAAK,KAAK,KAAK,KAAK,GAAG,OAAO;AAC1D,YAAM,OAAO,KAAK,MAAM,GAAG;AAE3B,UAAI,OAAO,SAAS,YAAY,SAAS,KAAM;AAC/C,UAAI,CAAC,MAAM,QAAQ,KAAK,SAAS,EAAG;AAGpC,UAAI,aAAa,KAAK,cAAc,UAAW;AAG/C,UAAI,WAAW,cAAc;AAC3B,YAAI,KAAK,WAAY;AACrB,YAAI,KAAK,YAAY,cAAe;AAAA,MACtC,WAAW,WAAW,SAAS,KAAK,YAAY,QAAQ;AACtD;AAAA,MACF;AAEA;AACA,UAAI,MAAM,SAAS,MAAO,OAAM,KAAK,IAAI;AAAA,IAC3C,QAAQ;AACN;AAAA,IACF;AAAA,EACF;AAEA,SAAO,EAAE,OAAO,OAAO,YAAY,KAAK,IAAI,IAAI,UAAU;AAC5D;AAQO,SAAS,cAAc,MAAyB,cAA+B;AACpF,MAAI,gBAAgB,EAAG,QAAO;AAC9B,MAAI,CAAC,KAAK,eAAgB,QAAO;AAEjC,QAAM,eAAe,IAAI,KAAK,KAAK,cAAc,EAAE,QAAQ;AAC3D,MAAI,CAAC,OAAO,SAAS,YAAY,EAAG,QAAO;AAE3C,QAAM,aAAa,eAAe,KAAK,KAAK,KAAK;AACjD,SAAO,KAAK,IAAI,IAAI,eAAe;AACrC;AAKO,SAAS,YACd,WACA,QACA,MAC0B;AAC1B,QAAM,WAAW,SAAS,WAAW,MAAM;AAC3C,MAAI,CAAC,SAAU,QAAO;AAEtB,QAAM,UAA6B;AAAA,IACjC,GAAG;AAAA,IACH,iBAAgB,oBAAI,KAAK,GAAE,YAAY;AAAA,IACvC,YAAY;AAAA,EACd;AAEA,QAAM,WAAW,SAAS,WAAW,MAAM;AAC3C,QAAM,UAAU,GAAG,QAAQ;AAE3B,KAAG,cAAc,SAAS,KAAK,UAAU,SAAS,MAAM,CAAC,GAAG,OAAO;AACnE,KAAG,WAAW,SAAS,QAAQ;AAE/B,SAAO;AACT;AAMO,SAAS,oBACd,YACA,OACA,iBACS;AAGT,SAAO;AACT;","names":[]}
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import {
|
|
2
2
|
QmdClient
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-BLKTA7MM.js";
|
|
4
4
|
import {
|
|
5
5
|
launchProcess
|
|
6
6
|
} from "./chunk-LK6SGL53.js";
|
|
7
|
-
import {
|
|
8
|
-
StorageManager
|
|
9
|
-
} from "./chunk-QWUUMMIK.js";
|
|
10
7
|
import {
|
|
11
8
|
isSafeRouteNamespace
|
|
12
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-QNJMBKFK.js";
|
|
10
|
+
import {
|
|
11
|
+
StorageManager
|
|
12
|
+
} from "./chunk-GV6NLQ4X.js";
|
|
13
13
|
import {
|
|
14
14
|
log
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-2ODBA7MQ.js";
|
|
16
16
|
|
|
17
17
|
// src/search/noop-backend.ts
|
|
18
18
|
var NoopSearchBackend = class {
|
|
@@ -104,8 +104,13 @@ async function scanDir(dir) {
|
|
|
104
104
|
async function scanMemoryDir(memoryDir) {
|
|
105
105
|
const factsDir = path.join(memoryDir, "facts");
|
|
106
106
|
const correctionsDir = path.join(memoryDir, "corrections");
|
|
107
|
-
const
|
|
108
|
-
|
|
107
|
+
const proceduresDir = path.join(memoryDir, "procedures");
|
|
108
|
+
const [facts, corrections, procedures] = await Promise.all([
|
|
109
|
+
scanDir(factsDir),
|
|
110
|
+
scanDir(correctionsDir),
|
|
111
|
+
scanDir(proceduresDir)
|
|
112
|
+
]);
|
|
113
|
+
return [...facts, ...corrections, ...procedures];
|
|
109
114
|
}
|
|
110
115
|
|
|
111
116
|
// src/search/lancedb-backend.ts
|
|
@@ -1730,7 +1735,8 @@ var NamespaceStorageRouter = class {
|
|
|
1730
1735
|
await this.defaultNamespaceRoot();
|
|
1731
1736
|
}
|
|
1732
1737
|
const root = this.namespaceRootSync(ns);
|
|
1733
|
-
const sm = new StorageManager(root);
|
|
1738
|
+
const sm = new StorageManager(root, this.config.entitySchemas);
|
|
1739
|
+
sm.citationTemplate = this.config.inlineSourceAttributionFormat;
|
|
1734
1740
|
this.cache.set(ns, sm);
|
|
1735
1741
|
return sm;
|
|
1736
1742
|
}
|
|
@@ -1796,15 +1802,24 @@ var NamespaceSearchRouter = class {
|
|
|
1796
1802
|
);
|
|
1797
1803
|
return mergeNamespaceSearchResults(resultsByNamespace, maxResults);
|
|
1798
1804
|
}
|
|
1805
|
+
/**
|
|
1806
|
+
* Update all namespace backends.
|
|
1807
|
+
* Returns the number of backends for which an update was attempted
|
|
1808
|
+
* (i.e., available and collection present). Callers can treat 0 as a
|
|
1809
|
+
* signal that no backend was eligible — useful for success-verification in
|
|
1810
|
+
* startup-sync when namespacesEnabled is true.
|
|
1811
|
+
*/
|
|
1799
1812
|
async updateNamespaces(namespaces) {
|
|
1800
1813
|
const unique = Array.from(new Set(namespaces.map((value) => value.trim()).filter(Boolean)));
|
|
1801
|
-
await Promise.all(
|
|
1814
|
+
const results = await Promise.all(
|
|
1802
1815
|
unique.map(async (namespace) => {
|
|
1803
1816
|
const record = await this.backendRecordFor(namespace);
|
|
1804
|
-
if (!record.available || record.collectionState === "missing") return;
|
|
1817
|
+
if (!record.available || record.collectionState === "missing") return 0;
|
|
1805
1818
|
await record.backend.update();
|
|
1819
|
+
return 1;
|
|
1806
1820
|
})
|
|
1807
1821
|
);
|
|
1822
|
+
return results.reduce((sum, v) => sum + v, 0);
|
|
1808
1823
|
}
|
|
1809
1824
|
async embedNamespaces(namespaces) {
|
|
1810
1825
|
const unique = Array.from(new Set(namespaces.map((value) => value.trim()).filter(Boolean)));
|
|
@@ -1820,6 +1835,10 @@ var NamespaceSearchRouter = class {
|
|
|
1820
1835
|
const record = await this.backendRecordFor(namespace);
|
|
1821
1836
|
return record.collectionState;
|
|
1822
1837
|
}
|
|
1838
|
+
/** Clear cached backend records so the next access re-probes availability. */
|
|
1839
|
+
clearCache() {
|
|
1840
|
+
this.cache.clear();
|
|
1841
|
+
}
|
|
1823
1842
|
async backendRecordFor(namespace) {
|
|
1824
1843
|
const key = namespace.trim() || this.config.defaultNamespace;
|
|
1825
1844
|
const existing = this.cache.get(key);
|
|
@@ -1883,4 +1902,4 @@ export {
|
|
|
1883
1902
|
namespaceCollectionName,
|
|
1884
1903
|
NamespaceSearchRouter
|
|
1885
1904
|
};
|
|
1886
|
-
//# sourceMappingURL=chunk-
|
|
1905
|
+
//# sourceMappingURL=chunk-ZVBB3T7V.js.map
|