@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,376 @@
|
|
|
1
|
+
import {
|
|
2
|
+
computePairId,
|
|
3
|
+
isCoolingDown,
|
|
4
|
+
listPairs,
|
|
5
|
+
writePairs
|
|
6
|
+
} from "./chunk-YNB73F22.js";
|
|
7
|
+
import {
|
|
8
|
+
extractJsonCandidates
|
|
9
|
+
} from "./chunk-UZB5KHKX.js";
|
|
10
|
+
import {
|
|
11
|
+
log
|
|
12
|
+
} from "./chunk-2ODBA7MQ.js";
|
|
13
|
+
|
|
14
|
+
// src/contradiction/contradiction-judge.ts
|
|
15
|
+
import { createHash } from "crypto";
|
|
16
|
+
var CONTRADICTION_JUDGE_PROMPT = `You are a memory contradiction classifier. You will receive pairs of stored memories and must classify their semantic relationship.
|
|
17
|
+
|
|
18
|
+
For each pair, respond with a JSON array where each element has:
|
|
19
|
+
- "pairKey": the pairKey provided in the input
|
|
20
|
+
- "verdict": one of "contradicts", "independent", "duplicates", "needs-user"
|
|
21
|
+
- "rationale": one sentence explaining why
|
|
22
|
+
- "confidence": number between 0 and 1
|
|
23
|
+
|
|
24
|
+
VERDICT DEFINITIONS:
|
|
25
|
+
- "contradicts": The two memories make claims that cannot both be true. One must be wrong or outdated.
|
|
26
|
+
- "duplicates": The two memories convey essentially the same information (near-paraphrase).
|
|
27
|
+
- "independent": The memories are topically similar but do not conflict or duplicate.
|
|
28
|
+
- "needs-user": Cannot determine with sufficient confidence; requires human review.
|
|
29
|
+
|
|
30
|
+
IMPORTANT:
|
|
31
|
+
- Be conservative. When in doubt, prefer "needs-user" over a wrong classification.
|
|
32
|
+
- Two memories about the same entity/topic are NOT necessarily contradictory.
|
|
33
|
+
- Temporal changes ("Joshua uses pnpm" vs "Joshua switched to npm") ARE contradictions.
|
|
34
|
+
- Different aspects of the same entity ("Joshua uses pnpm" vs "Joshua works on Remnic") are "independent".`;
|
|
35
|
+
var verdictCache = /* @__PURE__ */ new Map();
|
|
36
|
+
var CACHE_MAX = 1e4;
|
|
37
|
+
function pairKey(idA, idB) {
|
|
38
|
+
const sorted = [idA, idB].sort();
|
|
39
|
+
return `${sorted[0]}:${sorted[1]}`;
|
|
40
|
+
}
|
|
41
|
+
function contentHash(a) {
|
|
42
|
+
const sides = [
|
|
43
|
+
[a.textA.trim(), (a.categoryA ?? "").trim()].join("|"),
|
|
44
|
+
[a.textB.trim(), (a.categoryB ?? "").trim()].join("|")
|
|
45
|
+
].sort();
|
|
46
|
+
const normalized = sides.join("|||");
|
|
47
|
+
return createHash("sha256").update(normalized).digest("hex").slice(0, 16);
|
|
48
|
+
}
|
|
49
|
+
async function judgeContradictionPairs(pairs, config, localLlm, fallbackLlm, cache) {
|
|
50
|
+
const startTime = Date.now();
|
|
51
|
+
const results = /* @__PURE__ */ new Map();
|
|
52
|
+
const activeCache = cache ?? verdictCache;
|
|
53
|
+
let cached = 0;
|
|
54
|
+
let judged = 0;
|
|
55
|
+
const toJudge = [];
|
|
56
|
+
for (const pair of pairs) {
|
|
57
|
+
const key = pairKey(pair.memoryIdA, pair.memoryIdB);
|
|
58
|
+
const hash = contentHash(pair);
|
|
59
|
+
const cachedResult = activeCache.get(hash);
|
|
60
|
+
if (cachedResult) {
|
|
61
|
+
results.set(key, { ...cachedResult, memoryIdA: pair.memoryIdA, memoryIdB: pair.memoryIdB });
|
|
62
|
+
cached++;
|
|
63
|
+
} else {
|
|
64
|
+
toJudge.push(pair);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
if (toJudge.length === 0) {
|
|
68
|
+
return { results, cached, judged, elapsed: Date.now() - startTime };
|
|
69
|
+
}
|
|
70
|
+
const pairDescriptions = toJudge.map((p, i) => {
|
|
71
|
+
const pk = pairKey(p.memoryIdA, p.memoryIdB);
|
|
72
|
+
const catA = p.categoryA ? ` [${p.categoryA}]` : "";
|
|
73
|
+
const catB = p.categoryB ? ` [${p.categoryB}]` : "";
|
|
74
|
+
return `Pair ${i + 1} (pairKey: "${pk}"):${catA} "${p.textA}"${catB} "${p.textB}"`;
|
|
75
|
+
});
|
|
76
|
+
const userMessage = `Classify these ${toJudge.length} memory pair(s):
|
|
77
|
+
|
|
78
|
+
${pairDescriptions.join("\n\n")}`;
|
|
79
|
+
let llmResponse = null;
|
|
80
|
+
if (localLlm) {
|
|
81
|
+
try {
|
|
82
|
+
llmResponse = await callLlm(localLlm, config, userMessage);
|
|
83
|
+
} catch (err) {
|
|
84
|
+
log.warn("[contradiction-judge] local LLM call failed: %s", err instanceof Error ? err.message : err);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
if (!llmResponse && fallbackLlm) {
|
|
88
|
+
try {
|
|
89
|
+
llmResponse = await callLlm(fallbackLlm, config, userMessage);
|
|
90
|
+
} catch (err) {
|
|
91
|
+
log.warn("[contradiction-judge] fallback LLM call failed: %s", err instanceof Error ? err.message : err);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
if (llmResponse) {
|
|
95
|
+
const candidates = extractJsonCandidates(llmResponse);
|
|
96
|
+
const parsed = parseJudgeResponse(candidates, toJudge);
|
|
97
|
+
for (const result of parsed) {
|
|
98
|
+
const key = pairKey(result.memoryIdA, result.memoryIdB);
|
|
99
|
+
results.set(key, result);
|
|
100
|
+
const input = toJudge.find(
|
|
101
|
+
(p) => pairKey(p.memoryIdA, p.memoryIdB) === key
|
|
102
|
+
);
|
|
103
|
+
if (input) {
|
|
104
|
+
const hash = contentHash(input);
|
|
105
|
+
if (activeCache.size >= CACHE_MAX) {
|
|
106
|
+
const firstKey = activeCache.keys().next().value;
|
|
107
|
+
if (firstKey !== void 0) activeCache.delete(firstKey);
|
|
108
|
+
}
|
|
109
|
+
activeCache.set(hash, result);
|
|
110
|
+
}
|
|
111
|
+
judged++;
|
|
112
|
+
}
|
|
113
|
+
} else {
|
|
114
|
+
for (const pair of toJudge) {
|
|
115
|
+
const key = pairKey(pair.memoryIdA, pair.memoryIdB);
|
|
116
|
+
const result = {
|
|
117
|
+
memoryIdA: pair.memoryIdA,
|
|
118
|
+
memoryIdB: pair.memoryIdB,
|
|
119
|
+
verdict: "needs-user",
|
|
120
|
+
rationale: "LLM call failed; requires manual review",
|
|
121
|
+
confidence: 0
|
|
122
|
+
};
|
|
123
|
+
results.set(key, result);
|
|
124
|
+
judged++;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return { results, cached, judged, elapsed: Date.now() - startTime };
|
|
128
|
+
}
|
|
129
|
+
async function callLlm(client, config, userMessage) {
|
|
130
|
+
const messages = [
|
|
131
|
+
{ role: "system", content: CONTRADICTION_JUDGE_PROMPT },
|
|
132
|
+
{ role: "user", content: userMessage }
|
|
133
|
+
];
|
|
134
|
+
if ("chatCompletion" in client && typeof client.chatCompletion === "function") {
|
|
135
|
+
const result = await client.chatCompletion(messages, {
|
|
136
|
+
temperature: 0.1,
|
|
137
|
+
maxTokens: 4096
|
|
138
|
+
});
|
|
139
|
+
return result?.content ?? "";
|
|
140
|
+
}
|
|
141
|
+
if ("complete" in client && typeof client.complete === "function") {
|
|
142
|
+
const result = await client.complete(messages);
|
|
143
|
+
return result.content ?? "";
|
|
144
|
+
}
|
|
145
|
+
return "";
|
|
146
|
+
}
|
|
147
|
+
function parseJudgeResponse(candidates, inputs) {
|
|
148
|
+
const VALID_VERDICTS = ["contradicts", "independent", "duplicates", "needs-user"];
|
|
149
|
+
for (const candidate of candidates) {
|
|
150
|
+
try {
|
|
151
|
+
const parsed = JSON.parse(candidate);
|
|
152
|
+
const items = Array.isArray(parsed) ? parsed : [parsed];
|
|
153
|
+
const results = [];
|
|
154
|
+
const matchedKeys = /* @__PURE__ */ new Set();
|
|
155
|
+
for (const item of items) {
|
|
156
|
+
if (!item || typeof item !== "object") continue;
|
|
157
|
+
const verdict = typeof item.verdict === "string" && VALID_VERDICTS.includes(item.verdict) ? item.verdict : "needs-user";
|
|
158
|
+
const pairKeyVal = typeof item.pairKey === "string" ? item.pairKey : null;
|
|
159
|
+
const input = pairKeyVal ? inputs.find((p) => pairKey(p.memoryIdA, p.memoryIdB) === pairKeyVal) : null;
|
|
160
|
+
const fallbackInput = input ?? inputs[results.length] ?? inputs[0];
|
|
161
|
+
if (!fallbackInput) continue;
|
|
162
|
+
matchedKeys.add(pairKey(fallbackInput.memoryIdA, fallbackInput.memoryIdB));
|
|
163
|
+
const confidence = typeof item.confidence === "number" ? Math.min(1, Math.max(0, item.confidence)) : 0.5;
|
|
164
|
+
results.push({
|
|
165
|
+
memoryIdA: fallbackInput.memoryIdA,
|
|
166
|
+
memoryIdB: fallbackInput.memoryIdB,
|
|
167
|
+
verdict,
|
|
168
|
+
rationale: typeof item.rationale === "string" ? item.rationale : "No rationale provided",
|
|
169
|
+
confidence
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
for (const inp of inputs) {
|
|
173
|
+
const key = pairKey(inp.memoryIdA, inp.memoryIdB);
|
|
174
|
+
if (!matchedKeys.has(key)) {
|
|
175
|
+
results.push({
|
|
176
|
+
memoryIdA: inp.memoryIdA,
|
|
177
|
+
memoryIdB: inp.memoryIdB,
|
|
178
|
+
verdict: "needs-user",
|
|
179
|
+
rationale: "LLM response omitted this pair",
|
|
180
|
+
confidence: 0
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
if (results.length > 0) return results;
|
|
185
|
+
} catch {
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
return inputs.map((p) => ({
|
|
190
|
+
memoryIdA: p.memoryIdA,
|
|
191
|
+
memoryIdB: p.memoryIdB,
|
|
192
|
+
verdict: "needs-user",
|
|
193
|
+
rationale: "Failed to parse judge response",
|
|
194
|
+
confidence: 0
|
|
195
|
+
}));
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// src/contradiction/contradiction-scan.ts
|
|
199
|
+
var ACTIVE_STATUSES = /* @__PURE__ */ new Set(["active"]);
|
|
200
|
+
var SCAN_CATEGORIES = /* @__PURE__ */ new Set([
|
|
201
|
+
"decision",
|
|
202
|
+
"principle",
|
|
203
|
+
"rule",
|
|
204
|
+
"entity",
|
|
205
|
+
"fact",
|
|
206
|
+
"preference"
|
|
207
|
+
]);
|
|
208
|
+
async function runContradictionScan(deps) {
|
|
209
|
+
const startTime = Date.now();
|
|
210
|
+
const { storage, config, memoryDir, embeddingLookup, localLlm, fallbackLlm, namespace } = deps;
|
|
211
|
+
const scanConfig = config.contradictionScan;
|
|
212
|
+
if (!scanConfig.enabled) {
|
|
213
|
+
log.info("[contradiction-scan] disabled by config");
|
|
214
|
+
return { scanned: 0, candidates: 0, judged: 0, queued: 0, cooledDown: 0, elapsedMs: 0 };
|
|
215
|
+
}
|
|
216
|
+
const memories = await loadEligibleMemories(storage, namespace);
|
|
217
|
+
log.info("[contradiction-scan] loaded %d eligible memories", memories.length);
|
|
218
|
+
if (memories.length < 2) {
|
|
219
|
+
return { scanned: memories.length, candidates: 0, judged: 0, queued: 0, cooledDown: 0, elapsedMs: Date.now() - startTime };
|
|
220
|
+
}
|
|
221
|
+
const existingPairs = listPairs(memoryDir, { filter: "all", namespace, limit: 1e4 }).pairs;
|
|
222
|
+
const existingMap = /* @__PURE__ */ new Map();
|
|
223
|
+
for (const p of existingPairs) {
|
|
224
|
+
existingMap.set(p.pairId, p);
|
|
225
|
+
}
|
|
226
|
+
const candidates = generatePairs(memories, existingMap, scanConfig, embeddingLookup);
|
|
227
|
+
const cooledDown = candidates.skipped;
|
|
228
|
+
log.info("[contradiction-scan] generated %d candidates (%d cooled down)", candidates.pairs.length, cooledDown);
|
|
229
|
+
if (candidates.pairs.length === 0) {
|
|
230
|
+
return {
|
|
231
|
+
scanned: memories.length,
|
|
232
|
+
candidates: 0,
|
|
233
|
+
judged: 0,
|
|
234
|
+
queued: 0,
|
|
235
|
+
cooledDown,
|
|
236
|
+
elapsedMs: Date.now() - startTime
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
const capped = candidates.pairs.sort((a, b) => computePairId(a.idA, a.idB).localeCompare(computePairId(b.idA, b.idB))).slice(0, scanConfig.maxPairsPerRun);
|
|
240
|
+
const judgeInputs = capped.map((pair) => ({
|
|
241
|
+
memoryIdA: pair.idA,
|
|
242
|
+
memoryIdB: pair.idB,
|
|
243
|
+
textA: pair.textA,
|
|
244
|
+
textB: pair.textB,
|
|
245
|
+
categoryA: pair.categoryA,
|
|
246
|
+
categoryB: pair.categoryB
|
|
247
|
+
}));
|
|
248
|
+
const judgeResult = await judgeContradictionPairs(judgeInputs, config, localLlm, fallbackLlm);
|
|
249
|
+
log.info("[contradiction-scan] judge completed: %d judged, %d cached in %dms", judgeResult.judged, judgeResult.cached, judgeResult.elapsed);
|
|
250
|
+
const queueEntries = [];
|
|
251
|
+
for (const [key, result] of judgeResult.results) {
|
|
252
|
+
queueEntries.push({
|
|
253
|
+
memoryIds: [result.memoryIdA, result.memoryIdB],
|
|
254
|
+
verdict: result.verdict,
|
|
255
|
+
rationale: result.rationale,
|
|
256
|
+
confidence: result.confidence,
|
|
257
|
+
detectedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
258
|
+
// Set lastReviewedAt for non-actionable verdicts so cooldown prevents re-judging
|
|
259
|
+
lastReviewedAt: result.verdict === "independent" ? (/* @__PURE__ */ new Date()).toISOString() : void 0,
|
|
260
|
+
namespace
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
const written = writePairs(memoryDir, queueEntries);
|
|
264
|
+
const elapsed = Date.now() - startTime;
|
|
265
|
+
log.info("[contradiction-scan] complete: %d queued in %dms", written.length, elapsed);
|
|
266
|
+
return {
|
|
267
|
+
scanned: memories.length,
|
|
268
|
+
candidates: candidates.pairs.length,
|
|
269
|
+
judged: judgeResult.judged,
|
|
270
|
+
queued: written.length,
|
|
271
|
+
cooledDown,
|
|
272
|
+
elapsedMs: elapsed
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
function generatePairs(memories, existingPairs, scanConfig, embeddingLookup) {
|
|
276
|
+
const pairs = [];
|
|
277
|
+
let skipped = 0;
|
|
278
|
+
const seen = /* @__PURE__ */ new Set();
|
|
279
|
+
const byEntity = /* @__PURE__ */ new Map();
|
|
280
|
+
for (const mem of memories) {
|
|
281
|
+
const entity = mem.frontmatter.entityRef;
|
|
282
|
+
if (entity) {
|
|
283
|
+
const existing = byEntity.get(entity) ?? [];
|
|
284
|
+
existing.push(mem);
|
|
285
|
+
byEntity.set(entity, existing);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
for (const [, group] of byEntity) {
|
|
289
|
+
if (group.length < 2) continue;
|
|
290
|
+
for (let i = 0; i < group.length; i++) {
|
|
291
|
+
for (let j = i + 1; j < group.length; j++) {
|
|
292
|
+
const a = group[i];
|
|
293
|
+
const b = group[j];
|
|
294
|
+
const pairId = computePairId(a.frontmatter.id, b.frontmatter.id);
|
|
295
|
+
if (seen.has(pairId)) continue;
|
|
296
|
+
seen.add(pairId);
|
|
297
|
+
const existing = existingPairs.get(pairId);
|
|
298
|
+
if (existing && isCoolingDown(existing, scanConfig.cooldownDays)) {
|
|
299
|
+
skipped++;
|
|
300
|
+
continue;
|
|
301
|
+
}
|
|
302
|
+
pairs.push({
|
|
303
|
+
idA: a.frontmatter.id,
|
|
304
|
+
idB: b.frontmatter.id,
|
|
305
|
+
textA: a.content,
|
|
306
|
+
textB: b.content,
|
|
307
|
+
categoryA: a.frontmatter.category,
|
|
308
|
+
categoryB: b.frontmatter.category
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
const noEntity = memories.filter((m) => !m.frontmatter.entityRef);
|
|
314
|
+
for (let i = 0; i < noEntity.length; i++) {
|
|
315
|
+
for (let j = i + 1; j < noEntity.length; j++) {
|
|
316
|
+
const a = noEntity[i];
|
|
317
|
+
const b = noEntity[j];
|
|
318
|
+
const overlap = jaccardOverlap(
|
|
319
|
+
a.frontmatter.tags ?? [],
|
|
320
|
+
b.frontmatter.tags ?? []
|
|
321
|
+
);
|
|
322
|
+
if (overlap < scanConfig.topicOverlapFloor) continue;
|
|
323
|
+
const pairId = computePairId(a.frontmatter.id, b.frontmatter.id);
|
|
324
|
+
if (seen.has(pairId)) continue;
|
|
325
|
+
seen.add(pairId);
|
|
326
|
+
const existing = existingPairs.get(pairId);
|
|
327
|
+
if (existing && isCoolingDown(existing, scanConfig.cooldownDays)) {
|
|
328
|
+
skipped++;
|
|
329
|
+
continue;
|
|
330
|
+
}
|
|
331
|
+
pairs.push({
|
|
332
|
+
idA: a.frontmatter.id,
|
|
333
|
+
idB: b.frontmatter.id,
|
|
334
|
+
textA: a.content,
|
|
335
|
+
textB: b.content,
|
|
336
|
+
categoryA: a.frontmatter.category,
|
|
337
|
+
categoryB: b.frontmatter.category
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
return { pairs, skipped };
|
|
342
|
+
}
|
|
343
|
+
async function loadEligibleMemories(storage, namespace) {
|
|
344
|
+
let all;
|
|
345
|
+
try {
|
|
346
|
+
all = await storage.readAllMemories();
|
|
347
|
+
} catch {
|
|
348
|
+
return [];
|
|
349
|
+
}
|
|
350
|
+
return all.filter((mem) => {
|
|
351
|
+
const fm = mem.frontmatter;
|
|
352
|
+
const status = fm.status ?? "active";
|
|
353
|
+
if (!ACTIVE_STATUSES.has(status)) return false;
|
|
354
|
+
const category = fm.category;
|
|
355
|
+
if (category && !SCAN_CATEGORIES.has(category)) return false;
|
|
356
|
+
if (!mem.content || mem.content.trim().length === 0) return false;
|
|
357
|
+
if (!fm.id) return false;
|
|
358
|
+
return true;
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
function jaccardOverlap(a, b) {
|
|
362
|
+
if (a.length === 0 && b.length === 0) return 0;
|
|
363
|
+
const setA = new Set(a.map((t) => t.toLowerCase()));
|
|
364
|
+
const setB = new Set(b.map((t) => t.toLowerCase()));
|
|
365
|
+
let intersection = 0;
|
|
366
|
+
for (const item of setA) {
|
|
367
|
+
if (setB.has(item)) intersection++;
|
|
368
|
+
}
|
|
369
|
+
const union = setA.size + setB.size - intersection;
|
|
370
|
+
return union === 0 ? 0 : intersection / union;
|
|
371
|
+
}
|
|
372
|
+
export {
|
|
373
|
+
ACTIVE_STATUSES,
|
|
374
|
+
runContradictionScan
|
|
375
|
+
};
|
|
376
|
+
//# sourceMappingURL=contradiction-scan-GR33PONM.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/contradiction/contradiction-judge.ts","../src/contradiction/contradiction-scan.ts"],"sourcesContent":["/**\n * Contradiction Judge — LLM-as-judge for semantic contradiction detection (issue #520).\n *\n * Pairs semantically-similar memories and classifies their relationship.\n * Reuses the extraction-judge adapter pattern but with a contradiction-specific\n * prompt and verdict taxonomy.\n *\n * Design constraints:\n * - Default verdict on any failure is \"needs-user\" (rule 48: least-privileged default).\n * - Never auto-resolve; all verdicts enter the review queue.\n * - Content-hash caching avoids redundant LLM calls across runs.\n */\n\nimport { createHash } from \"node:crypto\";\nimport { log } from \"../logger.js\";\nimport type { PluginConfig } from \"../types.js\";\nimport type { LocalLlmClient } from \"../local-llm.js\";\nimport type { FallbackLlmClient } from \"../fallback-llm.js\";\nimport { extractJsonCandidates } from \"../json-extract.js\";\n\n// ── Types ──────────────────────────────────────────────────────────────────────\n\nexport type ContradictionVerdict =\n | \"contradicts\"\n | \"independent\"\n | \"duplicates\"\n | \"needs-user\";\n\nexport interface ContradictionJudgeInput {\n /** Memory ID of the first fact. */\n memoryIdA: string;\n /** Memory ID of the second fact. */\n memoryIdB: string;\n /** Content text of the first fact. */\n textA: string;\n /** Content text of the second fact. */\n textB: string;\n /** Category of the first fact (optional context). */\n categoryA?: string;\n /** Category of the second fact (optional context). */\n categoryB?: string;\n}\n\nexport interface ContradictionJudgeResult {\n /** Memory IDs of the pair. */\n memoryIdA: string;\n memoryIdB: string;\n /** Verdict from the judge. */\n verdict: ContradictionVerdict;\n /** Human-readable rationale. */\n rationale: string;\n /** Confidence in [0, 1]. */\n confidence: number;\n}\n\nexport interface ContradictionJudgeBatchResult {\n /** Results keyed by pair key (\"idA:idB\"). */\n results: Map<string, ContradictionJudgeResult>;\n /** Number served from cache. */\n cached: number;\n /** Number produced by LLM call. */\n judged: number;\n /** Total wall-clock time in ms. */\n elapsed: number;\n}\n\n// ── Prompt ─────────────────────────────────────────────────────────────────────\n\nconst CONTRADICTION_JUDGE_PROMPT = `You are a memory contradiction classifier. You will receive pairs of stored memories and must classify their semantic relationship.\n\nFor each pair, respond with a JSON array where each element has:\n- \"pairKey\": the pairKey provided in the input\n- \"verdict\": one of \"contradicts\", \"independent\", \"duplicates\", \"needs-user\"\n- \"rationale\": one sentence explaining why\n- \"confidence\": number between 0 and 1\n\nVERDICT DEFINITIONS:\n- \"contradicts\": The two memories make claims that cannot both be true. One must be wrong or outdated.\n- \"duplicates\": The two memories convey essentially the same information (near-paraphrase).\n- \"independent\": The memories are topically similar but do not conflict or duplicate.\n- \"needs-user\": Cannot determine with sufficient confidence; requires human review.\n\nIMPORTANT:\n- Be conservative. When in doubt, prefer \"needs-user\" over a wrong classification.\n- Two memories about the same entity/topic are NOT necessarily contradictory.\n- Temporal changes (\"Joshua uses pnpm\" vs \"Joshua switched to npm\") ARE contradictions.\n- Different aspects of the same entity (\"Joshua uses pnpm\" vs \"Joshua works on Remnic\") are \"independent\".`;\n\n// ── Cache ──────────────────────────────────────────────────────────────────────\n\nlet verdictCache: Map<string, ContradictionJudgeResult> = new Map();\nconst CACHE_MAX = 10_000;\n\nfunction pairKey(idA: string, idB: string): string {\n const sorted = [idA, idB].sort();\n return `${sorted[0]}:${sorted[1]}`;\n}\n\nfunction contentHash(a: ContradictionJudgeInput): string {\n // Sort each side pair to be order-independent (matching pairKey behavior)\n const sides = [\n [a.textA.trim(), (a.categoryA ?? \"\").trim()].join(\"|\"),\n [a.textB.trim(), (a.categoryB ?? \"\").trim()].join(\"|\"),\n ].sort();\n const normalized = sides.join(\"|||\");\n return createHash(\"sha256\").update(normalized).digest(\"hex\").slice(0, 16);\n}\n\nexport function createVerdictCache(): Map<string, ContradictionJudgeResult> {\n return new Map();\n}\n\nexport function clearVerdictCache(): void {\n verdictCache.clear();\n}\n\nexport function verdictCacheSize(): number {\n return verdictCache.size;\n}\n\n// ── Public API ──────────────────────────────────────────────────────────────────\n\n/**\n * Judge a batch of memory pairs for contradiction.\n *\n * Uses content-hash caching to skip pairs already judged in a prior run.\n * On any LLM failure, all unresolved pairs default to \"needs-user\".\n */\nexport async function judgeContradictionPairs(\n pairs: ContradictionJudgeInput[],\n config: PluginConfig,\n localLlm: LocalLlmClient | null,\n fallbackLlm: FallbackLlmClient | null,\n cache?: Map<string, ContradictionJudgeResult>,\n): Promise<ContradictionJudgeBatchResult> {\n const startTime = Date.now();\n const results = new Map<string, ContradictionJudgeResult>();\n const activeCache = cache ?? verdictCache;\n let cached = 0;\n let judged = 0;\n\n // Partition into cached vs needs-judging\n const toJudge: ContradictionJudgeInput[] = [];\n for (const pair of pairs) {\n const key = pairKey(pair.memoryIdA, pair.memoryIdB);\n const hash = contentHash(pair);\n const cachedResult = activeCache.get(hash);\n if (cachedResult) {\n results.set(key, { ...cachedResult, memoryIdA: pair.memoryIdA, memoryIdB: pair.memoryIdB });\n cached++;\n } else {\n toJudge.push(pair);\n }\n }\n\n if (toJudge.length === 0) {\n return { results, cached, judged, elapsed: Date.now() - startTime };\n }\n\n // Build the prompt with all pairs\n const pairDescriptions = toJudge.map((p, i) => {\n const pk = pairKey(p.memoryIdA, p.memoryIdB);\n const catA = p.categoryA ? ` [${p.categoryA}]` : \"\";\n const catB = p.categoryB ? ` [${p.categoryB}]` : \"\";\n return `Pair ${i + 1} (pairKey: \"${pk}\"):${catA} \"${p.textA}\"${catB} \"${p.textB}\"`;\n });\n\n const userMessage = `Classify these ${toJudge.length} memory pair(s):\\n\\n${pairDescriptions.join(\"\\n\\n\")}`;\n\n // Try LLM call\n let llmResponse: string | null = null;\n\n if (localLlm) {\n try {\n llmResponse = await callLlm(localLlm, config, userMessage);\n } catch (err) {\n log.warn(\"[contradiction-judge] local LLM call failed: %s\", err instanceof Error ? err.message : err);\n }\n }\n\n if (!llmResponse && fallbackLlm) {\n try {\n llmResponse = await callLlm(fallbackLlm, config, userMessage);\n } catch (err) {\n log.warn(\"[contradiction-judge] fallback LLM call failed: %s\", err instanceof Error ? err.message : err);\n }\n }\n\n // Parse response or default to needs-user\n if (llmResponse) {\n const candidates = extractJsonCandidates(llmResponse);\n const parsed = parseJudgeResponse(candidates, toJudge);\n\n for (const result of parsed) {\n const key = pairKey(result.memoryIdA, result.memoryIdB);\n results.set(key, result);\n\n // Update cache\n const input = toJudge.find(\n (p) => pairKey(p.memoryIdA, p.memoryIdB) === key,\n );\n if (input) {\n const hash = contentHash(input);\n if (activeCache.size >= CACHE_MAX) {\n const firstKey = activeCache.keys().next().value;\n if (firstKey !== undefined) activeCache.delete(firstKey);\n }\n activeCache.set(hash, result);\n }\n judged++;\n }\n } else {\n // All unresolved → needs-user (rule 48)\n for (const pair of toJudge) {\n const key = pairKey(pair.memoryIdA, pair.memoryIdB);\n const result: ContradictionJudgeResult = {\n memoryIdA: pair.memoryIdA,\n memoryIdB: pair.memoryIdB,\n verdict: \"needs-user\",\n rationale: \"LLM call failed; requires manual review\",\n confidence: 0,\n };\n results.set(key, result);\n judged++;\n }\n }\n\n return { results, cached, judged, elapsed: Date.now() - startTime };\n}\n\n// ── Internals ──────────────────────────────────────────────────────────────────\n\nasync function callLlm(\n client: LocalLlmClient | FallbackLlmClient,\n config: PluginConfig,\n userMessage: string,\n): Promise<string> {\n const messages: Array<{ role: \"user\" | \"assistant\" | \"system\"; content: string }> = [\n { role: \"system\", content: CONTRADICTION_JUDGE_PROMPT },\n { role: \"user\", content: userMessage },\n ];\n if (\"chatCompletion\" in client && typeof client.chatCompletion === \"function\") {\n const result = await client.chatCompletion(messages, {\n temperature: 0.1,\n maxTokens: 4096,\n });\n return result?.content ?? \"\";\n }\n // FallbackLlmClient — try OpenAI-compatible chat completions\n if (\"complete\" in client && typeof (client as Record<string, unknown>).complete === \"function\") {\n const result = await (client as { complete: (msg: Array<{ role: string; content: string }>) => Promise<{ content: string }> }).complete(messages);\n return result.content ?? \"\";\n }\n return \"\";\n}\n\nfunction parseJudgeResponse(\n candidates: string[],\n inputs: ContradictionJudgeInput[],\n): ContradictionJudgeResult[] {\n const VALID_VERDICTS: ContradictionVerdict[] = [\"contradicts\", \"independent\", \"duplicates\", \"needs-user\"];\n\n for (const candidate of candidates) {\n try {\n const parsed = JSON.parse(candidate);\n const items = Array.isArray(parsed) ? parsed : [parsed];\n const results: ContradictionJudgeResult[] = [];\n const matchedKeys = new Set<string>();\n\n for (const item of items) {\n if (!item || typeof item !== \"object\") continue;\n\n const verdict = typeof item.verdict === \"string\" && VALID_VERDICTS.includes(item.verdict as ContradictionVerdict)\n ? (item.verdict as ContradictionVerdict)\n : \"needs-user\";\n\n const pairKeyVal = typeof item.pairKey === \"string\" ? item.pairKey : null;\n const input = pairKeyVal\n ? inputs.find((p) => pairKey(p.memoryIdA, p.memoryIdB) === pairKeyVal)\n : null;\n\n // If we can't match the pairKey, fall back to index-based matching\n const fallbackInput = input ?? inputs[results.length] ?? inputs[0];\n if (!fallbackInput) continue;\n\n matchedKeys.add(pairKey(fallbackInput.memoryIdA, fallbackInput.memoryIdB));\n\n const confidence = typeof item.confidence === \"number\"\n ? Math.min(1, Math.max(0, item.confidence))\n : 0.5;\n\n results.push({\n memoryIdA: fallbackInput.memoryIdA,\n memoryIdB: fallbackInput.memoryIdB,\n verdict,\n rationale: typeof item.rationale === \"string\" ? item.rationale : \"No rationale provided\",\n confidence,\n });\n }\n\n // Backfill any inputs the LLM omitted with needs-user\n for (const inp of inputs) {\n const key = pairKey(inp.memoryIdA, inp.memoryIdB);\n if (!matchedKeys.has(key)) {\n results.push({\n memoryIdA: inp.memoryIdA,\n memoryIdB: inp.memoryIdB,\n verdict: \"needs-user\",\n rationale: \"LLM response omitted this pair\",\n confidence: 0,\n });\n }\n }\n\n if (results.length > 0) return results;\n } catch {\n continue;\n }\n }\n\n // All parse attempts failed → needs-user for every input\n return inputs.map((p) => ({\n memoryIdA: p.memoryIdA,\n memoryIdB: p.memoryIdB,\n verdict: \"needs-user\" as ContradictionVerdict,\n rationale: \"Failed to parse judge response\",\n confidence: 0,\n }));\n}\n\nexport { pairKey as _pairKey, contentHash as _contentHash };\n","/**\n * Contradiction Scan — pair generator + scan driver (issue #520).\n *\n * Nightly cron that pairs semantically-similar active memories within\n * the same namespace, sends candidate pairs to the LLM-as-judge\n * contradiction classifier, and drops contradicting pairs into a\n * review queue for user resolution.\n *\n * Pair generation (cheap pre-filter):\n * 1. Find candidate peers via embedding cosine >= similarityFloor.\n * 2. Restrict to pairs sharing at least one entityRef OR overlapping\n * topic tokens >= topicOverlapFloor.\n * 3. Skip pairs already judged independent/both-valid within cooldown.\n * 4. Cap per-run work at maxPairsPerRun.\n */\n\nimport type { StorageManager } from \"../storage.js\";\nimport type { PluginConfig, MemoryFile, MemoryStatus } from \"../types.js\";\nimport type { SemanticDedupLookup } from \"../dedup/semantic.js\";\nimport { log } from \"../logger.js\";\nimport { judgeContradictionPairs, type ContradictionJudgeInput } from \"./contradiction-judge.js\";\nimport {\n writePairs,\n listPairs,\n isCoolingDown,\n computePairId,\n type ContradictionPair,\n} from \"./contradiction-review.js\";\nimport type { LocalLlmClient } from \"../local-llm.js\";\nimport type { FallbackLlmClient } from \"../fallback-llm.js\";\n\n// ── Types ──────────────────────────────────────────────────────────────────────\n\n/** The set of statuses that represent \"live\" memories eligible for scanning. */\nexport const ACTIVE_STATUSES: Set<MemoryStatus> = new Set([\"active\"]);\n\n/** High-value taxonomy buckets to scan. */\nconst SCAN_CATEGORIES = new Set([\n \"decision\",\n \"principle\",\n \"rule\",\n \"entity\",\n \"fact\",\n \"preference\",\n]);\n\nexport interface ScanResult {\n /** Total active memories scanned. */\n scanned: number;\n /** Candidate pairs generated by the pre-filter. */\n candidates: number;\n /** Pairs sent to judge. */\n judged: number;\n /** Pairs written to review queue. */\n queued: number;\n /** Pairs skipped due to cooldown. */\n cooledDown: number;\n /** Total wall-clock time in ms. */\n elapsedMs: number;\n}\n\nexport interface ScanDependencies {\n storage: StorageManager;\n config: PluginConfig;\n memoryDir: string;\n embeddingLookup?: SemanticDedupLookup;\n localLlm: LocalLlmClient | null;\n fallbackLlm: FallbackLlmClient | null;\n namespace?: string;\n}\n\n// ── Main scan driver ───────────────────────────────────────────────────────────\n\n/**\n * Run a contradiction scan over the memory corpus.\n *\n * This is the entry point called by the nightly cron and by the CLI.\n */\nexport async function runContradictionScan(deps: ScanDependencies): Promise<ScanResult> {\n const startTime = Date.now();\n const { storage, config, memoryDir, embeddingLookup, localLlm, fallbackLlm, namespace } = deps;\n const scanConfig = config.contradictionScan;\n\n if (!scanConfig.enabled) {\n log.info(\"[contradiction-scan] disabled by config\");\n return { scanned: 0, candidates: 0, judged: 0, queued: 0, cooledDown: 0, elapsedMs: 0 };\n }\n\n // 1. Load active memories in scan categories\n const memories = await loadEligibleMemories(storage, namespace);\n log.info(\"[contradiction-scan] loaded %d eligible memories\", memories.length);\n\n if (memories.length < 2) {\n return { scanned: memories.length, candidates: 0, judged: 0, queued: 0, cooledDown: 0, elapsedMs: Date.now() - startTime };\n }\n\n // 2. Load existing review pairs for cooldown checking\n const existingPairs = listPairs(memoryDir, { filter: \"all\", namespace, limit: 10000 }).pairs;\n const existingMap = new Map<string, ContradictionPair>();\n for (const p of existingPairs) {\n existingMap.set(p.pairId, p);\n }\n\n // 3. Generate candidate pairs\n const candidates = generatePairs(memories, existingMap, scanConfig, embeddingLookup);\n const cooledDown = candidates.skipped;\n log.info(\"[contradiction-scan] generated %d candidates (%d cooled down)\", candidates.pairs.length, cooledDown);\n\n if (candidates.pairs.length === 0) {\n return {\n scanned: memories.length,\n candidates: 0,\n judged: 0,\n queued: 0,\n cooledDown,\n elapsedMs: Date.now() - startTime,\n };\n }\n\n // 4. Cap at maxPairsPerRun (deterministic selection by pairId)\n const capped = candidates.pairs\n .sort((a, b) => computePairId(a.idA, a.idB).localeCompare(computePairId(b.idA, b.idB)))\n .slice(0, scanConfig.maxPairsPerRun);\n\n // 5. Build judge inputs\n const judgeInputs: ContradictionJudgeInput[] = capped.map((pair) => ({\n memoryIdA: pair.idA,\n memoryIdB: pair.idB,\n textA: pair.textA,\n textB: pair.textB,\n categoryA: pair.categoryA,\n categoryB: pair.categoryB,\n }));\n\n // 6. Send to judge\n const judgeResult = await judgeContradictionPairs(judgeInputs, config, localLlm, fallbackLlm);\n log.info(\"[contradiction-scan] judge completed: %d judged, %d cached in %dms\", judgeResult.judged, judgeResult.cached, judgeResult.elapsed);\n\n // 7. Write to review queue\n const queueEntries: Array<Omit<ContradictionPair, \"pairId\"> & { memoryIds: [string, string] }> = [];\n for (const [key, result] of judgeResult.results) {\n queueEntries.push({\n memoryIds: [result.memoryIdA, result.memoryIdB],\n verdict: result.verdict,\n rationale: result.rationale,\n confidence: result.confidence,\n detectedAt: new Date().toISOString(),\n // Set lastReviewedAt for non-actionable verdicts so cooldown prevents re-judging\n lastReviewedAt: result.verdict === \"independent\" ? new Date().toISOString() : undefined,\n namespace,\n });\n }\n\n const written = writePairs(memoryDir, queueEntries);\n const elapsed = Date.now() - startTime;\n log.info(\"[contradiction-scan] complete: %d queued in %dms\", written.length, elapsed);\n\n return {\n scanned: memories.length,\n candidates: candidates.pairs.length,\n judged: judgeResult.judged,\n queued: written.length,\n cooledDown,\n elapsedMs: elapsed,\n };\n}\n\n// ── Pair generation ────────────────────────────────────────────────────────────\n\ninterface CandidatePair {\n idA: string;\n idB: string;\n textA: string;\n textB: string;\n categoryA?: string;\n categoryB?: string;\n}\n\ninterface PairGenResult {\n pairs: CandidatePair[];\n skipped: number;\n}\n\nfunction generatePairs(\n memories: MemoryFile[],\n existingPairs: Map<string, ContradictionPair>,\n scanConfig: PluginConfig[\"contradictionScan\"],\n embeddingLookup?: SemanticDedupLookup,\n): PairGenResult {\n const pairs: CandidatePair[] = [];\n let skipped = 0;\n const seen = new Set<string>();\n\n // Build index by entityRef for fast lookup\n const byEntity = new Map<string, MemoryFile[]>();\n for (const mem of memories) {\n const entity = mem.frontmatter.entityRef;\n if (entity) {\n const existing = byEntity.get(entity) ?? [];\n existing.push(mem);\n byEntity.set(entity, existing);\n }\n }\n\n // Strategy 1: Entity-ref based pairing (high precision)\n for (const [, group] of byEntity) {\n if (group.length < 2) continue;\n for (let i = 0; i < group.length; i++) {\n for (let j = i + 1; j < group.length; j++) {\n const a = group[i];\n const b = group[j];\n const pairId = computePairId(a.frontmatter.id!, b.frontmatter.id!);\n\n if (seen.has(pairId)) continue;\n seen.add(pairId);\n\n // Check cooldown\n const existing = existingPairs.get(pairId);\n if (existing && isCoolingDown(existing, scanConfig.cooldownDays)) {\n skipped++;\n continue;\n }\n\n pairs.push({\n idA: a.frontmatter.id!,\n idB: b.frontmatter.id!,\n textA: a.content,\n textB: b.content,\n categoryA: a.frontmatter.category as string | undefined,\n categoryB: b.frontmatter.category as string | undefined,\n });\n }\n }\n }\n\n // Strategy 2: Tag/topic overlap for memories without shared entityRef\n const noEntity = memories.filter((m) => !m.frontmatter.entityRef);\n for (let i = 0; i < noEntity.length; i++) {\n for (let j = i + 1; j < noEntity.length; j++) {\n const a = noEntity[i];\n const b = noEntity[j];\n const overlap = jaccardOverlap(\n (a.frontmatter.tags as string[]) ?? [],\n (b.frontmatter.tags as string[]) ?? [],\n );\n\n if (overlap < scanConfig.topicOverlapFloor) continue;\n\n const pairId = computePairId(a.frontmatter.id!, b.frontmatter.id!);\n if (seen.has(pairId)) continue;\n seen.add(pairId);\n\n const existing = existingPairs.get(pairId);\n if (existing && isCoolingDown(existing, scanConfig.cooldownDays)) {\n skipped++;\n continue;\n }\n\n pairs.push({\n idA: a.frontmatter.id!,\n idB: b.frontmatter.id!,\n textA: a.content,\n textB: b.content,\n categoryA: a.frontmatter.category as string | undefined,\n categoryB: b.frontmatter.category as string | undefined,\n });\n }\n }\n\n return { pairs, skipped };\n}\n\n// ── Helpers ────────────────────────────────────────────────────────────────────\n\nasync function loadEligibleMemories(storage: StorageManager, namespace?: string): Promise<MemoryFile[]> {\n let all: MemoryFile[];\n try {\n all = await storage.readAllMemories();\n } catch {\n return [];\n }\n\n return all.filter((mem) => {\n const fm = mem.frontmatter;\n // Only active memories (rule 53: explicit ACTIVE_STATUSES set)\n const status = (fm.status as MemoryStatus) ?? \"active\";\n if (!ACTIVE_STATUSES.has(status)) return false;\n\n // Only scan high-value categories\n const category = fm.category as string | undefined;\n if (category && !SCAN_CATEGORIES.has(category)) return false;\n\n // Must have content\n if (!mem.content || mem.content.trim().length === 0) return false;\n\n // Must have an ID\n if (!fm.id) return false;\n\n // Namespace scoping is handled at the storage layer — memoryDir is\n // already namespace-scoped, so readAllMemories() returns only memories\n // within the requested namespace.\n\n return true;\n });\n}\n\nfunction jaccardOverlap(a: string[], b: string[]): number {\n if (a.length === 0 && b.length === 0) return 0;\n const setA = new Set(a.map((t) => t.toLowerCase()));\n const setB = new Set(b.map((t) => t.toLowerCase()));\n let intersection = 0;\n for (const item of setA) {\n if (setB.has(item)) intersection++;\n }\n const union = setA.size + setB.size - intersection;\n return union === 0 ? 0 : intersection / union;\n}\n"],"mappings":"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","names":[]}
|
package/dist/day-summary.d.ts
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
|
-
import { MemoryFile } from './types.js';
|
|
1
|
+
import { PluginConfig, MemoryFile } from './types.js';
|
|
2
2
|
|
|
3
3
|
declare function loadDaySummaryPrompt(): Promise<string>;
|
|
4
4
|
declare function formatDaySummaryMemories(memories: string | MemoryFile[]): string;
|
|
5
|
+
/**
|
|
6
|
+
* Build a one-line extension footer for day-summary and summary-snapshot contexts.
|
|
7
|
+
* Returns "" when extensions are disabled or none are found.
|
|
8
|
+
*/
|
|
9
|
+
declare function buildExtensionsFooterForSummary(config: PluginConfig): Promise<string>;
|
|
5
10
|
|
|
6
|
-
export { formatDaySummaryMemories, loadDaySummaryPrompt };
|
|
11
|
+
export { buildExtensionsFooterForSummary, formatDaySummaryMemories, loadDaySummaryPrompt };
|
package/dist/day-summary.js
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
|
+
buildExtensionsFooterForSummary,
|
|
2
3
|
formatDaySummaryMemories,
|
|
3
4
|
loadDaySummaryPrompt
|
|
4
|
-
} from "./chunk-
|
|
5
|
-
import "./chunk-
|
|
5
|
+
} from "./chunk-GZCUW5IC.js";
|
|
6
|
+
import "./chunk-EJI5XIBB.js";
|
|
7
|
+
import "./chunk-2ODBA7MQ.js";
|
|
6
8
|
export {
|
|
9
|
+
buildExtensionsFooterForSummary,
|
|
7
10
|
formatDaySummaryMemories,
|
|
8
11
|
loadDaySummaryPrompt
|
|
9
12
|
};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { MemoryFile, PluginConfig } from './types.js';
|
|
2
|
+
import { TrustZoneName } from './trust-zones.js';
|
|
3
|
+
import { T as Taxonomy } from './types-DJhqDJUV.js';
|
|
4
|
+
import { DirectAnswerResult } from './direct-answer.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Direct-answer wiring (issue #518 slice 3).
|
|
8
|
+
*
|
|
9
|
+
* Binds the pure eligibility decision (`direct-answer.ts`) to the data
|
|
10
|
+
* sources needed to build candidates: storage, trust-zones, taxonomy,
|
|
11
|
+
* and importance scoring. Kept as a separate module so that:
|
|
12
|
+
*
|
|
13
|
+
* - The eligibility layer stays pure and unit-testable without stores.
|
|
14
|
+
* - Each caller injects its own source accessors. The orchestrator
|
|
15
|
+
* binding is a follow-on slice; tests here use mock sources.
|
|
16
|
+
* - The wiring is safe to ship alone — nothing calls `tryDirectAnswer`
|
|
17
|
+
* yet, so enabling this module's presence does not change recall
|
|
18
|
+
* behavior. The next slice adds exactly one call site before QMD.
|
|
19
|
+
*
|
|
20
|
+
* Short-circuit contract:
|
|
21
|
+
*
|
|
22
|
+
* - When `config.recallDirectAnswerEnabled === false`, the function
|
|
23
|
+
* returns the eligibility verdict with reason `"disabled"` without
|
|
24
|
+
* touching any source accessor. This is the documented default.
|
|
25
|
+
* - When enabled, the wiring cheaply drops non-trusted-zone memories
|
|
26
|
+
* and ineligible taxonomy buckets before computing importance, so
|
|
27
|
+
* the eligibility module sees a pre-filtered candidate set. The
|
|
28
|
+
* eligibility module still performs the same checks itself — this
|
|
29
|
+
* module is purely an I/O and prefiltering layer.
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Caller-provided accessors for candidate sourcing. Decouples the
|
|
34
|
+
* wiring from any specific storage / trust-zone / importance backend.
|
|
35
|
+
*/
|
|
36
|
+
interface DirectAnswerSources {
|
|
37
|
+
/**
|
|
38
|
+
* List memories eligible to be considered for direct-answer.
|
|
39
|
+
* Callers are expected to return only active, non-superseded memories
|
|
40
|
+
* in the requested namespace; the wiring will cheaply re-filter on
|
|
41
|
+
* trust zone and taxonomy bucket and hand the rest to the eligibility
|
|
42
|
+
* module, which applies the full gate ladder.
|
|
43
|
+
*/
|
|
44
|
+
listCandidateMemories(options: {
|
|
45
|
+
namespace: string;
|
|
46
|
+
abortSignal?: AbortSignal;
|
|
47
|
+
}): Promise<MemoryFile[]>;
|
|
48
|
+
/**
|
|
49
|
+
* Resolve the trust-zone record for a memory. Returns `null` when
|
|
50
|
+
* the memory has no trust-zone record (treated as not trusted).
|
|
51
|
+
*/
|
|
52
|
+
trustZoneFor(memoryId: string): Promise<TrustZoneName | null>;
|
|
53
|
+
/**
|
|
54
|
+
* Resolve a calibrated importance score in [0, 1] for a memory.
|
|
55
|
+
*/
|
|
56
|
+
importanceFor(memory: MemoryFile): number;
|
|
57
|
+
/**
|
|
58
|
+
* Taxonomy used to classify memories into direct-answer buckets.
|
|
59
|
+
*/
|
|
60
|
+
taxonomy: Taxonomy;
|
|
61
|
+
}
|
|
62
|
+
interface DirectAnswerWiringInput {
|
|
63
|
+
query: string;
|
|
64
|
+
namespace: string;
|
|
65
|
+
config: Pick<PluginConfig, "recallDirectAnswerEnabled" | "recallDirectAnswerTokenOverlapFloor" | "recallDirectAnswerImportanceFloor" | "recallDirectAnswerAmbiguityMargin" | "recallDirectAnswerEligibleTaxonomyBuckets">;
|
|
66
|
+
sources: DirectAnswerSources;
|
|
67
|
+
queryEntityRefs?: string[];
|
|
68
|
+
abortSignal?: AbortSignal;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Attempt direct-answer resolution. Returns the eligibility verdict
|
|
72
|
+
* produced by `isDirectAnswerEligible` with candidates materialized
|
|
73
|
+
* from the caller-supplied sources.
|
|
74
|
+
*/
|
|
75
|
+
declare function tryDirectAnswer(input: DirectAnswerWiringInput): Promise<DirectAnswerResult>;
|
|
76
|
+
|
|
77
|
+
export { type DirectAnswerSources, type DirectAnswerWiringInput, tryDirectAnswer };
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import {
|
|
2
|
+
resolveCategory
|
|
3
|
+
} from "./chunk-OIT5QGG4.js";
|
|
4
|
+
import {
|
|
5
|
+
isDirectAnswerEligible
|
|
6
|
+
} from "./chunk-Y4FHOFJ2.js";
|
|
7
|
+
import {
|
|
8
|
+
throwIfAborted
|
|
9
|
+
} from "./chunk-PVGDJXVK.js";
|
|
10
|
+
import {
|
|
11
|
+
normalizeRecallTokens
|
|
12
|
+
} from "./chunk-DT5TVLJE.js";
|
|
13
|
+
|
|
14
|
+
// src/direct-answer-wiring.ts
|
|
15
|
+
async function tryDirectAnswer(input) {
|
|
16
|
+
const { query, namespace, config, sources, queryEntityRefs, abortSignal } = input;
|
|
17
|
+
const eligibilityConfig = {
|
|
18
|
+
enabled: config.recallDirectAnswerEnabled,
|
|
19
|
+
tokenOverlapFloor: config.recallDirectAnswerTokenOverlapFloor,
|
|
20
|
+
importanceFloor: config.recallDirectAnswerImportanceFloor,
|
|
21
|
+
ambiguityMargin: config.recallDirectAnswerAmbiguityMargin,
|
|
22
|
+
eligibleTaxonomyBuckets: config.recallDirectAnswerEligibleTaxonomyBuckets
|
|
23
|
+
};
|
|
24
|
+
if (!eligibilityConfig.enabled) {
|
|
25
|
+
return isDirectAnswerEligible({
|
|
26
|
+
query,
|
|
27
|
+
candidates: [],
|
|
28
|
+
config: eligibilityConfig,
|
|
29
|
+
queryEntityRefs
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
if (normalizeRecallTokens(query).length === 0) {
|
|
33
|
+
return isDirectAnswerEligible({
|
|
34
|
+
query,
|
|
35
|
+
candidates: [],
|
|
36
|
+
config: eligibilityConfig,
|
|
37
|
+
queryEntityRefs
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
throwIfAborted(abortSignal, "direct-answer wiring aborted");
|
|
41
|
+
const memories = await sources.listCandidateMemories({ namespace, abortSignal });
|
|
42
|
+
throwIfAborted(abortSignal, "direct-answer wiring aborted");
|
|
43
|
+
const candidates = [];
|
|
44
|
+
for (const memory of memories) {
|
|
45
|
+
throwIfAborted(abortSignal, "direct-answer wiring aborted");
|
|
46
|
+
const trustZone = await sources.trustZoneFor(memory.frontmatter.id);
|
|
47
|
+
throwIfAborted(abortSignal, "direct-answer wiring aborted");
|
|
48
|
+
if (trustZone !== "trusted") continue;
|
|
49
|
+
const decision = resolveCategory(
|
|
50
|
+
memory.content,
|
|
51
|
+
memory.frontmatter.category,
|
|
52
|
+
sources.taxonomy
|
|
53
|
+
);
|
|
54
|
+
const taxonomyBucket = decision.categoryId;
|
|
55
|
+
if (!eligibilityConfig.eligibleTaxonomyBuckets.includes(taxonomyBucket)) continue;
|
|
56
|
+
const importanceScore = sources.importanceFor(memory);
|
|
57
|
+
candidates.push({
|
|
58
|
+
memory,
|
|
59
|
+
trustZone,
|
|
60
|
+
taxonomyBucket,
|
|
61
|
+
importanceScore
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
throwIfAborted(abortSignal, "direct-answer wiring aborted");
|
|
65
|
+
return isDirectAnswerEligible({
|
|
66
|
+
query,
|
|
67
|
+
candidates,
|
|
68
|
+
config: eligibilityConfig,
|
|
69
|
+
queryEntityRefs
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
export {
|
|
73
|
+
tryDirectAnswer
|
|
74
|
+
};
|
|
75
|
+
//# sourceMappingURL=direct-answer-wiring.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/direct-answer-wiring.ts"],"sourcesContent":["/**\n * Direct-answer wiring (issue #518 slice 3).\n *\n * Binds the pure eligibility decision (`direct-answer.ts`) to the data\n * sources needed to build candidates: storage, trust-zones, taxonomy,\n * and importance scoring. Kept as a separate module so that:\n *\n * - The eligibility layer stays pure and unit-testable without stores.\n * - Each caller injects its own source accessors. The orchestrator\n * binding is a follow-on slice; tests here use mock sources.\n * - The wiring is safe to ship alone — nothing calls `tryDirectAnswer`\n * yet, so enabling this module's presence does not change recall\n * behavior. The next slice adds exactly one call site before QMD.\n *\n * Short-circuit contract:\n *\n * - When `config.recallDirectAnswerEnabled === false`, the function\n * returns the eligibility verdict with reason `\"disabled\"` without\n * touching any source accessor. This is the documented default.\n * - When enabled, the wiring cheaply drops non-trusted-zone memories\n * and ineligible taxonomy buckets before computing importance, so\n * the eligibility module sees a pre-filtered candidate set. The\n * eligibility module still performs the same checks itself — this\n * module is purely an I/O and prefiltering layer.\n */\n\nimport type { MemoryFile, PluginConfig } from \"./types.js\";\nimport type { TrustZoneName } from \"./trust-zones.js\";\nimport type { Taxonomy } from \"./taxonomy/types.js\";\nimport { resolveCategory } from \"./taxonomy/resolver.js\";\nimport { normalizeRecallTokens } from \"./recall-tokenization.js\";\nimport { throwIfAborted } from \"./abort-error.js\";\nimport {\n isDirectAnswerEligible,\n type DirectAnswerCandidate,\n type DirectAnswerConfig,\n type DirectAnswerResult,\n} from \"./direct-answer.js\";\n\n/**\n * Caller-provided accessors for candidate sourcing. Decouples the\n * wiring from any specific storage / trust-zone / importance backend.\n */\nexport interface DirectAnswerSources {\n /**\n * List memories eligible to be considered for direct-answer.\n * Callers are expected to return only active, non-superseded memories\n * in the requested namespace; the wiring will cheaply re-filter on\n * trust zone and taxonomy bucket and hand the rest to the eligibility\n * module, which applies the full gate ladder.\n */\n listCandidateMemories(options: {\n namespace: string;\n abortSignal?: AbortSignal;\n }): Promise<MemoryFile[]>;\n /**\n * Resolve the trust-zone record for a memory. Returns `null` when\n * the memory has no trust-zone record (treated as not trusted).\n */\n trustZoneFor(memoryId: string): Promise<TrustZoneName | null>;\n /**\n * Resolve a calibrated importance score in [0, 1] for a memory.\n */\n importanceFor(memory: MemoryFile): number;\n /**\n * Taxonomy used to classify memories into direct-answer buckets.\n */\n taxonomy: Taxonomy;\n}\n\nexport interface DirectAnswerWiringInput {\n query: string;\n namespace: string;\n config: Pick<\n PluginConfig,\n | \"recallDirectAnswerEnabled\"\n | \"recallDirectAnswerTokenOverlapFloor\"\n | \"recallDirectAnswerImportanceFloor\"\n | \"recallDirectAnswerAmbiguityMargin\"\n | \"recallDirectAnswerEligibleTaxonomyBuckets\"\n >;\n sources: DirectAnswerSources;\n queryEntityRefs?: string[];\n abortSignal?: AbortSignal;\n}\n\n/**\n * Attempt direct-answer resolution. Returns the eligibility verdict\n * produced by `isDirectAnswerEligible` with candidates materialized\n * from the caller-supplied sources.\n */\nexport async function tryDirectAnswer(\n input: DirectAnswerWiringInput,\n): Promise<DirectAnswerResult> {\n const { query, namespace, config, sources, queryEntityRefs, abortSignal } = input;\n\n const eligibilityConfig: DirectAnswerConfig = {\n enabled: config.recallDirectAnswerEnabled,\n tokenOverlapFloor: config.recallDirectAnswerTokenOverlapFloor,\n importanceFloor: config.recallDirectAnswerImportanceFloor,\n ambiguityMargin: config.recallDirectAnswerAmbiguityMargin,\n eligibleTaxonomyBuckets: config.recallDirectAnswerEligibleTaxonomyBuckets,\n };\n\n // Short-circuit disabled case before touching any I/O.\n if (!eligibilityConfig.enabled) {\n return isDirectAnswerEligible({\n query,\n candidates: [],\n config: eligibilityConfig,\n queryEntityRefs,\n });\n }\n\n // Short-circuit empty-query case before any I/O. isDirectAnswerEligible\n // deterministically returns reason \"empty-query\" when the query\n // normalizes to zero searchable tokens; there's no point materializing\n // candidates just to reach the same verdict, and doing so would\n // surface avoidable upstream errors for requests that should exit\n // immediately.\n if (normalizeRecallTokens(query).length === 0) {\n return isDirectAnswerEligible({\n query,\n candidates: [],\n config: eligibilityConfig,\n queryEntityRefs,\n });\n }\n\n throwIfAborted(abortSignal, \"direct-answer wiring aborted\");\n const memories = await sources.listCandidateMemories({ namespace, abortSignal });\n throwIfAborted(abortSignal, \"direct-answer wiring aborted\");\n const candidates: DirectAnswerCandidate[] = [];\n\n for (const memory of memories) {\n // Throw rather than returning a partial verdict — a mid-loop abort\n // that left competing candidates unprocessed could otherwise surface\n // a spurious \"eligible\" result that the ambiguity gate never got a\n // chance to reject. The check repeats after every await so an\n // abort that lands during the in-flight I/O on the final memory\n // (after which no further iteration would exist) still stops us.\n throwIfAborted(abortSignal, \"direct-answer wiring aborted\");\n\n const trustZone = await sources.trustZoneFor(memory.frontmatter.id);\n throwIfAborted(abortSignal, \"direct-answer wiring aborted\");\n\n // Cheap pre-filter: non-trusted memories can't qualify, so skip\n // taxonomy and importance resolution for them.\n if (trustZone !== \"trusted\") continue;\n\n const decision = resolveCategory(\n memory.content,\n memory.frontmatter.category,\n sources.taxonomy,\n );\n const taxonomyBucket = decision.categoryId;\n if (!eligibilityConfig.eligibleTaxonomyBuckets.includes(taxonomyBucket)) continue;\n\n const importanceScore = sources.importanceFor(memory);\n\n candidates.push({\n memory,\n trustZone,\n taxonomyBucket,\n importanceScore,\n });\n }\n\n // Final check — if abort landed during the trust-zone await for the\n // last memory, the loop condition no longer fires. Guard before we\n // hand candidates to the eligibility gate.\n throwIfAborted(abortSignal, \"direct-answer wiring aborted\");\n\n return isDirectAnswerEligible({\n query,\n candidates,\n config: eligibilityConfig,\n queryEntityRefs,\n });\n}\n"],"mappings":";;;;;;;;;;;;;;AA2FA,eAAsB,gBACpB,OAC6B;AAC7B,QAAM,EAAE,OAAO,WAAW,QAAQ,SAAS,iBAAiB,YAAY,IAAI;AAE5E,QAAM,oBAAwC;AAAA,IAC5C,SAAS,OAAO;AAAA,IAChB,mBAAmB,OAAO;AAAA,IAC1B,iBAAiB,OAAO;AAAA,IACxB,iBAAiB,OAAO;AAAA,IACxB,yBAAyB,OAAO;AAAA,EAClC;AAGA,MAAI,CAAC,kBAAkB,SAAS;AAC9B,WAAO,uBAAuB;AAAA,MAC5B;AAAA,MACA,YAAY,CAAC;AAAA,MACb,QAAQ;AAAA,MACR;AAAA,IACF,CAAC;AAAA,EACH;AAQA,MAAI,sBAAsB,KAAK,EAAE,WAAW,GAAG;AAC7C,WAAO,uBAAuB;AAAA,MAC5B;AAAA,MACA,YAAY,CAAC;AAAA,MACb,QAAQ;AAAA,MACR;AAAA,IACF,CAAC;AAAA,EACH;AAEA,iBAAe,aAAa,8BAA8B;AAC1D,QAAM,WAAW,MAAM,QAAQ,sBAAsB,EAAE,WAAW,YAAY,CAAC;AAC/E,iBAAe,aAAa,8BAA8B;AAC1D,QAAM,aAAsC,CAAC;AAE7C,aAAW,UAAU,UAAU;AAO7B,mBAAe,aAAa,8BAA8B;AAE1D,UAAM,YAAY,MAAM,QAAQ,aAAa,OAAO,YAAY,EAAE;AAClE,mBAAe,aAAa,8BAA8B;AAI1D,QAAI,cAAc,UAAW;AAE7B,UAAM,WAAW;AAAA,MACf,OAAO;AAAA,MACP,OAAO,YAAY;AAAA,MACnB,QAAQ;AAAA,IACV;AACA,UAAM,iBAAiB,SAAS;AAChC,QAAI,CAAC,kBAAkB,wBAAwB,SAAS,cAAc,EAAG;AAEzE,UAAM,kBAAkB,QAAQ,cAAc,MAAM;AAEpD,eAAW,KAAK;AAAA,MACd;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC;AAAA,EACH;AAKA,iBAAe,aAAa,8BAA8B;AAE1D,SAAO,uBAAuB;AAAA,IAC5B;AAAA,IACA;AAAA,IACA,QAAQ;AAAA,IACR;AAAA,EACF,CAAC;AACH;","names":[]}
|