@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
package/src/qmd.ts
CHANGED
|
@@ -18,6 +18,33 @@ import {
|
|
|
18
18
|
} from "./search/port.js";
|
|
19
19
|
import { launchProcess, type CommandChildProcess } from "./runtime/child-process.js";
|
|
20
20
|
import { mergeEnv } from "./runtime/env.js";
|
|
21
|
+
import {
|
|
22
|
+
classifyProbeFailure,
|
|
23
|
+
compareQmdVersions,
|
|
24
|
+
getQmdPostInstallProbeTargets,
|
|
25
|
+
parseQmdVersion,
|
|
26
|
+
parseQmdVersionOutput,
|
|
27
|
+
QMD_PROBE_RETRY_BACKOFF_MS,
|
|
28
|
+
QMD_PROBE_TIMEOUT_MS,
|
|
29
|
+
QMD_SUPPORTED_VERSION,
|
|
30
|
+
qmdVersionToString,
|
|
31
|
+
resolveQmdCapabilities,
|
|
32
|
+
shouldAutoUpgradeQmd,
|
|
33
|
+
versionAtLeast,
|
|
34
|
+
type QmdProbeFailureKind,
|
|
35
|
+
} from "./qmd-preflight.js";
|
|
36
|
+
// Re-export version-check / probe-preflight helpers (moved to qmd-preflight.ts,
|
|
37
|
+
// issue #1841) so existing imports from "./qmd.js" keep resolving.
|
|
38
|
+
export {
|
|
39
|
+
compareQmdVersions,
|
|
40
|
+
getQmdPostInstallProbeTargets,
|
|
41
|
+
parseQmdVersion,
|
|
42
|
+
parseQmdVersionOutput,
|
|
43
|
+
QMD_SUPPORTED_VERSION,
|
|
44
|
+
resolveQmdCapabilities,
|
|
45
|
+
shouldAutoUpgradeQmd,
|
|
46
|
+
versionAtLeast,
|
|
47
|
+
};
|
|
21
48
|
|
|
22
49
|
export interface QmdClientOptions {
|
|
23
50
|
slowLog?: { enabled: boolean; thresholdMs: number };
|
|
@@ -133,11 +160,9 @@ const QMD_TIMEOUT_MS = 30_000;
|
|
|
133
160
|
// `qmdDaemonTimeoutMs` config knob (issue #1335), e.g. to give CPU-only HyDE
|
|
134
161
|
// queries more headroom. The effective value lives in `this.daemonTimeoutMs`.
|
|
135
162
|
const QMD_DAEMON_TIMEOUT_MS = 8_000;
|
|
136
|
-
const QMD_PROBE_TIMEOUT_MS = 8_000;
|
|
137
163
|
const QMD_UPDATE_BACKOFF_MS = 15 * 60 * 1000; // 15m
|
|
138
164
|
const QMD_EMBED_BACKOFF_MS = 60 * 60 * 1000; // 60m
|
|
139
165
|
const QMD_CLI_WARN_THROTTLE_MS = 15 * 60 * 1000; // 15m
|
|
140
|
-
export const QMD_SUPPORTED_VERSION = "2.5.3";
|
|
141
166
|
const QMD_PACKAGE_NAME = "@tobilu/qmd";
|
|
142
167
|
const QMD_AUTO_UPGRADE_TIMEOUT_MS = 120_000;
|
|
143
168
|
const QMD_AUTO_UPGRADE_CHECK_INTERVAL_MS = 24 * 60 * 60_000;
|
|
@@ -269,118 +294,6 @@ function isVectorDimensionMismatchError(err: unknown): boolean {
|
|
|
269
294
|
);
|
|
270
295
|
}
|
|
271
296
|
|
|
272
|
-
export function parseQmdVersion(version: string | null): QmdVersionTuple | null {
|
|
273
|
-
if (!version) return null;
|
|
274
|
-
const match = version.match(/v?(\d{1,10})\.(\d{1,10})\.(\d{1,10})/i);
|
|
275
|
-
if (!match) return null;
|
|
276
|
-
return [
|
|
277
|
-
Number.parseInt(match[1] ?? "0", 10),
|
|
278
|
-
Number.parseInt(match[2] ?? "0", 10),
|
|
279
|
-
Number.parseInt(match[3] ?? "0", 10),
|
|
280
|
-
];
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
export function parseQmdVersionOutput(stdout: string, stderr: string): string | null {
|
|
284
|
-
const lines = `${stdout}\n${stderr}`
|
|
285
|
-
.split("\n")
|
|
286
|
-
.map((s) => s.trim())
|
|
287
|
-
.filter((s) => s.length > 0);
|
|
288
|
-
if (lines.length === 0) return null;
|
|
289
|
-
const semanticLines = lines.filter((line) => parseQmdVersion(line) !== null);
|
|
290
|
-
if (semanticLines.length === 0) return lines[0] ?? null;
|
|
291
|
-
return semanticLines.find((line) => /\bqmd\b/i.test(line)) ?? semanticLines[0] ?? null;
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
export function compareQmdVersions(
|
|
295
|
-
left: QmdVersionTuple | null,
|
|
296
|
-
right: QmdVersionTuple | null,
|
|
297
|
-
): number {
|
|
298
|
-
if (!left && !right) return 0;
|
|
299
|
-
if (!left) return -1;
|
|
300
|
-
if (!right) return 1;
|
|
301
|
-
for (let i = 0; i < 3; i += 1) {
|
|
302
|
-
if ((left[i] ?? 0) > (right[i] ?? 0)) return 1;
|
|
303
|
-
if ((left[i] ?? 0) < (right[i] ?? 0)) return -1;
|
|
304
|
-
}
|
|
305
|
-
return 0;
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
export function versionAtLeast(
|
|
309
|
-
current: QmdVersionTuple | null,
|
|
310
|
-
target: QmdVersionTuple,
|
|
311
|
-
): boolean {
|
|
312
|
-
return compareQmdVersions(current, target) >= 0;
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
export function resolveQmdCapabilities(version: string | null): QmdCapabilities {
|
|
316
|
-
const parsedVersion = parseQmdVersion(version);
|
|
317
|
-
const atLeast = (target: QmdVersionTuple): boolean => versionAtLeast(parsedVersion, target);
|
|
318
|
-
return {
|
|
319
|
-
version,
|
|
320
|
-
parsedVersion,
|
|
321
|
-
stableSdk: atLeast([2, 0, 0]),
|
|
322
|
-
unifiedSearch: atLeast([2, 0, 0]),
|
|
323
|
-
getDocumentBody: atLeast([2, 0, 0]),
|
|
324
|
-
maintenanceApi: atLeast([2, 0, 0]),
|
|
325
|
-
legacySkillInstall: atLeast([2, 0, 1]),
|
|
326
|
-
intentHints: atLeast([1, 1, 5]),
|
|
327
|
-
explainTraces: atLeast([1, 1, 2]),
|
|
328
|
-
candidateLimit: atLeast([1, 1, 2]),
|
|
329
|
-
v2McpQueryTool: atLeast([2, 0, 0]),
|
|
330
|
-
structuredSearches: atLeast([2, 0, 0]),
|
|
331
|
-
queryRerankToggle: atLeast([2, 1, 0]),
|
|
332
|
-
chunkStrategy: atLeast([2, 1, 0]),
|
|
333
|
-
qmdBench: atLeast([2, 1, 0]),
|
|
334
|
-
perCollectionModels: atLeast([2, 1, 0]),
|
|
335
|
-
jsonLineNumbers: atLeast([2, 1, 0]),
|
|
336
|
-
editorLinks: atLeast([2, 1, 0]),
|
|
337
|
-
doctor: atLeast([2, 5, 0]),
|
|
338
|
-
versionedSkills: atLeast([2, 5, 0]),
|
|
339
|
-
absoluteSnippetLines: atLeast([2, 5, 0]),
|
|
340
|
-
fullQueryOutput: atLeast([2, 5, 0]),
|
|
341
|
-
forceCpu: atLeast([2, 5, 0]),
|
|
342
|
-
gpuBackendOverride: atLeast([2, 5, 0]),
|
|
343
|
-
embedParallelism: atLeast([2, 5, 0]),
|
|
344
|
-
modelEnvConsistency: atLeast([2, 5, 0]),
|
|
345
|
-
scopedEmbed: atLeast([2, 5, 0]),
|
|
346
|
-
safeStatusDeviceProbe: atLeast([2, 5, 0]),
|
|
347
|
-
mcpIndexSelection: atLeast([2, 5, 0]),
|
|
348
|
-
outputFormatFlag: atLeast([2, 5, 3]),
|
|
349
|
-
};
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
export function shouldAutoUpgradeQmd(
|
|
353
|
-
installedVersion: string | null,
|
|
354
|
-
supportedVersion: string = QMD_SUPPORTED_VERSION,
|
|
355
|
-
): boolean {
|
|
356
|
-
const installed = parseQmdVersion(installedVersion);
|
|
357
|
-
const supported = parseQmdVersion(supportedVersion);
|
|
358
|
-
if (!installed || !supported) return false;
|
|
359
|
-
return compareQmdVersions(installed, supported) < 0;
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
export function getQmdPostInstallProbeTargets(
|
|
363
|
-
qmdPath: string,
|
|
364
|
-
qmdPathSource: "configured" | "auto-path" | "auto-fallback",
|
|
365
|
-
): Array<{ qmdPath: string; source: "auto-path" | "auto-fallback" }> {
|
|
366
|
-
const targets: Array<{ qmdPath: string; source: "auto-path" | "auto-fallback" }> = [
|
|
367
|
-
{ qmdPath: "qmd", source: "auto-path" },
|
|
368
|
-
];
|
|
369
|
-
const normalizedPath = qmdPath.trim();
|
|
370
|
-
if (
|
|
371
|
-
qmdPathSource === "auto-fallback" &&
|
|
372
|
-
normalizedPath.length > 0 &&
|
|
373
|
-
normalizedPath !== "qmd"
|
|
374
|
-
) {
|
|
375
|
-
targets.push({ qmdPath: normalizedPath, source: "auto-fallback" });
|
|
376
|
-
}
|
|
377
|
-
return targets;
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
function qmdVersionToString(version: QmdVersionTuple): string {
|
|
381
|
-
return `${version[0]}.${version[1]}.${version[2]}`;
|
|
382
|
-
}
|
|
383
|
-
|
|
384
297
|
function normalizeSearchOptions(options?: SearchQueryOptions): SearchQueryOptions | undefined {
|
|
385
298
|
if (!options) return undefined;
|
|
386
299
|
const intent = typeof options.intent === "string" ? options.intent.trim() : "";
|
|
@@ -671,7 +584,13 @@ function runCommandWithTimeout(
|
|
|
671
584
|
settled = true;
|
|
672
585
|
cleanup();
|
|
673
586
|
child.kill("SIGKILL");
|
|
674
|
-
|
|
587
|
+
// Flag the deadline breach so classifyProbeFailure can tell a genuine
|
|
588
|
+
// timeout from a non-zero-exit error whose message merely embeds stderr.
|
|
589
|
+
reject(
|
|
590
|
+
Object.assign(new Error(`${label} timed out after ${options.timeoutMs}ms`), {
|
|
591
|
+
timedOut: true as const,
|
|
592
|
+
}),
|
|
593
|
+
);
|
|
675
594
|
}, options.timeoutMs);
|
|
676
595
|
const onAbort = () => {
|
|
677
596
|
if (settled) return;
|
|
@@ -1437,6 +1356,18 @@ export class QmdClient implements SearchBackend {
|
|
|
1437
1356
|
}
|
|
1438
1357
|
}
|
|
1439
1358
|
|
|
1359
|
+
/**
|
|
1360
|
+
* Run a single `qmd --version` probe against `qmdPath`. Extracted as its own
|
|
1361
|
+
* method so tests can inject a fake probe runner that exercises the preflight
|
|
1362
|
+
* retry/classification paths without spawning a real qmd binary. Issue #1841.
|
|
1363
|
+
*/
|
|
1364
|
+
private runVersionProbe(
|
|
1365
|
+
qmdPath: string,
|
|
1366
|
+
signal?: AbortSignal,
|
|
1367
|
+
): Promise<{ stdout: string; stderr: string }> {
|
|
1368
|
+
return runQmd(["--version"], QMD_PROBE_TIMEOUT_MS, qmdPath, signal, this.qmdRuntimeEnv);
|
|
1369
|
+
}
|
|
1370
|
+
|
|
1440
1371
|
private async probeCli(
|
|
1441
1372
|
options: {
|
|
1442
1373
|
allowAutoUpgrade?: boolean;
|
|
@@ -1490,24 +1421,79 @@ export class QmdClient implements SearchBackend {
|
|
|
1490
1421
|
};
|
|
1491
1422
|
|
|
1492
1423
|
if (this.configuredQmdPath) {
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1424
|
+
const configuredPath = this.configuredQmdPath;
|
|
1425
|
+
// Retry only TRANSIENT (timeout/abort) probes of the configured path: a
|
|
1426
|
+
// binary that resolved and ran before is more likely slow under load than
|
|
1427
|
+
// gone. ENOENT/EACCES is a hard misconfiguration and must fail fast.
|
|
1428
|
+
// Issue #1841.
|
|
1429
|
+
let failureKind: QmdProbeFailureKind = "other";
|
|
1430
|
+
let retries = 0;
|
|
1431
|
+
let callerCancelled = false;
|
|
1432
|
+
for (let attempt = 0; attempt <= QMD_PROBE_RETRY_BACKOFF_MS.length; attempt += 1) {
|
|
1433
|
+
try {
|
|
1434
|
+
const result = await this.runVersionProbe(configuredPath, options.signal);
|
|
1435
|
+
await recordProbeSuccess(result, configuredPath, "configured");
|
|
1436
|
+
return true;
|
|
1437
|
+
} catch (err) {
|
|
1438
|
+
markProbeFailure(err);
|
|
1439
|
+
configuredProbeFailure = this.lastCliProbeError;
|
|
1440
|
+
// Detect CALLER cancellation FIRST: an aborted signal / AbortError is
|
|
1441
|
+
// non-actionable noise — the caller chose to stop. Other QMD paths
|
|
1442
|
+
// treat it via `isCallerCancellation`; do the same here so a cancelled
|
|
1443
|
+
// probe never reads as a transient host-load failure. Issue #1841.
|
|
1444
|
+
if (isCallerCancellation(err, options.signal)) {
|
|
1445
|
+
callerCancelled = true;
|
|
1446
|
+
break;
|
|
1447
|
+
}
|
|
1448
|
+
failureKind = classifyProbeFailure(err);
|
|
1449
|
+
// Never retry a hard misconfiguration (missing/not-executable) — only
|
|
1450
|
+
// transient slowness. (Caller cancellation is handled above.)
|
|
1451
|
+
if (
|
|
1452
|
+
failureKind === "transient" &&
|
|
1453
|
+
attempt < QMD_PROBE_RETRY_BACKOFF_MS.length
|
|
1454
|
+
) {
|
|
1455
|
+
try {
|
|
1456
|
+
await sleepWithSignal(
|
|
1457
|
+
QMD_PROBE_RETRY_BACKOFF_MS[attempt] ?? 500,
|
|
1458
|
+
options.signal,
|
|
1459
|
+
);
|
|
1460
|
+
} catch {
|
|
1461
|
+
// Aborted mid-backoff: caller cancelled — stop, emit no warning.
|
|
1462
|
+
callerCancelled = true;
|
|
1463
|
+
break;
|
|
1464
|
+
}
|
|
1465
|
+
retries += 1;
|
|
1466
|
+
continue;
|
|
1467
|
+
}
|
|
1468
|
+
break;
|
|
1469
|
+
}
|
|
1470
|
+
}
|
|
1471
|
+
// Distinct operator-facing warnings so a slow binary is not mistaken for
|
|
1472
|
+
// a misconfiguration (and vice versa). Do not hard-fail here: fall through
|
|
1473
|
+
// to PATH/fallback probing so recall stays healthy when config is stale.
|
|
1474
|
+
// Caller cancellation is silent noise (issue #1841): suppress all warnings
|
|
1475
|
+
// for it exactly as the other QMD paths do.
|
|
1476
|
+
if (!callerCancelled) {
|
|
1477
|
+
if (failureKind === "transient") {
|
|
1478
|
+
this.logCliProbeWarning(
|
|
1479
|
+
`QMD: qmdPath ${configuredPath} version check timed out (host may be under load); retried ${retries} time${retries === 1 ? "" : "s"}`,
|
|
1480
|
+
);
|
|
1481
|
+
} else if (failureKind === "missing") {
|
|
1482
|
+
this.logCliProbeWarning(
|
|
1483
|
+
`QMD: configured qmdPath not found or not executable (${configuredPath}): ${this.lastCliProbeError}`,
|
|
1484
|
+
);
|
|
1485
|
+
} else {
|
|
1486
|
+
// Preserve the historical generic message for exit-code/other failures.
|
|
1487
|
+
this.logCliProbeWarning(
|
|
1488
|
+
`QMD: configured qmdPath failed (${configuredPath}): ${this.lastCliProbeError}`,
|
|
1489
|
+
);
|
|
1490
|
+
}
|
|
1505
1491
|
}
|
|
1506
1492
|
}
|
|
1507
1493
|
|
|
1508
1494
|
// Try PATH first
|
|
1509
1495
|
try {
|
|
1510
|
-
const result = await
|
|
1496
|
+
const result = await this.runVersionProbe("qmd", options.signal);
|
|
1511
1497
|
await recordProbeSuccess(result, "qmd", "auto-path");
|
|
1512
1498
|
return true;
|
|
1513
1499
|
} catch (err) {
|
|
@@ -1515,7 +1501,7 @@ export class QmdClient implements SearchBackend {
|
|
|
1515
1501
|
// Try fallback paths
|
|
1516
1502
|
for (const fallbackPath of this.qmdFallbackPaths) {
|
|
1517
1503
|
try {
|
|
1518
|
-
const result = await
|
|
1504
|
+
const result = await this.runVersionProbe(fallbackPath, options.signal);
|
|
1519
1505
|
await recordProbeSuccess(result, fallbackPath, "auto-fallback");
|
|
1520
1506
|
log.info(`QMD: found at ${fallbackPath}`);
|
|
1521
1507
|
return true;
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
displayErrorDetail,
|
|
5
5
|
isLikelyBetterSqlite3NativeBindingError,
|
|
6
6
|
openBetterSqlite3,
|
|
7
|
+
probeBetterSqlite3Driver,
|
|
7
8
|
} from "./better-sqlite.js";
|
|
8
9
|
|
|
9
10
|
test("isLikelyBetterSqlite3NativeBindingError recognizes missing and mismatched native bindings", () => {
|
|
@@ -22,6 +23,50 @@ test("isLikelyBetterSqlite3NativeBindingError recognizes missing and mismatched
|
|
|
22
23
|
assert.equal(isLikelyBetterSqlite3NativeBindingError(new Error("SQLITE_BUSY: database is locked")), false);
|
|
23
24
|
});
|
|
24
25
|
|
|
26
|
+
test("isLikelyBetterSqlite3NativeBindingError walks the cause chain of a sanitized wrapper (issue #1848)", () => {
|
|
27
|
+
// Production path: loadBetterSqlite3() catches the ABI error and re-throws a
|
|
28
|
+
// sanitized unavailableError WRAPPER whose message drops the ABI markers. The
|
|
29
|
+
// original error survives only on .cause — the classifier must walk it, or
|
|
30
|
+
// the startup probe / projection doctor / browse warn all MISS the hint.
|
|
31
|
+
const abiCause = new Error(
|
|
32
|
+
"The module was compiled against a different Node.js version using NODE_MODULE_VERSION 127",
|
|
33
|
+
);
|
|
34
|
+
const wrapper = new Error(
|
|
35
|
+
"better-sqlite3 is unavailable. Remnic attempted to load the native SQLite binding and could not.",
|
|
36
|
+
{ cause: abiCause },
|
|
37
|
+
);
|
|
38
|
+
assert.equal(isLikelyBetterSqlite3NativeBindingError(wrapper), true);
|
|
39
|
+
|
|
40
|
+
// Deeply nested wrapper (wrapper.cause.cause = ABI error) is still found.
|
|
41
|
+
const deepWrapper = new Error("outer sanitized layer", { cause: wrapper });
|
|
42
|
+
assert.equal(isLikelyBetterSqlite3NativeBindingError(deepWrapper), true);
|
|
43
|
+
|
|
44
|
+
// A wrapper whose cause is NOT a native-binding failure stays negative.
|
|
45
|
+
const benignWrapper = new Error("better-sqlite3 is unavailable.", {
|
|
46
|
+
cause: new Error("SQLITE_BUSY: database is locked"),
|
|
47
|
+
});
|
|
48
|
+
assert.equal(isLikelyBetterSqlite3NativeBindingError(benignWrapper), false);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test("isLikelyBetterSqlite3NativeBindingError inspects AggregateError.errors siblings", () => {
|
|
52
|
+
// Module loaders can surface failures as an AggregateError; the ABI mismatch
|
|
53
|
+
// may live in a sibling rather than the aggregate's own message.
|
|
54
|
+
const agg = new AggregateError([
|
|
55
|
+
new Error("some unrelated loader step"),
|
|
56
|
+
new Error("Could not locate the bindings file. Tried: better_sqlite3.node"),
|
|
57
|
+
]);
|
|
58
|
+
assert.equal(isLikelyBetterSqlite3NativeBindingError(agg), true);
|
|
59
|
+
|
|
60
|
+
const benignAgg = new AggregateError([new Error("unrelated"), new Error("SQLITE_BUSY")]);
|
|
61
|
+
assert.equal(isLikelyBetterSqlite3NativeBindingError(benignAgg), false);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test("isLikelyBetterSqlite3NativeBindingError is cycle-safe on a self-referential cause", () => {
|
|
65
|
+
const cyclic = new Error("SQLITE_BUSY: database is locked");
|
|
66
|
+
cyclic.cause = cyclic; // would loop forever without a visited set
|
|
67
|
+
assert.equal(isLikelyBetterSqlite3NativeBindingError(cyclic), false);
|
|
68
|
+
});
|
|
69
|
+
|
|
25
70
|
test("displayErrorDetail surfaces only error class + code, never the raw message (CodeQL js/stack-trace-exposure)", () => {
|
|
26
71
|
// MODULE_NOT_FOUND messages embed an absolute "Require stack:" path block.
|
|
27
72
|
const moduleNotFound = Object.assign(
|
|
@@ -59,3 +104,12 @@ test("openBetterSqlite3 can open an in-memory database after install verificatio
|
|
|
59
104
|
db.close();
|
|
60
105
|
}
|
|
61
106
|
});
|
|
107
|
+
|
|
108
|
+
test("probeBetterSqlite3Driver succeeds (ok=true) under the verified process and warms the ctor cache", () => {
|
|
109
|
+
const probe = probeBetterSqlite3Driver();
|
|
110
|
+
assert.equal(probe.ok, true);
|
|
111
|
+
assert.equal(probe.detail, "");
|
|
112
|
+
assert.equal(probe.nativeBindingMismatch, false);
|
|
113
|
+
// A second probe reuses the warmed cache (no re-load); still ok.
|
|
114
|
+
assert.equal(probeBetterSqlite3Driver().ok, true);
|
|
115
|
+
});
|
|
@@ -28,6 +28,35 @@ export function openBetterSqlite3(
|
|
|
28
28
|
return new Database(file, options);
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
export interface BetterSqlite3DriverProbe {
|
|
32
|
+
/** true when the native binding loaded successfully under this process. */
|
|
33
|
+
ok: boolean;
|
|
34
|
+
/** Path-free detail string (class + code) from `displayErrorDetail`; "" when ok. */
|
|
35
|
+
detail: string;
|
|
36
|
+
/** true when the failure is classified as a native-binding ABI mismatch. */
|
|
37
|
+
nativeBindingMismatch: boolean;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Attempt to load the better-sqlite3 native driver under the running process
|
|
42
|
+
* WITHOUT opening a database. Used by the server startup check (issue #1829)
|
|
43
|
+
* to surface a wrong-ABI build loudly instead of letting each memory browse
|
|
44
|
+
* silently fall back to a full-corpus scan. Never throws — callers log the
|
|
45
|
+
* result. A successful probe also warms the ctor cache for later opens.
|
|
46
|
+
*/
|
|
47
|
+
export function probeBetterSqlite3Driver(): BetterSqlite3DriverProbe {
|
|
48
|
+
try {
|
|
49
|
+
loadBetterSqlite3();
|
|
50
|
+
return { ok: true, detail: "", nativeBindingMismatch: false };
|
|
51
|
+
} catch (error) {
|
|
52
|
+
return {
|
|
53
|
+
ok: false,
|
|
54
|
+
detail: displayErrorDetail(error),
|
|
55
|
+
nativeBindingMismatch: isLikelyBetterSqlite3NativeBindingError(error),
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
31
60
|
function requireBetterSqlite3Ctor(require: RuntimeRequire): BetterSqlite3Ctor {
|
|
32
61
|
const loaded = require("better-sqlite3") as
|
|
33
62
|
| BetterSqlite3Ctor
|
|
@@ -50,9 +79,49 @@ function rawErrorMessage(error: unknown): string {
|
|
|
50
79
|
return error instanceof Error ? error.message : String(error ?? "");
|
|
51
80
|
}
|
|
52
81
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
82
|
+
/**
|
|
83
|
+
* Iterate an error and every node reachable along its `cause` chain and any
|
|
84
|
+
* `AggregateError.errors` siblings (depth-first), cycle-safe via a visited set.
|
|
85
|
+
*
|
|
86
|
+
* Native-binding failures thrown from openBetterSqlite3 /
|
|
87
|
+
* probeBetterSqlite3Driver are caught and re-thrown as a sanitized
|
|
88
|
+
* `unavailableError` WRAPPER: the wrapper's message drops the ABI markers
|
|
89
|
+
* (NODE_MODULE_VERSION, better_sqlite3.node, …) that live on the ORIGINAL
|
|
90
|
+
* error. So classification must walk `.cause` (and aggregate siblings) instead
|
|
91
|
+
* of inspecting only the top wrapper's message — otherwise startup logs, browse
|
|
92
|
+
* warnings, and the memory_projection doctor check all MISS the native-binding
|
|
93
|
+
* hint (issue #1848).
|
|
94
|
+
*/
|
|
95
|
+
function* errorChain(error: unknown): Generator<unknown> {
|
|
96
|
+
const seen = new Set<unknown>();
|
|
97
|
+
const stack: unknown[] = [error];
|
|
98
|
+
while (stack.length > 0) {
|
|
99
|
+
const node = stack.pop();
|
|
100
|
+
if (node === null || node === undefined) continue;
|
|
101
|
+
if (typeof node !== "object") continue;
|
|
102
|
+
if (seen.has(node)) continue;
|
|
103
|
+
seen.add(node);
|
|
104
|
+
yield node;
|
|
105
|
+
// Narrow with `in` (not a cast) so the property access is compiler-checked.
|
|
106
|
+
if ("cause" in node) {
|
|
107
|
+
const cause = node.cause;
|
|
108
|
+
if (cause !== null && cause !== undefined) stack.push(cause);
|
|
109
|
+
}
|
|
110
|
+
if ("errors" in node) {
|
|
111
|
+
const errors = node.errors;
|
|
112
|
+
if (Array.isArray(errors)) {
|
|
113
|
+
for (const member of errors) {
|
|
114
|
+
if (member !== null && member !== undefined) stack.push(member);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// True when a single error's RAW message carries a native-binding ABI marker.
|
|
122
|
+
// Raw (not displayErrorDetail) so redaction can't strip detection markers
|
|
123
|
+
// (e.g. the loader path containing "better_sqlite3.node").
|
|
124
|
+
function messageHasNativeBindingMarker(error: unknown): boolean {
|
|
56
125
|
const detail = rawErrorMessage(error);
|
|
57
126
|
return (
|
|
58
127
|
detail.includes("Could not locate the bindings file") ||
|
|
@@ -63,6 +132,17 @@ export function isLikelyBetterSqlite3NativeBindingError(error: unknown): boolean
|
|
|
63
132
|
);
|
|
64
133
|
}
|
|
65
134
|
|
|
135
|
+
export function isLikelyBetterSqlite3NativeBindingError(error: unknown): boolean {
|
|
136
|
+
// Walk the full cause / aggregate chain so a SANITIZED wrapper
|
|
137
|
+
// (unavailableError) — whose message drops the ABI markers that live on the
|
|
138
|
+
// original error — is still classified. Inspecting only the top-level message
|
|
139
|
+
// misses the real mismatch when the failure was wrapped (issue #1848).
|
|
140
|
+
for (const node of errorChain(error)) {
|
|
141
|
+
if (messageHasNativeBindingMarker(node)) return true;
|
|
142
|
+
}
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
|
|
66
146
|
function unavailableError(error: unknown): Error {
|
|
67
147
|
const detail = displayErrorDetail(error);
|
|
68
148
|
const nativeBindingHint = isLikelyBetterSqlite3NativeBindingError(error)
|
|
@@ -132,3 +132,59 @@ test("input conversation is not mutated", () => {
|
|
|
132
132
|
cleanConversation(input, ALL_ON);
|
|
133
133
|
assert.equal(JSON.stringify(input), before);
|
|
134
134
|
});
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
test("merges consecutive same-speaker runs across diarized speakers", () => {
|
|
138
|
+
const result = cleanConversation(
|
|
139
|
+
conversation([
|
|
140
|
+
{ speakerKey: "0", text: "Hello there.", startIso: "2026-06-10T10:00:00Z", endIso: "2026-06-10T10:00:03Z" },
|
|
141
|
+
{ speakerKey: "0", text: "How are you?", startIso: "2026-06-10T10:00:04Z", endIso: "2026-06-10T10:00:07Z" },
|
|
142
|
+
{ speakerKey: "1", text: "Good, thanks.", startIso: "2026-06-10T10:00:08Z", endIso: "2026-06-10T10:00:11Z" },
|
|
143
|
+
{ speakerKey: "1", text: "And you?", startIso: "2026-06-10T10:00:12Z", endIso: "2026-06-10T10:00:14Z" },
|
|
144
|
+
{ speakerKey: "0", text: "Busy day.", startIso: "2026-06-10T10:00:15Z", endIso: "2026-06-10T10:00:18Z" },
|
|
145
|
+
]),
|
|
146
|
+
ALL_ON,
|
|
147
|
+
);
|
|
148
|
+
// Three runs: 0+0, 1+1, 0. Generic-label protection is off here, so
|
|
149
|
+
// diarized same-speaker adjacency merges within the gap.
|
|
150
|
+
assert.equal(result.conversation.segments.length, 3);
|
|
151
|
+
assert.equal(result.conversation.segments[0].text, "Hello there. How are you?");
|
|
152
|
+
assert.equal(result.conversation.segments[1].text, "Good, thanks. And you?");
|
|
153
|
+
assert.equal(result.conversation.segments[2].text, "Busy day.");
|
|
154
|
+
assert.equal(result.mergedSegments, 2);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
test("preserveUtteranceBoundaries keeps generic-labeled utterances distinct (#1811)", () => {
|
|
158
|
+
const settings: WearableCleanupSettings = { ...ALL_ON, preserveUtteranceBoundaries: true };
|
|
159
|
+
const result = cleanConversation(
|
|
160
|
+
conversation([
|
|
161
|
+
{ speakerKey: "unknown", text: "First.", startIso: "2026-06-10T10:00:00Z", endIso: "2026-06-10T10:00:02Z" },
|
|
162
|
+
{ speakerKey: "unknown", text: "Second.", startIso: "2026-06-10T10:00:03Z", endIso: "2026-06-10T10:00:05Z" },
|
|
163
|
+
{ speakerKey: "Unknown", text: "Third.", startIso: "2026-06-10T10:00:06Z", endIso: "2026-06-10T10:00:08Z" },
|
|
164
|
+
{ speakerKey: "0", text: "Diarized A.", startIso: "2026-06-10T10:00:09Z", endIso: "2026-06-10T10:00:11Z" },
|
|
165
|
+
{ speakerKey: "0", text: "Diarized B.", startIso: "2026-06-10T10:00:12Z", endIso: "2026-06-10T10:00:14Z" },
|
|
166
|
+
]),
|
|
167
|
+
settings,
|
|
168
|
+
);
|
|
169
|
+
// Generic "unknown"/"Unknown" utterances are NOT merged into one block.
|
|
170
|
+
assert.equal(result.conversation.segments.length, 4);
|
|
171
|
+
assert.deepEqual(
|
|
172
|
+
result.conversation.segments.map((segment) => segment.text),
|
|
173
|
+
["First.", "Second.", "Third.", "Diarized A. Diarized B."],
|
|
174
|
+
);
|
|
175
|
+
assert.equal(result.mergedSegments, 1);
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
test("preserveUtteranceBoundaries default (off) still collapses generic labels", () => {
|
|
179
|
+
// The flag is opt-in: without it the legacy merge-everything behavior
|
|
180
|
+
// is unchanged for sources that never set it.
|
|
181
|
+
const result = cleanConversation(
|
|
182
|
+
conversation([
|
|
183
|
+
{ speakerKey: "unknown", text: "First.", startIso: "2026-06-10T10:00:00Z", endIso: "2026-06-10T10:00:02Z" },
|
|
184
|
+
{ speakerKey: "unknown", text: "Second.", startIso: "2026-06-10T10:00:03Z", endIso: "2026-06-10T10:00:05Z" },
|
|
185
|
+
]),
|
|
186
|
+
ALL_ON,
|
|
187
|
+
);
|
|
188
|
+
assert.equal(result.conversation.segments.length, 1);
|
|
189
|
+
assert.equal(result.conversation.segments[0].text, "First. Second.");
|
|
190
|
+
});
|
package/src/wearables/cleanup.ts
CHANGED
|
@@ -24,6 +24,14 @@ export interface CleanupResult {
|
|
|
24
24
|
/** Merge consecutive same-speaker segments when gaps are below this. */
|
|
25
25
|
const MERGE_GAP_MS = 30_000;
|
|
26
26
|
|
|
27
|
+
/**
|
|
28
|
+
* Speaker labels that carry no real diarization signal. Bee (and other
|
|
29
|
+
* providers that fall back to a placeholder) emit "Unknown"/"unknown"
|
|
30
|
+
* or an empty string when diarization is unavailable; merging on such a
|
|
31
|
+
* label would collapse an entire conversation into one segment (issue #1811).
|
|
32
|
+
*/
|
|
33
|
+
const GENERIC_SPEAKER_PATTERN = /^unknown$/i;
|
|
34
|
+
|
|
27
35
|
/**
|
|
28
36
|
* Standalone filler tokens stripped when `stripFillers` is on. Matched
|
|
29
37
|
* case-insensitively on word boundaries, only as whole tokens — "um"
|
|
@@ -47,6 +55,9 @@ export function cleanConversation(
|
|
|
47
55
|
let segments = conversation.segments.map((segment) => ({ ...segment }));
|
|
48
56
|
let droppedSegments = 0;
|
|
49
57
|
let mergedSegments = 0;
|
|
58
|
+
// Optional on the type so other connectors keep the legacy no-op
|
|
59
|
+
// default; absent means "off" (unchanged merge behavior).
|
|
60
|
+
const preserveBoundaries = settings.preserveUtteranceBoundaries === true;
|
|
50
61
|
|
|
51
62
|
if (settings.stripFillers) {
|
|
52
63
|
for (const segment of segments) {
|
|
@@ -86,7 +97,7 @@ export function cleanConversation(
|
|
|
86
97
|
const merged: WearableTranscriptSegment[] = [];
|
|
87
98
|
for (const segment of segments) {
|
|
88
99
|
const previous = merged[merged.length - 1];
|
|
89
|
-
if (previous && canMerge(previous, segment)) {
|
|
100
|
+
if (previous && canMerge(previous, segment, preserveBoundaries)) {
|
|
90
101
|
previous.text = `${previous.text} ${segment.text}`.trim();
|
|
91
102
|
if (segment.endIso) previous.endIso = segment.endIso;
|
|
92
103
|
mergedSegments += 1;
|
|
@@ -107,8 +118,18 @@ export function cleanConversation(
|
|
|
107
118
|
function canMerge(
|
|
108
119
|
previous: WearableTranscriptSegment,
|
|
109
120
|
next: WearableTranscriptSegment,
|
|
121
|
+
preserveUtteranceBoundaries: boolean,
|
|
110
122
|
): boolean {
|
|
111
123
|
if (previous.speakerKey !== next.speakerKey) return false;
|
|
124
|
+
// A generic/low-confidence speaker label ("Unknown", empty) carries no
|
|
125
|
+
// real diarization: merging on it collapses unrelated utterances. When
|
|
126
|
+
// the source opts into boundary preservation, refuse to merge across
|
|
127
|
+
// such a label (issue #1811). The keys are equal here, so checking one
|
|
128
|
+
// suffices; diarized labels still merge normally.
|
|
129
|
+
if (preserveUtteranceBoundaries) {
|
|
130
|
+
const label = previous.speakerKey.trim();
|
|
131
|
+
if (label.length === 0 || GENERIC_SPEAKER_PATTERN.test(label)) return false;
|
|
132
|
+
}
|
|
112
133
|
const previousEnd = previous.endIso ? Date.parse(previous.endIso) : NaN;
|
|
113
134
|
const nextStart = next.startIso ? Date.parse(next.startIso) : NaN;
|
|
114
135
|
// Without timestamps, adjacency is the only signal — still merge.
|
|
@@ -44,9 +44,34 @@ test("source settings default to the fully-automated smart pipeline", () => {
|
|
|
44
44
|
stripFillers: true,
|
|
45
45
|
collapseRepeats: true,
|
|
46
46
|
dropLowQuality: true,
|
|
47
|
+
// Opt-in for Bee only; limitless keeps the legacy default (issue #1811).
|
|
48
|
+
preserveUtteranceBoundaries: false,
|
|
47
49
|
});
|
|
48
50
|
});
|
|
49
51
|
|
|
52
|
+
test("bee source defaults to preserving utterance boundaries (#1811)", () => {
|
|
53
|
+
const parsed = parseWearablesConfig({
|
|
54
|
+
enabled: true,
|
|
55
|
+
sources: { bee: { enabled: true } },
|
|
56
|
+
});
|
|
57
|
+
assert.equal(parsed.sources.bee.cleanup.preserveUtteranceBoundaries, true);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test("cleanup.preserveUtteranceBoundaries is parsed and overrides the source default", () => {
|
|
61
|
+
const parsed = parseWearablesConfig({
|
|
62
|
+
enabled: true,
|
|
63
|
+
sources: { bee: { enabled: true, cleanup: { preserveUtteranceBoundaries: false } } },
|
|
64
|
+
});
|
|
65
|
+
assert.equal(parsed.sources.bee.cleanup.preserveUtteranceBoundaries, false);
|
|
66
|
+
assert.throws(
|
|
67
|
+
() =>
|
|
68
|
+
parseWearablesConfig({
|
|
69
|
+
sources: { bee: { cleanup: { preserveUtteranceBoundaries: "maybe" } } },
|
|
70
|
+
}),
|
|
71
|
+
/cleanup.preserveUtteranceBoundaries/,
|
|
72
|
+
);
|
|
73
|
+
});
|
|
74
|
+
|
|
50
75
|
test("top-level defaults are full-featured: digest and off-the-record on", () => {
|
|
51
76
|
const parsed = parseWearablesConfig({});
|
|
52
77
|
assert.equal(parsed.digestEnabled, true);
|