@remnic/core 9.3.613 → 9.3.615
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/access-cli.js +59 -58
- package/dist/access-cli.js.map +1 -1
- package/dist/access-http.d.ts +4 -2
- package/dist/access-http.js +23 -23
- package/dist/access-mcp.d.ts +9 -2
- package/dist/access-mcp.js +20 -20
- package/dist/access-schema.d.ts +26 -14
- package/dist/access-schema.js +3 -3
- package/dist/{access-service-D2J9dh_9.d.ts → access-service-CBNEKjzN.d.ts} +71 -6
- package/dist/access-service.d.ts +2 -2
- package/dist/access-service.js +17 -17
- package/dist/active-recall.js +20 -3
- package/dist/active-recall.js.map +1 -1
- package/dist/adapters/index.js +4 -4
- package/dist/adapters/registry.js +2 -2
- package/dist/behavior-learner.js +2 -3
- package/dist/behavior-learner.js.map +1 -1
- package/dist/bootstrap.d.ts +1 -1
- package/dist/briefing.js +3 -3
- package/dist/buffer.d.ts +1 -1
- package/dist/buffer.js +1 -1
- package/dist/calibration.d.ts +5 -2
- package/dist/calibration.js +7 -5
- package/dist/calibration.js.map +1 -1
- package/dist/{capsule-crypto-7FJQINUR.js → capsule-crypto-YO5QJ6L3.js} +2 -2
- package/dist/causal-consolidation.d.ts +8 -2
- package/dist/causal-consolidation.js +13 -11
- package/dist/causal-consolidation.js.map +1 -1
- package/dist/{chunk-3BP57I6J.js → chunk-2F6NP3NT.js} +2 -1
- package/dist/{chunk-3BP57I6J.js.map → chunk-2F6NP3NT.js.map} +1 -1
- package/dist/{chunk-AU7Q3LSC.js → chunk-2QSZNTDO.js} +4 -4
- package/dist/{chunk-HSVJGWYS.js → chunk-2ROPI5OE.js} +2 -2
- package/dist/{chunk-C4SQJZAF.js → chunk-2SGJY2UY.js} +6 -3
- package/dist/chunk-2SGJY2UY.js.map +1 -0
- package/dist/{chunk-ZDTVJXIP.js → chunk-3MAONBX3.js} +13 -5
- package/dist/chunk-3MAONBX3.js.map +1 -0
- package/dist/{chunk-G3Z3QEF5.js → chunk-3PY7VHV7.js} +2 -2
- package/dist/chunk-3PY7VHV7.js.map +1 -0
- package/dist/{chunk-CF3ZF2YU.js → chunk-3QSU4NFF.js} +3 -3
- package/dist/{chunk-AJA46VX5.js → chunk-3T74IZB3.js} +11 -2
- package/dist/chunk-3T74IZB3.js.map +1 -0
- package/dist/{chunk-KVEVLBKC.js → chunk-4HFJQCJZ.js} +13 -8
- package/dist/chunk-4HFJQCJZ.js.map +1 -0
- package/dist/{chunk-KGK2QKWL.js → chunk-4R4KTDIE.js} +1 -1
- package/dist/chunk-4R4KTDIE.js.map +1 -0
- package/dist/{chunk-OI27U2HT.js → chunk-5BTCT236.js} +2 -2
- package/dist/{chunk-TH67Q46T.js → chunk-5OHHEORR.js} +64 -21
- package/dist/chunk-5OHHEORR.js.map +1 -0
- package/dist/{chunk-CO7ZO4TU.js → chunk-5VDJMYTF.js} +2 -2
- package/dist/{chunk-BFBF3XEF.js → chunk-6BDVBBBY.js} +33 -25
- package/dist/{chunk-BFBF3XEF.js.map → chunk-6BDVBBBY.js.map} +1 -1
- package/dist/{chunk-EAZGEEG2.js → chunk-6L46YAEZ.js} +45 -9
- package/dist/chunk-6L46YAEZ.js.map +1 -0
- package/dist/{chunk-YFS5OEKO.js → chunk-7MLB4NCL.js} +2 -2
- package/dist/{chunk-LZ3VEOU5.js → chunk-AL4RAJL5.js} +22 -5
- package/dist/chunk-AL4RAJL5.js.map +1 -0
- package/dist/{chunk-557IAFPD.js → chunk-APRRL26Q.js} +2 -2
- package/dist/{chunk-QDDHYAKV.js → chunk-AZDOWD2L.js} +2 -2
- package/dist/{chunk-MLT75J5S.js → chunk-B6SU7YSE.js} +3 -3
- package/dist/{chunk-FXKPZ3H6.js → chunk-BPSGLMQ4.js} +2 -2
- package/dist/{chunk-2NLLXCJG.js → chunk-BXLOS5AJ.js} +2 -2
- package/dist/{chunk-NOMEVTUD.js → chunk-C6C7XVKG.js} +5 -4
- package/dist/chunk-C6C7XVKG.js.map +1 -0
- package/dist/{chunk-XKIQZXUB.js → chunk-CI7RKSRE.js} +7 -1
- package/dist/chunk-CI7RKSRE.js.map +1 -0
- package/dist/{chunk-IK34DVAC.js → chunk-CIOMS6DI.js} +2 -2
- package/dist/{chunk-2I5JGH3M.js → chunk-CYEPCZN5.js} +2 -2
- package/dist/{chunk-2I5JGH3M.js.map → chunk-CYEPCZN5.js.map} +1 -1
- package/dist/{chunk-JHMFYY7L.js → chunk-DCGT4FPP.js} +13 -5
- package/dist/chunk-DCGT4FPP.js.map +1 -0
- package/dist/{chunk-7DZRO2DC.js → chunk-DEPRLVLK.js} +2 -2
- package/dist/{chunk-CSKLPDN6.js → chunk-DEVUWMME.js} +52 -19
- package/dist/chunk-DEVUWMME.js.map +1 -0
- package/dist/{chunk-DHGSZ3UD.js → chunk-DGNQRNLL.js} +2 -2
- package/dist/{chunk-X7Y7WX73.js → chunk-DQEMWVMT.js} +1 -1
- package/dist/{chunk-HJNQQICM.js → chunk-EXUAP5LH.js} +108 -51
- package/dist/chunk-EXUAP5LH.js.map +1 -0
- package/dist/chunk-FAV25DUZ.js +12 -0
- package/dist/chunk-FAV25DUZ.js.map +1 -0
- package/dist/{chunk-ETUPBUHB.js → chunk-GDASG7NC.js} +2 -2
- package/dist/{chunk-L227SKTB.js → chunk-GDB4J2H3.js} +17 -1
- package/dist/chunk-GDB4J2H3.js.map +1 -0
- package/dist/{chunk-IP73YCZP.js → chunk-GLPBYIXN.js} +4 -2
- package/dist/chunk-GLPBYIXN.js.map +1 -0
- package/dist/{chunk-4HP7HIE3.js → chunk-HP5FMB6L.js} +2 -2
- package/dist/{chunk-EVZFIAPG.js → chunk-IBTZEBUD.js} +23 -10
- package/dist/chunk-IBTZEBUD.js.map +1 -0
- package/dist/{chunk-DOX2CG6Y.js → chunk-IEUU7O4F.js} +2 -2
- package/dist/{chunk-EUML3N6B.js → chunk-IMA6GU4Y.js} +3 -3
- package/dist/chunk-IMA6GU4Y.js.map +1 -0
- package/dist/{chunk-JNANKJLN.js → chunk-JOASJWQR.js} +2 -2
- package/dist/chunk-JOASJWQR.js.map +1 -0
- package/dist/{chunk-WSGF57U2.js → chunk-JQDZQ4TB.js} +2 -2
- package/dist/{chunk-HINSGUA7.js → chunk-KBL3JJR6.js} +9 -13
- package/dist/chunk-KBL3JJR6.js.map +1 -0
- package/dist/{chunk-IOTENEVL.js → chunk-KGLPJROV.js} +57 -50
- package/dist/chunk-KGLPJROV.js.map +1 -0
- package/dist/{chunk-W7L6HXUC.js → chunk-LXOM6IQU.js} +2 -2
- package/dist/{chunk-G6R5UD3Q.js → chunk-MGN7VHWQ.js} +42 -1
- package/dist/{chunk-G6R5UD3Q.js.map → chunk-MGN7VHWQ.js.map} +1 -1
- package/dist/{chunk-DLJ4IR6M.js → chunk-MHQC2WU2.js} +2 -2
- package/dist/chunk-MHQC2WU2.js.map +1 -0
- package/dist/{chunk-5RPTH6AU.js → chunk-NM5NQYJE.js} +20 -19
- package/dist/chunk-NM5NQYJE.js.map +1 -0
- package/dist/{chunk-6JGNHWCI.js → chunk-OBIRVF36.js} +3 -3
- package/dist/{chunk-CHCA44C3.js → chunk-ODPLEWB6.js} +3 -3
- package/dist/chunk-ODPLEWB6.js.map +1 -0
- package/dist/{chunk-HENLZHIT.js → chunk-OIF36KGD.js} +7 -4
- package/dist/chunk-OIF36KGD.js.map +1 -0
- package/dist/{chunk-GUPISBV2.js → chunk-PP2JH3GP.js} +2 -2
- package/dist/{chunk-OXJBNGBK.js → chunk-PSUB67YB.js} +2 -2
- package/dist/{chunk-UWY7GIVS.js → chunk-PYIFUBRK.js} +45 -13
- package/dist/chunk-PYIFUBRK.js.map +1 -0
- package/dist/{chunk-KIB7SDIJ.js → chunk-Q6YIJGXJ.js} +2 -2
- package/dist/{chunk-ZT3EGNLR.js → chunk-QPD426WT.js} +2 -2
- package/dist/{chunk-RLV3PQGH.js → chunk-QVO4YOB7.js} +6 -6
- package/dist/{chunk-GMAG2HS4.js → chunk-RG3LBSGH.js} +46 -9
- package/dist/chunk-RG3LBSGH.js.map +1 -0
- package/dist/{chunk-XSWKORGM.js → chunk-S53OYO3F.js} +3 -1
- package/dist/chunk-S53OYO3F.js.map +1 -0
- package/dist/{chunk-YCN4BVDK.js → chunk-SCPFRKIT.js} +4 -2
- package/dist/chunk-SCPFRKIT.js.map +1 -0
- package/dist/{chunk-NZPF2SYV.js → chunk-T7N6KQGS.js} +138 -5
- package/dist/chunk-T7N6KQGS.js.map +1 -0
- package/dist/{chunk-VJXSUAO7.js → chunk-TNOWU6RP.js} +13 -10
- package/dist/chunk-TNOWU6RP.js.map +1 -0
- package/dist/{chunk-PCI747N2.js → chunk-TZVQQTG4.js} +48 -19
- package/dist/chunk-TZVQQTG4.js.map +1 -0
- package/dist/{chunk-KQAFEZQX.js → chunk-VDX2J7OX.js} +2 -2
- package/dist/{chunk-IK7DCC5H.js → chunk-VMGLYN42.js} +2 -2
- package/dist/{chunk-KM2A35EO.js → chunk-WB3LYXC5.js} +11 -7
- package/dist/chunk-WB3LYXC5.js.map +1 -0
- package/dist/{chunk-PPPZY2EU.js → chunk-WD2W4234.js} +9 -3
- package/dist/chunk-WD2W4234.js.map +1 -0
- package/dist/{chunk-NSKYFGDL.js → chunk-X4QQB7O6.js} +2 -2
- package/dist/{chunk-HPWVAEET.js → chunk-X6IRLNOO.js} +3 -7
- package/dist/chunk-X6IRLNOO.js.map +1 -0
- package/dist/{chunk-46GJIW5M.js → chunk-XAZOWLW4.js} +5 -5
- package/dist/{chunk-46GJIW5M.js.map → chunk-XAZOWLW4.js.map} +1 -1
- package/dist/{chunk-XPSVGJYA.js → chunk-YRMKDTKF.js} +12 -9
- package/dist/chunk-YRMKDTKF.js.map +1 -0
- package/dist/{chunk-6ZZP4EJF.js → chunk-ZJR7VG5L.js} +3 -3
- package/dist/{chunk-6ZZP4EJF.js.map → chunk-ZJR7VG5L.js.map} +1 -1
- package/dist/{chunk-2QANQKSQ.js → chunk-ZK32E74R.js} +156 -45
- package/dist/chunk-ZK32E74R.js.map +1 -0
- package/dist/{cli-OrfKXNU4.d.ts → cli-Cw729yLf.d.ts} +6 -2
- package/dist/cli.d.ts +3 -3
- package/dist/cli.js +61 -60
- package/dist/compounding/engine.js +3 -3
- package/dist/compounding/preference-consolidator.js +39 -11
- package/dist/compounding/preference-consolidator.js.map +1 -1
- package/dist/config.js +1 -1
- package/dist/connectors/codex-materialize-runner.js +3 -3
- package/dist/connectors/index.js +3 -3
- package/dist/consolidation-provenance-check.js +1 -1
- package/dist/contradiction/index.js +4 -4
- package/dist/conversation-index/backend.js +2 -2
- package/dist/conversation-index/indexer.js +1 -1
- package/dist/cross-namespace-budget.js +1 -1
- package/dist/enrichment/index.js +1 -1
- package/dist/entity-retrieval.js +3 -3
- package/dist/evals.js +1 -1
- package/dist/explicit-capture.d.ts +11 -1
- package/dist/explicit-capture.js +1 -1
- package/dist/extraction-judge.js +8 -1
- package/dist/extraction.js +2 -2
- package/dist/fallback-llm.d.ts +23 -6
- package/dist/fallback-llm.js +5 -3
- package/dist/{first-start-migration-GYJWIH36.js → first-start-migration-FF7YFGRP.js} +6 -6
- package/dist/index.d.ts +3 -3
- package/dist/index.js +95 -94
- package/dist/index.js.map +1 -1
- package/dist/lcm/archive.js +2 -2
- package/dist/lcm/engine.js +5 -5
- package/dist/lcm/index.js +7 -7
- package/dist/lcm/summarizer.js +3 -3
- package/dist/maintenance/memory-governance-cron.d.ts +6 -4
- package/dist/maintenance/memory-governance-cron.js +1 -1
- package/dist/maintenance/memory-governance.js +3 -3
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +3 -3
- package/dist/maintenance/rebuild-memory-projection.js +4 -4
- package/dist/mcp-memory-inspector-app.d.ts +2 -2
- package/dist/mcp-memory-inspector-app.js +1 -1
- package/dist/migrate/from-engram.js +1 -1
- package/dist/namespaces/migrate.js +16 -15
- package/dist/namespaces/search.js +12 -11
- package/dist/namespaces/storage.js +3 -3
- package/dist/network/webdav.d.ts +2 -0
- package/dist/network/webdav.js +1 -1
- package/dist/objective-state-writers.js +2 -2
- package/dist/operator-toolkit.d.ts +3 -1
- package/dist/operator-toolkit.js +21 -20
- package/dist/{orchestrator-DTRQG75J.d.ts → orchestrator-CqWOjfgl.d.ts} +46 -3
- package/dist/orchestrator.d.ts +1 -1
- package/dist/orchestrator.js +48 -45
- package/dist/patterns-cli.js +1 -1
- package/dist/qmd-recall-cache.d.ts +2 -0
- package/dist/qmd-recall-cache.js +1 -1
- package/dist/qmd.d.ts +37 -2
- package/dist/qmd.js +4 -1
- package/dist/recall-explain-renderer.js +3 -3
- package/dist/recall-planner-llm.d.ts +57 -0
- package/dist/recall-planner-llm.js +167 -0
- package/dist/recall-planner-llm.js.map +1 -0
- package/dist/recall-xray-cli.js +4 -4
- package/dist/recall-xray-renderer.js +3 -3
- package/dist/recall-xray.js +2 -2
- package/dist/resume-bundles.js +2 -2
- package/dist/retrieval-agents.js +2 -2
- package/dist/routing/store.js +1 -1
- package/dist/schemas.d.ts +22 -22
- package/dist/search/factory.js +11 -10
- package/dist/search/index.js +11 -10
- package/dist/search/lancedb-backend.d.ts +1 -1
- package/dist/search/lancedb-backend.js +3 -2
- package/dist/search/meilisearch-backend.d.ts +1 -1
- package/dist/search/meilisearch-backend.js +3 -2
- package/dist/search/noop-backend.d.ts +1 -1
- package/dist/search/noop-backend.js +1 -1
- package/dist/search/orama-backend.d.ts +1 -1
- package/dist/search/orama-backend.js +3 -2
- package/dist/search/port.d.ts +6 -1
- package/dist/search/port.js +7 -0
- package/dist/search/remote-backend.d.ts +1 -1
- package/dist/search/remote-backend.js +1 -1
- package/dist/semantic-consolidation.js +4 -4
- package/dist/semantic-rule-promotion.js +3 -3
- package/dist/semantic-rule-verifier.js +3 -3
- package/dist/session-observer-state.js +1 -1
- package/dist/storage.js +2 -2
- package/dist/summarizer.js +2 -2
- package/dist/temporal-index.js +1 -1
- package/dist/{tier-stats-SKML2OSF.js → tier-stats-3LYQ3VV5.js} +3 -3
- package/dist/transfer/backup.js +2 -2
- package/dist/transfer/capsule-export.js +2 -2
- package/dist/transfer/capsule-import.js +2 -2
- package/dist/transfer/export-sqlite.js +1 -1
- package/dist/transfer/types.d.ts +12 -12
- package/dist/types.d.ts +32 -0
- package/dist/types.js +1 -1
- package/dist/utility-learner.js +1 -1
- package/dist/utility-runtime.js +2 -2
- package/dist/verified-recall.js +3 -3
- package/dist/work/board.js +2 -2
- package/dist/work/storage.d.ts +2 -0
- package/dist/work/storage.js +1 -1
- package/package.json +1 -1
- package/src/access-http.ts +24 -10
- package/src/access-mcp.test.ts +160 -0
- package/src/access-mcp.ts +72 -7
- package/src/access-schema.ts +11 -0
- package/src/access-service-coding-write.test.ts +478 -0
- package/src/access-service.ts +237 -32
- package/src/active-recall.test.ts +40 -0
- package/src/active-recall.ts +19 -2
- package/src/behavior-learner.ts +5 -3
- package/src/buffer-session.test.ts +58 -0
- package/src/buffer-surprise-trigger.test.ts +4 -18
- package/src/buffer.ts +39 -11
- package/src/calibration.ts +10 -4
- package/src/causal-consolidation.test.ts +47 -2
- package/src/causal-consolidation.ts +13 -9
- package/src/cli.ts +19 -4
- package/src/compounding/engine.ts +2 -0
- package/src/compounding/preference-consolidator.test.ts +292 -0
- package/src/compounding/preference-consolidator.ts +55 -19
- package/src/config.test.ts +213 -0
- package/src/config.ts +175 -4
- package/src/connectors/codex-materialize-runner.ts +7 -4
- package/src/consolidation-provenance-check.ts +24 -5
- package/src/conversation-index/indexer.test.ts +22 -0
- package/src/conversation-index/indexer.ts +7 -3
- package/src/cross-namespace-budget.test.ts +44 -21
- package/src/cross-namespace-budget.ts +2 -2
- package/src/enrichment/pipeline.ts +11 -16
- package/src/evals.ts +1 -1
- package/src/explicit-capture.ts +19 -2
- package/src/extraction-judge-chain.test.ts +55 -0
- package/src/extraction-judge.ts +7 -9
- package/src/extraction.ts +16 -5
- package/src/fallback-llm.test.ts +600 -1
- package/src/fallback-llm.ts +91 -22
- package/src/maintenance/memory-governance-cron.ts +39 -29
- package/src/mcp-memory-inspector-app.ts +54 -12
- package/src/message-parts/index.ts +6 -0
- package/src/message-parts/message-parts.test.ts +30 -0
- package/src/migrate/from-engram.ts +19 -5
- package/src/namespaces/search.test.ts +15 -2
- package/src/namespaces/search.ts +1 -1
- package/src/network/webdav.ts +61 -21
- package/src/operator-toolkit.ts +6 -2
- package/src/orchestrator.ts +173 -20
- package/src/qmd-client.test.ts +85 -0
- package/src/qmd-recall-cache.test.ts +16 -0
- package/src/qmd-recall-cache.ts +7 -0
- package/src/qmd.test.ts +54 -0
- package/src/qmd.ts +119 -19
- package/src/recall-planner-llm.test.ts +224 -0
- package/src/recall-planner-llm.ts +289 -0
- package/src/routing/store.ts +4 -8
- package/src/search/factory.ts +3 -0
- package/src/search/lancedb-backend.ts +15 -3
- package/src/search/meilisearch-backend.ts +70 -7
- package/src/search/noop-backend.ts +5 -1
- package/src/search/orama-backend.ts +15 -3
- package/src/search/port.ts +15 -0
- package/src/search/remote-backend.ts +5 -1
- package/src/session-observer-state.ts +1 -1
- package/src/summarizer.ts +3 -3
- package/src/temporal-index.test.ts +18 -0
- package/src/temporal-index.ts +45 -0
- package/src/training-export/cli-date-validation.test.ts +36 -0
- package/src/training-export/date-parse.ts +21 -2
- package/src/transfer/export-sqlite.ts +3 -0
- package/src/types.ts +35 -0
- package/src/utility-learner.ts +1 -0
- package/src/work/storage.ts +23 -0
- package/dist/chunk-2QANQKSQ.js.map +0 -1
- package/dist/chunk-5RPTH6AU.js.map +0 -1
- package/dist/chunk-AJA46VX5.js.map +0 -1
- package/dist/chunk-C4SQJZAF.js.map +0 -1
- package/dist/chunk-CHCA44C3.js.map +0 -1
- package/dist/chunk-CSKLPDN6.js.map +0 -1
- package/dist/chunk-DLJ4IR6M.js.map +0 -1
- package/dist/chunk-EAZGEEG2.js.map +0 -1
- package/dist/chunk-EUML3N6B.js.map +0 -1
- package/dist/chunk-EVZFIAPG.js.map +0 -1
- package/dist/chunk-G3Z3QEF5.js.map +0 -1
- package/dist/chunk-GMAG2HS4.js.map +0 -1
- package/dist/chunk-HENLZHIT.js.map +0 -1
- package/dist/chunk-HINSGUA7.js.map +0 -1
- package/dist/chunk-HJNQQICM.js.map +0 -1
- package/dist/chunk-HPWVAEET.js.map +0 -1
- package/dist/chunk-IOTENEVL.js.map +0 -1
- package/dist/chunk-IP73YCZP.js.map +0 -1
- package/dist/chunk-JHMFYY7L.js.map +0 -1
- package/dist/chunk-JNANKJLN.js.map +0 -1
- package/dist/chunk-KGK2QKWL.js.map +0 -1
- package/dist/chunk-KM2A35EO.js.map +0 -1
- package/dist/chunk-KVEVLBKC.js.map +0 -1
- package/dist/chunk-L227SKTB.js.map +0 -1
- package/dist/chunk-LZ3VEOU5.js.map +0 -1
- package/dist/chunk-NOMEVTUD.js.map +0 -1
- package/dist/chunk-NZPF2SYV.js.map +0 -1
- package/dist/chunk-PCI747N2.js.map +0 -1
- package/dist/chunk-PPPZY2EU.js.map +0 -1
- package/dist/chunk-TH67Q46T.js.map +0 -1
- package/dist/chunk-UWY7GIVS.js.map +0 -1
- package/dist/chunk-VJXSUAO7.js.map +0 -1
- package/dist/chunk-XKIQZXUB.js.map +0 -1
- package/dist/chunk-XPSVGJYA.js.map +0 -1
- package/dist/chunk-XSWKORGM.js.map +0 -1
- package/dist/chunk-YCN4BVDK.js.map +0 -1
- package/dist/chunk-ZDTVJXIP.js.map +0 -1
- /package/dist/{capsule-crypto-7FJQINUR.js.map → capsule-crypto-YO5QJ6L3.js.map} +0 -0
- /package/dist/{chunk-AU7Q3LSC.js.map → chunk-2QSZNTDO.js.map} +0 -0
- /package/dist/{chunk-HSVJGWYS.js.map → chunk-2ROPI5OE.js.map} +0 -0
- /package/dist/{chunk-CF3ZF2YU.js.map → chunk-3QSU4NFF.js.map} +0 -0
- /package/dist/{chunk-OI27U2HT.js.map → chunk-5BTCT236.js.map} +0 -0
- /package/dist/{chunk-CO7ZO4TU.js.map → chunk-5VDJMYTF.js.map} +0 -0
- /package/dist/{chunk-YFS5OEKO.js.map → chunk-7MLB4NCL.js.map} +0 -0
- /package/dist/{chunk-557IAFPD.js.map → chunk-APRRL26Q.js.map} +0 -0
- /package/dist/{chunk-QDDHYAKV.js.map → chunk-AZDOWD2L.js.map} +0 -0
- /package/dist/{chunk-MLT75J5S.js.map → chunk-B6SU7YSE.js.map} +0 -0
- /package/dist/{chunk-FXKPZ3H6.js.map → chunk-BPSGLMQ4.js.map} +0 -0
- /package/dist/{chunk-2NLLXCJG.js.map → chunk-BXLOS5AJ.js.map} +0 -0
- /package/dist/{chunk-IK34DVAC.js.map → chunk-CIOMS6DI.js.map} +0 -0
- /package/dist/{chunk-7DZRO2DC.js.map → chunk-DEPRLVLK.js.map} +0 -0
- /package/dist/{chunk-DHGSZ3UD.js.map → chunk-DGNQRNLL.js.map} +0 -0
- /package/dist/{chunk-X7Y7WX73.js.map → chunk-DQEMWVMT.js.map} +0 -0
- /package/dist/{chunk-ETUPBUHB.js.map → chunk-GDASG7NC.js.map} +0 -0
- /package/dist/{chunk-4HP7HIE3.js.map → chunk-HP5FMB6L.js.map} +0 -0
- /package/dist/{chunk-DOX2CG6Y.js.map → chunk-IEUU7O4F.js.map} +0 -0
- /package/dist/{chunk-WSGF57U2.js.map → chunk-JQDZQ4TB.js.map} +0 -0
- /package/dist/{chunk-W7L6HXUC.js.map → chunk-LXOM6IQU.js.map} +0 -0
- /package/dist/{chunk-6JGNHWCI.js.map → chunk-OBIRVF36.js.map} +0 -0
- /package/dist/{chunk-GUPISBV2.js.map → chunk-PP2JH3GP.js.map} +0 -0
- /package/dist/{chunk-OXJBNGBK.js.map → chunk-PSUB67YB.js.map} +0 -0
- /package/dist/{chunk-KIB7SDIJ.js.map → chunk-Q6YIJGXJ.js.map} +0 -0
- /package/dist/{chunk-ZT3EGNLR.js.map → chunk-QPD426WT.js.map} +0 -0
- /package/dist/{chunk-RLV3PQGH.js.map → chunk-QVO4YOB7.js.map} +0 -0
- /package/dist/{chunk-KQAFEZQX.js.map → chunk-VDX2J7OX.js.map} +0 -0
- /package/dist/{chunk-IK7DCC5H.js.map → chunk-VMGLYN42.js.map} +0 -0
- /package/dist/{chunk-NSKYFGDL.js.map → chunk-X4QQB7O6.js.map} +0 -0
- /package/dist/{first-start-migration-GYJWIH36.js.map → first-start-migration-FF7YFGRP.js.map} +0 -0
- /package/dist/{tier-stats-SKML2OSF.js.map → tier-stats-3LYQ3VV5.js.map} +0 -0
|
@@ -30,6 +30,298 @@ test("consolidatePreferences preserves never-use facts as negative preferences",
|
|
|
30
30
|
);
|
|
31
31
|
});
|
|
32
32
|
|
|
33
|
+
test("consolidatePreferences preserves like facts as positive preferences", () => {
|
|
34
|
+
const result = consolidatePreferences([
|
|
35
|
+
memory("The user likes React for dashboards."),
|
|
36
|
+
]);
|
|
37
|
+
|
|
38
|
+
assert.equal(result.preferences.length, 1);
|
|
39
|
+
assert.equal(
|
|
40
|
+
result.preferences[0]?.statement,
|
|
41
|
+
"The user prefers React for dashboards",
|
|
42
|
+
);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
test("consolidatePreferences preserves like-to facts as infinitive preferences", () => {
|
|
46
|
+
const result = consolidatePreferences([
|
|
47
|
+
memory("The user would like to use React for dashboards."),
|
|
48
|
+
]);
|
|
49
|
+
|
|
50
|
+
assert.equal(result.preferences.length, 1);
|
|
51
|
+
assert.equal(
|
|
52
|
+
result.preferences[0]?.statement,
|
|
53
|
+
"The user prefers to use React for dashboards",
|
|
54
|
+
);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test("consolidatePreferences does not join separate like-to follow-up sentences", () => {
|
|
58
|
+
const result = consolidatePreferences([
|
|
59
|
+
memory("The user would like to use React. They moved to Svelte."),
|
|
60
|
+
]);
|
|
61
|
+
|
|
62
|
+
assert.equal(result.preferences.length, 1);
|
|
63
|
+
assert.equal(result.preferences[0]?.statement, "The user prefers to use React");
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test("consolidatePreferences preserves love facts as positive preferences", () => {
|
|
67
|
+
const result = consolidatePreferences([
|
|
68
|
+
memory("The user loves Svelte for prototypes."),
|
|
69
|
+
]);
|
|
70
|
+
|
|
71
|
+
assert.equal(result.preferences.length, 1);
|
|
72
|
+
assert.equal(
|
|
73
|
+
result.preferences[0]?.statement,
|
|
74
|
+
"The user prefers Svelte for prototypes",
|
|
75
|
+
);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test("consolidatePreferences preserves does-not-love facts as negative preferences", () => {
|
|
79
|
+
const result = consolidatePreferences([
|
|
80
|
+
memory("The user does not love React for dashboards."),
|
|
81
|
+
]);
|
|
82
|
+
|
|
83
|
+
assert.equal(result.preferences.length, 1);
|
|
84
|
+
assert.equal(
|
|
85
|
+
result.preferences[0]?.statement,
|
|
86
|
+
"The user would not prefer React for dashboards",
|
|
87
|
+
);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
test("consolidatePreferences preserves did-not-love facts as negative preferences", () => {
|
|
91
|
+
const result = consolidatePreferences([
|
|
92
|
+
memory("The user did not love React for dashboards."),
|
|
93
|
+
]);
|
|
94
|
+
|
|
95
|
+
assert.equal(result.preferences.length, 1);
|
|
96
|
+
assert.equal(
|
|
97
|
+
result.preferences[0]?.statement,
|
|
98
|
+
"The user would not prefer React for dashboards",
|
|
99
|
+
);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test("consolidatePreferences preserves would-not-love facts as negative preferences", () => {
|
|
103
|
+
const result = consolidatePreferences([
|
|
104
|
+
memory("The user would not love React for dashboards."),
|
|
105
|
+
]);
|
|
106
|
+
|
|
107
|
+
assert.equal(result.preferences.length, 1);
|
|
108
|
+
assert.equal(
|
|
109
|
+
result.preferences[0]?.statement,
|
|
110
|
+
"The user would not prefer React for dashboards",
|
|
111
|
+
);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
test("consolidatePreferences preserves would-not-like-to facts as negative preferences", () => {
|
|
115
|
+
const result = consolidatePreferences([
|
|
116
|
+
memory("The user would not like to use React for dashboards."),
|
|
117
|
+
]);
|
|
118
|
+
|
|
119
|
+
assert.equal(result.preferences.length, 1);
|
|
120
|
+
assert.equal(
|
|
121
|
+
result.preferences[0]?.statement,
|
|
122
|
+
"The user would not prefer to use React for dashboards",
|
|
123
|
+
);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
test("consolidatePreferences preserves no-longer-loves facts as negative preferences", () => {
|
|
127
|
+
const result = consolidatePreferences([
|
|
128
|
+
memory("The user no longer loves React."),
|
|
129
|
+
]);
|
|
130
|
+
|
|
131
|
+
assert.equal(result.preferences.length, 1);
|
|
132
|
+
assert.equal(result.preferences[0]?.statement, "The user would not prefer React");
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
test("consolidatePreferences preserves disliked facts as negative preferences", () => {
|
|
136
|
+
const result = consolidatePreferences([
|
|
137
|
+
memory("The user disliked React for dashboards."),
|
|
138
|
+
]);
|
|
139
|
+
|
|
140
|
+
assert.equal(result.preferences.length, 1);
|
|
141
|
+
assert.equal(
|
|
142
|
+
result.preferences[0]?.statement,
|
|
143
|
+
"The user would not prefer React for dashboards",
|
|
144
|
+
);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
test("consolidatePreferences ignores double-negated dislike facts", () => {
|
|
148
|
+
const result = consolidatePreferences([
|
|
149
|
+
memory("The user does not dislike React."),
|
|
150
|
+
]);
|
|
151
|
+
|
|
152
|
+
assert.equal(result.preferences.length, 0);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
test("consolidatePreferences ignores repeated double-negated dislike facts", () => {
|
|
156
|
+
const result = consolidatePreferences([
|
|
157
|
+
memory("The user does not dislike React and does not dislike Svelte."),
|
|
158
|
+
]);
|
|
159
|
+
|
|
160
|
+
assert.equal(result.preferences.length, 0);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
test("consolidatePreferences preserves explicit preferences after double-negated aversion", () => {
|
|
164
|
+
const result = consolidatePreferences([
|
|
165
|
+
memory("The user does not dislike React, but prefers Svelte for dashboards."),
|
|
166
|
+
]);
|
|
167
|
+
|
|
168
|
+
assert.equal(result.preferences.length, 1);
|
|
169
|
+
assert.equal(
|
|
170
|
+
result.preferences[0]?.statement,
|
|
171
|
+
"The user prefers Svelte for dashboards",
|
|
172
|
+
);
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
test("consolidatePreferences preserves replacement love clauses after withdrawn preferences", () => {
|
|
176
|
+
const result = consolidatePreferences([
|
|
177
|
+
memory("The user did not love React but now loves Svelte for dashboards."),
|
|
178
|
+
]);
|
|
179
|
+
|
|
180
|
+
assert.equal(result.preferences.length, 1);
|
|
181
|
+
assert.equal(
|
|
182
|
+
result.preferences[0]?.statement,
|
|
183
|
+
"The user prefers Svelte for dashboards",
|
|
184
|
+
);
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
test("consolidatePreferences preserves short replacement love clauses after withdrawn preferences", () => {
|
|
188
|
+
const result = consolidatePreferences([
|
|
189
|
+
memory("The user did not love React but now loves Go."),
|
|
190
|
+
]);
|
|
191
|
+
|
|
192
|
+
assert.equal(result.preferences.length, 1);
|
|
193
|
+
assert.equal(result.preferences[0]?.statement, "The user prefers Go");
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
test("consolidatePreferences preserves and-now replacement clauses after withdrawn preferences", () => {
|
|
197
|
+
const result = consolidatePreferences([
|
|
198
|
+
memory("The user did not love React, and now loves Svelte for dashboards."),
|
|
199
|
+
]);
|
|
200
|
+
|
|
201
|
+
assert.equal(result.preferences.length, 1);
|
|
202
|
+
assert.equal(
|
|
203
|
+
result.preferences[0]?.statement,
|
|
204
|
+
"The user prefers Svelte for dashboards",
|
|
205
|
+
);
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
test("consolidatePreferences preserves plain-and replacement clauses after withdrawn preferences", () => {
|
|
209
|
+
const result = consolidatePreferences([
|
|
210
|
+
memory("The user did not love React and loves Svelte for dashboards."),
|
|
211
|
+
]);
|
|
212
|
+
|
|
213
|
+
assert.equal(result.preferences.length, 1);
|
|
214
|
+
assert.equal(
|
|
215
|
+
result.preferences[0]?.statement,
|
|
216
|
+
"The user prefers Svelte for dashboards",
|
|
217
|
+
);
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
test("consolidatePreferences keeps compound negated-use clauses negative", () => {
|
|
221
|
+
const result = consolidatePreferences([
|
|
222
|
+
memory("The user does not use React and uses Svelte for dashboards."),
|
|
223
|
+
]);
|
|
224
|
+
|
|
225
|
+
assert.equal(result.preferences.length, 1);
|
|
226
|
+
assert.equal(result.preferences[0]?.statement, "The user would not prefer React");
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
test("consolidatePreferences preserves replacement like-to clauses after withdrawn preferences", () => {
|
|
230
|
+
const result = consolidatePreferences([
|
|
231
|
+
memory("The user did not love React but like to use Svelte for dashboards."),
|
|
232
|
+
]);
|
|
233
|
+
|
|
234
|
+
assert.equal(result.preferences.length, 1);
|
|
235
|
+
assert.equal(
|
|
236
|
+
result.preferences[0]?.statement,
|
|
237
|
+
"The user prefers to use Svelte for dashboards",
|
|
238
|
+
);
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
test("consolidatePreferences preserves replacement enjoying clauses after withdrawn preferences", () => {
|
|
242
|
+
const result = consolidatePreferences([
|
|
243
|
+
memory("The user did not love React but enjoying Svelte dashboard work."),
|
|
244
|
+
]);
|
|
245
|
+
|
|
246
|
+
assert.equal(result.preferences.length, 1);
|
|
247
|
+
assert.equal(
|
|
248
|
+
result.preferences[0]?.statement,
|
|
249
|
+
"The user prefers: enjoying Svelte dashboard work",
|
|
250
|
+
);
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
test("consolidatePreferences preserves replacement use clauses after withdrawn preferences", () => {
|
|
254
|
+
const result = consolidatePreferences([
|
|
255
|
+
memory("The user did not love React but now uses Svelte for dashboards."),
|
|
256
|
+
]);
|
|
257
|
+
|
|
258
|
+
assert.equal(result.preferences.length, 1);
|
|
259
|
+
assert.equal(
|
|
260
|
+
result.preferences[0]?.statement,
|
|
261
|
+
"The user prefers to use Svelte for dashboards",
|
|
262
|
+
);
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
test("consolidatePreferences still admits preference stem variants in facts", () => {
|
|
266
|
+
const result = consolidatePreferences([
|
|
267
|
+
memory("The user is enjoying Svelte specialization work."),
|
|
268
|
+
]);
|
|
269
|
+
|
|
270
|
+
assert.equal(result.preferences.length, 1);
|
|
271
|
+
assert.equal(
|
|
272
|
+
result.preferences[0]?.statement,
|
|
273
|
+
"The user is enjoying Svelte specialization work",
|
|
274
|
+
);
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
test("consolidatePreferences ignores like analogies in facts", () => {
|
|
278
|
+
const result = consolidatePreferences([
|
|
279
|
+
memory("The user said Next.js is like React."),
|
|
280
|
+
]);
|
|
281
|
+
|
|
282
|
+
assert.equal(result.preferences.length, 0);
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
test("consolidatePreferences ignores stale past-tense liked facts", () => {
|
|
286
|
+
const result = consolidatePreferences([
|
|
287
|
+
memory("The user liked Angular before switching to React."),
|
|
288
|
+
]);
|
|
289
|
+
|
|
290
|
+
assert.equal(result.preferences.length, 0);
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
test("consolidatePreferences preserves multiline use-for facts", () => {
|
|
294
|
+
const result = consolidatePreferences([
|
|
295
|
+
memory("The user uses React\nfor dashboards."),
|
|
296
|
+
]);
|
|
297
|
+
|
|
298
|
+
assert.equal(result.preferences.length, 1);
|
|
299
|
+
assert.equal(
|
|
300
|
+
result.preferences[0]?.statement,
|
|
301
|
+
"The user prefers to use React for dashboards",
|
|
302
|
+
);
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
test("consolidatePreferences does not join separate use and movement sentences", () => {
|
|
306
|
+
const result = consolidatePreferences([
|
|
307
|
+
memory("The user uses React.\nThey moved to Svelte."),
|
|
308
|
+
]);
|
|
309
|
+
|
|
310
|
+
assert.equal(result.preferences.length, 0);
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
test("consolidatePreferences preserves a first use clause before later sentences", () => {
|
|
314
|
+
const result = consolidatePreferences([
|
|
315
|
+
memory("The user uses React for dashboards.\nThey moved to Svelte."),
|
|
316
|
+
]);
|
|
317
|
+
|
|
318
|
+
assert.equal(result.preferences.length, 1);
|
|
319
|
+
assert.equal(
|
|
320
|
+
result.preferences[0]?.statement,
|
|
321
|
+
"The user prefers to use React for dashboards",
|
|
322
|
+
);
|
|
323
|
+
});
|
|
324
|
+
|
|
33
325
|
test("consolidatePreferences preserves does-not-use facts as negative preferences", () => {
|
|
34
326
|
const result = consolidatePreferences([
|
|
35
327
|
memory("The user does not use React for dashboards."),
|
|
@@ -48,15 +48,24 @@ const PREFERENCE_EXTRACTORS: Array<{
|
|
|
48
48
|
}> = [
|
|
49
49
|
// "Avoids X" / negated use/work/code statements → negative preference
|
|
50
50
|
{
|
|
51
|
-
pattern: /(?:avoids?|dislikes?|hates?|does\s+not\s+like|doesn'?t\s+like|never\s+uses?|does\s+not\s+uses?|doesn'?t\s+uses?|does\s+not\s+use|doesn'?t\s+use|never\s+works?\s+(?:with|in)|does\s+not\s+works?\s+(?:with|in)|doesn'?t\s+works?\s+(?:with|in)|never\s+codes?\s+(?:in|with)|does\s+not\s+codes?\s+(?:in|with)|doesn'?t\s+codes?\s+(?:in|with)|refuses\s+to\s+(?:use|work\s+(?:with|in)|code\s+(?:in|with)))\s+(
|
|
51
|
+
pattern: /(?:avoids?|avoided|dislikes?|disliked|hates?|hated|does\s+not\s+(?:prefer|like|love|enjoy|favou?r)s?|doesn'?t\s+(?:prefer|like|love|enjoy|favou?r)s?|did\s+not\s+(?:prefer|like|love|enjoy|favou?r)s?|didn'?t\s+(?:prefer|like|love|enjoy|favou?r)s?|would\s+not\s+(?:prefer|like|love|enjoy|favou?r)s?|wouldn'?t\s+(?:prefer|like|love|enjoy|favou?r)s?|won'?t\s+(?:prefer|like|love|enjoy|favou?r)s?|never\s+(?:prefers?|likes?|loves?|enjoys?|favou?rs?)|no\s+longer\s+(?:prefers?|likes?|loves?|enjoys?|favou?rs?)|never\s+uses?|does\s+not\s+uses?|doesn'?t\s+uses?|does\s+not\s+use|doesn'?t\s+use|would\s+not\s+use|wouldn'?t\s+use|won'?t\s+use|never\s+works?\s+(?:with|in)|does\s+not\s+works?\s+(?:with|in)|doesn'?t\s+works?\s+(?:with|in)|would\s+not\s+works?\s+(?:with|in)|wouldn'?t\s+works?\s+(?:with|in)|won'?t\s+works?\s+(?:with|in)|never\s+codes?\s+(?:in|with)|does\s+not\s+codes?\s+(?:in|with)|doesn'?t\s+codes?\s+(?:in|with)|would\s+not\s+codes?\s+(?:in|with)|wouldn'?t\s+codes?\s+(?:in|with)|won'?t\s+codes?\s+(?:in|with)|refuses\s+to\s+(?:use|work\s+(?:with|in)|code\s+(?:in|with)))\s+([^.!?]+?)(?=\s+(?:(?:but|however|though|although|except|instead|rather)\b|and\s+(?:(?:now|currently|instead|rather)\b|(?=(?:prefer\w*|enjoy\w*|likes|like\s+to|loves?|loving|favou?r\w*|uses?|works?\s+(?:with|in)|codes?\s+(?:in|with)|interested\s+in|passionate\s+about|focused\s+on|specializ\w*)\b)))|[.!?]|$)/im,
|
|
52
52
|
transform: (match) => {
|
|
53
53
|
const subject = match[1].replace(/\.$/, "").trim();
|
|
54
54
|
return `The user would not prefer ${subject}`;
|
|
55
55
|
},
|
|
56
56
|
},
|
|
57
|
+
// "Would like to X" / "like to X" → preference for doing X
|
|
58
|
+
{
|
|
59
|
+
pattern: /\blikes?\s+to\s+([^.!?]+?)(?:\s+(?:for|when|in|over)\s+([^.!?]+?))?(?=[.!?]|$)/im,
|
|
60
|
+
transform: (match) => {
|
|
61
|
+
const action = match[1].replace(/\.$/, "").trim();
|
|
62
|
+
const context = match[2] ? ` for ${match[2].replace(/\.$/, "").trim()}` : "";
|
|
63
|
+
return `The user prefers to ${action}${context}`;
|
|
64
|
+
},
|
|
65
|
+
},
|
|
57
66
|
// Direct preference statements
|
|
58
67
|
{
|
|
59
|
-
pattern:
|
|
68
|
+
pattern: /\b(?:prefers?|enjoys?|likes|loves?|loving|favou?rs?)\s+(.+?)(?:\s+(?:for|when|in|over)\s+(.+?))?$/im,
|
|
60
69
|
transform: (match) => {
|
|
61
70
|
const subject = match[1].replace(/\.$/, "").trim();
|
|
62
71
|
const context = match[2] ? ` for ${match[2].replace(/\.$/, "").trim()}` : "";
|
|
@@ -65,10 +74,12 @@ const PREFERENCE_EXTRACTORS: Array<{
|
|
|
65
74
|
},
|
|
66
75
|
// "Uses X for Y" → preference for X in Y context
|
|
67
76
|
{
|
|
68
|
-
pattern: /(?:uses?|works?\s+(?:with|in)|codes?\s+(?:in|with))\s+(
|
|
77
|
+
pattern: /(?:uses?|works?\s+(?:with|in)|codes?\s+(?:in|with))\s+([^.!?]+?)(?:\s+(?:for|to|when)\s+([^.!?]+?))?(?=[.!?]|$)/i,
|
|
69
78
|
transform: (match) => {
|
|
70
|
-
const tool = match[1].replace(/\.$/, "").trim();
|
|
71
|
-
const context = match[2]
|
|
79
|
+
const tool = match[1].replace(/\s+/g, " ").replace(/\.$/, "").trim();
|
|
80
|
+
const context = match[2]
|
|
81
|
+
? ` for ${match[2].replace(/\s+/g, " ").replace(/\.$/, "").trim()}`
|
|
82
|
+
: "";
|
|
72
83
|
return `The user prefers to use ${tool}${context}`;
|
|
73
84
|
},
|
|
74
85
|
},
|
|
@@ -91,6 +102,26 @@ const PREFERENCE_EXTRACTORS: Array<{
|
|
|
91
102
|
},
|
|
92
103
|
];
|
|
93
104
|
|
|
105
|
+
const FACT_PREFERENCE_LANGUAGE_PATTERN =
|
|
106
|
+
/\b(?:prefer\w*|enjoy\w*|likes|like\s+to|loves?|loving|favou?r\w*|avoid\w*|dislik\w*|hat(?:e|es|ed|ing)|interested\s+in|passionate\s+about|specializ\w*|go-to)\b/i;
|
|
107
|
+
const FACT_USE_CONTEXT_PATTERN =
|
|
108
|
+
/\b(?:uses?|works?\s+(?:with|in)|codes?\s+(?:in|with))\b[^.!?]+\b(?:for|to|when)\b/i;
|
|
109
|
+
const DOUBLE_NEGATED_AVERSION_PATTERN =
|
|
110
|
+
/\b(?:does\s+not|doesn'?t|did\s+not|didn'?t|never|no\s+longer)\s+(?:avoids?|avoided|dislikes?|disliked|hates?|hated)\b/i;
|
|
111
|
+
const DOUBLE_NEGATED_AVERSION_GLOBAL_PATTERN =
|
|
112
|
+
/\b(?:does\s+not|doesn'?t|did\s+not|didn'?t|never|no\s+longer)\s+(?:avoids?|avoided|dislikes?|disliked|hates?|hated)\b/gi;
|
|
113
|
+
const WITHDRAWN_PREFERENCE_PATTERN =
|
|
114
|
+
/\b(?:avoids?|avoided|dislikes?|disliked|hates?|hated|does\s+not\s+(?:prefer|like|love|enjoy|favou?r)s?|doesn'?t\s+(?:prefer|like|love|enjoy|favou?r)s?|did\s+not\s+(?:prefer|like|love|enjoy|favou?r)s?|didn'?t\s+(?:prefer|like|love|enjoy|favou?r)s?|would\s+not\s+(?:prefer|like|love|enjoy|favou?r)s?|wouldn'?t\s+(?:prefer|like|love|enjoy|favou?r)s?|won'?t\s+(?:prefer|like|love|enjoy|favou?r)s?|never\s+(?:prefers?|likes?|loves?|enjoys?|favou?rs?)|no\s+longer\s+(?:prefers?|likes?|loves?|enjoys?|favou?rs?))\b/i;
|
|
115
|
+
const REPLACEMENT_PREFERENCE_CLAUSE_PATTERN =
|
|
116
|
+
/\b(?:(?:but|however|though|although)\s+(?:now\s+|currently\s+|instead\s+|rather\s+)?|and\s+(?:now\s+|currently\s+|instead\s+|rather\s+)?)(?=(?:prefer\w*|enjoy\w*|likes|like\s+to|loves?|loving|favou?r\w*|uses?|works?\s+(?:with|in)|codes?\s+(?:in|with)|interested\s+in|passionate\s+about|focused\s+on|specializ\w*)\b)/i;
|
|
117
|
+
|
|
118
|
+
function replacementPreferenceClause(content: string): string | null {
|
|
119
|
+
const match = REPLACEMENT_PREFERENCE_CLAUSE_PATTERN.exec(content);
|
|
120
|
+
if (!match) return null;
|
|
121
|
+
const clause = content.slice(match.index + match[0].length).trim();
|
|
122
|
+
return clause.replace(/[.!?]+$/, "").trim().length > 0 ? clause : null;
|
|
123
|
+
}
|
|
124
|
+
|
|
94
125
|
/**
|
|
95
126
|
* Fallback: convert any preference/correction memory content into a
|
|
96
127
|
* "The user prefers..." statement by prepending a suitable prefix.
|
|
@@ -184,17 +215,9 @@ export function consolidatePreferences(
|
|
|
184
215
|
if (m.frontmatter.category !== "fact") return false;
|
|
185
216
|
if (m.frontmatter.status && m.frontmatter.status !== "active") return false;
|
|
186
217
|
if ((m.frontmatter.confidence ?? 0) < minConfidence) return false;
|
|
187
|
-
const lower = m.content.toLowerCase();
|
|
188
218
|
return (
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
lower.includes("like to") ||
|
|
192
|
-
lower.includes("interested in") ||
|
|
193
|
-
lower.includes("passionate about") ||
|
|
194
|
-
lower.includes("specializ") ||
|
|
195
|
-
lower.includes("favourite") ||
|
|
196
|
-
lower.includes("favorite") ||
|
|
197
|
-
(lower.includes("use") && lower.includes("for"))
|
|
219
|
+
FACT_PREFERENCE_LANGUAGE_PATTERN.test(m.content) ||
|
|
220
|
+
FACT_USE_CONTEXT_PATTERN.test(m.content)
|
|
198
221
|
);
|
|
199
222
|
});
|
|
200
223
|
|
|
@@ -225,24 +248,37 @@ export function consolidatePreferences(
|
|
|
225
248
|
|
|
226
249
|
for (const mem of deduped.slice(0, maxPreferences * 2)) {
|
|
227
250
|
const content = mem.content.trim();
|
|
251
|
+
const hasDoubleNegatedAversion = DOUBLE_NEGATED_AVERSION_PATTERN.test(content);
|
|
252
|
+
const hasWithdrawnPreference = WITHDRAWN_PREFERENCE_PATTERN.test(content);
|
|
253
|
+
const strippedContent = hasDoubleNegatedAversion
|
|
254
|
+
? content.replace(DOUBLE_NEGATED_AVERSION_GLOBAL_PATTERN, " ").trim()
|
|
255
|
+
: content;
|
|
256
|
+
const replacementContent = hasWithdrawnPreference
|
|
257
|
+
? replacementPreferenceClause(strippedContent)
|
|
258
|
+
: null;
|
|
259
|
+
const extractionContent = replacementContent ?? strippedContent;
|
|
228
260
|
let statement: string | null = null;
|
|
229
261
|
|
|
230
262
|
// Try pattern-based extraction first
|
|
231
263
|
for (const extractor of PREFERENCE_EXTRACTORS) {
|
|
232
|
-
const match =
|
|
264
|
+
const match = extractionContent.match(extractor.pattern);
|
|
233
265
|
if (match) {
|
|
234
|
-
statement = extractor.transform(match,
|
|
266
|
+
statement = extractor.transform(match, extractionContent);
|
|
235
267
|
break;
|
|
236
268
|
}
|
|
237
269
|
}
|
|
238
270
|
|
|
271
|
+
if (!statement && hasDoubleNegatedAversion) {
|
|
272
|
+
continue;
|
|
273
|
+
}
|
|
274
|
+
|
|
239
275
|
// Fallback: generic prefix
|
|
240
276
|
if (!statement) {
|
|
241
|
-
statement = fallbackPreferenceStatement(content, mem.frontmatter.category);
|
|
277
|
+
statement = fallbackPreferenceStatement(replacementContent ?? content, mem.frontmatter.category);
|
|
242
278
|
}
|
|
243
279
|
|
|
244
280
|
// Skip if statement is too short or too generic
|
|
245
|
-
if (statement.length < 20) continue;
|
|
281
|
+
if (statement.length < 20 && !replacementContent) continue;
|
|
246
282
|
|
|
247
283
|
const keywords = extractKeywords(statement);
|
|
248
284
|
|
package/src/config.test.ts
CHANGED
|
@@ -3,6 +3,63 @@ import test from "node:test";
|
|
|
3
3
|
|
|
4
4
|
import { parseConfig } from "./config.js";
|
|
5
5
|
|
|
6
|
+
test("parseConfig emitLegacyTools defaults to true and coerces config/env (issue #1427)", () => {
|
|
7
|
+
// Default: legacy aliases on, for backward compatibility.
|
|
8
|
+
assert.equal(parseConfig({}).emitLegacyTools, true);
|
|
9
|
+
// `null` means "unset → use default", consistent with the repo convention for
|
|
10
|
+
// optional fields (e.g. taskModelChain: null → undefined). Not a hard error.
|
|
11
|
+
assert.equal(parseConfig({ emitLegacyTools: null }).emitLegacyTools, true);
|
|
12
|
+
// Boolean + boolean-like string config values.
|
|
13
|
+
assert.equal(parseConfig({ emitLegacyTools: false }).emitLegacyTools, false);
|
|
14
|
+
assert.equal(parseConfig({ emitLegacyTools: "false" }).emitLegacyTools, false);
|
|
15
|
+
assert.equal(parseConfig({ emitLegacyTools: "0" }).emitLegacyTools, false);
|
|
16
|
+
assert.equal(parseConfig({ emitLegacyTools: "true" }).emitLegacyTools, true);
|
|
17
|
+
|
|
18
|
+
// Env var fallback (REMNIC_ preferred, ENGRAM_ legacy) when config field absent.
|
|
19
|
+
const prevRemnic = process.env.REMNIC_EMIT_LEGACY_TOOLS;
|
|
20
|
+
const prevEngram = process.env.ENGRAM_EMIT_LEGACY_TOOLS;
|
|
21
|
+
try {
|
|
22
|
+
process.env.REMNIC_EMIT_LEGACY_TOOLS = "false";
|
|
23
|
+
assert.equal(parseConfig({}).emitLegacyTools, false, "REMNIC_ env disables");
|
|
24
|
+
// Explicit config field wins over env.
|
|
25
|
+
assert.equal(parseConfig({ emitLegacyTools: true }).emitLegacyTools, true, "config field wins over env");
|
|
26
|
+
delete process.env.REMNIC_EMIT_LEGACY_TOOLS;
|
|
27
|
+
process.env.ENGRAM_EMIT_LEGACY_TOOLS = "false";
|
|
28
|
+
assert.equal(parseConfig({}).emitLegacyTools, false, "ENGRAM_ env fallback disables");
|
|
29
|
+
} finally {
|
|
30
|
+
if (prevRemnic === undefined) delete process.env.REMNIC_EMIT_LEGACY_TOOLS;
|
|
31
|
+
else process.env.REMNIC_EMIT_LEGACY_TOOLS = prevRemnic;
|
|
32
|
+
if (prevEngram === undefined) delete process.env.ENGRAM_EMIT_LEGACY_TOOLS;
|
|
33
|
+
else process.env.ENGRAM_EMIT_LEGACY_TOOLS = prevEngram;
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test("parseConfig rejects a present-but-malformed emitLegacyTools (gotcha #51, #1427)", () => {
|
|
38
|
+
// A typo must fail fast, not silently fall through to the default (true) and
|
|
39
|
+
// re-enable legacy tool advertising.
|
|
40
|
+
for (const bad of ["fales", "maybe", 2, "2", "enabled"]) {
|
|
41
|
+
assert.throws(
|
|
42
|
+
() => parseConfig({ emitLegacyTools: bad }),
|
|
43
|
+
/emitLegacyTools must be a boolean-like value/,
|
|
44
|
+
`emitLegacyTools=${JSON.stringify(bad)} should throw`,
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
// Malformed env var also fails fast (only when the config field is absent).
|
|
48
|
+
const prev = process.env.REMNIC_EMIT_LEGACY_TOOLS;
|
|
49
|
+
try {
|
|
50
|
+
process.env.REMNIC_EMIT_LEGACY_TOOLS = "maybe";
|
|
51
|
+
assert.throws(
|
|
52
|
+
() => parseConfig({}),
|
|
53
|
+
/REMNIC_EMIT_LEGACY_TOOLS must be a boolean-like value/,
|
|
54
|
+
);
|
|
55
|
+
// An explicit valid config field overrides a malformed env (field wins first).
|
|
56
|
+
assert.equal(parseConfig({ emitLegacyTools: false }).emitLegacyTools, false);
|
|
57
|
+
} finally {
|
|
58
|
+
if (prev === undefined) delete process.env.REMNIC_EMIT_LEGACY_TOOLS;
|
|
59
|
+
else process.env.REMNIC_EMIT_LEGACY_TOOLS = prev;
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
|
|
6
63
|
test("parseConfig expands tilde paths for core storage directories", () => {
|
|
7
64
|
const previousHome = process.env.HOME;
|
|
8
65
|
process.env.HOME = "/Users/remnic-test";
|
|
@@ -72,6 +129,33 @@ test("parseConfig codex missing entirely → installExtension defaults to true",
|
|
|
72
129
|
assert.equal(result.codex.installExtension, true);
|
|
73
130
|
});
|
|
74
131
|
|
|
132
|
+
test("parseConfig recallPlannerLlmEnabled defaults to false and coerces boolean-like strings (opt-in, issue #1367)", () => {
|
|
133
|
+
assert.equal(parseConfig({}).recallPlannerLlmEnabled, false);
|
|
134
|
+
assert.equal(parseConfig({ recallPlannerLlmEnabled: true }).recallPlannerLlmEnabled, true);
|
|
135
|
+
// CLI/env surfaces pass strings — these must enable the gate (gotcha #36).
|
|
136
|
+
assert.equal(parseConfig({ recallPlannerLlmEnabled: "true" }).recallPlannerLlmEnabled, true);
|
|
137
|
+
assert.equal(parseConfig({ recallPlannerLlmEnabled: "1" }).recallPlannerLlmEnabled, true);
|
|
138
|
+
assert.equal(parseConfig({ recallPlannerLlmEnabled: "on" }).recallPlannerLlmEnabled, true);
|
|
139
|
+
// Boolean-like falses and junk stay off.
|
|
140
|
+
assert.equal(parseConfig({ recallPlannerLlmEnabled: "false" }).recallPlannerLlmEnabled, false);
|
|
141
|
+
assert.equal(parseConfig({ recallPlannerLlmEnabled: "0" }).recallPlannerLlmEnabled, false);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
test("parseConfig coerces boolean-like strings for all recallPlanner gates (issue #1367, gotcha #36)", () => {
|
|
145
|
+
// Rollout switches must honor string config from CLI/env surfaces.
|
|
146
|
+
assert.equal(parseConfig({ recallPlannerShadowMode: "true" }).recallPlannerShadowMode, true);
|
|
147
|
+
assert.equal(parseConfig({ recallPlannerShadowMode: "off" }).recallPlannerShadowMode, false);
|
|
148
|
+
assert.equal(parseConfig({}).recallPlannerShadowMode, false);
|
|
149
|
+
|
|
150
|
+
assert.equal(parseConfig({ recallPlannerTelemetryEnabled: "false" }).recallPlannerTelemetryEnabled, false);
|
|
151
|
+
assert.equal(parseConfig({}).recallPlannerTelemetryEnabled, true);
|
|
152
|
+
|
|
153
|
+
// The enable gate must be disableable via string "false" (the old `!== false`
|
|
154
|
+
// check treated "false" as truthy → could not disable).
|
|
155
|
+
assert.equal(parseConfig({ recallPlannerEnabled: "false" }).recallPlannerEnabled, false);
|
|
156
|
+
assert.equal(parseConfig({}).recallPlannerEnabled, true);
|
|
157
|
+
});
|
|
158
|
+
|
|
75
159
|
test("parseConfig dreaming.maxEntries=0 preserves the runtime disable switch", () => {
|
|
76
160
|
const result = parseConfig({ dreaming: { maxEntries: 0 } });
|
|
77
161
|
assert.equal(result.dreaming.maxEntries, 0);
|
|
@@ -116,6 +200,59 @@ test("parseConfig initGateTimeoutMs defaults to OpenClaw cold-start budget", ()
|
|
|
116
200
|
assert.equal(result.initGateTimeoutMs, 30_000);
|
|
117
201
|
});
|
|
118
202
|
|
|
203
|
+
test("parseConfig qmdSearchStrategy defaults to hybrid and validates the enum", () => {
|
|
204
|
+
// Default must equal the historical lex+vec+hyde behavior. Issue #1335.
|
|
205
|
+
assert.equal(parseConfig({}).qmdSearchStrategy, "hybrid");
|
|
206
|
+
assert.equal(parseConfig({ qmdSearchStrategy: "hybrid" }).qmdSearchStrategy, "hybrid");
|
|
207
|
+
assert.equal(parseConfig({ qmdSearchStrategy: "lex-vec" }).qmdSearchStrategy, "lex-vec");
|
|
208
|
+
assert.equal(parseConfig({ qmdSearchStrategy: "lex" }).qmdSearchStrategy, "lex");
|
|
209
|
+
assert.equal(parseConfig({ qmdSearchStrategy: "LEX" }).qmdSearchStrategy, "lex");
|
|
210
|
+
|
|
211
|
+
for (const value of ["hyde", "vec", "bm25", "", 42]) {
|
|
212
|
+
assert.throws(
|
|
213
|
+
() => parseConfig({ qmdSearchStrategy: value }),
|
|
214
|
+
/qmdSearchStrategy must be one of/,
|
|
215
|
+
`invalid qmdSearchStrategy ${String(value)} should throw`,
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
test("parseConfig qmdSubprocessStrategy defaults to query (honors QMD query intent)", () => {
|
|
221
|
+
// Default must remain `qmd query` (LLM expansion + rerank) per CLAUDE.md gotcha #7.
|
|
222
|
+
assert.equal(parseConfig({}).qmdSubprocessStrategy, "query");
|
|
223
|
+
assert.equal(parseConfig({ qmdSubprocessStrategy: "query" }).qmdSubprocessStrategy, "query");
|
|
224
|
+
assert.equal(parseConfig({ qmdSubprocessStrategy: "search" }).qmdSubprocessStrategy, "search");
|
|
225
|
+
assert.equal(parseConfig({ qmdSubprocessStrategy: "SEARCH" }).qmdSubprocessStrategy, "search");
|
|
226
|
+
|
|
227
|
+
for (const value of ["bm25", "vsearch", "", 7]) {
|
|
228
|
+
assert.throws(
|
|
229
|
+
() => parseConfig({ qmdSubprocessStrategy: value }),
|
|
230
|
+
/qmdSubprocessStrategy must be one of/,
|
|
231
|
+
`invalid qmdSubprocessStrategy ${String(value)} should throw`,
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
test("parseConfig qmdDaemonTimeoutMs defaults to 8000 and clamps valid integers", () => {
|
|
237
|
+
assert.equal(parseConfig({}).qmdDaemonTimeoutMs, 8_000);
|
|
238
|
+
assert.equal(parseConfig({ qmdDaemonTimeoutMs: 20_000 }).qmdDaemonTimeoutMs, 20_000);
|
|
239
|
+
assert.equal(parseConfig({ qmdDaemonTimeoutMs: "20000" }).qmdDaemonTimeoutMs, 20_000);
|
|
240
|
+
// Below floor clamps up; above ceiling clamps down.
|
|
241
|
+
assert.equal(parseConfig({ qmdDaemonTimeoutMs: 100 }).qmdDaemonTimeoutMs, 1_000);
|
|
242
|
+
assert.equal(parseConfig({ qmdDaemonTimeoutMs: 999_999 }).qmdDaemonTimeoutMs, 120_000);
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
test("parseConfig qmdDaemonTimeoutMs rejects non-numeric and non-integer input", () => {
|
|
246
|
+
// gotcha #51 + codex review on #1422: silent coercion hides config mistakes.
|
|
247
|
+
for (const value of ["abc", "", 2500.9, "2500.9", Number.NaN, Infinity, true, {}]) {
|
|
248
|
+
assert.throws(
|
|
249
|
+
() => parseConfig({ qmdDaemonTimeoutMs: value }),
|
|
250
|
+
/qmdDaemonTimeoutMs must be an integer/,
|
|
251
|
+
`invalid qmdDaemonTimeoutMs ${String(value)} should throw`,
|
|
252
|
+
);
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
|
|
119
256
|
test("parseConfig initGateTimeoutMs accepts CLI-style numeric strings", () => {
|
|
120
257
|
const result = parseConfig({ initGateTimeoutMs: "45000" });
|
|
121
258
|
assert.equal(result.initGateTimeoutMs, 45_000);
|
|
@@ -140,6 +277,82 @@ test("parseConfig modelSource=gateway does not inherit OPENAI_API_KEY from the p
|
|
|
140
277
|
}
|
|
141
278
|
});
|
|
142
279
|
|
|
280
|
+
test("parseConfig normalizes taskModelChain", () => {
|
|
281
|
+
const cfg = parseConfig({
|
|
282
|
+
taskModelChain: {
|
|
283
|
+
primary: " openai/cheap-primary ",
|
|
284
|
+
fallbacks: ["openai/cheap-primary", " fireworks/accounts/fireworks/models/glm-5p1 ", ""],
|
|
285
|
+
},
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
assert.deepEqual(cfg.taskModelChain, {
|
|
289
|
+
primary: "openai/cheap-primary",
|
|
290
|
+
fallbacks: ["fireworks/accounts/fireworks/models/glm-5p1"],
|
|
291
|
+
});
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
test("parseConfig treats an absent taskModelChain as not configured", () => {
|
|
295
|
+
assert.equal(parseConfig({}).taskModelChain, undefined);
|
|
296
|
+
assert.equal(parseConfig({ taskModelChain: null }).taskModelChain, undefined);
|
|
297
|
+
assert.equal(parseConfig({ taskModelChain: undefined }).taskModelChain, undefined);
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
test("parseConfig rejects a present-but-malformed taskModelChain (gotcha #51)", () => {
|
|
301
|
+
// A typo'd chain must surface loudly instead of silently reverting to defaults.
|
|
302
|
+
assert.throws(() => parseConfig({ taskModelChain: [] }), /taskModelChain must be an object/);
|
|
303
|
+
assert.throws(() => parseConfig({ taskModelChain: "openai/x" }), /taskModelChain must be an object/);
|
|
304
|
+
assert.throws(() => parseConfig({ taskModelChain: { primary: " " } }), /taskModelChain\.primary is required/);
|
|
305
|
+
assert.throws(() => parseConfig({ taskModelChain: { fallbacks: ["openai/fallback-only"] } }), /taskModelChain\.primary is required/);
|
|
306
|
+
assert.throws(
|
|
307
|
+
() => parseConfig({ taskModelChain: { primary: "openai/p", fallbacks: "not-an-array" } }),
|
|
308
|
+
/taskModelChain\.fallbacks must be an array/,
|
|
309
|
+
);
|
|
310
|
+
assert.throws(
|
|
311
|
+
() => parseConfig({ taskModelChain: { primary: "openai/p", fallbacks: [123] } }),
|
|
312
|
+
/taskModelChain\.fallbacks must contain only strings/,
|
|
313
|
+
);
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
test("parseConfig rejects unqualified taskModelChain model strings (codex review #1425)", () => {
|
|
317
|
+
// A slash-less id like "gpt-4.1" parses here but FallbackLlmClient.parseModelString
|
|
318
|
+
// drops it, leaving the chain silently using a different model — reject at parse.
|
|
319
|
+
assert.throws(
|
|
320
|
+
() => parseConfig({ taskModelChain: { primary: "gpt-4.1" } }),
|
|
321
|
+
/taskModelChain\.primary must be in "provider\/model" form/,
|
|
322
|
+
);
|
|
323
|
+
assert.throws(
|
|
324
|
+
() => parseConfig({ taskModelChain: { primary: "openai/" } }),
|
|
325
|
+
/taskModelChain\.primary must be in "provider\/model" form/,
|
|
326
|
+
);
|
|
327
|
+
assert.throws(
|
|
328
|
+
() => parseConfig({ taskModelChain: { primary: "/gpt-4.1" } }),
|
|
329
|
+
/taskModelChain\.primary must be in "provider\/model" form/,
|
|
330
|
+
);
|
|
331
|
+
assert.throws(
|
|
332
|
+
() => parseConfig({ taskModelChain: { primary: "openai/gpt", fallbacks: ["bare-model"] } }),
|
|
333
|
+
/taskModelChain\.fallbacks entries must be in "provider\/model" form/,
|
|
334
|
+
);
|
|
335
|
+
// Multi-slash provider/model paths remain valid.
|
|
336
|
+
assert.deepEqual(
|
|
337
|
+
parseConfig({
|
|
338
|
+
taskModelChain: { primary: "fireworks/accounts/fireworks/models/glm-5p1" },
|
|
339
|
+
}).taskModelChain,
|
|
340
|
+
{ primary: "fireworks/accounts/fireworks/models/glm-5p1" },
|
|
341
|
+
);
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
test("parseConfig rejects unknown taskModelChain keys (codex review #1425)", () => {
|
|
345
|
+
// A misspelled "fallback" must not silently drop the fallback chain.
|
|
346
|
+
assert.throws(
|
|
347
|
+
() => parseConfig({ taskModelChain: { primary: "openai/p", fallback: ["openai/q"] } }),
|
|
348
|
+
/taskModelChain has unknown property: fallback/,
|
|
349
|
+
);
|
|
350
|
+
assert.throws(
|
|
351
|
+
() => parseConfig({ taskModelChain: { primary: "openai/p", fallbackModels: ["openai/q"], extra: 1 } }),
|
|
352
|
+
/taskModelChain has unknown properties:/,
|
|
353
|
+
);
|
|
354
|
+
});
|
|
355
|
+
|
|
143
356
|
test("parseConfig modelSource=gateway still honors an explicit openaiApiKey override", () => {
|
|
144
357
|
const original = process.env.OPENAI_API_KEY;
|
|
145
358
|
process.env.OPENAI_API_KEY = "sk-env-should-not-be-used";
|