@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
|
@@ -1,6 +1,11 @@
|
|
|
1
|
+
import {
|
|
2
|
+
discoverMemoryExtensions,
|
|
3
|
+
renderExtensionsFooter,
|
|
4
|
+
resolveExtensionsRoot
|
|
5
|
+
} from "./chunk-EJI5XIBB.js";
|
|
1
6
|
import {
|
|
2
7
|
log
|
|
3
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-2ODBA7MQ.js";
|
|
4
9
|
|
|
5
10
|
// src/day-summary.ts
|
|
6
11
|
import { existsSync } from "fs";
|
|
@@ -95,9 +100,17 @@ function formatDaySummaryMemories(memories) {
|
|
|
95
100
|
${memory.content}`;
|
|
96
101
|
}).join("\n\n").trim();
|
|
97
102
|
}
|
|
103
|
+
async function buildExtensionsFooterForSummary(config) {
|
|
104
|
+
if (!config.memoryExtensionsEnabled) return "";
|
|
105
|
+
const root = resolveExtensionsRoot(config);
|
|
106
|
+
const extensions = await discoverMemoryExtensions(root, log);
|
|
107
|
+
if (extensions.length === 0) return "";
|
|
108
|
+
return renderExtensionsFooter(extensions);
|
|
109
|
+
}
|
|
98
110
|
|
|
99
111
|
export {
|
|
100
112
|
loadDaySummaryPrompt,
|
|
101
|
-
formatDaySummaryMemories
|
|
113
|
+
formatDaySummaryMemories,
|
|
114
|
+
buildExtensionsFooterForSummary
|
|
102
115
|
};
|
|
103
|
-
//# sourceMappingURL=chunk-
|
|
116
|
+
//# sourceMappingURL=chunk-GZCUW5IC.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/day-summary.ts"],"sourcesContent":["import { existsSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { log } from \"./logger.js\";\nimport type { MemoryFile, PluginConfig } from \"./types.js\";\nimport { discoverMemoryExtensions, renderExtensionsFooter, resolveExtensionsRoot } from \"./memory-extension-host/index.js\";\n\nconst PROMPT_RELATIVE_PATH = path.join(\"prompts\", \"day_summary.prompt.md\");\n\n// Embedded fallback prompt for packaged/plugin-install builds where\n// prompts/day_summary.prompt.md may not be present in the runtime bundle.\nconst EMBEDDED_DAY_SUMMARY_PROMPT = `# Baseline day-summary prompt\n\nYou are writing an Engram end-of-day summary.\n\nYour job:\n- compress the day into a short, useful recap\n- prioritize concrete events, decisions, mood/energy signals, and open loops\n- include a few practical next actions for tomorrow\n- avoid hype, fluff, therapy-speak, and invented facts\n\nOutput JSON with these keys:\n- \\`summary\\` — one short paragraph\n- \\`bullets\\` — 2 to 5 bullets with the most important moments\n- \\`next_actions\\` — 1 to 3 concrete actions\n- \\`risks_or_open_loops\\` — 0 to 3 things that still need attention\n\nRules:\n- stay grounded in the input only\n- if the day was mixed, say so plainly\n- do not overstate confidence or importance\n- prefer specific verbs over vague abstractions\n\nBrevity:\n- keep the summary under 90 words\n- keep bullets short and information-dense\n- omit anything that does not change what tomorrow should care about\n\nStructure:\n- \\`summary\\` should be one paragraph only\n- \\`bullets\\` should contain the most important moments, not generic restatements\n- \\`next_actions\\` and \\`risks_or_open_loops\\` should be distinct and non-overlapping\n\nRisk:\n- explicitly surface unresolved blockers, dependencies, or fragile assumptions\n- do not bury open loops inside the summary if they deserve separate attention\n\nTone:\n- sound like a clear internal daily note, not a report template\n- stay natural and direct while remaining compact`;\n\nfunction candidateRoots(): string[] {\n const currentFile = fileURLToPath(import.meta.url);\n const here = path.dirname(currentFile);\n const candidates = [path.resolve(here, \"..\"), path.resolve(here, \"..\", \"..\")];\n\n const seen = new Set<string>();\n return candidates.filter((candidate) => {\n const normalized = path.resolve(candidate);\n if (seen.has(normalized)) return false;\n seen.add(normalized);\n return true;\n });\n}\n\nfunction resolvePromptPath(): string | null {\n for (const root of candidateRoots()) {\n const candidate = path.join(root, PROMPT_RELATIVE_PATH);\n if (existsSync(candidate)) {\n return candidate;\n }\n }\n return null;\n}\n\nexport async function loadDaySummaryPrompt(): Promise<string> {\n const promptPath = resolvePromptPath();\n if (promptPath) {\n try {\n const raw = await readFile(promptPath, \"utf-8\");\n // CRLF-compatible regex: allow \\r\\n or \\n line endings\n const match = raw.match(/```(?:[a-zA-Z0-9_-]+)?\\r?\\n([\\s\\S]*?)\\r?\\n```/);\n if (match?.[1]) {\n return match[1].trim();\n }\n log.warn(\"day summary prompt file does not contain a fenced prompt block; using embedded fallback\");\n } catch (err) {\n log.warn(`day summary prompt file read failed; using embedded fallback: ${err}`);\n }\n }\n return EMBEDDED_DAY_SUMMARY_PROMPT;\n}\n\nexport function formatDaySummaryMemories(memories: string | MemoryFile[]): string {\n if (typeof memories === \"string\") {\n return memories.trim();\n }\n\n return memories\n .map((memory) => {\n const category = memory.frontmatter.category || \"fact\";\n const created = memory.frontmatter.created || \"unknown\";\n return `[${memory.frontmatter.id}] (${category}, ${created})\\n${memory.content}`;\n })\n .join(\"\\n\\n\")\n .trim();\n}\n\n/**\n * Build a one-line extension footer for day-summary and summary-snapshot contexts.\n * Returns \"\" when extensions are disabled or none are found.\n */\nexport async function buildExtensionsFooterForSummary(\n config: PluginConfig,\n): Promise<string> {\n if (!config.memoryExtensionsEnabled) return \"\";\n const root = resolveExtensionsRoot(config);\n const extensions = await discoverMemoryExtensions(root, log);\n if (extensions.length === 0) return \"\";\n return renderExtensionsFooter(extensions);\n}\n"],"mappings":";;;;;;;;;;AAAA,SAAS,kBAAkB;AAC3B,SAAS,gBAAgB;AACzB,OAAO,UAAU;AACjB,SAAS,qBAAqB;AAK9B,IAAM,uBAAuB,KAAK,KAAK,WAAW,uBAAuB;AAIzE,IAAM,8BAA8B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAwCpC,SAAS,iBAA2B;AAClC,QAAM,cAAc,cAAc,YAAY,GAAG;AACjD,QAAM,OAAO,KAAK,QAAQ,WAAW;AACrC,QAAM,aAAa,CAAC,KAAK,QAAQ,MAAM,IAAI,GAAG,KAAK,QAAQ,MAAM,MAAM,IAAI,CAAC;AAE5E,QAAM,OAAO,oBAAI,IAAY;AAC7B,SAAO,WAAW,OAAO,CAAC,cAAc;AACtC,UAAM,aAAa,KAAK,QAAQ,SAAS;AACzC,QAAI,KAAK,IAAI,UAAU,EAAG,QAAO;AACjC,SAAK,IAAI,UAAU;AACnB,WAAO;AAAA,EACT,CAAC;AACH;AAEA,SAAS,oBAAmC;AAC1C,aAAW,QAAQ,eAAe,GAAG;AACnC,UAAM,YAAY,KAAK,KAAK,MAAM,oBAAoB;AACtD,QAAI,WAAW,SAAS,GAAG;AACzB,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;AAEA,eAAsB,uBAAwC;AAC5D,QAAM,aAAa,kBAAkB;AACrC,MAAI,YAAY;AACd,QAAI;AACF,YAAM,MAAM,MAAM,SAAS,YAAY,OAAO;AAE9C,YAAM,QAAQ,IAAI,MAAM,+CAA+C;AACvE,UAAI,QAAQ,CAAC,GAAG;AACd,eAAO,MAAM,CAAC,EAAE,KAAK;AAAA,MACvB;AACA,UAAI,KAAK,yFAAyF;AAAA,IACpG,SAAS,KAAK;AACZ,UAAI,KAAK,iEAAiE,GAAG,EAAE;AAAA,IACjF;AAAA,EACF;AACA,SAAO;AACT;AAEO,SAAS,yBAAyB,UAAyC;AAChF,MAAI,OAAO,aAAa,UAAU;AAChC,WAAO,SAAS,KAAK;AAAA,EACvB;AAEA,SAAO,SACJ,IAAI,CAAC,WAAW;AACf,UAAM,WAAW,OAAO,YAAY,YAAY;AAChD,UAAM,UAAU,OAAO,YAAY,WAAW;AAC9C,WAAO,IAAI,OAAO,YAAY,EAAE,MAAM,QAAQ,KAAK,OAAO;AAAA,EAAM,OAAO,OAAO;AAAA,EAChF,CAAC,EACA,KAAK,MAAM,EACX,KAAK;AACV;AAMA,eAAsB,gCACpB,QACiB;AACjB,MAAI,CAAC,OAAO,wBAAyB,QAAO;AAC5C,QAAM,OAAO,sBAAsB,MAAM;AACzC,QAAM,aAAa,MAAM,yBAAyB,MAAM,GAAG;AAC3D,MAAI,WAAW,WAAW,EAAG,QAAO;AACpC,SAAO,uBAAuB,UAAU;AAC1C;","names":[]}
|
|
@@ -2,6 +2,9 @@ import {
|
|
|
2
2
|
resolveCueAnchorStoreDir,
|
|
3
3
|
validateCueAnchor
|
|
4
4
|
} from "./chunk-C6QPK5GG.js";
|
|
5
|
+
import {
|
|
6
|
+
throwIfAborted
|
|
7
|
+
} from "./chunk-PVGDJXVK.js";
|
|
5
8
|
import {
|
|
6
9
|
resolveAbstractionNodeStoreDir,
|
|
7
10
|
validateAbstractionNode
|
|
@@ -96,13 +99,13 @@ async function readCueAnchors(options) {
|
|
|
96
99
|
return anchors;
|
|
97
100
|
}
|
|
98
101
|
async function searchHarmonicRetrieval(options) {
|
|
99
|
-
throwIfAborted(options.abortSignal);
|
|
102
|
+
throwIfAborted(options.abortSignal, "harmonic retrieval aborted");
|
|
100
103
|
const queryTokens = new Set(normalizeRecallTokens(options.query, ["what", "which"]));
|
|
101
104
|
if (queryTokens.size === 0 || options.maxResults <= 0) return [];
|
|
102
105
|
const nodes = await readAbstractionNodes(options);
|
|
103
106
|
const candidates = /* @__PURE__ */ new Map();
|
|
104
107
|
for (const node of nodes) {
|
|
105
|
-
throwIfAborted(options.abortSignal);
|
|
108
|
+
throwIfAborted(options.abortSignal, "harmonic retrieval aborted");
|
|
106
109
|
const { score, matchedFields } = scoreNode(node, queryTokens);
|
|
107
110
|
if (score <= 0) continue;
|
|
108
111
|
candidates.set(node.nodeId, {
|
|
@@ -114,11 +117,11 @@ async function searchHarmonicRetrieval(options) {
|
|
|
114
117
|
});
|
|
115
118
|
}
|
|
116
119
|
if (options.anchorsEnabled) {
|
|
117
|
-
throwIfAborted(options.abortSignal);
|
|
120
|
+
throwIfAborted(options.abortSignal, "harmonic retrieval aborted");
|
|
118
121
|
const anchors = await readCueAnchors(options);
|
|
119
122
|
const nodeIndex = new Map(nodes.map((node) => [node.nodeId, node]));
|
|
120
123
|
for (const anchor of anchors) {
|
|
121
|
-
throwIfAborted(options.abortSignal);
|
|
124
|
+
throwIfAborted(options.abortSignal, "harmonic retrieval aborted");
|
|
122
125
|
const { score, matchedFields } = scoreAnchor(anchor, queryTokens);
|
|
123
126
|
if (score <= 0) continue;
|
|
124
127
|
for (const nodeRef of anchor.nodeRefs) {
|
|
@@ -160,14 +163,8 @@ async function searchHarmonicRetrieval(options) {
|
|
|
160
163
|
(left, right) => right.score - left.score || right.anchorScore - left.anchorScore || right.node.recordedAt.localeCompare(left.node.recordedAt)
|
|
161
164
|
).slice(0, options.maxResults);
|
|
162
165
|
}
|
|
163
|
-
function throwIfAborted(signal) {
|
|
164
|
-
if (!signal?.aborted) return;
|
|
165
|
-
const err = new Error("harmonic retrieval aborted");
|
|
166
|
-
Object.defineProperty(err, "name", { value: "AbortError" });
|
|
167
|
-
throw err;
|
|
168
|
-
}
|
|
169
166
|
|
|
170
167
|
export {
|
|
171
168
|
searchHarmonicRetrieval
|
|
172
169
|
};
|
|
173
|
-
//# sourceMappingURL=chunk-
|
|
170
|
+
//# sourceMappingURL=chunk-HMDCOMYU.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/harmonic-retrieval.ts"],"sourcesContent":["import path from \"node:path\";\nimport { listJsonFiles, readJsonFile } from \"./json-store.js\";\nimport { throwIfAborted } from \"./abort-error.js\";\nimport {\n resolveAbstractionNodeStoreDir,\n validateAbstractionNode,\n type AbstractionNode,\n} from \"./abstraction-nodes.js\";\nimport {\n resolveCueAnchorStoreDir,\n validateCueAnchor,\n type CueAnchor,\n type CueAnchorType,\n} from \"./cue-anchors.js\";\nimport { countRecallTokenOverlap, normalizeRecallTokens } from \"./recall-tokenization.js\";\n\nexport interface HarmonicMatchedAnchor {\n anchorId: string;\n anchorType: CueAnchorType;\n anchorValue: string;\n}\n\nexport interface HarmonicRetrievalResult {\n node: AbstractionNode;\n score: number;\n nodeScore: number;\n anchorScore: number;\n matchedFields: string[];\n matchedAnchors: HarmonicMatchedAnchor[];\n}\n\ninterface HarmonicCandidate {\n node: AbstractionNode;\n nodeScore: number;\n anchorScore: number;\n matchedFields: Set<string>;\n matchedAnchors: Map<string, HarmonicMatchedAnchor>;\n}\n\nfunction scoreNode(node: AbstractionNode, queryTokens: Set<string>): { score: number; matchedFields: string[] } {\n const matchedFields: string[] = [];\n let score = 0;\n\n const titleMatches = countRecallTokenOverlap(queryTokens, node.title);\n if (titleMatches > 0) {\n score += titleMatches * 3;\n matchedFields.push(\"title\");\n }\n\n const summaryMatches = countRecallTokenOverlap(queryTokens, node.summary);\n if (summaryMatches > 0) {\n score += summaryMatches * 3;\n matchedFields.push(\"summary\");\n }\n\n const tagMatches = countRecallTokenOverlap(queryTokens, node.tags?.join(\" \"));\n if (tagMatches > 0) {\n score += tagMatches * 2;\n matchedFields.push(\"tags\");\n }\n\n const entityMatches = countRecallTokenOverlap(queryTokens, node.entityRefs?.join(\" \"));\n if (entityMatches > 0) {\n score += entityMatches * 2;\n matchedFields.push(\"entityRefs\");\n }\n\n const kindMatches = countRecallTokenOverlap(queryTokens, `${node.kind} ${node.abstractionLevel}`);\n if (kindMatches > 0) {\n score += kindMatches;\n matchedFields.push(\"kind\");\n }\n\n return { score, matchedFields };\n}\n\nfunction scoreAnchor(anchor: CueAnchor, queryTokens: Set<string>): { score: number; matchedFields: string[] } {\n const matchedFields: string[] = [];\n let score = 0;\n\n const valueMatches = countRecallTokenOverlap(queryTokens, anchor.anchorValue);\n const normalizedMatches = countRecallTokenOverlap(queryTokens, anchor.normalizedCue);\n const cueMatches = Math.max(valueMatches, normalizedMatches);\n if (cueMatches > 0) {\n score += cueMatches * 4;\n if (valueMatches > 0) matchedFields.push(\"anchorValue\");\n if (normalizedMatches > 0) matchedFields.push(\"anchor\");\n }\n\n const typeMatches = countRecallTokenOverlap(queryTokens, anchor.anchorType);\n if (typeMatches > 0) {\n score += typeMatches;\n matchedFields.push(\"anchorType\");\n }\n\n const tagMatches = countRecallTokenOverlap(queryTokens, anchor.tags?.join(\" \"));\n if (tagMatches > 0) {\n score += tagMatches * 2;\n matchedFields.push(\"anchorTags\");\n }\n\n return { score, matchedFields };\n}\n\nasync function readAbstractionNodes(options: {\n memoryDir: string;\n abstractionNodeStoreDir?: string;\n}): Promise<AbstractionNode[]> {\n const rootDir = resolveAbstractionNodeStoreDir(options.memoryDir, options.abstractionNodeStoreDir);\n const files = await listJsonFiles(path.join(rootDir, \"nodes\"));\n const nodes: AbstractionNode[] = [];\n for (const filePath of files) {\n try {\n nodes.push(validateAbstractionNode(await readJsonFile(filePath)));\n } catch {\n // fail-open: invalid artifacts stay visible via status tooling instead of recall\n }\n }\n return nodes;\n}\n\nasync function readCueAnchors(options: {\n memoryDir: string;\n abstractionNodeStoreDir?: string;\n}): Promise<CueAnchor[]> {\n const abstractionRoot = resolveAbstractionNodeStoreDir(options.memoryDir, options.abstractionNodeStoreDir);\n const rootDir = resolveCueAnchorStoreDir(abstractionRoot);\n const files = await listJsonFiles(rootDir);\n const anchors: CueAnchor[] = [];\n for (const filePath of files) {\n try {\n anchors.push(validateCueAnchor(await readJsonFile(filePath)));\n } catch {\n // fail-open: invalid artifacts stay visible via status tooling instead of recall\n }\n }\n return anchors;\n}\n\nexport async function searchHarmonicRetrieval(options: {\n memoryDir: string;\n abstractionNodeStoreDir?: string;\n query: string;\n maxResults: number;\n sessionKey?: string;\n anchorsEnabled: boolean;\n abortSignal?: AbortSignal;\n}): Promise<HarmonicRetrievalResult[]> {\n throwIfAborted(options.abortSignal, \"harmonic retrieval aborted\");\n const queryTokens = new Set(normalizeRecallTokens(options.query, [\"what\", \"which\"]));\n if (queryTokens.size === 0 || options.maxResults <= 0) return [];\n\n const nodes = await readAbstractionNodes(options);\n const candidates = new Map<string, HarmonicCandidate>();\n\n for (const node of nodes) {\n throwIfAborted(options.abortSignal, \"harmonic retrieval aborted\");\n const { score, matchedFields } = scoreNode(node, queryTokens);\n if (score <= 0) continue;\n candidates.set(node.nodeId, {\n node,\n nodeScore: score,\n anchorScore: 0,\n matchedFields: new Set(matchedFields),\n matchedAnchors: new Map(),\n });\n }\n\n if (options.anchorsEnabled) {\n throwIfAborted(options.abortSignal, \"harmonic retrieval aborted\");\n const anchors = await readCueAnchors(options);\n const nodeIndex = new Map(nodes.map((node) => [node.nodeId, node]));\n for (const anchor of anchors) {\n throwIfAborted(options.abortSignal, \"harmonic retrieval aborted\");\n const { score, matchedFields } = scoreAnchor(anchor, queryTokens);\n if (score <= 0) continue;\n for (const nodeRef of anchor.nodeRefs) {\n const node = nodeIndex.get(nodeRef);\n if (!node) continue;\n const existing = candidates.get(nodeRef) ?? {\n node,\n nodeScore: 0,\n anchorScore: 0,\n matchedFields: new Set<string>(),\n matchedAnchors: new Map<string, HarmonicMatchedAnchor>(),\n };\n existing.anchorScore += score;\n existing.matchedFields.add(\"anchor\");\n for (const field of matchedFields) existing.matchedFields.add(field);\n existing.matchedAnchors.set(anchor.anchorId, {\n anchorId: anchor.anchorId,\n anchorType: anchor.anchorType,\n anchorValue: anchor.anchorValue,\n });\n candidates.set(nodeRef, existing);\n }\n }\n }\n\n return [...candidates.values()]\n .map((candidate) => {\n let score = candidate.nodeScore + candidate.anchorScore;\n if (options.sessionKey && candidate.node.sessionKey === options.sessionKey) score += 0.5;\n return {\n node: candidate.node,\n score,\n nodeScore: candidate.nodeScore,\n anchorScore: candidate.anchorScore,\n matchedFields: [...candidate.matchedFields].sort(),\n matchedAnchors: [...candidate.matchedAnchors.values()].sort((left, right) =>\n left.anchorType.localeCompare(right.anchorType) || left.anchorValue.localeCompare(right.anchorValue)\n ),\n };\n })\n .filter((result) => result.nodeScore > 0 || result.anchorScore > 0)\n .sort(\n (left, right) =>\n right.score - left.score\n || right.anchorScore - left.anchorScore\n || right.node.recordedAt.localeCompare(left.node.recordedAt),\n )\n .slice(0, options.maxResults);\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
// src/citations.ts
|
|
2
|
+
var OPEN_TAG = "<oai-mem-citation>";
|
|
3
|
+
var CLOSE_TAG = "</oai-mem-citation>";
|
|
4
|
+
var ENTRIES_OPEN = "<citation_entries>";
|
|
5
|
+
var ENTRIES_CLOSE = "</citation_entries>";
|
|
6
|
+
var ROLLOUT_OPEN = "<rollout_ids>";
|
|
7
|
+
var ROLLOUT_CLOSE = "</rollout_ids>";
|
|
8
|
+
var THREAD_OPEN = "<thread_ids>";
|
|
9
|
+
var THREAD_CLOSE = "</thread_ids>";
|
|
10
|
+
function parseOaiMemCitation(text) {
|
|
11
|
+
const openIdx = text.indexOf(OPEN_TAG);
|
|
12
|
+
if (openIdx < 0) return null;
|
|
13
|
+
const closeIdx = text.indexOf(CLOSE_TAG, openIdx + OPEN_TAG.length);
|
|
14
|
+
if (closeIdx < 0) return null;
|
|
15
|
+
const inner = text.slice(openIdx + OPEN_TAG.length, closeIdx);
|
|
16
|
+
const entries = parseEntries(inner);
|
|
17
|
+
const rolloutIds = parseIds(inner, ROLLOUT_OPEN, ROLLOUT_CLOSE) ?? parseIds(inner, THREAD_OPEN, THREAD_CLOSE) ?? [];
|
|
18
|
+
if (entries.length === 0 && rolloutIds.length === 0) return null;
|
|
19
|
+
return { entries, rolloutIds };
|
|
20
|
+
}
|
|
21
|
+
function parseEntries(block) {
|
|
22
|
+
const start = block.indexOf(ENTRIES_OPEN);
|
|
23
|
+
if (start < 0) return [];
|
|
24
|
+
const end = block.indexOf(ENTRIES_CLOSE, start + ENTRIES_OPEN.length);
|
|
25
|
+
if (end < 0) return [];
|
|
26
|
+
const raw = block.slice(start + ENTRIES_OPEN.length, end);
|
|
27
|
+
const lines = raw.split("\n").map((l) => l.trim()).filter((l) => l.length > 0);
|
|
28
|
+
const results = [];
|
|
29
|
+
for (const line of lines) {
|
|
30
|
+
const parsed = parseEntryLine(line);
|
|
31
|
+
if (parsed) results.push(parsed);
|
|
32
|
+
}
|
|
33
|
+
return results;
|
|
34
|
+
}
|
|
35
|
+
function parseEntryLine(line) {
|
|
36
|
+
const match = line.match(/^(.+):(\d+)-(\d+)\|note=\[(.*)?\]$/);
|
|
37
|
+
if (!match) return null;
|
|
38
|
+
const [, pathRaw, startRaw, endRaw, noteRaw] = match;
|
|
39
|
+
if (!pathRaw || !startRaw || !endRaw) return null;
|
|
40
|
+
const lineStart = parseInt(startRaw, 10);
|
|
41
|
+
const lineEnd = parseInt(endRaw, 10);
|
|
42
|
+
if (!Number.isFinite(lineStart) || !Number.isFinite(lineEnd)) return null;
|
|
43
|
+
if (lineStart < 0 || lineEnd < 0) return null;
|
|
44
|
+
return {
|
|
45
|
+
path: pathRaw.trim(),
|
|
46
|
+
lineStart,
|
|
47
|
+
lineEnd,
|
|
48
|
+
note: noteRaw ?? ""
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
function parseIds(block, openTag, closeTag) {
|
|
52
|
+
const start = block.indexOf(openTag);
|
|
53
|
+
if (start < 0) return null;
|
|
54
|
+
const end = block.indexOf(closeTag, start + openTag.length);
|
|
55
|
+
if (end < 0) return null;
|
|
56
|
+
const raw = block.slice(start + openTag.length, end);
|
|
57
|
+
const lines = raw.split("\n").map((l) => l.trim()).filter((l) => l.length > 0);
|
|
58
|
+
const seen = /* @__PURE__ */ new Set();
|
|
59
|
+
const unique = [];
|
|
60
|
+
for (const id of lines) {
|
|
61
|
+
if (!seen.has(id)) {
|
|
62
|
+
seen.add(id);
|
|
63
|
+
unique.push(id);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return unique.length > 0 ? unique : null;
|
|
67
|
+
}
|
|
68
|
+
function sanitizeNoteForCitation(note) {
|
|
69
|
+
return note.replace(/[\r\n]+/g, " ").trim();
|
|
70
|
+
}
|
|
71
|
+
function formatOaiMemCitation(block) {
|
|
72
|
+
const entryLines = block.entries.map((e) => `${e.path}:${e.lineStart}-${e.lineEnd}|note=[${sanitizeNoteForCitation(e.note)}]`).join("\n");
|
|
73
|
+
const idLines = block.rolloutIds.join("\n");
|
|
74
|
+
return [
|
|
75
|
+
OPEN_TAG,
|
|
76
|
+
ENTRIES_OPEN,
|
|
77
|
+
entryLines,
|
|
78
|
+
ENTRIES_CLOSE,
|
|
79
|
+
ROLLOUT_OPEN,
|
|
80
|
+
idLines,
|
|
81
|
+
ROLLOUT_CLOSE,
|
|
82
|
+
CLOSE_TAG
|
|
83
|
+
].join("\n");
|
|
84
|
+
}
|
|
85
|
+
function buildCitationGuidance(citations) {
|
|
86
|
+
if (citations.length === 0) return "";
|
|
87
|
+
const entryExamples = citations.map(
|
|
88
|
+
(c) => `${c.path}:${c.lineStart}-${c.lineEnd}|note=[${sanitizeNoteForCitation(c.noteDefault)}]`
|
|
89
|
+
);
|
|
90
|
+
const rolloutExamples = citations.filter((c) => c.rolloutId != null).map((c) => c.rolloutId);
|
|
91
|
+
const seenRollouts = /* @__PURE__ */ new Set();
|
|
92
|
+
const uniqueRollouts = [];
|
|
93
|
+
for (const id of rolloutExamples) {
|
|
94
|
+
if (!seenRollouts.has(id)) {
|
|
95
|
+
seenRollouts.add(id);
|
|
96
|
+
uniqueRollouts.push(id);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
const lines = [
|
|
100
|
+
"",
|
|
101
|
+
"[Remnic citation guidance]",
|
|
102
|
+
"If you used any of the memories above, append the following to the END of your final reply:",
|
|
103
|
+
OPEN_TAG,
|
|
104
|
+
ENTRIES_OPEN,
|
|
105
|
+
...entryExamples,
|
|
106
|
+
ENTRIES_CLOSE,
|
|
107
|
+
ROLLOUT_OPEN,
|
|
108
|
+
...uniqueRollouts,
|
|
109
|
+
ROLLOUT_CLOSE,
|
|
110
|
+
CLOSE_TAG
|
|
111
|
+
];
|
|
112
|
+
return lines.join("\n");
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export {
|
|
116
|
+
parseOaiMemCitation,
|
|
117
|
+
sanitizeNoteForCitation,
|
|
118
|
+
formatOaiMemCitation,
|
|
119
|
+
buildCitationGuidance
|
|
120
|
+
};
|
|
121
|
+
//# sourceMappingURL=chunk-IQT3XTKW.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/citations.ts"],"sourcesContent":["/**\n * OAI-mem-citation parser and formatter (issue #379).\n *\n * Mirrors the citation block format used by Codex's `citations.rs` so that\n * Remnic recall responses produce compatible citation blocks. The model\n * appends these blocks to its final reply, and downstream hooks parse them\n * to increment memory usage tracking.\n *\n * Block format:\n *\n * <oai-mem-citation>\n * <citation_entries>\n * path/to/file.md:1-5|note=[short explanation]\n * </citation_entries>\n * <rollout_ids>\n * rollout-abc123\n * </rollout_ids>\n * </oai-mem-citation>\n */\n\n/** A single citation entry referencing a memory file and line range. */\nexport interface CitationEntry {\n path: string;\n lineStart: number;\n lineEnd: number;\n note: string;\n}\n\n/** Parsed citation block containing entries and rollout/thread IDs. */\nexport interface CitationBlock {\n entries: CitationEntry[];\n rolloutIds: string[];\n}\n\n/** Metadata attached to a recall result for citation guidance. */\nexport interface CitationMetadata {\n memoryId: string;\n path: string;\n lineStart: number;\n lineEnd: number;\n rolloutId?: string;\n noteDefault: string;\n}\n\n// ---------------------------------------------------------------------------\n// Parser\n// ---------------------------------------------------------------------------\n\nconst OPEN_TAG = \"<oai-mem-citation>\";\nconst CLOSE_TAG = \"</oai-mem-citation>\";\nconst ENTRIES_OPEN = \"<citation_entries>\";\nconst ENTRIES_CLOSE = \"</citation_entries>\";\nconst ROLLOUT_OPEN = \"<rollout_ids>\";\nconst ROLLOUT_CLOSE = \"</rollout_ids>\";\n/** Legacy alias accepted during parsing (Codex historically used thread_ids). */\nconst THREAD_OPEN = \"<thread_ids>\";\nconst THREAD_CLOSE = \"</thread_ids>\";\n\n/**\n * Parse an `<oai-mem-citation>` block from arbitrary text.\n *\n * Returns `null` when no valid block is found. Malformed entry lines are\n * silently skipped; only valid lines contribute to the result. Rollout IDs\n * are deduped while preserving insertion order.\n */\nexport function parseOaiMemCitation(text: string): CitationBlock | null {\n const openIdx = text.indexOf(OPEN_TAG);\n if (openIdx < 0) return null;\n const closeIdx = text.indexOf(CLOSE_TAG, openIdx + OPEN_TAG.length);\n if (closeIdx < 0) return null;\n\n const inner = text.slice(openIdx + OPEN_TAG.length, closeIdx);\n\n const entries = parseEntries(inner);\n const rolloutIds = parseIds(inner, ROLLOUT_OPEN, ROLLOUT_CLOSE)\n ?? parseIds(inner, THREAD_OPEN, THREAD_CLOSE)\n ?? [];\n\n if (entries.length === 0 && rolloutIds.length === 0) return null;\n\n return { entries, rolloutIds };\n}\n\nfunction parseEntries(block: string): CitationEntry[] {\n const start = block.indexOf(ENTRIES_OPEN);\n if (start < 0) return [];\n const end = block.indexOf(ENTRIES_CLOSE, start + ENTRIES_OPEN.length);\n if (end < 0) return [];\n\n const raw = block.slice(start + ENTRIES_OPEN.length, end);\n const lines = raw.split(\"\\n\").map((l) => l.trim()).filter((l) => l.length > 0);\n const results: CitationEntry[] = [];\n for (const line of lines) {\n const parsed = parseEntryLine(line);\n if (parsed) results.push(parsed);\n }\n return results;\n}\n\n/**\n * Parse a single citation entry line:\n * `<path>:<line_start>-<line_end>|note=[<note>]`\n *\n * Splits on the LAST `:` before the range pattern, not the first, because\n * file paths can contain colons on some systems (e.g., Windows drive letters\n * like `C:\\` or macOS resource forks).\n */\nfunction parseEntryLine(line: string): CitationEntry | null {\n // Match the range+note suffix anchored at the end, allowing the path prefix\n // to contain colons. The (.+) is greedy so it consumes everything up to the\n // LAST `:` before the `\\d+-\\d+|note=[...]` suffix.\n const match = line.match(/^(.+):(\\d+)-(\\d+)\\|note=\\[(.*)?\\]$/);\n if (!match) return null;\n const [, pathRaw, startRaw, endRaw, noteRaw] = match;\n if (!pathRaw || !startRaw || !endRaw) return null;\n const lineStart = parseInt(startRaw, 10);\n const lineEnd = parseInt(endRaw, 10);\n if (!Number.isFinite(lineStart) || !Number.isFinite(lineEnd)) return null;\n if (lineStart < 0 || lineEnd < 0) return null;\n return {\n path: pathRaw.trim(),\n lineStart,\n lineEnd,\n note: noteRaw ?? \"\",\n };\n}\n\nfunction parseIds(block: string, openTag: string, closeTag: string): string[] | null {\n const start = block.indexOf(openTag);\n if (start < 0) return null;\n const end = block.indexOf(closeTag, start + openTag.length);\n if (end < 0) return null;\n\n const raw = block.slice(start + openTag.length, end);\n const lines = raw.split(\"\\n\").map((l) => l.trim()).filter((l) => l.length > 0);\n\n // Deduplicate preserving insertion order.\n const seen = new Set<string>();\n const unique: string[] = [];\n for (const id of lines) {\n if (!seen.has(id)) {\n seen.add(id);\n unique.push(id);\n }\n }\n return unique.length > 0 ? unique : null;\n}\n\n// ---------------------------------------------------------------------------\n// Sanitizer\n// ---------------------------------------------------------------------------\n\n/**\n * Replace newline characters (\\n, \\r) with spaces so that note values can be\n * safely embedded in a single citation entry line without corrupting the\n * line-based parser (`parseEntryLine` splits on `\\n`).\n */\nexport function sanitizeNoteForCitation(note: string): string {\n return note.replace(/[\\r\\n]+/g, \" \").trim();\n}\n\n// ---------------------------------------------------------------------------\n// Formatter\n// ---------------------------------------------------------------------------\n\n/**\n * Format a `CitationBlock` into the canonical `<oai-mem-citation>` XML block.\n */\nexport function formatOaiMemCitation(block: CitationBlock): string {\n const entryLines = block.entries\n .map((e) => `${e.path}:${e.lineStart}-${e.lineEnd}|note=[${sanitizeNoteForCitation(e.note)}]`)\n .join(\"\\n\");\n const idLines = block.rolloutIds.join(\"\\n\");\n\n return [\n OPEN_TAG,\n ENTRIES_OPEN,\n entryLines,\n ENTRIES_CLOSE,\n ROLLOUT_OPEN,\n idLines,\n ROLLOUT_CLOSE,\n CLOSE_TAG,\n ].join(\"\\n\");\n}\n\n// ---------------------------------------------------------------------------\n// Guidance builder\n// ---------------------------------------------------------------------------\n\n/**\n * Build the citation guidance text appended to recall responses so that the\n * model knows how to emit a citation block in its reply.\n *\n * Returns an empty string when `citations` is empty (no guidance needed).\n */\nexport function buildCitationGuidance(citations: CitationMetadata[]): string {\n if (citations.length === 0) return \"\";\n\n const entryExamples = citations.map((c) =>\n `${c.path}:${c.lineStart}-${c.lineEnd}|note=[${sanitizeNoteForCitation(c.noteDefault)}]`,\n );\n const rolloutExamples = citations\n .filter((c) => c.rolloutId != null)\n .map((c) => c.rolloutId!);\n\n // Dedupe rollout IDs preserving order.\n const seenRollouts = new Set<string>();\n const uniqueRollouts: string[] = [];\n for (const id of rolloutExamples) {\n if (!seenRollouts.has(id)) {\n seenRollouts.add(id);\n uniqueRollouts.push(id);\n }\n }\n\n const lines = [\n \"\",\n \"[Remnic citation guidance]\",\n \"If you used any of the memories above, append the following to the END of your final reply:\",\n OPEN_TAG,\n ENTRIES_OPEN,\n ...entryExamples,\n ENTRIES_CLOSE,\n ROLLOUT_OPEN,\n ...uniqueRollouts,\n ROLLOUT_CLOSE,\n CLOSE_TAG,\n ];\n\n return lines.join(\"\\n\");\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
StorageManager
|
|
3
|
-
} from "./chunk-QWUUMMIK.js";
|
|
4
1
|
import {
|
|
5
2
|
decideLifecycleTransition
|
|
6
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-TBBDFYXW.js";
|
|
4
|
+
import {
|
|
5
|
+
StorageManager
|
|
6
|
+
} from "./chunk-GV6NLQ4X.js";
|
|
7
7
|
|
|
8
8
|
// src/maintenance/memory-governance.ts
|
|
9
9
|
import path from "path";
|
|
@@ -717,4 +717,4 @@ export {
|
|
|
717
717
|
listMemoryGovernanceRuns,
|
|
718
718
|
readMemoryGovernanceRunArtifact
|
|
719
719
|
};
|
|
720
|
-
//# sourceMappingURL=chunk-
|
|
720
|
+
//# sourceMappingURL=chunk-ITRLGI2T.js.map
|
|
@@ -59,6 +59,8 @@ var CATEGORY_BOOSTS = {
|
|
|
59
59
|
// Durable rules/values
|
|
60
60
|
rule: 0.11,
|
|
61
61
|
// Causal IF→THEN rules
|
|
62
|
+
procedure: 0.1,
|
|
63
|
+
// Repeatable workflows (issue #519)
|
|
62
64
|
preference: 0.1,
|
|
63
65
|
// User preferences matter
|
|
64
66
|
commitment: 0.1,
|
|
@@ -309,10 +311,21 @@ function importanceLevel(score) {
|
|
|
309
311
|
if (score >= 0.2) return "low";
|
|
310
312
|
return "trivial";
|
|
311
313
|
}
|
|
314
|
+
var IMPORTANCE_LEVEL_RANK = {
|
|
315
|
+
trivial: 0,
|
|
316
|
+
low: 1,
|
|
317
|
+
normal: 2,
|
|
318
|
+
high: 3,
|
|
319
|
+
critical: 4
|
|
320
|
+
};
|
|
321
|
+
function isAboveImportanceThreshold(candidate, threshold) {
|
|
322
|
+
return IMPORTANCE_LEVEL_RANK[candidate] >= IMPORTANCE_LEVEL_RANK[threshold];
|
|
323
|
+
}
|
|
312
324
|
|
|
313
325
|
export {
|
|
314
326
|
scoreImportance,
|
|
315
327
|
rescoreMemoryImportance,
|
|
316
|
-
importanceLevel
|
|
328
|
+
importanceLevel,
|
|
329
|
+
isAboveImportanceThreshold
|
|
317
330
|
};
|
|
318
|
-
//# sourceMappingURL=chunk-
|
|
331
|
+
//# sourceMappingURL=chunk-J4IYOZZ5.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/importance.ts"],"sourcesContent":["/**\n * Local Importance Scoring (Phase 1B)\n *\n * Zero-LLM heuristic system that evaluates each memory's significance.\n * Analyzes content for markers like explicit importance statements,\n * personal information, instructions, emotional content, and factual density.\n */\n\nimport type { ImportanceLevel, ImportanceScore, MemoryCategory, MemoryFile } from \"./types.js\";\n\n// ---------------------------------------------------------------------------\n// Marker patterns for each tier\n// ---------------------------------------------------------------------------\n\n/** Critical importance markers (0.9-1.0) */\nconst CRITICAL_PATTERNS = [\n // Explicit importance\n /\\b(critical|crucial|essential|must|always|never)\\b/i,\n /\\b(important|remember this|don't forget)\\b/i,\n // Personal identity\n /\\b(my name is|i am|i'm called)\\b/i,\n /\\b(my (birthday|phone|email|address|ssn|password))\\b/i,\n // Strong preferences\n /\\b(i (hate|love|despise|adore))\\b/i,\n /\\b(absolutely|definitely|certainly) (not|must|should)\\b/i,\n // Corrections (high weight)\n /\\b(actually|no,? that's wrong|correction:?|let me correct)\\b/i,\n /\\b(i said|i meant|i was wrong)\\b/i,\n];\n\n/** High importance markers (0.7-0.9) */\nconst HIGH_PATTERNS = [\n // Decisions\n /\\b(decided|decision|chose|choosing|picked|selected)\\b/i,\n /\\b(we('ll| will) (go with|use|implement))\\b/i,\n // Instructions\n /\\b(make sure|ensure|always|don't|do not|never|avoid)\\b/i,\n /\\b(you (should|must|need to))\\b/i,\n // Temporal references (deadlines, schedules)\n /\\b(by (monday|tuesday|wednesday|thursday|friday|saturday|sunday))\\b/i,\n /\\b(deadline|due (date|by)|by (end of|next))\\b/i,\n /\\b(tomorrow|next week|this week|today)\\b/i,\n // Preferences\n /\\b(i (prefer|like|want|need|dislike))\\b/i,\n /\\b(my (preference|style|approach|way))\\b/i,\n // Commitments\n /\\b(i('ll| will)|promise|commit|guarantee)\\b/i,\n /\\b(scheduled|appointment|meeting|call)\\b/i,\n];\n\n/** Normal importance markers (0.4-0.7) */\nconst NORMAL_PATTERNS = [\n // Factual content\n /\\b(is|are|was|were|has|have|does|do)\\b/i,\n /\\b(because|since|therefore|thus|so)\\b/i,\n // Emotional content\n /\\b(happy|sad|frustrated|excited|worried|anxious)\\b/i,\n /\\b(feel|feeling|felt)\\b/i,\n // Technical details\n /\\b(version|api|endpoint|database|server|config)\\b/i,\n /\\b(function|class|method|variable|parameter)\\b/i,\n];\n\n/** Low importance markers (0.2-0.4) */\nconst LOW_PATTERNS = [\n // Uncertainty\n /\\b(maybe|perhaps|possibly|might|could be)\\b/i,\n /\\b(i think|i guess|not sure|uncertain)\\b/i,\n /\\b(probably|likely|seems like)\\b/i,\n // Hedging\n /\\b(kind of|sort of|somewhat|a bit)\\b/i,\n /\\b(in a way|to some extent)\\b/i,\n];\n\n/** Trivial content markers (0.0-0.2) */\nconst TRIVIAL_PATTERNS = [\n // Greetings and filler\n /^(hi|hello|hey|yo|sup|greetings)[.!,]?\\s*$/i,\n /^(ok|okay|k|sure|yes|no|yep|nope|yeah|nah)[.!]?\\s*$/i,\n /^(thanks|thank you|thx|ty|cheers)[.!]?\\s*$/i,\n /^(got it|understood|roger|copy|noted)[.!]?\\s*$/i,\n /^(bye|goodbye|later|see ya|ttyl)[.!]?\\s*$/i,\n /^(lol|haha|hehe|lmao|rofl)[.!]?\\s*$/i,\n /^(hmm+|uhh*|ahh*|err*|umm*)[.!]?\\s*$/i,\n // Very short content\n /^.{1,10}$/,\n];\n\n// ---------------------------------------------------------------------------\n// Category-based importance boosts\n// ---------------------------------------------------------------------------\n\nconst CATEGORY_BOOSTS: Record<MemoryCategory, number> = {\n correction: 0.15, // Corrections are always important\n principle: 0.12, // Durable rules/values\n rule: 0.11, // Causal IF→THEN rules\n procedure: 0.10, // Repeatable workflows (issue #519)\n preference: 0.10, // User preferences matter\n commitment: 0.10, // Promises/obligations\n decision: 0.08, // Decisions with rationale\n relationship: 0.05, // Entity relationships\n skill: 0.05, // Capabilities\n moment: 0.03, // Emotional milestones\n entity: 0.02, // Entity facts\n fact: 0.00, // Base facts, no boost\n};\n\n// ---------------------------------------------------------------------------\n// Keyword extraction\n// ---------------------------------------------------------------------------\n\n/** Common stop words to filter out */\nconst STOP_WORDS = new Set([\n \"a\", \"an\", \"the\", \"is\", \"are\", \"was\", \"were\", \"be\", \"been\", \"being\",\n \"have\", \"has\", \"had\", \"do\", \"does\", \"did\", \"will\", \"would\", \"could\",\n \"should\", \"may\", \"might\", \"must\", \"shall\", \"can\", \"need\", \"dare\",\n \"ought\", \"used\", \"to\", \"of\", \"in\", \"for\", \"on\", \"with\", \"at\", \"by\",\n \"from\", \"as\", \"into\", \"through\", \"during\", \"before\", \"after\", \"above\",\n \"below\", \"between\", \"under\", \"again\", \"further\", \"then\", \"once\",\n \"here\", \"there\", \"when\", \"where\", \"why\", \"how\", \"all\", \"each\", \"few\",\n \"more\", \"most\", \"other\", \"some\", \"such\", \"no\", \"nor\", \"not\", \"only\",\n \"own\", \"same\", \"so\", \"than\", \"too\", \"very\", \"just\", \"and\", \"but\",\n \"or\", \"if\", \"because\", \"until\", \"while\", \"this\", \"that\", \"these\",\n \"those\", \"i\", \"me\", \"my\", \"myself\", \"we\", \"our\", \"ours\", \"ourselves\",\n \"you\", \"your\", \"yours\", \"yourself\", \"yourselves\", \"he\", \"him\", \"his\",\n \"himself\", \"she\", \"her\", \"hers\", \"herself\", \"it\", \"its\", \"itself\",\n \"they\", \"them\", \"their\", \"theirs\", \"themselves\", \"what\", \"which\",\n \"who\", \"whom\", \"whose\", \"am\", \"been\", \"being\", \"about\", \"against\",\n]);\n\n/**\n * Extract salient keywords from content.\n * Returns top N keywords sorted by relevance.\n */\nfunction extractKeywords(content: string, maxKeywords: number = 5): string[] {\n // Tokenize and normalize\n const words = content\n .toLowerCase()\n .replace(/[^a-z0-9\\s-]/g, \" \")\n .split(/\\s+/)\n .filter((w) => w.length >= 3 && !STOP_WORDS.has(w));\n\n // Count frequencies\n const freq = new Map<string, number>();\n for (const word of words) {\n freq.set(word, (freq.get(word) ?? 0) + 1);\n }\n\n // Sort by frequency, take top N\n return [...freq.entries()]\n .sort((a, b) => b[1] - a[1])\n .slice(0, maxKeywords)\n .map(([word]) => word);\n}\n\n// ---------------------------------------------------------------------------\n// Main scoring function\n// ---------------------------------------------------------------------------\n\n/**\n * Calculate importance score for a memory.\n * Pure local heuristics, zero LLM calls.\n */\nexport function scoreImportance(\n content: string,\n category: MemoryCategory,\n tags: string[] = [],\n): ImportanceScore {\n const reasons: string[] = [];\n let score = 0.5; // Start at normal baseline\n\n const lowerContent = content.toLowerCase();\n const contentLength = content.length;\n\n // Check for trivial content first (short-circuit)\n for (const pattern of TRIVIAL_PATTERNS) {\n if (pattern.test(content)) {\n return {\n score: 0.1,\n level: \"trivial\",\n reasons: [\"Trivial content (greeting, filler, or very short)\"],\n keywords: [],\n };\n }\n }\n\n // Check critical patterns\n for (const pattern of CRITICAL_PATTERNS) {\n if (pattern.test(content)) {\n score += 0.20;\n reasons.push(`Critical marker: ${pattern.source.slice(0, 30)}`);\n break; // Only count once per tier\n }\n }\n\n // Check high patterns\n for (const pattern of HIGH_PATTERNS) {\n if (pattern.test(content)) {\n score += 0.12;\n reasons.push(`High importance marker detected`);\n break;\n }\n }\n\n // Check low patterns (reduces score)\n for (const pattern of LOW_PATTERNS) {\n if (pattern.test(content)) {\n score -= 0.15;\n reasons.push(`Uncertainty/hedging detected`);\n break;\n }\n }\n\n // Category boost\n const categoryBoost = CATEGORY_BOOSTS[category] ?? 0;\n if (categoryBoost > 0) {\n score += categoryBoost;\n reasons.push(`Category boost: ${category}`);\n }\n\n // Length bonus (longer = more substance, capped)\n if (contentLength > 200) {\n const lengthBonus = Math.min((contentLength - 200) / 1000, 0.1);\n score += lengthBonus;\n if (lengthBonus > 0.05) {\n reasons.push(\"Substantial content length\");\n }\n }\n\n // Check for personal pronouns (signals personal relevance)\n if (/\\b(my|mine|i|i'm|i've|i'd|i'll)\\b/i.test(content)) {\n score += 0.05;\n reasons.push(\"Personal reference\");\n }\n\n // Check for numbers/specifics (concrete details)\n if (/\\b\\d{2,}\\b/.test(content) || /\\b(version|v\\d|api|config)\\b/i.test(content)) {\n score += 0.03;\n reasons.push(\"Contains specific details\");\n }\n\n // Tag-based boosts\n const importantTags = tags.filter((t) =>\n [\"important\", \"critical\", \"preference\", \"decision\", \"rule\", \"principle\"].includes(t.toLowerCase())\n );\n if (importantTags.length > 0) {\n score += 0.05 * importantTags.length;\n reasons.push(`Important tags: ${importantTags.join(\", \")}`);\n }\n\n // Clamp score to 0-1 range\n score = Math.max(0, Math.min(1, score));\n\n // Determine level from score\n let level: ImportanceLevel;\n if (score >= 0.9) {\n level = \"critical\";\n } else if (score >= 0.7) {\n level = \"high\";\n } else if (score >= 0.4) {\n level = \"normal\";\n } else if (score >= 0.2) {\n level = \"low\";\n } else {\n level = \"trivial\";\n }\n\n // Extract keywords\n const keywords = extractKeywords(content);\n\n return {\n score: Math.round(score * 100) / 100, // Round to 2 decimal places\n level,\n reasons: reasons.slice(0, 5), // Cap at 5 reasons\n keywords,\n };\n}\n\n/**\n * Recompute importance for an existing memory file using current local heuristics.\n */\nexport function rescoreMemoryImportance(memory: MemoryFile): ImportanceScore {\n return scoreImportance(memory.content, memory.frontmatter.category, memory.frontmatter.tags ?? []);\n}\n\n/**\n * Get importance level from numeric score.\n */\nexport function importanceLevel(score: number): ImportanceLevel {\n if (score >= 0.9) return \"critical\";\n if (score >= 0.7) return \"high\";\n if (score >= 0.4) return \"normal\";\n if (score >= 0.2) return \"low\";\n return \"trivial\";\n}\n\n// ---------------------------------------------------------------------------\n// Importance threshold helper (issue #372)\n// ---------------------------------------------------------------------------\n\n/**\n * Ordering for importance levels used by the extraction write gate.\n * Higher indices mean more important.\n */\nconst IMPORTANCE_LEVEL_RANK: Record<ImportanceLevel, number> = {\n trivial: 0,\n low: 1,\n normal: 2,\n high: 3,\n critical: 4,\n};\n\n/**\n * Return true if `candidate` meets or exceeds `threshold`. Used by the\n * extraction write gate in orchestrator.persistExtraction() to drop trivial\n * content before it is written to the memory store.\n *\n * Note: callers should pass the ALREADY-BOOSTED level returned by\n * scoreImportance(), because category boosts (e.g. corrections) are applied\n * inside scoreImportance() before the level is derived.\n */\nexport function isAboveImportanceThreshold(\n candidate: ImportanceLevel,\n threshold: ImportanceLevel,\n): boolean {\n return IMPORTANCE_LEVEL_RANK[candidate] >= IMPORTANCE_LEVEL_RANK[threshold];\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
+
import {
|
|
2
|
+
countRecallTokenOverlap,
|
|
3
|
+
normalizeRecallTokens
|
|
4
|
+
} from "./chunk-DT5TVLJE.js";
|
|
1
5
|
import {
|
|
2
6
|
StorageManager
|
|
3
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-GV6NLQ4X.js";
|
|
4
8
|
import {
|
|
5
9
|
getCachedRuleMemories,
|
|
6
10
|
setCachedRuleMemories
|
|
7
|
-
} from "./chunk-
|
|
8
|
-
import {
|
|
9
|
-
countRecallTokenOverlap,
|
|
10
|
-
normalizeRecallTokens
|
|
11
|
-
} from "./chunk-DT5TVLJE.js";
|
|
11
|
+
} from "./chunk-6PFRXT4K.js";
|
|
12
12
|
|
|
13
13
|
// src/semantic-rule-verifier.ts
|
|
14
14
|
var DEFAULT_MIN_EFFECTIVE_CONFIDENCE = 0.45;
|
|
@@ -110,4 +110,4 @@ async function searchVerifiedSemanticRules(options) {
|
|
|
110
110
|
export {
|
|
111
111
|
searchVerifiedSemanticRules
|
|
112
112
|
};
|
|
113
|
-
//# sourceMappingURL=chunk-
|
|
113
|
+
//# sourceMappingURL=chunk-JIU55F3X.js.map
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
} from "./chunk-MARWOCVP.js";
|
|
8
8
|
import {
|
|
9
9
|
log
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-2ODBA7MQ.js";
|
|
11
11
|
|
|
12
12
|
// src/local-llm.ts
|
|
13
13
|
import { existsSync, readFileSync } from "fs";
|
|
@@ -17,6 +17,10 @@ function trimTrailingSlashes(s) {
|
|
|
17
17
|
while (end > 0 && s[end - 1] === "/") end--;
|
|
18
18
|
return s.substring(0, end);
|
|
19
19
|
}
|
|
20
|
+
var THINKING_COMPATIBLE_BACKENDS = /* @__PURE__ */ new Set([
|
|
21
|
+
"lmstudio",
|
|
22
|
+
"vllm"
|
|
23
|
+
]);
|
|
20
24
|
var LOCAL_SERVERS = [
|
|
21
25
|
{
|
|
22
26
|
type: "ollama",
|
|
@@ -75,9 +79,16 @@ var LocalLlmClient = class _LocalLlmClient {
|
|
|
75
79
|
this.modelRegistry = modelRegistry;
|
|
76
80
|
}
|
|
77
81
|
/**
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
82
|
+
* Request thinking/reasoning suppression on the next chat completion.
|
|
83
|
+
*
|
|
84
|
+
* When `true`, the client will inject
|
|
85
|
+
* `chat_template_kwargs: { enable_thinking: false }` into the request
|
|
86
|
+
* body — **but only when the detected backend is known to support it**
|
|
87
|
+
* (LM Studio, vLLM; see `THINKING_COMPATIBLE_BACKENDS`). Strict
|
|
88
|
+
* OpenAI-compat backends reject unknown fields with 400; on those the
|
|
89
|
+
* client fails open (thinking runs normally). This is the safe
|
|
90
|
+
* default for Remnic extraction / consolidation: measurable latency
|
|
91
|
+
* win on thinking-capable backends, zero risk on others. Issue #548.
|
|
81
92
|
*/
|
|
82
93
|
set disableThinking(value) {
|
|
83
94
|
this._disableThinking = value;
|
|
@@ -575,7 +586,7 @@ var LocalLlmClient = class _LocalLlmClient {
|
|
|
575
586
|
if (options.responseFormat?.type === "json_schema") {
|
|
576
587
|
requestBody.response_format = options.responseFormat;
|
|
577
588
|
}
|
|
578
|
-
if (this._disableThinking) {
|
|
589
|
+
if (this._disableThinking && this.detectedType !== null && THINKING_COMPATIBLE_BACKENDS.has(this.detectedType)) {
|
|
579
590
|
requestBody.chat_template_kwargs = { enable_thinking: false };
|
|
580
591
|
}
|
|
581
592
|
const baseUrl = trimTrailingSlashes(
|
|
@@ -922,4 +933,4 @@ var LocalLlmClient = class _LocalLlmClient {
|
|
|
922
933
|
export {
|
|
923
934
|
LocalLlmClient
|
|
924
935
|
};
|
|
925
|
-
//# sourceMappingURL=chunk-
|
|
936
|
+
//# sourceMappingURL=chunk-JL2PU6AI.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/local-llm.ts"],"sourcesContent":["import { log } from \"./logger.js\";\nimport type { PluginConfig } from \"./types.js\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport os from \"node:os\";\nimport type { ModelRegistry } from \"./model-registry.js\";\nimport { launchProcessSync } from \"./runtime/child-process.js\";\nimport { mergeEnv, readEnvVar } from \"./runtime/env.js\";\n\n/** Trim trailing slash characters without backtracking regex. */\nfunction trimTrailingSlashes(s: string): string {\n let end = s.length;\n while (end > 0 && s[end - 1] === \"/\") end--;\n return s.substring(0, end);\n}\n\n/**\n * Local LLM client for OpenAI-compatible endpoints (LM Studio, Ollama, MLX, etc.)\n *\n * Based on openclaw-tactician's provider detection patterns for consistency.\n * Provides privacy-preserving, cost-effective LLM operations with\n * graceful fallback to cloud providers when local LLM is unavailable.\n */\nexport type LocalLlmType = \"lmstudio\" | \"ollama\" | \"mlx\" | \"vllm\" | \"generic\";\n\n/**\n * Backends known to honor `chat_template_kwargs: { enable_thinking: false }`\n * on OpenAI-compatible `/v1/chat/completions`. LM Studio and vLLM both\n * forward this field to the jinja chat template, where thinking-capable\n * models (Qwen 3.5, Gemma 4, DeepSeek) suppress reasoning tokens.\n *\n * Strict OpenAI-compatible backends (standard OpenAI, Azure OpenAI, some\n * proxies) reject unknown request fields with 400 — which trips the\n * `localLlm400*` cooldown path. `LocalLlmClient` therefore only injects\n * the kwarg when the detected backend is in this set; unknown / `generic`\n * / `ollama` / `mlx` fail open (no injection, no 400 risk). Issue #548.\n */\nconst THINKING_COMPATIBLE_BACKENDS: ReadonlySet<LocalLlmType> = new Set([\n \"lmstudio\",\n \"vllm\",\n]);\n\ninterface LocalServerConfig {\n type: LocalLlmType;\n defaultPort: number;\n healthEndpoint: string;\n modelsEndpoint: string;\n detectFn: (response: unknown) => boolean;\n}\n\nconst LOCAL_SERVERS: LocalServerConfig[] = [\n {\n type: \"ollama\",\n defaultPort: 11434,\n healthEndpoint: \"/\",\n modelsEndpoint: \"/api/tags\",\n detectFn: (resp) => typeof resp === \"string\" && resp.includes(\"Ollama\"),\n },\n {\n type: \"mlx\",\n defaultPort: 8080,\n healthEndpoint: \"/v1/models\",\n modelsEndpoint: \"/v1/models\",\n detectFn: (resp) =>\n typeof resp === \"object\" &&\n resp !== null &&\n \"data\" in resp &&\n Array.isArray((resp as { data: unknown[] }).data),\n },\n {\n type: \"lmstudio\",\n defaultPort: 1234,\n healthEndpoint: \"/v1/models\",\n modelsEndpoint: \"/v1/models\",\n detectFn: (resp) =>\n typeof resp === \"object\" &&\n resp !== null &&\n \"data\" in resp &&\n Array.isArray((resp as { data: unknown[] }).data),\n },\n {\n type: \"vllm\",\n defaultPort: 8000,\n healthEndpoint: \"/health\",\n modelsEndpoint: \"/v1/models\",\n detectFn: (resp) => resp === \"\" || (typeof resp === \"object\" && resp !== null),\n },\n];\n\nexport interface LocalModelInfo {\n id: string;\n contextWindow?: number;\n maxTokens?: number;\n}\n\nexport type LocalLlmRequestPriority = \"recall-critical\" | \"background\";\n\ninterface LocalLlmChatCompletionOptions {\n temperature?: number;\n maxTokens?: number;\n responseFormat?: { type: string };\n timeoutMs?: number;\n operation?: string;\n priority?: LocalLlmRequestPriority;\n}\n\ninterface LocalLlmQueuedRequest {\n messages: Array<{ role: string; content: string }>;\n options: LocalLlmChatCompletionOptions;\n priority: LocalLlmRequestPriority;\n enqueuedAtMs: number;\n resolve: (value: LocalLlmChatCompletionResult | null) => void;\n}\n\ninterface LocalLlmChatCompletionResult {\n content: string;\n usage?: { promptTokens: number; completionTokens: number; totalTokens: number };\n}\n\nconst LOCAL_LLM_GLOBAL_BACKEND_STATE = \"__openclawEngramLocalLlmBackendState\";\n\ntype LocalLlmBackendState = {\n untilMs: number;\n reason: string;\n};\nexport class LocalLlmClient {\n private config: PluginConfig;\n private isAvailable: boolean | null = null;\n private lastHealthCheck: number = 0;\n private detectedType: LocalLlmType | null = null;\n private cachedModelInfo: LocalModelInfo | null = null;\n private cachedLmsContext: number | null = null;\n private lastLmsCheck: number = 0;\n private consecutive400s: number = 0;\n private cooldownUntilMs: number = 0;\n private modelRegistry?: ModelRegistry;\n private _disableThinking: boolean = false;\n private readonly requestQueues: Record<LocalLlmRequestPriority, LocalLlmQueuedRequest[]> = {\n \"recall-critical\": [],\n background: [],\n };\n private readonly queueProcessing = new Set<LocalLlmRequestPriority>();\n private queueDrainScheduled: boolean = false;\n private static readonly HEALTH_CHECK_INTERVAL_MS = 60000; // 1 minute\n private static readonly LMS_CACHE_INTERVAL_MS = 30000; // 30 seconds\n\n constructor(config: PluginConfig, modelRegistry?: ModelRegistry) {\n this.config = config;\n this.modelRegistry = modelRegistry;\n }\n\n /**\n * Request thinking/reasoning suppression on the next chat completion.\n *\n * When `true`, the client will inject\n * `chat_template_kwargs: { enable_thinking: false }` into the request\n * body — **but only when the detected backend is known to support it**\n * (LM Studio, vLLM; see `THINKING_COMPATIBLE_BACKENDS`). Strict\n * OpenAI-compat backends reject unknown fields with 400; on those the\n * client fails open (thinking runs normally). This is the safe\n * default for Remnic extraction / consolidation: measurable latency\n * win on thinking-capable backends, zero risk on others. Issue #548.\n */\n set disableThinking(value: boolean) {\n this._disableThinking = value;\n }\n\n private resolveHomeDir(): string {\n return this.config.localLlmHomeDir || readEnvVar(\"HOME\") || os.homedir();\n }\n\n private buildRequestHeaders(base: Record<string, string> = {}): Record<string, string> {\n const headers: Record<string, string> = {\n ...base,\n ...(this.config.localLlmHeaders ?? {}),\n };\n if (this.config.localLlmApiKey && this.config.localLlmAuthHeader !== false) {\n headers.Authorization = `Bearer ${this.config.localLlmApiKey}`;\n }\n return headers;\n }\n\n private isAbortError(err: unknown): boolean {\n if (!err || typeof err !== \"object\") return false;\n const maybe = err as { name?: string; message?: string };\n return (\n maybe.name === \"AbortError\" ||\n maybe.message === \"This operation was aborted\" ||\n maybe.message === \"The operation was aborted\"\n );\n }\n\n /**\n * Set the ModelRegistry for caching detected capabilities\n */\n setModelRegistry(registry: ModelRegistry): void {\n this.modelRegistry = registry;\n }\n\n /**\n * Get the detected server type (null if not detected)\n */\n getDetectedType(): LocalLlmType | null {\n return this.detectedType;\n }\n\n private getBackendKey(): string {\n return trimTrailingSlashes(\n this.config.localLlmUrl.replace(\"localhost\", \"127.0.0.1\"),\n ).replace(/\\/v1$/, \"\");\n }\n\n private getGlobalBackendState(): Map<string, LocalLlmBackendState> {\n const globalAny = globalThis as typeof globalThis & {\n [LOCAL_LLM_GLOBAL_BACKEND_STATE]?: Map<string, LocalLlmBackendState>;\n };\n if (!globalAny[LOCAL_LLM_GLOBAL_BACKEND_STATE]) {\n globalAny[LOCAL_LLM_GLOBAL_BACKEND_STATE] = new Map();\n }\n return globalAny[LOCAL_LLM_GLOBAL_BACKEND_STATE];\n }\n\n private getTrippedBackendState(now: number): LocalLlmBackendState | null {\n const state = this.getGlobalBackendState().get(this.getBackendKey()) ?? null;\n if (!state) return null;\n if (state.untilMs <= now) {\n this.getGlobalBackendState().delete(this.getBackendKey());\n this.lastHealthCheck = 0;\n return null;\n }\n return state;\n }\n\n private markBackendUnavailable(reason: string, durationMs: number): void {\n const normalizedReason = this.normalizeBackendTripReason(reason);\n if (durationMs > 0) {\n const untilMs = Date.now() + durationMs;\n this.getGlobalBackendState().set(this.getBackendKey(), { untilMs, reason: normalizedReason });\n } else {\n this.getGlobalBackendState().delete(this.getBackendKey());\n }\n this.isAvailable = false;\n this.lastHealthCheck = 0;\n log.warn(\n `local LLM backend unavailable for ${durationMs}ms: model=${this.config.localLlmModel} reason=${normalizedReason}`,\n );\n }\n\n private extractNonRecoverableBackendReason(reason: string): string | null {\n const match = reason.match(\n /Failed to load model|Library not loaded|different Team IDs|code signature|llm_engine_mlx_amphibian/i,\n );\n return match?.[0] ?? null;\n }\n\n private extractNonRecoverableBackendReasonFromErrorText(errorText: string): string | null {\n const directReason = this.extractNonRecoverableBackendReason(errorText);\n if (directReason) return directReason;\n try {\n const parsed = JSON.parse(errorText) as { error?: { message?: string } };\n return this.extractNonRecoverableBackendReason(parsed?.error?.message ?? \"\");\n } catch {\n return null;\n }\n }\n\n private normalizeBackendTripReason(reason: string): string {\n const cleaned = reason.replace(/\\s+/g, \" \").replace(/^[-:–—\\s]+/, \"\").trim();\n if (!cleaned) return \"unknown local backend failure\";\n return cleaned.length > 160 ? `${cleaned.slice(0, 157)}...` : cleaned;\n }\n\n /**\n * Fetch with timeout for health checks\n */\n private async fetchWithTimeout(\n url: string,\n timeoutMs: number = 2000,\n headers?: Record<string, string>,\n ): Promise<{ ok: boolean; data: unknown; status: number | null }> {\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), timeoutMs);\n\n try {\n const response = await fetch(url, {\n signal: controller.signal,\n headers: this.buildRequestHeaders({ Accept: \"application/json\", ...(headers ?? {}) }),\n });\n clearTimeout(timeout);\n\n if (!response.ok) {\n return { ok: false, data: null, status: response.status };\n }\n\n const contentType = response.headers.get(\"content-type\");\n if (contentType?.includes(\"application/json\")) {\n return { ok: true, data: await response.json(), status: response.status };\n } else {\n return { ok: true, data: await response.text(), status: response.status };\n }\n } catch (err) {\n clearTimeout(timeout);\n return { ok: false, data: null, status: null };\n }\n }\n\n /**\n * Check if local LLM is available\n * Uses 127.0.0.1 instead of localhost to avoid DNS issues (consistent with tactician)\n */\n async checkAvailability(): Promise<boolean> {\n // Cache health check results for 1 minute\n const now = Date.now();\n const trippedState = this.getTrippedBackendState(now);\n if (trippedState) {\n this.isAvailable = false;\n this.lastHealthCheck = 0;\n log.info(\n `local LLM availability: backend circuit open for ${Math.max(0, trippedState.untilMs - now)}ms (${trippedState.reason})`,\n );\n return false;\n }\n if (this.isAvailable !== null && now - this.lastHealthCheck < LocalLlmClient.HEALTH_CHECK_INTERVAL_MS) {\n return this.isAvailable;\n }\n\n // Normalize URL - replace localhost with 127.0.0.1, remove trailing slashes\n const baseUrl = trimTrailingSlashes(\n this.config.localLlmUrl.replace(\"localhost\", \"127.0.0.1\"),\n );\n let sawUnauthorizedProbe = false;\n\n // Try to detect which server type is running\n for (const serverConfig of LOCAL_SERVERS) {\n const healthUrl = `${baseUrl}${serverConfig.healthEndpoint}`;\n log.debug(`checking ${serverConfig.type} at ${healthUrl}`);\n\n const result = await this.fetchWithTimeout(healthUrl);\n if (result.ok && serverConfig.detectFn(result.data)) {\n this.isAvailable = true;\n this.detectedType = serverConfig.type;\n this.lastHealthCheck = now;\n log.info(`detected ${serverConfig.type} at ${baseUrl}`);\n return true;\n }\n if (result.status === 401 || result.status === 403) {\n sawUnauthorizedProbe = true;\n }\n }\n\n // Generic check if specific detection failed\n try {\n const modelsUrl = `${baseUrl}/v1/models`;\n const result = await this.fetchWithTimeout(modelsUrl);\n if (result.ok) {\n this.isAvailable = true;\n this.detectedType = \"generic\";\n this.lastHealthCheck = now;\n log.info(`detected generic OpenAI-compatible server at ${baseUrl}`);\n return true;\n }\n if (result.status === 401 || result.status === 403) {\n sawUnauthorizedProbe = true;\n }\n } catch {\n // Fall through to unavailable\n }\n\n this.isAvailable = false;\n this.detectedType = null;\n this.lastHealthCheck = now;\n if (sawUnauthorizedProbe) {\n log.warn(\n `local LLM availability probe was unauthorized at ${baseUrl}; verify localLlmApiKey and localLlmAuthHeader settings`,\n );\n }\n log.debug(\"local LLM not available at\", baseUrl);\n return false;\n }\n\n /**\n * Try to get context window from LM Studio settings.json as fallback.\n * This reads the defaultContextLength setting which is what LM Studio uses\n * when loading models without explicit context configuration.\n */\n private getContextFromLmStudioSettings(): number | null {\n try {\n const homeDir = this.resolveHomeDir();\n const settingsPath = `${homeDir}/.cache/lm-studio/settings.json`;\n\n if (!existsSync(settingsPath)) {\n log.debug(`LM Studio settings: file not found at ${settingsPath}`);\n return null;\n }\n\n const content = readFileSync(settingsPath, \"utf-8\");\n const settings = JSON.parse(content) as {\n defaultContextLength?: {\n type?: string;\n value?: number;\n };\n };\n\n if (settings.defaultContextLength?.value) {\n const contextWindow = settings.defaultContextLength.value;\n log.debug(`LM Studio settings: found default context length: ${contextWindow}`);\n return contextWindow;\n }\n\n return null;\n } catch (err) {\n const errorMsg = err instanceof Error ? err.message : String(err);\n log.debug(`LM Studio settings: failed to read - ${errorMsg}`);\n return null;\n }\n }\n\n /**\n * Try to get context window from LMS CLI (LM Studio specific).\n * Uses --json flag for reliable parsing.\n * Returns null if LMS CLI is not available or model not found.\n */\n private getContextFromLmsCli(modelId: string): number | null {\n try {\n // Check if lms CLI exists in common locations.\n // HOME may be absent in launchd environments, so prefer the resolved helper.\n const homeDir = this.resolveHomeDir();\n const lmsPaths = [\n this.config.localLmsCliPath || \"\",\n `${homeDir}/.cache/lm-studio/bin/lms`,\n \"/usr/local/bin/lms\",\n \"/opt/homebrew/bin/lms\",\n ];\n\n const lmsPath = lmsPaths.find((p) => p.length > 0 && existsSync(p));\n if (!lmsPath) {\n log.debug(`LMS CLI: not found in standard locations (checked: ${lmsPaths.join(\", \")})`);\n return null;\n }\n\n // Run lms ps --json to get loaded models with context\n // Use spawnSync with shell and explicit PATH to ensure lms can find its dependencies\n log.debug(`LMS CLI: running: ${lmsPath} ps --json`);\n const existingPath = readEnvVar(\"PATH\") || \"\";\n const result = launchProcessSync(lmsPath, [\"ps\", \"--json\"], {\n encoding: \"utf-8\",\n timeout: 5000,\n shell: false, // Don't use shell for JSON output - more reliable\n env: mergeEnv({\n PATH: `${this.config.localLmsBinDir || `${homeDir}/.cache/lm-studio/bin`}:/usr/local/bin:/opt/homebrew/bin:/usr/bin:/bin:${existingPath}`,\n HOME: homeDir,\n }),\n });\n\n if (result.error) {\n log.debug(`LMS CLI: spawn error - ${result.error.message}`);\n return null;\n }\n\n if (result.stderr && result.stderr.trim()) {\n log.debug(`LMS CLI: stderr - ${result.stderr.slice(0, 200)}`);\n }\n\n const output = result.stdout || \"\";\n if (!output.trim()) {\n log.debug(\"LMS CLI: empty output - LM Studio may not be running or no models loaded\");\n return null;\n }\n\n // Parse JSON output\n let models: Array<{\n identifier?: string;\n modelKey?: string;\n contextLength?: number;\n maxContextLength?: number;\n }>;\n\n try {\n models = JSON.parse(output) as typeof models;\n } catch (parseErr) {\n log.debug(`LMS CLI: JSON parse error - ${parseErr}`);\n return null;\n }\n\n if (!Array.isArray(models) || models.length === 0) {\n log.debug(\"LMS CLI: no models loaded\");\n return null;\n }\n\n // Find the model matching our configured model ID\n const model = models.find((m) =>\n m.identifier === modelId ||\n m.modelKey === modelId ||\n (m.identifier?.includes(modelId.replace(/@\\d+bit$/, \"\")))\n );\n\n if (!model) {\n log.debug(`LMS CLI: model \"${modelId}\" not found in loaded models: ${models.map(m => m.identifier).join(\", \")}`);\n return null;\n }\n\n // Use contextLength (actual configured) or fall back to maxContextLength (model max)\n const contextWindow = model.contextLength || model.maxContextLength;\n\n if (contextWindow) {\n log.info(`LMS CLI detected context window: ${contextWindow} for ${modelId} (max: ${model.maxContextLength})`);\n return contextWindow;\n }\n\n return null;\n } catch (err) {\n // LMS CLI not available or failed\n const errorMsg = err instanceof Error ? err.message : String(err);\n log.debug(`LMS CLI: failed - ${errorMsg}`);\n return null;\n }\n }\n\n /**\n * Get full model info from LMS CLI including context length and max context length.\n * Returns null if LMS CLI is unavailable or model not found.\n */\n private getLmsModelInfo(modelId: string): { contextLength: number; maxContextLength: number; identifier: string } | null {\n try {\n const result = launchProcessSync(\"lms\", [\"ps\", \"--json\"], {\n encoding: \"utf-8\",\n timeout: 5000,\n shell: false,\n });\n\n if (result.error) {\n return null;\n }\n\n const output = result.stdout || \"\";\n if (!output.trim()) {\n return null;\n }\n\n let models: Array<{\n identifier?: string;\n modelKey?: string;\n contextLength?: number;\n maxContextLength?: number;\n }>;\n\n try {\n models = JSON.parse(output) as typeof models;\n } catch {\n return null;\n }\n\n if (!Array.isArray(models) || models.length === 0) {\n return null;\n }\n\n const model = models.find((m) =>\n m.identifier === modelId ||\n m.modelKey === modelId ||\n (m.identifier?.includes(modelId.replace(/@\\d+bit$/, \"\")))\n );\n\n if (!model || !model.contextLength) {\n return null;\n }\n\n return {\n contextLength: model.contextLength,\n maxContextLength: model.maxContextLength || model.contextLength,\n identifier: model.identifier || modelId,\n };\n } catch {\n return null;\n }\n }\n\n /**\n * Get context window for the configured model, using cache if available.\n * This method caches the result to avoid repeated LMS CLI calls.\n * Order: ModelRegistry (persistent) -> memory cache -> LMS CLI -> settings.json\n */\n getCachedContextWindow(modelId: string): number | null {\n const now = Date.now();\n\n // 1. Check ModelRegistry for persisted context window\n if (this.modelRegistry) {\n const caps = this.modelRegistry.getCapabilities(modelId);\n if (caps.source === \"lmstudio\" && caps.contextWindow) {\n log.debug(`ModelRegistry: using persisted LM Studio context: ${caps.contextWindow}`);\n // Also update memory cache\n this.cachedLmsContext = caps.contextWindow;\n this.lastLmsCheck = now;\n return caps.contextWindow;\n }\n }\n\n // 2. Return in-memory cached value if still valid\n if (this.cachedLmsContext && now - this.lastLmsCheck < LocalLlmClient.LMS_CACHE_INTERVAL_MS) {\n log.debug(`LMS CLI: returning in-memory cached context: ${this.cachedLmsContext}`);\n return this.cachedLmsContext;\n }\n\n // 3. Try LMS CLI (authoritative source)\n const lmsInfo = this.getLmsModelInfo(modelId);\n if (lmsInfo?.contextLength) {\n this.cachedLmsContext = lmsInfo.contextLength;\n this.lastLmsCheck = now;\n // Calculate appropriate output tokens based on context size\n // Use 12.5% of context window, capped at 16K (generous but safe)\n const calculatedOutputTokens = Math.min(Math.floor(lmsInfo.contextLength / 8), 16384);\n const outputTokens = Math.max(calculatedOutputTokens, 4096); // Minimum 4K\n // Persist to ModelRegistry with detected capabilities\n if (this.modelRegistry) {\n this.modelRegistry.setCapabilities(modelId, {\n maxPositionEmbeddings: lmsInfo.maxContextLength || lmsInfo.contextLength,\n contextWindow: lmsInfo.contextLength,\n supportsExtendedContext: (lmsInfo.maxContextLength || lmsInfo.contextLength) > 65536,\n typicalOutputTokens: outputTokens,\n source: \"lmstudio\",\n });\n log.info(`LMS CLI: Stored capabilities for ${modelId}: ${lmsInfo.contextLength} context, ${outputTokens} output tokens`);\n }\n return lmsInfo.contextLength;\n }\n\n // Legacy: Try LMS CLI context only (fallback)\n const legacyContext = this.getContextFromLmsCli(modelId);\n if (legacyContext) {\n this.cachedLmsContext = legacyContext;\n this.lastLmsCheck = now;\n // Persist to ModelRegistry with calculated output tokens\n if (this.modelRegistry) {\n const calculatedOutputTokens = Math.min(Math.floor(legacyContext / 8), 16384);\n const outputTokens = Math.max(calculatedOutputTokens, 4096);\n this.modelRegistry.setCapabilities(modelId, {\n maxPositionEmbeddings: legacyContext,\n contextWindow: legacyContext,\n supportsExtendedContext: false,\n typicalOutputTokens: outputTokens,\n source: \"lmstudio\",\n });\n }\n return legacyContext;\n }\n\n // 4. Fall back to LM Studio settings.json\n const settingsContext = this.getContextFromLmStudioSettings();\n if (settingsContext) {\n log.info(`LM Studio settings: using default context: ${settingsContext}`);\n this.cachedLmsContext = settingsContext;\n this.lastLmsCheck = now;\n return settingsContext;\n }\n\n return null;\n }\n\n /**\n * Clear the LMS context cache. Call this when the model changes.\n */\n clearContextCache(): void {\n this.cachedLmsContext = null;\n this.lastLmsCheck = 0;\n log.debug(\"LMS CLI: context cache cleared\");\n }\n\n private remainingCooldownMs(now: number = Date.now()): number {\n return Math.max(0, this.cooldownUntilMs - now);\n }\n\n private scheduleQueueDrain(): void {\n if (this.queueDrainScheduled) return;\n this.queueDrainScheduled = true;\n\n queueMicrotask(() => {\n this.queueDrainScheduled = false;\n this.startAvailableQueuedRequests();\n });\n }\n\n private hasQueuedRequests(): boolean {\n return (\n this.requestQueues[\"recall-critical\"].length > 0 ||\n this.requestQueues.background.length > 0\n );\n }\n\n private dequeueQueuedRequest(priority: LocalLlmRequestPriority): LocalLlmQueuedRequest | null {\n const next = this.requestQueues[priority].shift();\n return next ?? null;\n }\n\n private failOpenQueuedRequestsForCooldown(): number {\n let dropped = 0;\n for (const priority of [\"recall-critical\", \"background\"] as const) {\n while (this.requestQueues[priority].length > 0) {\n const queued = this.requestQueues[priority].shift();\n queued?.resolve(null);\n dropped += 1;\n }\n }\n return dropped;\n }\n\n private startAvailableQueuedRequests(): void {\n if (!this.queueProcessing.has(\"recall-critical\")) {\n const nextCritical = this.dequeueQueuedRequest(\"recall-critical\");\n if (nextCritical) {\n this.queueProcessing.add(\"recall-critical\");\n void this.runQueuedRequest(nextCritical);\n }\n }\n\n if (!this.queueProcessing.has(\"background\")) {\n const nextBackground = this.dequeueQueuedRequest(\"background\");\n if (nextBackground) {\n this.queueProcessing.add(\"background\");\n void this.runQueuedRequest(nextBackground);\n }\n }\n }\n\n private async runQueuedRequest(next: LocalLlmQueuedRequest): Promise<void> {\n try {\n const remainingCooldownMs = this.remainingCooldownMs();\n if (remainingCooldownMs > 0) {\n const additionalDropped = this.failOpenQueuedRequestsForCooldown();\n log.warn(\n `local LLM: cooldown active (${remainingCooldownMs}ms remaining), dropping ${additionalDropped + 1} queued request(s) fail-open`,\n );\n next.resolve(null);\n return;\n }\n\n let result: LocalLlmChatCompletionResult | null = null;\n try {\n result = await this.runChatCompletionRequest(next.messages, next.options, {\n priority: next.priority,\n enqueuedAtMs: next.enqueuedAtMs,\n });\n } catch (err) {\n log.warn(`local LLM queue drain failed open: ${err instanceof Error ? err.message : String(err)}`);\n }\n next.resolve(result);\n } finally {\n this.queueProcessing.delete(next.priority);\n if (this.hasQueuedRequests()) {\n this.scheduleQueueDrain();\n }\n }\n }\n\n private async runChatCompletionRequest(\n messages: Array<{ role: string; content: string }>,\n options: LocalLlmChatCompletionOptions,\n queueMeta?: { priority: LocalLlmRequestPriority; enqueuedAtMs: number },\n ): Promise<LocalLlmChatCompletionResult | null> {\n log.debug(\n `local LLM chatCompletion: localLlmEnabled=${this.config.localLlmEnabled}, model=${this.config.localLlmModel}`,\n );\n\n const operation = options.operation ?? \"unspecified\";\n const startedAtMs = Date.now();\n if (queueMeta) {\n log.debug(\n `local LLM queue start: priority=${queueMeta.priority} waitMs=${startedAtMs - queueMeta.enqueuedAtMs} op=${operation}`,\n );\n }\n\n try {\n const isAvailable = await this.checkAvailability();\n if (!isAvailable) {\n log.debug(\n `local LLM: checkAvailability returned false for ${this.config.localLlmUrl}`,\n );\n return null;\n }\n\n const promptChars = messages.reduce((sum, m) => sum + (m.content?.length ?? 0), 0);\n const requestBody: Record<string, unknown> = {\n model: this.config.localLlmModel,\n messages,\n temperature: options.temperature ?? 0.7,\n // Use max_tokens consistent with cloud models\n max_tokens: options.maxTokens ?? 4096,\n };\n\n // Skip response_format for local LLMs - they don't support json_object type\n // The prompts already instruct the model to output JSON\n // Only send if it's json_schema type which some local LLMs support\n if (options.responseFormat?.type === \"json_schema\") {\n requestBody.response_format = options.responseFormat;\n }\n\n // Suppress thinking/reasoning for thinking-capable models\n // (Qwen 3.5, Gemma 4, DeepSeek). These models default to\n // thinking-on via their chat template; sending\n // `chat_template_kwargs: { enable_thinking: false }` tells the\n // template to skip reasoning tokens.\n //\n // Gate the injection on detected backend support (issue #548,\n // Codex P1 on PR #550): `chat_template_kwargs` is an LM Studio /\n // vLLM / llama.cpp extension, not part of standard OpenAI chat\n // completions. Strict OpenAI-compatible backends reject\n // unknown fields with 400, which trips the 400-cooldown path and\n // can effectively disable local extraction. Fail open when the\n // backend hasn't been positively identified as thinking-capable.\n if (\n this._disableThinking &&\n this.detectedType !== null &&\n THINKING_COMPATIBLE_BACKENDS.has(this.detectedType)\n ) {\n requestBody.chat_template_kwargs = { enable_thinking: false };\n }\n\n // Normalize URL (use 127.0.0.1 instead of localhost)\n const baseUrl = trimTrailingSlashes(\n this.config.localLlmUrl.replace(\"localhost\", \"127.0.0.1\"),\n );\n const chatUrl = baseUrl.endsWith(\"/v1\")\n ? `${baseUrl}/chat/completions`\n : `${baseUrl}/v1/chat/completions`;\n\n const requestBodyJson = JSON.stringify(requestBody);\n log.debug(\n `local LLM: sending request to ${chatUrl} with model ${this.config.localLlmModel}`,\n );\n // Avoid logging request bodies by default (can contain sensitive user content).\n log.debug(`local LLM: request body length=${requestBodyJson.length}`);\n\n // Write request body to file for debugging\n if (this.config.debug) {\n try {\n const { writeFileSync } = await import(\"node:fs\");\n writeFileSync(\"/tmp/engram-last-request.json\", requestBodyJson);\n } catch {\n /* ignore */\n }\n }\n\n const effectiveTimeoutMs =\n typeof options.timeoutMs === \"number\"\n ? Math.min(this.config.localLlmTimeoutMs, options.timeoutMs)\n : this.config.localLlmTimeoutMs;\n const maxAttempts = 1 + Math.max(0, this.config.localLlmRetry5xxCount);\n let response: Response | null = null;\n let lastAbortError: Error | null = null;\n for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {\n const attemptAbort = new AbortController();\n const attemptTimeout = setTimeout(() => attemptAbort.abort(), effectiveTimeoutMs);\n try {\n response = await fetch(chatUrl, {\n method: \"POST\",\n headers: this.buildRequestHeaders({\n \"Content-Type\": \"application/json\",\n }),\n body: JSON.stringify(requestBody),\n signal: attemptAbort.signal,\n });\n } catch (err) {\n if (!this.isAbortError(err)) throw err;\n lastAbortError = err instanceof Error ? err : new Error(String(err));\n if (attempt < maxAttempts) {\n const backoffMs = this.config.localLlmRetryBackoffMs * attempt;\n log.warn(\n `local LLM request aborted: op=${operation} attempt=${attempt}/${maxAttempts} timeoutMs=${effectiveTimeoutMs} model=${this.config.localLlmModel}; retrying after ${backoffMs}ms`,\n );\n await new Promise((resolve) => setTimeout(resolve, backoffMs));\n continue;\n }\n break;\n } finally {\n clearTimeout(attemptTimeout);\n }\n\n if (response.ok) break;\n if (response.status >= 500 && attempt < maxAttempts) {\n try {\n const errorText = await response.clone().text();\n const nonRecoverableReason =\n this.extractNonRecoverableBackendReasonFromErrorText(errorText);\n if (nonRecoverableReason) {\n this.markBackendUnavailable(\n nonRecoverableReason,\n this.config.localLlm400CooldownMs,\n );\n this.consecutive400s = 0;\n return null;\n }\n } catch (e) {\n log.debug(`local LLM failed to inspect retryable error body: ${e}`);\n }\n }\n if (response.status < 500 || attempt >= maxAttempts) break;\n\n const backoffMs = this.config.localLlmRetryBackoffMs * attempt;\n log.warn(\n `local LLM request got ${response.status}; retrying (attempt ${attempt + 1}/${maxAttempts}) after ${backoffMs}ms`,\n );\n await new Promise((resolve) => setTimeout(resolve, backoffMs));\n }\n log.debug(\n `local LLM: received response, status=${response?.status}, ok=${response?.ok}`,\n );\n\n if (!response) {\n if (lastAbortError) {\n log.warn(\n `local LLM request aborted after ${maxAttempts} attempt(s): op=${operation} timeoutMs=${effectiveTimeoutMs} model=${this.config.localLlmModel} promptChars=${promptChars} durationMs=${Date.now() - startedAtMs}`,\n );\n } else {\n log.warn(\n `local LLM request failed: no response object (op=${operation} model=${this.config.localLlmModel} durationMs=${Date.now() - startedAtMs})`,\n );\n }\n return null;\n }\n\n if (!response.ok) {\n let reason = \"\";\n let errorText = \"\";\n try {\n errorText = await response.text();\n // Try to extract a stable error message without logging content.\n try {\n const parsed = JSON.parse(errorText) as { error?: { message?: string } };\n reason = parsed?.error?.message ? ` — ${parsed.error.message}` : \"\";\n } catch {\n // Keep a short preview in debug only.\n log.debug(`local LLM error body: ${errorText.slice(0, 500)}`);\n }\n } catch (e) {\n log.debug(`local LLM failed to read error body: ${e}`);\n }\n log.warn(\n `local LLM request failed: ${response.status} ${response.statusText}${reason} ` +\n `(op=${operation}, model=${this.config.localLlmModel}, url=${chatUrl}, promptChars=${promptChars}, maxTokens=${requestBody.max_tokens as number})`,\n );\n const nonRecoverableReason =\n this.extractNonRecoverableBackendReason(reason) ??\n this.extractNonRecoverableBackendReasonFromErrorText(errorText);\n if (nonRecoverableReason) {\n this.markBackendUnavailable(\n nonRecoverableReason,\n this.config.localLlm400CooldownMs,\n );\n this.consecutive400s = 0;\n return null;\n }\n if (response.status === 400) {\n this.consecutive400s += 1;\n if (this.consecutive400s >= this.config.localLlm400TripThreshold) {\n this.cooldownUntilMs = Date.now() + this.config.localLlm400CooldownMs;\n log.warn(\n `local LLM: entering cooldown for ${this.config.localLlm400CooldownMs}ms ` +\n `after ${this.consecutive400s} consecutive 400 responses`,\n );\n this.consecutive400s = 0;\n }\n } else {\n this.consecutive400s = 0;\n }\n return null;\n }\n this.consecutive400s = 0;\n\n const data = (await response.json()) as {\n choices?: Array<{\n message?: { content?: string; reasoning_content?: string };\n }>;\n usage?: {\n prompt_tokens?: number;\n completion_tokens?: number;\n total_tokens?: number;\n };\n };\n\n log.debug(\n `local LLM response: choices=${data.choices?.length}, usage=${JSON.stringify(data.usage)}`,\n );\n\n // Thinking models (e.g. Qwen 3.5) may put their response in\n // `reasoning_content` and leave `content` empty. Fall back to\n // reasoning_content so engram still gets a usable result.\n const msg = data.choices?.[0]?.message;\n const content = msg?.content || msg?.reasoning_content || \"\";\n if (!content) {\n log.warn(`local LLM returned empty content. choices=${JSON.stringify(data.choices)?.slice(0, 200)}`);\n return null;\n }\n\n // Estimate tokens if not provided by local LLM\n const usage = data.usage\n ? {\n promptTokens: data.usage.prompt_tokens ?? 0,\n completionTokens: data.usage.completion_tokens ?? 0,\n totalTokens: data.usage.total_tokens ?? 0,\n }\n : this.estimateTokens(messages, content);\n\n const durationMs = Date.now() - startedAtMs;\n if (this.config.slowLogEnabled && durationMs >= this.config.slowLogThresholdMs) {\n const promptChars = messages.reduce((sum, m) => sum + (m.content?.length ?? 0), 0);\n const op = options.operation ? ` op=${options.operation}` : \"\";\n log.warn(\n `SLOW local LLM:${op} durationMs=${durationMs} model=${this.config.localLlmModel} url=${chatUrl} promptChars=${promptChars} outputTokens=${usage.completionTokens} totalTokens=${usage.totalTokens}`,\n );\n }\n\n log.debug(\"local LLM: request succeeded, tokens:\", usage.totalTokens);\n return { content, usage };\n } catch (err) {\n const errMsg = err instanceof Error ? err.message : String(err);\n const durationMs = Date.now() - startedAtMs;\n if (this.isAbortError(err)) {\n log.warn(\n `local LLM request aborted: op=${operation} timeoutMs=${options.timeoutMs ?? this.config.localLlmTimeoutMs} model=${this.config.localLlmModel} durationMs=${durationMs} error=${errMsg}`,\n );\n return null;\n }\n log.warn(`local LLM request error: op=${operation} error=${errMsg}`);\n this.isAvailable = false; // Mark as unavailable on non-abort errors\n const nonRecoverableReason = this.extractNonRecoverableBackendReason(errMsg);\n if (nonRecoverableReason) {\n this.markBackendUnavailable(\n nonRecoverableReason,\n this.config.localLlm400CooldownMs,\n );\n }\n return null;\n } finally {\n if (queueMeta) {\n const finishedAtMs = Date.now();\n const waitMs = startedAtMs - queueMeta.enqueuedAtMs;\n log.debug(\n `local LLM queue finish: priority=${queueMeta.priority} waitMs=${waitMs} runMs=${finishedAtMs - startedAtMs} totalMs=${finishedAtMs - queueMeta.enqueuedAtMs} op=${operation}`,\n );\n }\n }\n }\n\n /**\n * Query the local LLM server for loaded model information.\n * Returns null if unavailable or if the model is not found.\n */\n async getLoadedModelInfo(): Promise<LocalModelInfo | null> {\n const baseUrl = trimTrailingSlashes(\n this.config.localLlmUrl.replace(\"localhost\", \"127.0.0.1\"),\n );\n\n // Handle URL construction - localLlmUrl may already include /v1\n const modelsUrl = baseUrl.endsWith(\"/v1\")\n ? `${baseUrl}/models`\n : `${baseUrl}/v1/models`;\n log.debug(`Fetching model info from ${modelsUrl}`);\n\n try {\n const result = await this.fetchWithTimeout(modelsUrl, 3000);\n if (!result.ok) {\n if (result.status === 401 || result.status === 403) {\n log.warn(\n `Local LLM: unauthorized while fetching models from ${modelsUrl}; verify localLlmApiKey and localLlmAuthHeader settings`,\n );\n }\n log.warn(`Local LLM: Failed to fetch models from ${modelsUrl} - server returned error`);\n return null;\n }\n if (!result.data) {\n log.warn(`Local LLM: No data returned from ${modelsUrl}`);\n return null;\n }\n\n const data = result.data as {\n data?: Array<{\n id?: string;\n object?: string;\n owned_by?: string;\n // LM Studio specific fields\n max_context_length?: number;\n max_tokens?: number;\n // Ollama specific\n name?: string;\n details?: {\n parameter_size?: string;\n family?: string;\n };\n }>;\n };\n\n if (!Array.isArray(data.data) || data.data.length === 0) {\n log.warn(\"Local LLM returned no models\");\n return null;\n }\n\n // Verbose model listings are noisy on every gateway restart. Keep it debug-only.\n const modelIds = data.data.map((m) => m.id).filter(Boolean);\n log.debug(\n `Local LLM: Found ${modelIds.length} model(s). First 10: ${modelIds.slice(0, 10).join(\", \")}`,\n );\n\n // Find the model matching our configured model ID\n const configuredModel = this.config.localLlmModel;\n let model = data.data.find((m) => m.id === configuredModel);\n\n // If not found by exact match, try partial match (handle suffixes like @4bit)\n if (!model) {\n model = data.data.find((m) =>\n configuredModel.includes(m.id || \"\") ||\n (m.id || \"\").includes(configuredModel.replace(/@\\d+bit$/, \"\"))\n );\n }\n\n // If still not found, use the first loaded model and warn\n if (!model) {\n model = data.data[0];\n const availablePreview = data.data\n .map((m) => m.id)\n .filter(Boolean)\n .slice(0, 10)\n .join(\", \");\n log.warn(\n `Configured model \"${configuredModel}\" not found in local LLM. ` +\n `Using \"${model.id}\" instead. Available (first 10): ${availablePreview}`\n );\n }\n\n // Extract context window - try multiple field names\n let contextWindow = model.max_context_length || model.max_tokens;\n\n // If API doesn't report context window, try LMS CLI (LM Studio specific)\n if (!contextWindow) {\n log.info(\"Local LLM: API did not report context window, trying LMS CLI...\");\n const lmsContext = this.getCachedContextWindow(model.id || \"\");\n if (lmsContext) {\n contextWindow = lmsContext;\n }\n }\n\n this.cachedModelInfo = {\n id: model.id || \"unknown\",\n contextWindow: contextWindow,\n maxTokens: model.max_tokens,\n };\n\n log.info(\n `Local LLM model detected: ${this.cachedModelInfo.id}, ` +\n `context window: ${contextWindow?.toLocaleString() || \"unknown (may use default)\"}`\n );\n\n return this.cachedModelInfo;\n } catch (err) {\n log.warn(`Failed to fetch model info: ${err}`);\n return null;\n }\n }\n\n /**\n * Check if the configured model is available and get its actual context window.\n * Warns if there's a mismatch between expected and actual context.\n */\n async validateModelConfig(expectedContextWindow?: number): Promise<{\n available: boolean;\n actualContextWindow?: number;\n warnings: string[];\n }> {\n const warnings: string[] = [];\n\n const modelInfo = await this.getLoadedModelInfo();\n if (!modelInfo) {\n return { available: false, warnings: [\"Could not query local LLM for model info\"] };\n }\n\n // If we have expected context and the server reports one, check for mismatch\n if (expectedContextWindow && modelInfo.contextWindow) {\n if (modelInfo.contextWindow < expectedContextWindow) {\n warnings.push(\n `Context window mismatch: Model ${modelInfo.id} supports ${modelInfo.contextWindow.toLocaleString()} tokens, ` +\n `but engram is configured for ${expectedContextWindow.toLocaleString()}. ` +\n `Set localLlmMaxContext: ${modelInfo.contextWindow} in config to avoid errors.`\n );\n }\n }\n\n // Warn if server doesn't report context window (common with some local LLM setups)\n if (!modelInfo.contextWindow) {\n warnings.push(\n `Local LLM server did not report context window for ${modelInfo.id}. ` +\n `If you get \"context length exceeded\" errors, set localLlmMaxContext in config.`\n );\n }\n\n return {\n available: true,\n actualContextWindow: modelInfo.contextWindow,\n warnings,\n };\n }\n\n /**\n * Make a chat completion request to local LLM\n */\n async chatCompletion(\n messages: Array<{ role: string; content: string }>,\n options: LocalLlmChatCompletionOptions = {},\n ): Promise<LocalLlmChatCompletionResult | null> {\n if (!this.config.localLlmEnabled) {\n log.debug(\"local LLM: disabled, returning null\");\n return null;\n }\n\n const remainingMs = this.remainingCooldownMs();\n if (remainingMs > 0) {\n log.debug(`local LLM: cooldown active (${remainingMs}ms remaining), skipping request`);\n return null;\n }\n if (options.priority) {\n const priority = options.priority;\n return await new Promise<LocalLlmChatCompletionResult | null>((resolve) => {\n this.requestQueues[priority].push({\n messages,\n options,\n priority,\n enqueuedAtMs: Date.now(),\n resolve,\n });\n this.scheduleQueueDrain();\n });\n }\n\n return await this.runChatCompletionRequest(messages, options);\n }\n\n /**\n * Estimate tokens when local LLM doesn't return usage stats\n * Rough estimate: 1 token ≈ 4 characters\n */\n private estimateTokens(\n messages: Array<{ role: string; content: string }>,\n response: string\n ): { promptTokens: number; completionTokens: number; totalTokens: number } {\n const promptChars = messages.reduce((sum, m) => sum + m.content.length, 0);\n const promptTokens = Math.ceil(promptChars / 4);\n const completionTokens = Math.ceil(response.length / 4);\n\n return {\n promptTokens,\n completionTokens,\n totalTokens: promptTokens + completionTokens,\n };\n }\n\n /**\n * Try local LLM first, fallback to cloud provider if configured\n */\n async withFallback<T>(\n localOperation: () => Promise<T | null>,\n fallbackOperation: () => Promise<T>,\n operationName: string\n ): Promise<T> {\n // Try local LLM first if enabled\n if (this.config.localLlmEnabled) {\n const localResult = await localOperation();\n if (localResult !== null) {\n log.debug(`${operationName}: used local LLM`);\n return localResult;\n }\n\n // Local failed or unavailable\n if (this.config.localLlmFallback) {\n log.info(`${operationName}: local LLM unavailable, falling back to cloud`);\n } else {\n throw new Error(`${operationName}: local LLM unavailable and fallback disabled`);\n }\n }\n\n // Use fallback (cloud provider)\n return fallbackOperation();\n }\n}\n"],"mappings":"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","names":["backoffMs","promptChars"]}
|