@remnic/core 9.3.771 → 9.4.1
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-admin-ops-surface.d.ts +5 -5
- package/dist/access-admin-ops-surface.js +36 -35
- package/dist/access-boundary.d.ts +5 -5
- package/dist/access-boundary.js +37 -36
- package/dist/access-cli.js +109 -108
- package/dist/access-cli.js.map +1 -1
- package/dist/access-http.d.ts +5 -5
- package/dist/access-http.js +48 -47
- package/dist/access-identity-continuity-surface.d.ts +4 -4
- package/dist/access-identity-continuity-surface.js +36 -35
- package/dist/access-lcm-surface.d.ts +5 -5
- package/dist/access-lcm-surface.js +36 -35
- package/dist/access-mcp.d.ts +5 -5
- package/dist/access-mcp.js +44 -43
- package/dist/access-observe-write-surface.d.ts +5 -5
- package/dist/access-observe-write-surface.js +36 -35
- package/dist/access-operations-batch.js +41 -40
- package/dist/access-operations.d.ts +5 -5
- package/dist/access-operations.js +43 -42
- package/dist/access-recall-surface.d.ts +5 -5
- package/dist/access-recall-surface.js +36 -35
- package/dist/access-schema.js +3 -3
- package/dist/{access-service-CdW6B2qQ.d.ts → access-service-m-2VO_Ig.d.ts} +3 -3
- package/dist/access-service.d.ts +5 -5
- package/dist/access-service.js +36 -35
- package/dist/access-surface-catalog.d.ts +5 -5
- package/dist/action-confidence.d.ts +1 -1
- package/dist/active-memory-bridge.d.ts +1 -1
- package/dist/active-recall.d.ts +1 -1
- package/dist/active-recall.js +2 -2
- package/dist/adapters/index.js +4 -4
- package/dist/adapters/registry.js +2 -2
- package/dist/{auto-sync-2SOUW4JW.js → auto-sync-HMHQHUAG.js} +9 -9
- package/dist/behavior-learner.d.ts +1 -1
- package/dist/behavior-signals.d.ts +1 -1
- package/dist/bootstrap.d.ts +4 -4
- package/dist/bootstrap.js +2 -2
- package/dist/boxes.js +2 -2
- package/dist/briefing.d.ts +1 -1
- package/dist/briefing.js +12 -12
- package/dist/buffer-surprise-report.d.ts +1 -1
- package/dist/buffer.d.ts +1 -1
- package/dist/buffer.js +2 -2
- package/dist/calibration.d.ts +1 -1
- package/dist/calibration.js +4 -4
- package/dist/capabilities.d.ts +1 -1
- package/dist/{catalog-bflYNWsM.d.ts → catalog-BxWokdC2.d.ts} +1 -1
- package/dist/causal-behavior.d.ts +1 -1
- package/dist/causal-behavior.js +4 -4
- package/dist/causal-chain.js +4 -4
- package/dist/causal-consolidation.d.ts +1 -1
- package/dist/causal-consolidation.js +17 -17
- package/dist/causal-retrieval.js +4 -4
- package/dist/causal-trajectory-graph.d.ts +1 -1
- package/dist/causal-trajectory-graph.js +1 -1
- package/dist/causal-trajectory.js +1 -1
- package/dist/{chunk-PSNMICC2.js → chunk-27FLVMDC.js} +14 -14
- package/dist/{chunk-7FL4CNPV.js → chunk-2A3OXAP2.js} +2 -2
- package/dist/{chunk-U5HD7BB2.js → chunk-2FOD2YY3.js} +10 -10
- package/dist/{chunk-GG6AJN7A.js → chunk-2IZAGAJR.js} +2 -2
- package/dist/{chunk-6Y4JTNX7.js → chunk-2MR3MFQB.js} +2 -2
- package/dist/{chunk-AU7Q3LSC.js → chunk-2QSZNTDO.js} +4 -4
- package/dist/{chunk-IBAU2RZN.js → chunk-2U2A5HF4.js} +11 -5
- package/dist/chunk-2U2A5HF4.js.map +1 -0
- package/dist/chunk-37CTGERM.js +62 -0
- package/dist/chunk-37CTGERM.js.map +1 -0
- package/dist/{chunk-MYNRJGOY.js → chunk-3F4N4WDH.js} +76 -111
- package/dist/chunk-3F4N4WDH.js.map +1 -0
- package/dist/{chunk-7OSULYN2.js → chunk-4BFRBP45.js} +2 -2
- package/dist/{chunk-DQC3ZZYM.js → chunk-4KJLYTLS.js} +2 -2
- package/dist/{chunk-DMKMQTBH.js → chunk-4P4626RY.js} +5 -5
- package/dist/{chunk-GY3SKOS4.js → chunk-5EZXHU36.js} +2 -2
- package/dist/{chunk-OW4UXZNP.js → chunk-5QZDMSSN.js} +61 -61
- package/dist/chunk-64UDOIMI.js +121 -0
- package/dist/chunk-64UDOIMI.js.map +1 -0
- package/dist/{chunk-OTMAYEY5.js → chunk-66FLU5BT.js} +3 -3
- package/dist/{chunk-J2QYB2D5.js → chunk-6BISDEN6.js} +7 -7
- package/dist/{chunk-CXBFYSQY.js → chunk-6ZIETXZ2.js} +3 -3
- package/dist/{chunk-KYYL4U6X.js → chunk-7N2RUBAP.js} +3 -3
- package/dist/{chunk-U3TRHUSJ.js → chunk-7QWMTOIF.js} +45 -45
- package/dist/{chunk-L24JROPR.js → chunk-7WSLR2CU.js} +2 -2
- package/dist/{chunk-T4AWNIXJ.js → chunk-7Z2DZNWN.js} +18 -9
- package/dist/{chunk-T4AWNIXJ.js.map → chunk-7Z2DZNWN.js.map} +1 -1
- package/dist/{chunk-EXM3CQTZ.js → chunk-A3GOMRJG.js} +2 -2
- package/dist/{chunk-DCWIQFNA.js → chunk-AKUCU7CI.js} +7 -7
- package/dist/{chunk-XHYGJVXL.js → chunk-AXTXN4T6.js} +2 -2
- package/dist/{chunk-L42FA4BU.js → chunk-BGTAL46Y.js} +2 -2
- package/dist/{chunk-5QANTTY2.js → chunk-BRDJ2HFP.js} +2 -2
- package/dist/{chunk-7VFZTJ7K.js → chunk-BV2TIZ4F.js} +2 -2
- package/dist/{chunk-QMN3CIFS.js → chunk-CGS7UKWM.js} +2 -2
- package/dist/{chunk-LZ73Y56P.js → chunk-CLX5VDNY.js} +8 -8
- package/dist/{chunk-KYC3RLYJ.js → chunk-CXLNZXG5.js} +1 -1
- package/dist/{chunk-YOELDDBF.js → chunk-CZAA5N5S.js} +4 -4
- package/dist/{chunk-XG4NBBRW.js → chunk-CZAZX7JP.js} +5 -5
- package/dist/{chunk-QOGXUNWL.js → chunk-CZGIIADK.js} +7 -7
- package/dist/{chunk-QRDOSYOR.js → chunk-DBRYEIGY.js} +2 -2
- package/dist/{chunk-6QODJKOV.js → chunk-DGYCWCQ7.js} +3 -3
- package/dist/{chunk-RFZW7NID.js → chunk-DR6PEJU3.js} +4 -4
- package/dist/{chunk-UC4YOAC2.js → chunk-E5BK6CGV.js} +2 -2
- package/dist/{chunk-TKZ3AL5V.js → chunk-EHK6KZHK.js} +2 -2
- package/dist/{chunk-X5NUL5N4.js → chunk-EPGWWCZF.js} +3 -3
- package/dist/{chunk-JZ7P26GF.js → chunk-F53UF7CI.js} +2 -2
- package/dist/{chunk-SJHM6I4J.js → chunk-FC77VUX5.js} +2 -2
- package/dist/{chunk-DKTSR7EK.js → chunk-FDPZVY6D.js} +2 -2
- package/dist/{chunk-OUXPVWTZ.js → chunk-FLVL4L3L.js} +5 -5
- package/dist/{chunk-ZI3YYOOV.js → chunk-FZGGZAAU.js} +2 -2
- package/dist/{chunk-TQT3USCG.js → chunk-HG32URJJ.js} +2 -2
- package/dist/{chunk-ZQNCHPTZ.js → chunk-HLTAPHZZ.js} +2 -2
- package/dist/{chunk-LU7ATMRV.js → chunk-HRCACVHL.js} +3 -3
- package/dist/{chunk-LOPZ2DWR.js → chunk-I73G4UH7.js} +2 -2
- package/dist/{chunk-MJE4XV4A.js → chunk-IC6QX5TC.js} +2 -2
- package/dist/{chunk-27TJRC2Y.js → chunk-ILFJ7AVS.js} +7 -7
- package/dist/{chunk-VUA4S7P6.js → chunk-JIKJPDQJ.js} +4 -4
- package/dist/{chunk-BKTC7JCA.js → chunk-JLR3JSTL.js} +3 -3
- package/dist/{chunk-VQ34TERH.js → chunk-JZHHREBL.js} +2 -2
- package/dist/{chunk-IQVQJJL7.js → chunk-K2ZLHESQ.js} +2 -2
- package/dist/{chunk-PWFWCGOO.js → chunk-KAWTOYAW.js} +2 -2
- package/dist/{chunk-YRZMCPC5.js → chunk-KCJ5H6XQ.js} +4 -4
- package/dist/{chunk-DJECGBBM.js → chunk-KQDNAXOM.js} +2 -2
- package/dist/{chunk-WIWPSQYU.js → chunk-KQPJLK3X.js} +2 -2
- package/dist/{chunk-XNWLD3RR.js → chunk-LOR6ZM5R.js} +3 -3
- package/dist/{chunk-EGLUVV6B.js → chunk-M7GGMGMZ.js} +10 -10
- package/dist/{chunk-YTU7WKYN.js → chunk-MNUHL34L.js} +3 -3
- package/dist/{chunk-ZCWIH4LH.js → chunk-MRHFJV44.js} +2 -2
- package/dist/{chunk-W4H5A3TJ.js → chunk-MTR7BZQN.js} +3 -3
- package/dist/{chunk-PNLCEFE4.js → chunk-MTWKDOVO.js} +2 -2
- package/dist/{chunk-TECYUPFB.js → chunk-NG7YBCJO.js} +4 -4
- package/dist/{chunk-OSMBC3JU.js → chunk-O2EL7EGG.js} +2 -2
- package/dist/{chunk-HW5EF6LV.js → chunk-O7BBFJLP.js} +2 -2
- package/dist/{chunk-EHQ5HO5T.js → chunk-OBMR4VIX.js} +1 -1
- package/dist/{chunk-VFHDUFH5.js → chunk-OHDJ34CC.js} +2 -2
- package/dist/{chunk-6SHAUM5S.js → chunk-OUMTBFSQ.js} +4 -4
- package/dist/{chunk-27CQIO7T.js → chunk-P6SYWE4G.js} +57 -10
- package/dist/chunk-P6SYWE4G.js.map +1 -0
- package/dist/{chunk-EEKDRAWX.js → chunk-PRTA6444.js} +2 -2
- package/dist/{chunk-IQ7WCZRW.js → chunk-PUBTHW5O.js} +3 -3
- package/dist/{chunk-AGNBY3VG.js → chunk-PYHOAXTI.js} +2 -2
- package/dist/{chunk-UUH4YQOF.js → chunk-QLULYKWZ.js} +2 -2
- package/dist/{chunk-DBSMLRTO.js → chunk-QT2RZNRT.js} +2 -2
- package/dist/{chunk-HM6UJPPO.js → chunk-QWGNDWKB.js} +2 -2
- package/dist/{chunk-KCQA46NR.js → chunk-R5VAVVHP.js} +2 -2
- package/dist/{chunk-SXN3LCUT.js → chunk-RHOZ5LLO.js} +100 -4
- package/dist/chunk-RHOZ5LLO.js.map +1 -0
- package/dist/{chunk-6KYMPV2O.js → chunk-RL6NEVXT.js} +45 -2
- package/dist/chunk-RL6NEVXT.js.map +1 -0
- package/dist/{chunk-O7GOFAM3.js → chunk-RP7YXA67.js} +2 -2
- package/dist/{chunk-BVKCV2ZY.js → chunk-SMX5267P.js} +2 -2
- package/dist/{chunk-XBZQRZ6G.js → chunk-TN7THW5U.js} +2 -2
- package/dist/{chunk-JRZV3767.js → chunk-UI4FVU6T.js} +2 -2
- package/dist/{chunk-VGUOEDTU.js → chunk-V3UE5I5F.js} +2 -2
- package/dist/{chunk-7WV3F5DQ.js → chunk-V7IZKJI3.js} +2 -2
- package/dist/{chunk-VL7DP3OW.js → chunk-VFSP73LP.js} +2 -2
- package/dist/{chunk-C2DDH5UN.js → chunk-X4A62ET7.js} +14 -14
- package/dist/{chunk-WRBDUNSW.js → chunk-XN3TUUX2.js} +3 -3
- package/dist/{chunk-NNL4LJAD.js → chunk-XZ5I7OZR.js} +5 -5
- package/dist/{chunk-K2I3AWBR.js → chunk-YDSWRD33.js} +5 -5
- package/dist/{chunk-3NN73CM7.js → chunk-YFBTVOMB.js} +3 -3
- package/dist/{chunk-44YXWE35.js → chunk-YPEMNNNY.js} +2 -2
- package/dist/{chunk-ZZMRXJFY.js → chunk-ZIDSD6VC.js} +2 -2
- package/dist/{chunk-BMANIB5B.js → chunk-ZPOROUTX.js} +3 -3
- package/dist/{cli-Dfnc66gs.d.ts → cli-EiBBOt3r.d.ts} +3 -3
- package/dist/cli.d.ts +6 -6
- package/dist/cli.js +74 -73
- package/dist/compounding/engine.d.ts +1 -1
- package/dist/compounding/engine.js +13 -13
- package/dist/compounding/preference-consolidator.d.ts +1 -1
- package/dist/compounding/preference-consolidator.js +1 -1
- package/dist/compression-optimizer.d.ts +1 -1
- package/dist/config.d.ts +1 -1
- package/dist/config.js +2 -2
- package/dist/connectors/codex-materialize-runner.d.ts +1 -1
- package/dist/connectors/codex-materialize-runner.js +13 -13
- package/dist/connectors/codex-materialize.d.ts +1 -1
- package/dist/connectors/codex-materialize.js +2 -2
- package/dist/connectors/index.d.ts +1 -1
- package/dist/connectors/index.js +14 -14
- package/dist/consolidation-provenance-check.d.ts +1 -1
- package/dist/consolidation-undo.d.ts +1 -1
- package/dist/contradiction/index.d.ts +1 -1
- package/dist/contradiction/index.js +3 -3
- package/dist/{contradiction-scan-3FJYWS3G.js → contradiction-scan-SSKCAMLX.js} +3 -3
- package/dist/conversation-index/backend.d.ts +1 -1
- package/dist/conversation-index/backend.js +5 -5
- package/dist/conversation-index/chunker.d.ts +1 -1
- package/dist/conversation-index/cleanup.js +2 -2
- package/dist/conversation-index/faiss-adapter.d.ts +1 -1
- package/dist/conversation-index/faiss-adapter.js +2 -2
- package/dist/conversation-index/indexer.d.ts +1 -1
- package/dist/conversation-index/indexer.js +2 -2
- package/dist/conversation-index/search.d.ts +1 -1
- package/dist/conversation-index/search.js +2 -2
- package/dist/day-summary.d.ts +1 -1
- package/dist/day-summary.js +2 -2
- package/dist/delinearize.d.ts +1 -1
- package/dist/direct-answer-wiring.d.ts +1 -1
- package/dist/direct-answer.d.ts +1 -1
- package/dist/embedding-fallback.d.ts +1 -1
- package/dist/embedding-fallback.js +2 -2
- package/dist/enrichment/index.d.ts +1 -1
- package/dist/entity-retrieval.d.ts +1 -1
- package/dist/entity-retrieval.js +13 -13
- package/dist/entity-schema.d.ts +1 -1
- package/dist/explicit-capture.d.ts +4 -4
- package/dist/extraction-faithfulness.d.ts +1 -1
- package/dist/extraction-faithfulness.js +5 -5
- package/dist/extraction-judge-telemetry.d.ts +1 -1
- package/dist/extraction-judge-telemetry.js +2 -2
- package/dist/extraction-judge-training.d.ts +1 -1
- package/dist/extraction-judge-training.js +2 -2
- package/dist/extraction-judge.d.ts +1 -1
- package/dist/extraction-judge.js +5 -5
- package/dist/extraction.d.ts +1 -1
- package/dist/extraction.js +9 -9
- package/dist/fallback-llm.d.ts +1 -1
- package/dist/fallback-llm.js +4 -4
- package/dist/{forget-OEE5OELK.js → forget-KHF7A44H.js} +3 -3
- package/dist/graph-dashboard-diff.d.ts +1 -1
- package/dist/graph-dashboard-key.d.ts +1 -1
- package/dist/graph-dashboard-parser.d.ts +1 -1
- package/dist/{graph-edge-decay-RXUCJIN4.js → graph-edge-decay-FO7AM4UR.js} +3 -3
- package/dist/graph-edge-reinforcement.d.ts +1 -1
- package/dist/graph-snapshot.d.ts +1 -1
- package/dist/graph-snapshot.js +2 -2
- package/dist/graph.d.ts +1 -1
- package/dist/graph.js +1 -1
- package/dist/identity-continuity.d.ts +1 -1
- package/dist/importance.d.ts +1 -1
- package/dist/index.d.ts +17 -12
- package/dist/index.js +124 -123
- package/dist/intent.d.ts +1 -1
- package/dist/lcm/archive.js +2 -2
- package/dist/lcm/engine.d.ts +1 -1
- package/dist/lcm/engine.js +7 -7
- package/dist/lcm/index.d.ts +1 -1
- package/dist/lcm/index.js +9 -9
- package/dist/lcm/schema.js +3 -3
- package/dist/lcm/summarizer.js +3 -3
- package/dist/lcm/tools.d.ts +1 -1
- package/dist/lifecycle.d.ts +1 -1
- package/dist/live-connectors-runner.d.ts +1 -1
- package/dist/local-llm.d.ts +1 -1
- package/dist/local-llm.js +2 -2
- package/dist/local-model-endpoint.d.ts +1 -1
- package/dist/logger.d.ts +8 -2
- package/dist/logger.js +1 -1
- package/dist/maintenance/memory-governance.d.ts +1 -1
- package/dist/maintenance/memory-governance.js +12 -12
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +12 -12
- package/dist/maintenance/rebuild-memory-projection.js +14 -14
- package/dist/mcp-memory-inspector-app.d.ts +5 -5
- package/dist/memory-action-policy.d.ts +1 -1
- package/dist/memory-cache.d.ts +1 -1
- package/dist/memory-lifecycle-ledger-utils.d.ts +1 -1
- package/dist/memory-projection-store.d.ts +23 -3
- package/dist/memory-projection-store.js +9 -2
- package/dist/memory-provenance.d.ts +1 -1
- package/dist/memory-worth-outcomes.d.ts +1 -1
- package/dist/model-registry.js +2 -2
- package/dist/models-json.d.ts +1 -1
- package/dist/models-json.js +2 -2
- package/dist/namespaces/migrate.d.ts +2 -2
- package/dist/namespaces/migrate.js +27 -26
- package/dist/namespaces/principal.d.ts +1 -1
- package/dist/namespaces/search.d.ts +1 -1
- package/dist/namespaces/search.js +15 -14
- package/dist/namespaces/storage.d.ts +2 -2
- package/dist/namespaces/storage.js +12 -12
- package/dist/native-knowledge.d.ts +1 -1
- package/dist/native-knowledge.js +2 -2
- package/dist/negative.js +2 -2
- package/dist/operator-toolkit.d.ts +11 -2
- package/dist/operator-toolkit.js +36 -33
- package/dist/orchestration/compression-guideline-coordinator.d.ts +1 -1
- package/dist/orchestration/compression-guideline-coordinator.js +2 -2
- package/dist/orchestration/maintenance.d.ts +2 -2
- package/dist/orchestration/maintenance.js +14 -14
- package/dist/{orchestrator-Y9wyG4jK.d.ts → orchestrator-B2Y28Z9t.d.ts} +3 -3
- package/dist/orchestrator.d.ts +4 -4
- package/dist/orchestrator.js +109 -108
- package/dist/patterns-cli.d.ts +1 -1
- package/dist/policy-runtime.d.ts +1 -1
- package/dist/profiling.js +2 -2
- package/dist/provenance.d.ts +1 -1
- package/dist/{purge-IHRX4Z7X.js → purge-NDWV6WYD.js} +4 -3
- package/dist/{purge-IHRX4Z7X.js.map → purge-NDWV6WYD.js.map} +1 -1
- package/dist/qmd-Db9mOkzB.d.ts +330 -0
- package/dist/qmd-preflight.d.ts +5 -0
- package/dist/qmd-preflight.js +31 -0
- package/dist/qmd-recall-cache.d.ts +1 -1
- package/dist/qmd.d.ts +3 -305
- package/dist/qmd.js +11 -9
- package/dist/recall-disclosure-escalation.d.ts +1 -1
- package/dist/recall-explain-renderer.d.ts +1 -1
- package/dist/recall-planner-llm.d.ts +1 -1
- package/dist/recall-planner-llm.js +4 -4
- package/dist/recall-qos.js +2 -2
- package/dist/recall-state.d.ts +1 -1
- package/dist/recall-state.js +2 -2
- package/dist/recall-tag-filter.d.ts +1 -1
- package/dist/recall-timings.d.ts +1 -1
- package/dist/recall-xray-cli.d.ts +1 -1
- package/dist/recall-xray-renderer.d.ts +1 -1
- package/dist/recall-xray.d.ts +1 -1
- package/dist/relevance.js +2 -2
- package/dist/{resolution-33PIX3TT.js → resolution-VH4NGENZ.js} +3 -3
- package/dist/resolution-VH4NGENZ.js.map +1 -0
- package/dist/resolve-auth-token.d.ts +1 -1
- package/dist/resolve-provider-secret.js +2 -2
- package/dist/resume-bundles.js +4 -4
- package/dist/retrieval-agents.d.ts +2 -2
- package/dist/retrieval-agents.js +2 -2
- package/dist/retrieval-tiers.d.ts +1 -1
- package/dist/routing/engine.d.ts +1 -1
- package/dist/routing/store.d.ts +1 -1
- package/dist/routing/store.js +2 -2
- package/dist/runtime/better-sqlite.d.ts +17 -1
- package/dist/runtime/better-sqlite.js +5 -3
- package/dist/schemas.d.ts +22 -22
- package/dist/search/embed-helper.d.ts +1 -1
- package/dist/search/embed-helper.js +2 -2
- package/dist/search/factory.d.ts +1 -1
- package/dist/search/factory.js +14 -13
- package/dist/search/index.d.ts +1 -1
- package/dist/search/index.js +18 -17
- package/dist/search/lancedb-backend.d.ts +1 -1
- package/dist/search/lancedb-backend.js +3 -3
- package/dist/search/meilisearch-backend.d.ts +1 -1
- package/dist/search/meilisearch-backend.js +3 -3
- package/dist/search/noop-backend.d.ts +1 -1
- package/dist/search/orama-backend.d.ts +1 -1
- package/dist/search/orama-backend.js +3 -3
- package/dist/search/port.d.ts +1 -1
- package/dist/search/remote-backend.d.ts +1 -1
- package/dist/search/remote-backend.js +2 -2
- package/dist/{semantic-consolidation-bfXJNKVf.d.ts → semantic-consolidation-BDVIbLRw.d.ts} +1 -1
- package/dist/semantic-consolidation.d.ts +2 -2
- package/dist/semantic-consolidation.js +14 -14
- package/dist/semantic-rule-promotion.js +12 -12
- package/dist/semantic-rule-verifier.d.ts +1 -1
- package/dist/semantic-rule-verifier.js +12 -12
- package/dist/session-observer-bands.d.ts +1 -1
- package/dist/session-observer-state.d.ts +1 -1
- package/dist/session-observer-state.js +2 -2
- package/dist/session-transcript-migration.js +3 -3
- package/dist/shared-context/manager.d.ts +1 -1
- package/dist/shared-context/manager.js +2 -2
- package/dist/signal.d.ts +1 -1
- package/dist/storage-guards.js +2 -2
- package/dist/storage.d.ts +1 -1
- package/dist/storage.js +11 -11
- package/dist/summarizer.d.ts +1 -1
- package/dist/summarizer.js +7 -7
- package/dist/summary-snapshot.d.ts +1 -1
- package/dist/temporal-supersession.d.ts +1 -1
- package/dist/temporal-supersession.js +2 -2
- package/dist/temporal-validity.d.ts +1 -1
- package/dist/threading.d.ts +1 -1
- package/dist/threading.js +2 -2
- package/dist/tier-migration.d.ts +1 -1
- package/dist/tier-routing.d.ts +1 -1
- package/dist/topics.d.ts +1 -1
- package/dist/transcript.d.ts +1 -1
- package/dist/transcript.js +2 -2
- package/dist/transfer/autodetect.js +1 -1
- package/dist/transfer/backup.js +1 -1
- package/dist/transfer/capsule-export.js +2 -2
- package/dist/transfer/export-sqlite.js +2 -2
- package/dist/transfer/import-sqlite.js +2 -2
- package/dist/transfer/types.d.ts +12 -12
- package/dist/trust-score-stage.d.ts +1 -1
- package/dist/trust-score.d.ts +1 -1
- package/dist/{types-D1iBqhJA.d.ts → types-ljkd82rn.d.ts} +13 -1
- package/dist/types.d.ts +1 -1
- package/dist/utility-runtime.d.ts +1 -1
- package/dist/verified-recall.js +13 -13
- package/package.json +2 -2
- package/src/logger.test.ts +120 -0
- package/src/logger.ts +37 -9
- package/src/memory-projection-open-failure.test.ts +322 -0
- package/src/memory-projection-store.ts +179 -2
- package/src/operator-toolkit.ts +64 -0
- package/src/qmd-preflight.test.ts +197 -0
- package/src/qmd-preflight.ts +167 -0
- package/src/qmd.ts +115 -129
- package/src/runtime/better-sqlite.test.ts +54 -0
- package/src/runtime/better-sqlite.ts +83 -3
- package/src/wearables/cleanup.test.ts +56 -0
- package/src/wearables/cleanup.ts +22 -1
- package/src/wearables/config.test.ts +25 -0
- package/src/wearables/config.ts +31 -4
- package/src/wearables/types.ts +12 -1
- package/dist/chunk-27CQIO7T.js.map +0 -1
- package/dist/chunk-6KYMPV2O.js.map +0 -1
- package/dist/chunk-IBAU2RZN.js.map +0 -1
- package/dist/chunk-MYNRJGOY.js.map +0 -1
- package/dist/chunk-NU3CSQ4H.js +0 -48
- package/dist/chunk-NU3CSQ4H.js.map +0 -1
- package/dist/chunk-SXN3LCUT.js.map +0 -1
- /package/dist/{auto-sync-2SOUW4JW.js.map → auto-sync-HMHQHUAG.js.map} +0 -0
- /package/dist/{chunk-PSNMICC2.js.map → chunk-27FLVMDC.js.map} +0 -0
- /package/dist/{chunk-7FL4CNPV.js.map → chunk-2A3OXAP2.js.map} +0 -0
- /package/dist/{chunk-U5HD7BB2.js.map → chunk-2FOD2YY3.js.map} +0 -0
- /package/dist/{chunk-GG6AJN7A.js.map → chunk-2IZAGAJR.js.map} +0 -0
- /package/dist/{chunk-6Y4JTNX7.js.map → chunk-2MR3MFQB.js.map} +0 -0
- /package/dist/{chunk-AU7Q3LSC.js.map → chunk-2QSZNTDO.js.map} +0 -0
- /package/dist/{chunk-7OSULYN2.js.map → chunk-4BFRBP45.js.map} +0 -0
- /package/dist/{chunk-DQC3ZZYM.js.map → chunk-4KJLYTLS.js.map} +0 -0
- /package/dist/{chunk-DMKMQTBH.js.map → chunk-4P4626RY.js.map} +0 -0
- /package/dist/{chunk-GY3SKOS4.js.map → chunk-5EZXHU36.js.map} +0 -0
- /package/dist/{chunk-OW4UXZNP.js.map → chunk-5QZDMSSN.js.map} +0 -0
- /package/dist/{chunk-OTMAYEY5.js.map → chunk-66FLU5BT.js.map} +0 -0
- /package/dist/{chunk-J2QYB2D5.js.map → chunk-6BISDEN6.js.map} +0 -0
- /package/dist/{chunk-CXBFYSQY.js.map → chunk-6ZIETXZ2.js.map} +0 -0
- /package/dist/{chunk-KYYL4U6X.js.map → chunk-7N2RUBAP.js.map} +0 -0
- /package/dist/{chunk-U3TRHUSJ.js.map → chunk-7QWMTOIF.js.map} +0 -0
- /package/dist/{chunk-L24JROPR.js.map → chunk-7WSLR2CU.js.map} +0 -0
- /package/dist/{chunk-EXM3CQTZ.js.map → chunk-A3GOMRJG.js.map} +0 -0
- /package/dist/{chunk-DCWIQFNA.js.map → chunk-AKUCU7CI.js.map} +0 -0
- /package/dist/{chunk-XHYGJVXL.js.map → chunk-AXTXN4T6.js.map} +0 -0
- /package/dist/{chunk-L42FA4BU.js.map → chunk-BGTAL46Y.js.map} +0 -0
- /package/dist/{chunk-5QANTTY2.js.map → chunk-BRDJ2HFP.js.map} +0 -0
- /package/dist/{chunk-7VFZTJ7K.js.map → chunk-BV2TIZ4F.js.map} +0 -0
- /package/dist/{chunk-QMN3CIFS.js.map → chunk-CGS7UKWM.js.map} +0 -0
- /package/dist/{chunk-LZ73Y56P.js.map → chunk-CLX5VDNY.js.map} +0 -0
- /package/dist/{chunk-KYC3RLYJ.js.map → chunk-CXLNZXG5.js.map} +0 -0
- /package/dist/{chunk-YOELDDBF.js.map → chunk-CZAA5N5S.js.map} +0 -0
- /package/dist/{chunk-XG4NBBRW.js.map → chunk-CZAZX7JP.js.map} +0 -0
- /package/dist/{chunk-QOGXUNWL.js.map → chunk-CZGIIADK.js.map} +0 -0
- /package/dist/{chunk-QRDOSYOR.js.map → chunk-DBRYEIGY.js.map} +0 -0
- /package/dist/{chunk-6QODJKOV.js.map → chunk-DGYCWCQ7.js.map} +0 -0
- /package/dist/{chunk-RFZW7NID.js.map → chunk-DR6PEJU3.js.map} +0 -0
- /package/dist/{chunk-UC4YOAC2.js.map → chunk-E5BK6CGV.js.map} +0 -0
- /package/dist/{chunk-TKZ3AL5V.js.map → chunk-EHK6KZHK.js.map} +0 -0
- /package/dist/{chunk-X5NUL5N4.js.map → chunk-EPGWWCZF.js.map} +0 -0
- /package/dist/{chunk-JZ7P26GF.js.map → chunk-F53UF7CI.js.map} +0 -0
- /package/dist/{chunk-SJHM6I4J.js.map → chunk-FC77VUX5.js.map} +0 -0
- /package/dist/{chunk-DKTSR7EK.js.map → chunk-FDPZVY6D.js.map} +0 -0
- /package/dist/{chunk-OUXPVWTZ.js.map → chunk-FLVL4L3L.js.map} +0 -0
- /package/dist/{chunk-ZI3YYOOV.js.map → chunk-FZGGZAAU.js.map} +0 -0
- /package/dist/{chunk-TQT3USCG.js.map → chunk-HG32URJJ.js.map} +0 -0
- /package/dist/{chunk-ZQNCHPTZ.js.map → chunk-HLTAPHZZ.js.map} +0 -0
- /package/dist/{chunk-LU7ATMRV.js.map → chunk-HRCACVHL.js.map} +0 -0
- /package/dist/{chunk-LOPZ2DWR.js.map → chunk-I73G4UH7.js.map} +0 -0
- /package/dist/{chunk-MJE4XV4A.js.map → chunk-IC6QX5TC.js.map} +0 -0
- /package/dist/{chunk-27TJRC2Y.js.map → chunk-ILFJ7AVS.js.map} +0 -0
- /package/dist/{chunk-VUA4S7P6.js.map → chunk-JIKJPDQJ.js.map} +0 -0
- /package/dist/{chunk-BKTC7JCA.js.map → chunk-JLR3JSTL.js.map} +0 -0
- /package/dist/{chunk-VQ34TERH.js.map → chunk-JZHHREBL.js.map} +0 -0
- /package/dist/{chunk-IQVQJJL7.js.map → chunk-K2ZLHESQ.js.map} +0 -0
- /package/dist/{chunk-PWFWCGOO.js.map → chunk-KAWTOYAW.js.map} +0 -0
- /package/dist/{chunk-YRZMCPC5.js.map → chunk-KCJ5H6XQ.js.map} +0 -0
- /package/dist/{chunk-DJECGBBM.js.map → chunk-KQDNAXOM.js.map} +0 -0
- /package/dist/{chunk-WIWPSQYU.js.map → chunk-KQPJLK3X.js.map} +0 -0
- /package/dist/{chunk-XNWLD3RR.js.map → chunk-LOR6ZM5R.js.map} +0 -0
- /package/dist/{chunk-EGLUVV6B.js.map → chunk-M7GGMGMZ.js.map} +0 -0
- /package/dist/{chunk-YTU7WKYN.js.map → chunk-MNUHL34L.js.map} +0 -0
- /package/dist/{chunk-ZCWIH4LH.js.map → chunk-MRHFJV44.js.map} +0 -0
- /package/dist/{chunk-W4H5A3TJ.js.map → chunk-MTR7BZQN.js.map} +0 -0
- /package/dist/{chunk-PNLCEFE4.js.map → chunk-MTWKDOVO.js.map} +0 -0
- /package/dist/{chunk-TECYUPFB.js.map → chunk-NG7YBCJO.js.map} +0 -0
- /package/dist/{chunk-OSMBC3JU.js.map → chunk-O2EL7EGG.js.map} +0 -0
- /package/dist/{chunk-HW5EF6LV.js.map → chunk-O7BBFJLP.js.map} +0 -0
- /package/dist/{chunk-EHQ5HO5T.js.map → chunk-OBMR4VIX.js.map} +0 -0
- /package/dist/{chunk-VFHDUFH5.js.map → chunk-OHDJ34CC.js.map} +0 -0
- /package/dist/{chunk-6SHAUM5S.js.map → chunk-OUMTBFSQ.js.map} +0 -0
- /package/dist/{chunk-EEKDRAWX.js.map → chunk-PRTA6444.js.map} +0 -0
- /package/dist/{chunk-IQ7WCZRW.js.map → chunk-PUBTHW5O.js.map} +0 -0
- /package/dist/{chunk-AGNBY3VG.js.map → chunk-PYHOAXTI.js.map} +0 -0
- /package/dist/{chunk-UUH4YQOF.js.map → chunk-QLULYKWZ.js.map} +0 -0
- /package/dist/{chunk-DBSMLRTO.js.map → chunk-QT2RZNRT.js.map} +0 -0
- /package/dist/{chunk-HM6UJPPO.js.map → chunk-QWGNDWKB.js.map} +0 -0
- /package/dist/{chunk-KCQA46NR.js.map → chunk-R5VAVVHP.js.map} +0 -0
- /package/dist/{chunk-O7GOFAM3.js.map → chunk-RP7YXA67.js.map} +0 -0
- /package/dist/{chunk-BVKCV2ZY.js.map → chunk-SMX5267P.js.map} +0 -0
- /package/dist/{chunk-XBZQRZ6G.js.map → chunk-TN7THW5U.js.map} +0 -0
- /package/dist/{chunk-JRZV3767.js.map → chunk-UI4FVU6T.js.map} +0 -0
- /package/dist/{chunk-VGUOEDTU.js.map → chunk-V3UE5I5F.js.map} +0 -0
- /package/dist/{chunk-7WV3F5DQ.js.map → chunk-V7IZKJI3.js.map} +0 -0
- /package/dist/{chunk-VL7DP3OW.js.map → chunk-VFSP73LP.js.map} +0 -0
- /package/dist/{chunk-C2DDH5UN.js.map → chunk-X4A62ET7.js.map} +0 -0
- /package/dist/{chunk-WRBDUNSW.js.map → chunk-XN3TUUX2.js.map} +0 -0
- /package/dist/{chunk-NNL4LJAD.js.map → chunk-XZ5I7OZR.js.map} +0 -0
- /package/dist/{chunk-K2I3AWBR.js.map → chunk-YDSWRD33.js.map} +0 -0
- /package/dist/{chunk-3NN73CM7.js.map → chunk-YFBTVOMB.js.map} +0 -0
- /package/dist/{chunk-44YXWE35.js.map → chunk-YPEMNNNY.js.map} +0 -0
- /package/dist/{chunk-ZZMRXJFY.js.map → chunk-ZIDSD6VC.js.map} +0 -0
- /package/dist/{chunk-BMANIB5B.js.map → chunk-ZPOROUTX.js.map} +0 -0
- /package/dist/{contradiction-scan-3FJYWS3G.js.map → contradiction-scan-SSKCAMLX.js.map} +0 -0
- /package/dist/{forget-OEE5OELK.js.map → forget-KHF7A44H.js.map} +0 -0
- /package/dist/{graph-edge-decay-RXUCJIN4.js.map → graph-edge-decay-FO7AM4UR.js.map} +0 -0
- /package/dist/{resolution-33PIX3TT.js.map → qmd-preflight.js.map} +0 -0
|
@@ -12,7 +12,10 @@ import type {
|
|
|
12
12
|
MemoryProjectionCurrentState,
|
|
13
13
|
MemoryStatus,
|
|
14
14
|
} from "./types.js";
|
|
15
|
+
import { log } from "./logger.js";
|
|
15
16
|
import {
|
|
17
|
+
displayErrorDetail,
|
|
18
|
+
isLikelyBetterSqlite3NativeBindingError,
|
|
16
19
|
openBetterSqlite3,
|
|
17
20
|
type BetterSqlite3Database,
|
|
18
21
|
} from "./runtime/better-sqlite.js";
|
|
@@ -314,14 +317,188 @@ export function initializeMemoryProjectionDb(db: BetterSqlite3Database): void {
|
|
|
314
317
|
.run("schemaVersion", String(MEMORY_PROJECTION_SCHEMA_VERSION));
|
|
315
318
|
}
|
|
316
319
|
|
|
320
|
+
// Test seam (issue #1829): the readonly projection opener is overridable so
|
|
321
|
+
// tests can inject an ABI-style throw against a PRESENT file without faking a
|
|
322
|
+
// corrupt native binding. Default delegates to the shared opener.
|
|
323
|
+
type ProjectionReadonlyOpener = typeof openBetterSqlite3;
|
|
324
|
+
let projectionReadonlyOpener: ProjectionReadonlyOpener = openBetterSqlite3;
|
|
325
|
+
|
|
326
|
+
/** @internal Test seam: override the readonly projection opener; returns a restore fn. */
|
|
327
|
+
export function __setProjectionReadonlyOpenerForTest(
|
|
328
|
+
opener: ProjectionReadonlyOpener | null,
|
|
329
|
+
): () => void {
|
|
330
|
+
const previous = projectionReadonlyOpener;
|
|
331
|
+
projectionReadonlyOpener = opener ?? openBetterSqlite3;
|
|
332
|
+
return () => {
|
|
333
|
+
projectionReadonlyOpener = previous;
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// Rate-limited suppression for open failures — mirrors warnProjectionFallback
|
|
338
|
+
// in storage-guards.ts (5-min dedup per memoryDir). A present-but-unopenable
|
|
339
|
+
// projection is a real failure that must surface, but it repeats on every
|
|
340
|
+
// browse, so the log is throttled to keep output actionable (issue #1829).
|
|
341
|
+
const PROJECTION_OPEN_FAILURE_LOG_INTERVAL_MS = 5 * 60_000;
|
|
342
|
+
const projectionOpenFailureLoggedAt = new Map<string, number>();
|
|
343
|
+
|
|
344
|
+
/** @internal Test seam: clear the rate-limit dedup map. */
|
|
345
|
+
export function __resetProjectionOpenFailureSuppressionForTest(): void {
|
|
346
|
+
projectionOpenFailureLoggedAt.clear();
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
function logProjectionOpenFailure(memoryDir: string, error: unknown): void {
|
|
350
|
+
const now = Date.now();
|
|
351
|
+
const warnedAt = projectionOpenFailureLoggedAt.get(memoryDir) ?? 0;
|
|
352
|
+
if (now - warnedAt < PROJECTION_OPEN_FAILURE_LOG_INTERVAL_MS) return;
|
|
353
|
+
projectionOpenFailureLoggedAt.set(memoryDir, now);
|
|
354
|
+
// Path-free detail only (rule 11 / CodeQL js/stack-trace-exposure): the raw
|
|
355
|
+
// message can embed absolute loader paths; displayErrorDetail returns class +
|
|
356
|
+
// code. This is the DISTINCT signal that was missing before #1829, when a
|
|
357
|
+
// wrong-ABI build collapsed into the same silent null as a missing file.
|
|
358
|
+
const detail = displayErrorDetail(error);
|
|
359
|
+
const nativeMismatch = isLikelyBetterSqlite3NativeBindingError(error);
|
|
360
|
+
const detailSuffix = detail ? ` (${detail})` : "";
|
|
361
|
+
const abiSuffix = nativeMismatch
|
|
362
|
+
? " — native binding built for the wrong Node.js ABI; rebuild better-sqlite3 for this process"
|
|
363
|
+
: "";
|
|
364
|
+
log.warn(
|
|
365
|
+
`storage.memory-projection: index present but could not be opened${detailSuffix}${abiSuffix}; falling back to full-corpus scan`,
|
|
366
|
+
);
|
|
367
|
+
}
|
|
368
|
+
|
|
317
369
|
function openProjectionReadonly(memoryDir: string): BetterSqlite3Database | null {
|
|
318
370
|
const dbPath = getMemoryProjectionPath(memoryDir);
|
|
371
|
+
// A MISSING file is a normal fallback (cold install, never built). A file
|
|
372
|
+
// that EXISTS but cannot open (wrong ABI / corrupt / bad perms) is a real
|
|
373
|
+
// failure that must surface (issue #1829): previously both collapsed into
|
|
374
|
+
// the same silent null, hiding native-binding mismatches behind 17-27s
|
|
375
|
+
// full-corpus scans on every memory list.
|
|
376
|
+
if (!fs.existsSync(dbPath)) return null;
|
|
319
377
|
try {
|
|
320
|
-
return
|
|
321
|
-
} catch {
|
|
378
|
+
return projectionReadonlyOpener(dbPath, { readonly: true, fileMustExist: true });
|
|
379
|
+
} catch (error) {
|
|
380
|
+
logProjectionOpenFailure(memoryDir, error);
|
|
322
381
|
return null;
|
|
323
382
|
}
|
|
324
383
|
}
|
|
384
|
+
|
|
385
|
+
export type ProjectionHealthState = "absent" | "openable" | "present-but-invalid" | "unopenable";
|
|
386
|
+
|
|
387
|
+
export interface ProjectionHealthProbe {
|
|
388
|
+
state: ProjectionHealthState;
|
|
389
|
+
/** Path-free detail (class + code) when state !== "absent"/"openable"; "" otherwise. */
|
|
390
|
+
detail: string;
|
|
391
|
+
/** true when the open failure is classified as a native-binding ABI mismatch. */
|
|
392
|
+
nativeBindingMismatch: boolean;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* Confirm the projection schema is present and queryable, not just that the
|
|
397
|
+
* sqlite file opens (issue #1848 round 2). A file that opens but has the
|
|
398
|
+
* projection tables missing — or is corrupt and only fails on first statement
|
|
399
|
+
* — previously reported `openable`/ok, so `remnic doctor` said healthy while
|
|
400
|
+
* browse silently fell back to full-corpus scans. `memory_current` is the
|
|
401
|
+
* core browse table; if it is absent or unreadable the projection cannot
|
|
402
|
+
* serve any query. Throws so the probe can report a distinct state.
|
|
403
|
+
*/
|
|
404
|
+
// Named so displayErrorDetail surfaces a clear, path-free identifier in the
|
|
405
|
+
// doctor report rather than a generic "Error".
|
|
406
|
+
class ProjectionSchemaError extends Error {
|
|
407
|
+
constructor(message: string) {
|
|
408
|
+
super(message);
|
|
409
|
+
this.name = "ProjectionSchemaError";
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* Columns `readProjectedMemoryBrowse` SELECTs from `memory_current` with no
|
|
415
|
+
* graceful fallback. `tags_json`/`preview_text` fall back via
|
|
416
|
+
* memoryCurrentSelectExpressions and `path_valid` falls back via the legacy
|
|
417
|
+
* full-scan branch — none of those make a browse query throw. A projection
|
|
418
|
+
* whose `memory_current` lacks any of these columns cannot serve browse
|
|
419
|
+
* queries: the SELECT throws and browse silently falls back to a full-corpus
|
|
420
|
+
* scan. Enumerated directly from readProjectedMemoryBrowse's SELECT list +
|
|
421
|
+
* ORDER BY references (issue #1848 round 3).
|
|
422
|
+
*/
|
|
423
|
+
const REQUIRED_MEMORY_CURRENT_COLUMNS = [
|
|
424
|
+
"memory_id",
|
|
425
|
+
"path_rel",
|
|
426
|
+
"category",
|
|
427
|
+
"status",
|
|
428
|
+
"created_at",
|
|
429
|
+
"updated_at",
|
|
430
|
+
"entity_ref",
|
|
431
|
+
] as const;
|
|
432
|
+
|
|
433
|
+
function assertProjectionSchemaReadable(db: BetterSqlite3Database): void {
|
|
434
|
+
const row = db
|
|
435
|
+
.prepare(
|
|
436
|
+
"SELECT name FROM sqlite_master WHERE type = 'table' AND name = 'memory_current'",
|
|
437
|
+
)
|
|
438
|
+
.get() as { name: string } | undefined;
|
|
439
|
+
if (!row?.name) {
|
|
440
|
+
throw new ProjectionSchemaError(
|
|
441
|
+
"projection schema is missing or was not initialized (memory_current table not found)",
|
|
442
|
+
);
|
|
443
|
+
}
|
|
444
|
+
// Round 3 (issue #1848): verify the columns readProjectedMemoryBrowse
|
|
445
|
+
// actually SELECT are present, not just that the table exists. A file like
|
|
446
|
+
// `CREATE TABLE memory_current (x INTEGER)` passed the round-2 table-existence
|
|
447
|
+
// + SELECT-1 check but cannot serve browse queries, so browse silently fell
|
|
448
|
+
// back to full-corpus scans while `remnic doctor` reported healthy. Classify
|
|
449
|
+
// such a stale/half-built table as present-but-invalid so the operator gets a
|
|
450
|
+
// rebuild hint instead of a false-clean bill of health.
|
|
451
|
+
const columns = listTableColumns(db, "memory_current");
|
|
452
|
+
const missing = REQUIRED_MEMORY_CURRENT_COLUMNS.filter((c) => !columns.has(c));
|
|
453
|
+
if (missing.length > 0) {
|
|
454
|
+
throw new ProjectionSchemaError(
|
|
455
|
+
`projection memory_current table is missing required column(s): ${missing.join(", ")}`,
|
|
456
|
+
);
|
|
457
|
+
}
|
|
458
|
+
// Also confirm the table is queryable — catches physical corruption that
|
|
459
|
+
// only surfaces on first read (the file opens but statements fail).
|
|
460
|
+
db.prepare("SELECT 1 FROM memory_current LIMIT 1").get();
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
/**
|
|
464
|
+
* Stat + attempt-open + schema-validate the memory projection WITHOUT keeping
|
|
465
|
+
* a handle, for the `remnic doctor` health check (issue #1829 / #1848).
|
|
466
|
+
* Distinguishes four states so operators can tell "not built yet" (normal) from
|
|
467
|
+
* "exists but broken" (needs a rebuild / native-binding rebuild). Never throws.
|
|
468
|
+
*/
|
|
469
|
+
export function probeProjectionHealth(memoryDir: string): ProjectionHealthProbe {
|
|
470
|
+
const dbPath = getMemoryProjectionPath(memoryDir);
|
|
471
|
+
if (!fs.existsSync(dbPath)) {
|
|
472
|
+
return { state: "absent", detail: "", nativeBindingMismatch: false };
|
|
473
|
+
}
|
|
474
|
+
let db: BetterSqlite3Database | null = null;
|
|
475
|
+
try {
|
|
476
|
+
db = projectionReadonlyOpener(dbPath, { readonly: true, fileMustExist: true });
|
|
477
|
+
// Validate the projection is actually USABLE, not just that the file
|
|
478
|
+
// opens (issue #1848 round 2): a file that opens but has the schema
|
|
479
|
+
// missing/corrupt previously reported ok, so doctor said healthy while
|
|
480
|
+
// browse silently fell back to full-corpus scans.
|
|
481
|
+
assertProjectionSchemaReadable(db);
|
|
482
|
+
return { state: "openable", detail: "", nativeBindingMismatch: false };
|
|
483
|
+
} catch (error) {
|
|
484
|
+
// If the file OPENED (db assigned) but the schema query failed, the
|
|
485
|
+
// native binding is fine — the projection itself is stale/corrupt.
|
|
486
|
+
if (db) {
|
|
487
|
+
return {
|
|
488
|
+
state: "present-but-invalid",
|
|
489
|
+
detail: displayErrorDetail(error),
|
|
490
|
+
nativeBindingMismatch: false,
|
|
491
|
+
};
|
|
492
|
+
}
|
|
493
|
+
return {
|
|
494
|
+
state: "unopenable",
|
|
495
|
+
detail: displayErrorDetail(error),
|
|
496
|
+
nativeBindingMismatch: isLikelyBetterSqlite3NativeBindingError(error),
|
|
497
|
+
};
|
|
498
|
+
} finally {
|
|
499
|
+
db?.close();
|
|
500
|
+
}
|
|
501
|
+
}
|
|
325
502
|
function updateProjectionBestEffort(
|
|
326
503
|
memoryDir: string,
|
|
327
504
|
sql: string,
|
package/src/operator-toolkit.ts
CHANGED
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
resolveOpenClawWorkspaceStatePath,
|
|
12
12
|
} from "./native-knowledge.js";
|
|
13
13
|
import { StorageManager } from "./storage.js";
|
|
14
|
+
import { probeProjectionHealth } from "./memory-projection-store.js";
|
|
14
15
|
import { listNamespaces } from "./namespaces/migrate.js";
|
|
15
16
|
import { summarizeNamespaceMaintenanceHealth } from "./maintenance/namespace-maintenance-fanout.js";
|
|
16
17
|
import {
|
|
@@ -1094,6 +1095,64 @@ export function summarizeProvenanceCoverage(
|
|
|
1094
1095
|
return { counts, total: memories.length };
|
|
1095
1096
|
}
|
|
1096
1097
|
|
|
1098
|
+
/**
|
|
1099
|
+
* Memory projection health (issue #1829). A present-but-unopenable projection
|
|
1100
|
+
* (wrong ABI / corrupt / bad perms) previously collapsed into the same silent
|
|
1101
|
+
* null as a missing file, hiding native-binding mismatches behind full-corpus
|
|
1102
|
+
* scans. This check stats + attempt-opens the projection so `remnic doctor`
|
|
1103
|
+
* distinguishes "not built yet" (normal) from "exists but broken" (needs a
|
|
1104
|
+
* rebuild / native-binding rebuild). Never throws.
|
|
1105
|
+
*/
|
|
1106
|
+
export function summarizeProjectionHealth(
|
|
1107
|
+
config: Pick<PluginConfig, "memoryDir">,
|
|
1108
|
+
): OperatorDoctorCheck {
|
|
1109
|
+
const probe = probeProjectionHealth(config.memoryDir);
|
|
1110
|
+
if (probe.state === "absent") {
|
|
1111
|
+
return {
|
|
1112
|
+
key: "memory_projection",
|
|
1113
|
+
status: "ok",
|
|
1114
|
+
summary: "Memory projection index not built yet (normal until first maintenance run).",
|
|
1115
|
+
details: { state: probe.state },
|
|
1116
|
+
};
|
|
1117
|
+
}
|
|
1118
|
+
if (probe.state === "openable") {
|
|
1119
|
+
return {
|
|
1120
|
+
key: "memory_projection",
|
|
1121
|
+
status: "ok",
|
|
1122
|
+
summary: "Memory projection index opens cleanly.",
|
|
1123
|
+
details: { state: probe.state },
|
|
1124
|
+
};
|
|
1125
|
+
}
|
|
1126
|
+
if (probe.state === "present-but-invalid") {
|
|
1127
|
+
const schemaDetailSuffix = probe.detail ? ` (${probe.detail})` : "";
|
|
1128
|
+
return {
|
|
1129
|
+
key: "memory_projection",
|
|
1130
|
+
status: "error",
|
|
1131
|
+
summary: `Memory projection index exists but its schema is missing or unreadable${schemaDetailSuffix}; memory browse is falling back to full-corpus scans.`,
|
|
1132
|
+
remediation:
|
|
1133
|
+
"Rebuild the projection with `remnic rebuild-memory-projection --write`.",
|
|
1134
|
+
details: { state: probe.state, detail: probe.detail },
|
|
1135
|
+
};
|
|
1136
|
+
}
|
|
1137
|
+
const nativeSuffix = probe.nativeBindingMismatch
|
|
1138
|
+
? " Native binding appears built for the wrong Node.js ABI."
|
|
1139
|
+
: "";
|
|
1140
|
+
const detailSuffix = probe.detail ? ` (${probe.detail})` : "";
|
|
1141
|
+
return {
|
|
1142
|
+
key: "memory_projection",
|
|
1143
|
+
status: "error",
|
|
1144
|
+
summary: `Memory projection index exists but could not be opened${detailSuffix}.${nativeSuffix}`,
|
|
1145
|
+
remediation: probe.nativeBindingMismatch
|
|
1146
|
+
? "Rebuild the better-sqlite3 native binding for this Node.js (`node scripts/ensure-better-sqlite3.mjs` or `pnpm rebuild better-sqlite3`), then restart."
|
|
1147
|
+
: "Rebuild the projection with `remnic rebuild-memory-projection --write`; check file permissions and integrity.",
|
|
1148
|
+
details: {
|
|
1149
|
+
state: probe.state,
|
|
1150
|
+
detail: probe.detail,
|
|
1151
|
+
nativeBindingMismatch: probe.nativeBindingMismatch,
|
|
1152
|
+
},
|
|
1153
|
+
};
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1097
1156
|
export async function runOperatorDoctor(options: OperatorDoctorOptions): Promise<OperatorDoctorReport> {
|
|
1098
1157
|
const now = options.now ?? new Date();
|
|
1099
1158
|
const configStatus = await loadCliPluginConfig(options.configPath);
|
|
@@ -1211,6 +1270,11 @@ export async function runOperatorDoctor(options: OperatorDoctorOptions): Promise
|
|
|
1211
1270
|
details: meta,
|
|
1212
1271
|
});
|
|
1213
1272
|
|
|
1273
|
+
// Memory projection health (issue #1829). Surfaces a present-but-unopenable
|
|
1274
|
+
// projection (wrong ABI / corrupt) that previously degraded every memory
|
|
1275
|
+
// list to a silent full-corpus scan. Absent and openable are both "ok".
|
|
1276
|
+
checks.push(summarizeProjectionHealth(config));
|
|
1277
|
+
|
|
1214
1278
|
// Provenance coverage (issue #1575 PR 3). Operators need to see the
|
|
1215
1279
|
// verified/unverified/none distribution grow as extraction backfills.
|
|
1216
1280
|
// Best-effort: a read failure surfaces as a warn, not an error.
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import test from "node:test";
|
|
3
|
+
import { abortError } from "./abort-error.js";
|
|
4
|
+
import { QmdClient } from "./qmd.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Internals exposed only for preflight testing. Cast once to a NAMED type (not
|
|
8
|
+
* an inline shape) so the compiler never fabricates a one-off member type; the
|
|
9
|
+
* unchecked access is scoped to these deliberately-injected seams. Issue #1841.
|
|
10
|
+
*/
|
|
11
|
+
type QmdPreflightInternals = {
|
|
12
|
+
configuredQmdPath: string | undefined;
|
|
13
|
+
runVersionProbe: (qmdPath: string, signal?: AbortSignal) => Promise<{ stdout: string; stderr: string }>;
|
|
14
|
+
logCliProbeWarning: (message: string) => void;
|
|
15
|
+
probeCli: (options?: {
|
|
16
|
+
allowAutoUpgrade?: boolean;
|
|
17
|
+
preserveStateOnFailure?: boolean;
|
|
18
|
+
signal?: AbortSignal;
|
|
19
|
+
}) => Promise<boolean>;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const CONFIGURED_PATH = "/opt/qmd/bin/qmd";
|
|
23
|
+
|
|
24
|
+
/** Build the Node spawn ENOENT error shape (Error carrying `code: "ENOENT"`). */
|
|
25
|
+
function spawnENOENT(qmdPath: string): Error & { code: string } {
|
|
26
|
+
return Object.assign(new Error(`spawn ${qmdPath} ENOENT`), {
|
|
27
|
+
code: "ENOENT" as const,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function timeoutError(): Error & { timedOut: true } {
|
|
32
|
+
// Mirrors the error runCommandWithTimeout rejects with on deadline: a plain
|
|
33
|
+
// Error flagged with `timedOut: true` — the structured signal classifyProbeFailure
|
|
34
|
+
// keys on (never the message text). Issue #1841.
|
|
35
|
+
return Object.assign(new Error("qmd --version timed out after 8000ms"), {
|
|
36
|
+
timedOut: true as const,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Wire a fresh client whose probe runner + warning sink are fully scripted. */
|
|
41
|
+
function harness(opts: {
|
|
42
|
+
configuredBehavior: () => Promise<{ stdout: string; stderr: string }>;
|
|
43
|
+
}): {
|
|
44
|
+
internals: QmdPreflightInternals;
|
|
45
|
+
warnings: string[];
|
|
46
|
+
configuredProbeCalls: () => number;
|
|
47
|
+
} {
|
|
48
|
+
const client = new QmdClient("memories", 3, {
|
|
49
|
+
qmdPath: CONFIGURED_PATH,
|
|
50
|
+
qmdFallbackPaths: [],
|
|
51
|
+
});
|
|
52
|
+
const internals = client as unknown as QmdPreflightInternals;
|
|
53
|
+
const warnings: string[] = [];
|
|
54
|
+
internals.logCliProbeWarning = (message: string) => {
|
|
55
|
+
warnings.push(message);
|
|
56
|
+
};
|
|
57
|
+
let configuredCalls = 0;
|
|
58
|
+
internals.runVersionProbe = async (qmdPath: string) => {
|
|
59
|
+
if (qmdPath === CONFIGURED_PATH) {
|
|
60
|
+
configuredCalls += 1;
|
|
61
|
+
return opts.configuredBehavior();
|
|
62
|
+
}
|
|
63
|
+
// Any non-configured path (PATH/fallback probing) is a hard miss here so
|
|
64
|
+
// probeCli returns false without spawning a real qmd binary.
|
|
65
|
+
throw spawnENOENT(qmdPath);
|
|
66
|
+
};
|
|
67
|
+
return {
|
|
68
|
+
internals,
|
|
69
|
+
warnings,
|
|
70
|
+
configuredProbeCalls: () => configuredCalls,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
test("QmdClient preflight: transient timeout retries then succeeds with NO failure warning", async () => {
|
|
75
|
+
// Two timeouts, then success on the second retry.
|
|
76
|
+
const scripted: Array<"timeout" | "ok"> = ["timeout", "timeout", "ok"];
|
|
77
|
+
let i = 0;
|
|
78
|
+
const { internals, warnings, configuredProbeCalls } = harness({
|
|
79
|
+
configuredBehavior: async () => {
|
|
80
|
+
const step = scripted[i++];
|
|
81
|
+
if (step === "timeout") throw timeoutError();
|
|
82
|
+
return { stdout: "qmd 1.2.3\n", stderr: "" };
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
const ok = await internals.probeCli({ allowAutoUpgrade: false });
|
|
87
|
+
|
|
88
|
+
assert.equal(ok, true, "configured probe should succeed after retry");
|
|
89
|
+
assert.equal(configuredProbeCalls(), 3, "initial attempt + 2 retries for transient timeout");
|
|
90
|
+
assert.deepEqual(warnings, [], "no failure warning emitted when retry succeeds");
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test("QmdClient preflight: persistent timeout emits the distinct under-load warning (not 'configured qmdPath failed')", async () => {
|
|
94
|
+
const { internals, warnings, configuredProbeCalls } = harness({
|
|
95
|
+
configuredBehavior: async () => {
|
|
96
|
+
throw timeoutError();
|
|
97
|
+
},
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
const ok = await internals.probeCli({ allowAutoUpgrade: false });
|
|
101
|
+
|
|
102
|
+
assert.equal(ok, false, "exhausted retries fall through to unavailable");
|
|
103
|
+
assert.equal(configuredProbeCalls(), 3, "initial attempt + 2 retries before declaring transient failure");
|
|
104
|
+
assert.equal(warnings.length, 1, "exactly one failure warning");
|
|
105
|
+
const msg = warnings[0] ?? "";
|
|
106
|
+
assert.match(
|
|
107
|
+
msg,
|
|
108
|
+
/version check timed out \(host may be under load\); retried 2 times/,
|
|
109
|
+
"transient failure uses the under-load wording"
|
|
110
|
+
);
|
|
111
|
+
assert.doesNotMatch(msg, /configured qmdPath failed/, "must not read as a generic misconfiguration");
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
test("QmdClient preflight: genuine ENOENT fails fast (no retry) with the not-found/not-executable warning", async () => {
|
|
115
|
+
const { internals, warnings, configuredProbeCalls } = harness({
|
|
116
|
+
configuredBehavior: async () => {
|
|
117
|
+
throw spawnENOENT(CONFIGURED_PATH);
|
|
118
|
+
},
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
const ok = await internals.probeCli({ allowAutoUpgrade: false });
|
|
122
|
+
|
|
123
|
+
assert.equal(ok, false, "missing binary is unavailable");
|
|
124
|
+
assert.equal(configuredProbeCalls(), 1, "ENOENT is a hard misconfiguration: never retried");
|
|
125
|
+
assert.equal(warnings.length, 1, "exactly one failure warning");
|
|
126
|
+
const msg = warnings[0] ?? "";
|
|
127
|
+
assert.match(msg, /configured qmdPath not found or not executable/, "missing binary uses the not-found wording");
|
|
128
|
+
assert.match(msg, /spawn .* ENOENT/, "underlying error detail preserved");
|
|
129
|
+
assert.doesNotMatch(msg, /timed out|under load/, "must not read as a transient timeout");
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
test("QmdClient preflight: non-timeout exit-code failure keeps the generic configured-failed message and does not retry", async () => {
|
|
133
|
+
const { internals, warnings, configuredProbeCalls } = harness({
|
|
134
|
+
configuredBehavior: async () => {
|
|
135
|
+
throw new Error("qmd --version exited with code 2");
|
|
136
|
+
},
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
const ok = await internals.probeCli({ allowAutoUpgrade: false });
|
|
140
|
+
|
|
141
|
+
assert.equal(ok, false, "exit-code failure is unavailable");
|
|
142
|
+
assert.equal(configuredProbeCalls(), 1, "non-transient, non-missing failures are not retried");
|
|
143
|
+
assert.equal(warnings.length, 1, "exactly one failure warning");
|
|
144
|
+
const msg = warnings[0] ?? "";
|
|
145
|
+
assert.match(msg, /configured qmdPath failed/, "ambiguous exit-code failures keep the historical generic wording");
|
|
146
|
+
assert.doesNotMatch(
|
|
147
|
+
msg,
|
|
148
|
+
/timed out|under load|not found or not executable/,
|
|
149
|
+
"must not be misclassified as timeout or missing"
|
|
150
|
+
);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
test("QmdClient preflight: caller abort emits NO warning and does not retry", async () => {
|
|
154
|
+
// The caller cancelled (aborted signal + AbortError). Other QMD paths treat
|
|
155
|
+
// this as non-actionable noise via isCallerCancellation; the configured-path
|
|
156
|
+
// probe must do the same — no under-load warning, no transient retry. #1841.
|
|
157
|
+
const controller = new AbortController();
|
|
158
|
+
controller.abort();
|
|
159
|
+
const { internals, warnings, configuredProbeCalls } = harness({
|
|
160
|
+
configuredBehavior: async () => {
|
|
161
|
+
// A real probe rejects with an AbortError when the caller's signal fires.
|
|
162
|
+
throw abortError("qmd --version aborted");
|
|
163
|
+
},
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
const ok = await internals.probeCli({ allowAutoUpgrade: false, signal: controller.signal });
|
|
167
|
+
|
|
168
|
+
assert.equal(ok, false, "aborted configured probe falls through to unavailable");
|
|
169
|
+
assert.equal(configuredProbeCalls(), 1, "caller cancellation never retries");
|
|
170
|
+
assert.deepEqual(warnings, [], "caller cancellation is silent noise: no operator-facing warning");
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
test("QmdClient preflight: non-zero exit whose stderr says 'timed out'/'not found' is classified 'other' (not transient/missing)", async () => {
|
|
174
|
+
// Mirrors runCommandWithTimeout's non-zero-exit rejection: the child's stderr
|
|
175
|
+
// (here containing "timed out" and "not found") is embedded in the message.
|
|
176
|
+
// A healthy binary can emit those words in error text; they must NOT upgrade
|
|
177
|
+
// the failure to transient/missing. classifyProbeFailure keys on structured
|
|
178
|
+
// signals, so this generic exit-code failure stays 'other'. Issue #1841.
|
|
179
|
+
const { internals, warnings, configuredProbeCalls } = harness({
|
|
180
|
+
configuredBehavior: async () => {
|
|
181
|
+
throw new Error("qmd --version failed (code 2): error: model timed out while loading; config not found");
|
|
182
|
+
},
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
const ok = await internals.probeCli({ allowAutoUpgrade: false });
|
|
186
|
+
|
|
187
|
+
assert.equal(ok, false, "exit-code failure is unavailable");
|
|
188
|
+
assert.equal(configuredProbeCalls(), 1, "embedded-stderr failure is not transient — no retry");
|
|
189
|
+
assert.equal(warnings.length, 1, "exactly one failure warning");
|
|
190
|
+
const msg = warnings[0] ?? "";
|
|
191
|
+
assert.match(msg, /configured qmdPath failed/, "classified as generic 'other'");
|
|
192
|
+
assert.doesNotMatch(
|
|
193
|
+
msg,
|
|
194
|
+
/under load|not found or not executable/,
|
|
195
|
+
"must not misclassify embedded-stderr words as transient/missing"
|
|
196
|
+
);
|
|
197
|
+
});
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QMD version-check PREFLIGHT / probe-classification helpers.
|
|
3
|
+
*
|
|
4
|
+
* Pure, self-contained functions extracted from qmd.ts (issue #1841) so the
|
|
5
|
+
* QmdClient class file stays under the structural-ratchet LOC ceiling. These
|
|
6
|
+
* cover two related concerns of the startup preflight:
|
|
7
|
+
*
|
|
8
|
+
* 1. VERSION parsing/comparison — turning `qmd --version` output into a tuple,
|
|
9
|
+
* comparing against the supported target, and resolving feature capability
|
|
10
|
+
* flags from the parsed version.
|
|
11
|
+
* 2. PROBE failure classification — distinguishing a slow/overloaded binary
|
|
12
|
+
* (transient → retry) from a genuine misconfiguration (ENOENT/EACCES → fail
|
|
13
|
+
* fast) using STRUCTURED signals, never the error message.
|
|
14
|
+
*
|
|
15
|
+
* No behavioral change from qmd.ts round 1 (issue #1841): structured-signal
|
|
16
|
+
* classification (code=ENOENT/EACCES=>missing, timedOut flag=>transient,
|
|
17
|
+
* caller-cancel silenced, non-zero-exit=>other); backoff [300,800].
|
|
18
|
+
*/
|
|
19
|
+
import { isAbortError } from "./abort-error.js";
|
|
20
|
+
import type { QmdCapabilities, QmdVersionTuple } from "./qmd.js";
|
|
21
|
+
|
|
22
|
+
// Deadline (ms) for a single `qmd --version` probe.
|
|
23
|
+
export const QMD_PROBE_TIMEOUT_MS = 8_000;
|
|
24
|
+
|
|
25
|
+
// Backoff schedule (ms) for retrying a TRANSIENT (timeout/abort) version-check
|
|
26
|
+
// probe of a CONFIGURED qmdPath before declaring it failed. A binary that
|
|
27
|
+
// resolved and ran before is more likely slow under load than gone, so we retry
|
|
28
|
+
// transient failures but NOT ENOENT/EACCES (hard misconfiguration, fail fast).
|
|
29
|
+
// Length == number of retries after the initial attempt. Issue #1841.
|
|
30
|
+
export const QMD_PROBE_RETRY_BACKOFF_MS = [300, 800];
|
|
31
|
+
|
|
32
|
+
export const QMD_SUPPORTED_VERSION = "2.5.3";
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Classify a `qmd --version` probe failure so the preflight can distinguish a
|
|
36
|
+
* slow/overloaded binary (a genuine deadline timeout) from a real
|
|
37
|
+
* misconfiguration (ENOENT/EACCES — the configured path is missing or not
|
|
38
|
+
* executable). Only transient failures are retried; missing binaries fail fast.
|
|
39
|
+
*
|
|
40
|
+
* Classification keys on STRUCTURED signals, never the error *message*: a
|
|
41
|
+
* non-zero-exit failure embeds the child's stderr in its message, so scanning
|
|
42
|
+
* that text for "abort"/"timed out"/"not found" would misclassify a healthy
|
|
43
|
+
* binary. The structured signals are: `code` (ENOENT/EACCES) for missing; the
|
|
44
|
+
* `timedOut` flag set by `runCommandWithTimeout` for a genuine timeout; an
|
|
45
|
+
* AbortError name for a deadline/abort under load. Issue #1841.
|
|
46
|
+
*/
|
|
47
|
+
export type QmdProbeFailureKind = "transient" | "missing" | "other";
|
|
48
|
+
export function classifyProbeFailure(err: unknown): QmdProbeFailureKind {
|
|
49
|
+
if (err && typeof err === "object") {
|
|
50
|
+
// Node spawn failures carry a structured `code`: ENOENT (not found) /
|
|
51
|
+
// EACCES (not executable).
|
|
52
|
+
if ("code" in err) {
|
|
53
|
+
const code = err.code;
|
|
54
|
+
if (code === "ENOENT" || code === "EACCES") return "missing";
|
|
55
|
+
}
|
|
56
|
+
// runCommandWithTimeout flags a genuine deadline breach with `timedOut`.
|
|
57
|
+
if ("timedOut" in err && err.timedOut === true) return "transient";
|
|
58
|
+
}
|
|
59
|
+
// An AbortError reaching here is a deadline/abort under load → transient.
|
|
60
|
+
// (Caller cancellation is intercepted by the retry loop before this runs.)
|
|
61
|
+
if (isAbortError(err)) return "transient";
|
|
62
|
+
// Anything else — including a non-zero exit whose message embeds stderr — is
|
|
63
|
+
// a generic failure: never scan the message to upgrade/downgrade it.
|
|
64
|
+
return "other";
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function parseQmdVersion(version: string | null): QmdVersionTuple | null {
|
|
68
|
+
if (!version) return null;
|
|
69
|
+
const match = version.match(/v?(\d{1,10})\.(\d{1,10})\.(\d{1,10})/i);
|
|
70
|
+
if (!match) return null;
|
|
71
|
+
return [
|
|
72
|
+
Number.parseInt(match[1] ?? "0", 10),
|
|
73
|
+
Number.parseInt(match[2] ?? "0", 10),
|
|
74
|
+
Number.parseInt(match[3] ?? "0", 10),
|
|
75
|
+
];
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function parseQmdVersionOutput(stdout: string, stderr: string): string | null {
|
|
79
|
+
const lines = `${stdout}\n${stderr}`
|
|
80
|
+
.split("\n")
|
|
81
|
+
.map((s) => s.trim())
|
|
82
|
+
.filter((s) => s.length > 0);
|
|
83
|
+
if (lines.length === 0) return null;
|
|
84
|
+
const semanticLines = lines.filter((line) => parseQmdVersion(line) !== null);
|
|
85
|
+
if (semanticLines.length === 0) return lines[0] ?? null;
|
|
86
|
+
return semanticLines.find((line) => /\bqmd\b/i.test(line)) ?? semanticLines[0] ?? null;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function compareQmdVersions(left: QmdVersionTuple | null, right: QmdVersionTuple | null): number {
|
|
90
|
+
if (!left && !right) return 0;
|
|
91
|
+
if (!left) return -1;
|
|
92
|
+
if (!right) return 1;
|
|
93
|
+
for (let i = 0; i < 3; i += 1) {
|
|
94
|
+
if ((left[i] ?? 0) > (right[i] ?? 0)) return 1;
|
|
95
|
+
if ((left[i] ?? 0) < (right[i] ?? 0)) return -1;
|
|
96
|
+
}
|
|
97
|
+
return 0;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function versionAtLeast(current: QmdVersionTuple | null, target: QmdVersionTuple): boolean {
|
|
101
|
+
return compareQmdVersions(current, target) >= 0;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function resolveQmdCapabilities(version: string | null): QmdCapabilities {
|
|
105
|
+
const parsedVersion = parseQmdVersion(version);
|
|
106
|
+
const atLeast = (target: QmdVersionTuple): boolean => versionAtLeast(parsedVersion, target);
|
|
107
|
+
return {
|
|
108
|
+
version,
|
|
109
|
+
parsedVersion,
|
|
110
|
+
stableSdk: atLeast([2, 0, 0]),
|
|
111
|
+
unifiedSearch: atLeast([2, 0, 0]),
|
|
112
|
+
getDocumentBody: atLeast([2, 0, 0]),
|
|
113
|
+
maintenanceApi: atLeast([2, 0, 0]),
|
|
114
|
+
legacySkillInstall: atLeast([2, 0, 1]),
|
|
115
|
+
intentHints: atLeast([1, 1, 5]),
|
|
116
|
+
explainTraces: atLeast([1, 1, 2]),
|
|
117
|
+
candidateLimit: atLeast([1, 1, 2]),
|
|
118
|
+
v2McpQueryTool: atLeast([2, 0, 0]),
|
|
119
|
+
structuredSearches: atLeast([2, 0, 0]),
|
|
120
|
+
queryRerankToggle: atLeast([2, 1, 0]),
|
|
121
|
+
chunkStrategy: atLeast([2, 1, 0]),
|
|
122
|
+
qmdBench: atLeast([2, 1, 0]),
|
|
123
|
+
perCollectionModels: atLeast([2, 1, 0]),
|
|
124
|
+
jsonLineNumbers: atLeast([2, 1, 0]),
|
|
125
|
+
editorLinks: atLeast([2, 1, 0]),
|
|
126
|
+
doctor: atLeast([2, 5, 0]),
|
|
127
|
+
versionedSkills: atLeast([2, 5, 0]),
|
|
128
|
+
absoluteSnippetLines: atLeast([2, 5, 0]),
|
|
129
|
+
fullQueryOutput: atLeast([2, 5, 0]),
|
|
130
|
+
forceCpu: atLeast([2, 5, 0]),
|
|
131
|
+
gpuBackendOverride: atLeast([2, 5, 0]),
|
|
132
|
+
embedParallelism: atLeast([2, 5, 0]),
|
|
133
|
+
modelEnvConsistency: atLeast([2, 5, 0]),
|
|
134
|
+
scopedEmbed: atLeast([2, 5, 0]),
|
|
135
|
+
safeStatusDeviceProbe: atLeast([2, 5, 0]),
|
|
136
|
+
mcpIndexSelection: atLeast([2, 5, 0]),
|
|
137
|
+
outputFormatFlag: atLeast([2, 5, 3]),
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export function shouldAutoUpgradeQmd(
|
|
142
|
+
installedVersion: string | null,
|
|
143
|
+
supportedVersion: string = QMD_SUPPORTED_VERSION
|
|
144
|
+
): boolean {
|
|
145
|
+
const installed = parseQmdVersion(installedVersion);
|
|
146
|
+
const supported = parseQmdVersion(supportedVersion);
|
|
147
|
+
if (!installed || !supported) return false;
|
|
148
|
+
return compareQmdVersions(installed, supported) < 0;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export function getQmdPostInstallProbeTargets(
|
|
152
|
+
qmdPath: string,
|
|
153
|
+
qmdPathSource: "configured" | "auto-path" | "auto-fallback"
|
|
154
|
+
): Array<{ qmdPath: string; source: "auto-path" | "auto-fallback" }> {
|
|
155
|
+
const targets: Array<{ qmdPath: string; source: "auto-path" | "auto-fallback" }> = [
|
|
156
|
+
{ qmdPath: "qmd", source: "auto-path" },
|
|
157
|
+
];
|
|
158
|
+
const normalizedPath = qmdPath.trim();
|
|
159
|
+
if (qmdPathSource === "auto-fallback" && normalizedPath.length > 0 && normalizedPath !== "qmd") {
|
|
160
|
+
targets.push({ qmdPath: normalizedPath, source: "auto-fallback" });
|
|
161
|
+
}
|
|
162
|
+
return targets;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export function qmdVersionToString(version: QmdVersionTuple): string {
|
|
166
|
+
return `${version[0]}.${version[1]}.${version[2]}`;
|
|
167
|
+
}
|