@remnic/core 9.63.4 → 9.63.6
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 +11 -11
- package/dist/access-admin-ops-surface.js +23 -23
- package/dist/access-authorization-probe.d.ts +11 -11
- package/dist/access-authorization-probe.js +24 -24
- package/dist/access-boundary.d.ts +11 -11
- package/dist/access-boundary.js +23 -23
- package/dist/access-cli.js +55 -55
- package/dist/access-coding-context-resolution.d.ts +1 -1
- package/dist/access-extraction-force-flush.d.ts +11 -11
- package/dist/access-extraction-force-flush.js +23 -23
- package/dist/access-health-types.d.ts +1 -1
- package/dist/access-http-lcm-compaction.d.ts +11 -11
- package/dist/access-http-lifecycle-flush.d.ts +11 -11
- package/dist/access-http-offline-stream.d.ts +11 -11
- package/dist/access-http-query.d.ts +2 -2
- package/dist/access-http-query.js +25 -25
- package/dist/access-http.d.ts +12 -12
- package/dist/access-http.js +34 -34
- package/dist/access-identity-continuity-surface.d.ts +9 -9
- package/dist/access-identity-continuity-surface.js +23 -23
- package/dist/access-lcm-surface.d.ts +11 -11
- package/dist/access-lcm-surface.js +23 -23
- package/dist/access-mcp.d.ts +11 -11
- package/dist/access-mcp.js +28 -28
- package/dist/access-memory-search-fanout.d.ts +2 -2
- package/dist/{access-namespace-preflight-yO4LMGFH.d.ts → access-namespace-preflight-DB0zXFek.d.ts} +1 -1
- package/dist/access-namespace-preflight.d.ts +3 -3
- package/dist/access-namespace-preflight.js +23 -23
- package/dist/access-observe-write-surface.d.ts +11 -11
- package/dist/access-observe-write-surface.js +23 -23
- package/dist/access-offline-manifest.d.ts +9 -9
- package/dist/access-offline-manifest.js +3 -3
- package/dist/access-operations-batch.js +24 -24
- package/dist/access-operations.d.ts +11 -11
- package/dist/access-operations.js +27 -27
- package/dist/access-recall-concurrency.d.ts +11 -11
- package/dist/access-recall-concurrency.js +23 -23
- package/dist/access-recall-response.d.ts +11 -11
- package/dist/access-recall-response.js +23 -23
- package/dist/access-recall-surface.d.ts +11 -11
- package/dist/access-recall-surface.js +23 -23
- package/dist/{access-service-D-bi3r01.d.ts → access-service-cKQrte9Z.d.ts} +7 -7
- package/dist/access-service-helpers.d.ts +11 -11
- package/dist/access-service.d.ts +11 -11
- package/dist/access-service.js +23 -23
- package/dist/access-surface-catalog.d.ts +11 -11
- package/dist/access-surface-catalog.js +25 -25
- package/dist/access-wearables-meetings-surface.d.ts +3 -3
- 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/ambient-provenance.d.ts +1 -1
- package/dist/artifact-search.d.ts +1 -1
- package/dist/behavior-learner.d.ts +1 -1
- package/dist/behavior-signals.d.ts +1 -1
- package/dist/bootstrap.d.ts +9 -9
- package/dist/briefing-window.d.ts +2 -2
- package/dist/briefing.d.ts +2 -2
- package/dist/briefing.js +11 -11
- package/dist/buffer-surprise-report.d.ts +1 -1
- package/dist/buffer-turn-helpers.d.ts +1 -1
- package/dist/buffer.d.ts +2 -2
- package/dist/buffer.js +4 -4
- package/dist/bulk-import/index.d.ts +3 -3
- package/dist/calibration.d.ts +1 -1
- package/dist/capabilities.d.ts +1 -1
- package/dist/{capsule-merge-HCJJMLPS.js → capsule-merge-SGIVB3WA.js} +5 -5
- package/dist/{catalog-BYIHQw1l.d.ts → catalog-CCzmf8IH.d.ts} +1 -1
- package/dist/causal-behavior.d.ts +1 -1
- package/dist/causal-consolidation.d.ts +1 -1
- package/dist/causal-consolidation.js +12 -12
- package/dist/causal-trajectory-graph.d.ts +1 -1
- package/dist/{chunk-XFCQIQQU.js → chunk-2UM6WHME.js} +3 -3
- package/dist/{chunk-CKWOMJDY.js → chunk-3Z5TGRFA.js} +8 -8
- package/dist/{chunk-JJX5XEIG.js → chunk-77QZW2D6.js} +4 -4
- package/dist/{chunk-X4MGNVOO.js → chunk-7AF243WZ.js} +1 -1
- package/dist/{chunk-2ASAS3CQ.js → chunk-7KQB4EBS.js} +2 -2
- package/dist/{chunk-3LJ2L3T3.js → chunk-7LF2OHIJ.js} +2 -2
- package/dist/{chunk-VAMQU4W7.js → chunk-AYVCKGA6.js} +2 -2
- package/dist/{chunk-GTRGH4ZT.js → chunk-BB2LH5JM.js} +2 -2
- package/dist/{chunk-XMMESCPD.js → chunk-C64XATK7.js} +17 -27
- package/dist/chunk-C64XATK7.js.map +1 -0
- package/dist/{chunk-DQV3LUQL.js → chunk-CUDSA46O.js} +2 -2
- package/dist/{chunk-PADG72LJ.js → chunk-DXDAQZ2K.js} +2 -2
- package/dist/{chunk-EJCN5ATI.js → chunk-DYZR3Z6D.js} +3 -3
- package/dist/{chunk-VN7IIQGS.js → chunk-E4KMY4KR.js} +2 -2
- package/dist/{chunk-ZQKXRKMR.js → chunk-EFKZT6AY.js} +2 -2
- package/dist/{chunk-DNRTKO2U.js → chunk-EJY3HJMT.js} +2 -2
- package/dist/{chunk-4EDAHS6D.js → chunk-ERPBRXVZ.js} +2 -2
- package/dist/{chunk-ZUZD2DRV.js → chunk-F6JYBVL6.js} +27 -57
- package/dist/{chunk-ZUZD2DRV.js.map → chunk-F6JYBVL6.js.map} +1 -1
- package/dist/{chunk-JWPG65LH.js → chunk-FHKXVTM5.js} +38 -39
- package/dist/chunk-FHKXVTM5.js.map +1 -0
- package/dist/{chunk-RPKJHYVE.js → chunk-FPEZ74F7.js} +2 -2
- package/dist/{chunk-Z334TJRB.js → chunk-GF7RQWXC.js} +2 -2
- package/dist/{chunk-6D7PZKCJ.js → chunk-GG4DN63W.js} +2 -2
- package/dist/{chunk-OR7KEGLR.js → chunk-GTD4Q62X.js} +2 -6
- package/dist/{chunk-QOMUE4FS.js → chunk-I5FLIB2J.js} +2 -2
- package/dist/{chunk-2JL7WPXL.js → chunk-IVXVLDNT.js} +12 -12
- package/dist/{chunk-NRFU5MVH.js → chunk-JOEJG4SD.js} +47 -1
- package/dist/chunk-JOEJG4SD.js.map +1 -0
- package/dist/{chunk-EJLTNPRY.js → chunk-KHCUVYCY.js} +3 -3
- package/dist/{chunk-QSIQVC32.js → chunk-KYTRF64R.js} +2 -2
- package/dist/{chunk-LNFYISZ6.js → chunk-LX4TNSAE.js} +4 -4
- package/dist/{chunk-H6ZXNH5I.js → chunk-MUJ4QFUQ.js} +3 -3
- package/dist/{chunk-JSH2TV34.js → chunk-NA56NSKL.js} +2 -2
- package/dist/{chunk-DFEYRCG2.js → chunk-NKB43PGI.js} +2 -2
- package/dist/{chunk-DLQQU4EG.js → chunk-NS5ETDYD.js} +2 -2
- package/dist/{chunk-VG6DC2GO.js → chunk-OETHJBAL.js} +53 -45
- package/dist/{chunk-VG6DC2GO.js.map → chunk-OETHJBAL.js.map} +1 -1
- package/dist/{chunk-BXOZALJ6.js → chunk-PB4WVYGC.js} +2 -2
- package/dist/{chunk-Q35XGIEM.js → chunk-PBRCYYO7.js} +2 -2
- package/dist/{chunk-QYN5PXDZ.js → chunk-PMVPCZJZ.js} +4 -4
- package/dist/{chunk-3USQWITL.js → chunk-QI5RDDEL.js} +3 -3
- package/dist/{chunk-I6OQCT3Y.js → chunk-QRWMS6SI.js} +2 -2
- package/dist/{chunk-ITYAXU4S.js → chunk-R6UMNP5C.js} +3 -3
- package/dist/{chunk-5W62VEFH.js → chunk-RWMSMTQP.js} +2 -2
- package/dist/{chunk-FKJVEW6O.js → chunk-SYVGVK4S.js} +2 -2
- package/dist/{chunk-GNKY5JEP.js → chunk-TSTI6A3A.js} +8 -8
- package/dist/{chunk-ISFUFLFM.js → chunk-UCLMKNHA.js} +2 -2
- package/dist/{chunk-ZMSZVISA.js → chunk-UFKPQBXH.js} +3 -3
- package/dist/{chunk-B2OOJCUX.js → chunk-UGHCZS6K.js} +2 -2
- package/dist/{chunk-RSLRGRBJ.js → chunk-V3KIRICY.js} +3 -3
- package/dist/{chunk-MRJ43KSG.js → chunk-VPHSMJ2E.js} +2 -2
- package/dist/{chunk-E2GYBTSP.js → chunk-VYEPACWY.js} +2 -2
- package/dist/{chunk-A4HXWNST.js → chunk-X7LEDA74.js} +2 -2
- package/dist/{chunk-KMAP7SNQ.js → chunk-XCB6QDL6.js} +2 -2
- package/dist/{chunk-WDUG2CH3.js → chunk-YEQCMA5L.js} +2 -2
- package/dist/{chunk-LZCSOUJ7.js → chunk-YFCEBB5P.js} +2 -2
- package/dist/{chunk-IKXUG6KC.js → chunk-YXMHUBN2.js} +2 -2
- package/dist/{chunk-GFNS3BHV.js → chunk-ZUPKNWRZ.js} +3 -3
- package/dist/{chunk-GSTIBXBW.js → chunk-ZVPVTPGI.js} +2 -2
- package/dist/{cli-DEoABKXH.d.ts → cli-BFBDYV0D.d.ts} +5 -5
- package/dist/cli.d.ts +13 -13
- package/dist/cli.js +45 -45
- package/dist/coding/pre-action-gate.d.ts +1 -1
- package/dist/compounding/engine.d.ts +2 -2
- package/dist/compounding/engine.js +11 -11
- package/dist/compounding/preference-consolidator.d.ts +1 -1
- package/dist/compression-optimizer.d.ts +1 -1
- package/dist/config.d.ts +1 -1
- package/dist/connectors/codex-materialize-runner.d.ts +1 -1
- package/dist/connectors/codex-materialize-runner.js +11 -11
- package/dist/connectors/codex-materialize.d.ts +1 -1
- package/dist/connectors/index.d.ts +1 -1
- package/dist/connectors/index.js +11 -11
- package/dist/consolidation-provenance-check.d.ts +2 -2
- package/dist/consolidation-provenance-check.js +5 -5
- package/dist/consolidation-undo.d.ts +2 -2
- package/dist/consolidation-undo.js +4 -4
- package/dist/contradiction/index.d.ts +2 -2
- package/dist/converge-config.d.ts +1 -1
- package/dist/convergence-refresh.d.ts +2 -2
- package/dist/convergence-refresh.js +13 -13
- package/dist/conversation-index/backend.d.ts +1 -1
- package/dist/conversation-index/chunker.d.ts +1 -1
- package/dist/conversation-index/faiss-adapter.d.ts +1 -1
- package/dist/conversation-index/indexer.d.ts +1 -1
- package/dist/conversation-index/search.d.ts +1 -1
- package/dist/corpus-watermark.d.ts +1 -1
- package/dist/corpus-watermark.js +13 -13
- package/dist/day-summary.d.ts +1 -1
- package/dist/delinearize.d.ts +1 -1
- package/dist/dependency-propagation-config.d.ts +1 -1
- package/dist/{dependency-propagation-delivery-BxhvAppj.d.ts → dependency-propagation-delivery-DiY5T63p.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/enrichment/index.d.ts +1 -1
- package/dist/entity-origin-fields.d.ts +1 -1
- package/dist/entity-retrieval.d.ts +2 -2
- package/dist/entity-retrieval.js +11 -11
- package/dist/entity-schema.d.ts +1 -1
- package/dist/episodic-context.d.ts +1 -1
- package/dist/explicit-capture.d.ts +9 -9
- package/dist/explicit-capture.js +5 -5
- package/dist/external-wiki-access.d.ts +11 -11
- package/dist/external-wiki-access.js +24 -24
- package/dist/external-wiki-collection-registration.d.ts +1 -1
- package/dist/external-wiki-collection.d.ts +1 -1
- package/dist/external-wiki-mcp-tools.d.ts +11 -11
- package/dist/extraction-error-classification.d.ts +1 -1
- package/dist/extraction-faithfulness.d.ts +1 -1
- package/dist/extraction-judge-telemetry.d.ts +1 -1
- package/dist/extraction-judge-training.d.ts +1 -1
- package/dist/extraction-judge.d.ts +1 -1
- package/dist/extraction-liveness.d.ts +1 -1
- package/dist/extraction-normalization.d.ts +1 -1
- package/dist/extraction-prompt.d.ts +1 -1
- package/dist/extraction-source-grounding-rules.d.ts +1 -1
- package/dist/extraction-source-grounding.d.ts +1 -1
- package/dist/extraction.d.ts +1 -1
- package/dist/extraction.js +14 -14
- package/dist/fallback-llm.d.ts +1 -1
- 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-reinforcement.d.ts +1 -1
- package/dist/graph-path-reconstruction.d.ts +1 -1
- package/dist/graph-path-scoring.d.ts +1 -1
- package/dist/graph-snapshot.d.ts +1 -1
- package/dist/graph.d.ts +1 -1
- package/dist/harmonic-retrieval.js +1 -1
- package/dist/importance.d.ts +1 -1
- package/dist/importers/index.d.ts +1 -1
- package/dist/in-flight-reads.d.ts +1 -1
- package/dist/index.d.ts +21 -21
- package/dist/index.js +55 -55
- package/dist/intent.d.ts +1 -1
- package/dist/lcm/engine.d.ts +1 -1
- package/dist/lcm/index.d.ts +1 -1
- 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-model-endpoint.d.ts +1 -1
- package/dist/maintenance/memory-governance.d.ts +1 -1
- package/dist/maintenance/memory-governance.js +11 -11
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.d.ts +2 -2
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +11 -11
- package/dist/maintenance/rebuild-memory-projection.d.ts +2 -2
- package/dist/maintenance/rebuild-memory-projection.js +12 -12
- package/dist/{maintenance-N5ur13Al.d.ts → maintenance-CaqA4z7V.d.ts} +3 -3
- package/dist/mcp-memory-inspector-app.d.ts +11 -11
- package/dist/memory-action-policy.d.ts +1 -1
- package/dist/memory-cache.d.ts +1 -1
- package/dist/memory-cache.js +1 -1
- package/dist/memory-corpus-version.js +2 -2
- package/dist/memory-lifecycle-ledger-utils.d.ts +1 -1
- package/dist/memory-projection-store.d.ts +1 -1
- package/dist/memory-provenance.d.ts +1 -1
- package/dist/memory-snapshot.d.ts +1 -1
- package/dist/memory-worth-outcomes.d.ts +2 -2
- package/dist/models-json.d.ts +1 -1
- package/dist/namespaces/migrate.d.ts +3 -3
- package/dist/namespaces/migrate.js +12 -12
- package/dist/namespaces/principal.d.ts +1 -1
- package/dist/namespaces/search.d.ts +1 -1
- package/dist/namespaces/search.js +4 -4
- package/dist/namespaces/storage.d.ts +3 -3
- package/dist/namespaces/storage.js +11 -11
- package/dist/native-knowledge.d.ts +1 -1
- package/dist/offline-sync-impression-drain.d.ts +1 -1
- package/dist/operator-doctor-corpus.d.ts +1 -1
- package/dist/operator-doctor-corpus.js +14 -14
- package/dist/operator-doctor-replica.d.ts +1 -1
- package/dist/operator-toolkit.d.ts +3 -3
- package/dist/operator-toolkit.js +20 -20
- package/dist/orchestration/compression-guideline-coordinator.d.ts +2 -2
- package/dist/orchestration/maintenance.d.ts +4 -4
- package/dist/orchestration/maintenance.js +17 -17
- package/dist/{orchestrator-ByLrpav4.d.ts → orchestrator-CSc9GfQv.d.ts} +8 -8
- package/dist/orchestrator.d.ts +9 -9
- package/dist/orchestrator.js +55 -55
- package/dist/page-versioning.js +4 -4
- package/dist/patterns-cli.d.ts +1 -1
- package/dist/{pipeline-BdF3ferY.d.ts → pipeline-X606GdRJ.d.ts} +1 -1
- package/dist/policy-runtime.d.ts +1 -1
- package/dist/proactive-contention.d.ts +1 -1
- package/dist/provenance.d.ts +1 -1
- package/dist/{public-http-DusietP2.d.ts → public-http-8PcrDIYi.d.ts} +1 -1
- package/dist/{qmd-CBSxevyb.d.ts → qmd-DCKeR-GX.d.ts} +1 -1
- package/dist/qmd-preflight.d.ts +2 -2
- package/dist/qmd-recall-cache.d.ts +1 -1
- package/dist/qmd.d.ts +2 -2
- package/dist/qmd.js +2 -2
- package/dist/recall-concurrency-config.d.ts +1 -1
- package/dist/recall-disclosure-escalation.d.ts +1 -1
- package/dist/recall-explain-renderer.d.ts +1 -1
- package/dist/recall-memory-map.d.ts +1 -1
- package/dist/recall-planner-llm.d.ts +1 -1
- package/dist/recall-state.d.ts +1 -1
- 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/reconcile/cursor.d.ts +1 -1
- package/dist/reconcile/manifest.d.ts +1 -1
- package/dist/reconcile/manifest.js +2 -2
- package/dist/reconcile/plan.d.ts +1 -1
- package/dist/replay/normalizers/chatgpt.d.ts +1 -1
- package/dist/replay/normalizers/claude.d.ts +1 -1
- package/dist/replay/normalizers/openclaw.d.ts +1 -1
- package/dist/replay/normalizers/shared.d.ts +1 -1
- package/dist/replay/runner.d.ts +1 -1
- package/dist/replay/types.d.ts +1 -1
- package/dist/replica-divergence.d.ts +1 -1
- package/dist/replica-peers-config.d.ts +1 -1
- package/dist/resolve-auth-token.d.ts +1 -1
- package/dist/retrieval-agents.d.ts +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/salvage-envelope.d.ts +1 -1
- package/dist/{scope-profiles-CeMv7pP0.d.ts → scope-profiles-_Y_RHCeE.d.ts} +1 -1
- package/dist/search/embed-helper.d.ts +1 -1
- package/dist/search/factory.d.ts +1 -1
- package/dist/search/factory.js +3 -3
- package/dist/search/index.d.ts +1 -1
- package/dist/search/index.js +3 -3
- package/dist/search/lancedb-backend.d.ts +1 -1
- package/dist/search/meilisearch-backend.d.ts +1 -1
- package/dist/search/noop-backend.d.ts +1 -1
- package/dist/search/orama-backend.d.ts +1 -1
- package/dist/search/port.d.ts +1 -1
- package/dist/search/remote-backend.d.ts +1 -1
- package/dist/{semantic-consolidation-B-85brYj.d.ts → semantic-consolidation-DT2VSrU0.d.ts} +1 -1
- package/dist/semantic-consolidation.d.ts +2 -2
- package/dist/semantic-consolidation.js +12 -12
- package/dist/semantic-rule-promotion.js +11 -11
- package/dist/semantic-rule-verifier.d.ts +1 -1
- package/dist/semantic-rule-verifier.js +11 -11
- package/dist/{service-CoO0HA2E.d.ts → service-BMo-gwDE.d.ts} +3 -3
- package/dist/session-observer-bands.d.ts +1 -1
- package/dist/session-observer-state.d.ts +1 -1
- package/dist/shared-context/manager.d.ts +1 -1
- package/dist/signal.d.ts +1 -1
- package/dist/source-agent-qualifier.d.ts +1 -1
- package/dist/source-agent-qualifier.js +13 -13
- package/dist/{storage-BVAVitkO.d.ts → storage-CGzJGUUu.d.ts} +49 -24
- package/dist/storage.d.ts +2 -2
- package/dist/storage.js +10 -10
- package/dist/summarizer.d.ts +1 -1
- package/dist/summary-snapshot.d.ts +1 -1
- package/dist/support-passport/index.d.ts +14 -14
- package/dist/support-passport/index.js +26 -26
- package/dist/temporal-supersession.d.ts +2 -2
- package/dist/temporal-timeline-recall.d.ts +1 -1
- package/dist/temporal-validity.d.ts +1 -1
- package/dist/threading.d.ts +1 -1
- package/dist/tier-migration.d.ts +2 -2
- package/dist/tier-routing.d.ts +1 -1
- package/dist/topics.d.ts +1 -1
- package/dist/transcript.d.ts +1 -1
- package/dist/transfer/capsule-import.js +5 -5
- package/dist/trust-score-stage.d.ts +1 -1
- package/dist/trust-score.d.ts +1 -1
- package/dist/{types-DWHOxlZP.d.ts → types-CBEAYU76.d.ts} +112 -128
- package/dist/types.d.ts +1 -1
- package/dist/utility-runtime.d.ts +1 -1
- package/dist/verified-recall.js +11 -11
- package/dist/write-envelope.d.ts +1 -1
- package/package.json +2 -2
- package/src/chat/chat-cli.test.ts +53 -0
- package/src/chat/chat-cli.ts +22 -58
- package/src/dedup/index.test.ts +65 -1
- package/src/dedup/index.ts +24 -2
- package/src/meetings/build.test.ts +20 -4
- package/src/meetings/build.ts +2 -2
- package/src/meetings/day-source.test.ts +3 -1
- package/src/meetings/day-source.ts +1 -12
- package/src/meetings/fuse.test.ts +64 -15
- package/src/meetings/fuse.ts +18 -12
- package/src/meetings/types.ts +4 -19
- package/src/memory-cache-derived.test.ts +90 -0
- package/src/memory-cache.ts +40 -42
- package/src/storage/content-hash-index.ts +54 -0
- package/src/storage-contract/content-hash.test.ts +59 -1
- package/src/storage-fact-hash-index-defer.test.ts +9 -9
- package/src/storage.ts +39 -40
- package/dist/chunk-JWPG65LH.js.map +0 -1
- package/dist/chunk-NRFU5MVH.js.map +0 -1
- package/dist/chunk-XMMESCPD.js.map +0 -1
- /package/dist/{capsule-merge-HCJJMLPS.js.map → capsule-merge-SGIVB3WA.js.map} +0 -0
- /package/dist/{chunk-XFCQIQQU.js.map → chunk-2UM6WHME.js.map} +0 -0
- /package/dist/{chunk-CKWOMJDY.js.map → chunk-3Z5TGRFA.js.map} +0 -0
- /package/dist/{chunk-JJX5XEIG.js.map → chunk-77QZW2D6.js.map} +0 -0
- /package/dist/{chunk-X4MGNVOO.js.map → chunk-7AF243WZ.js.map} +0 -0
- /package/dist/{chunk-2ASAS3CQ.js.map → chunk-7KQB4EBS.js.map} +0 -0
- /package/dist/{chunk-3LJ2L3T3.js.map → chunk-7LF2OHIJ.js.map} +0 -0
- /package/dist/{chunk-VAMQU4W7.js.map → chunk-AYVCKGA6.js.map} +0 -0
- /package/dist/{chunk-GTRGH4ZT.js.map → chunk-BB2LH5JM.js.map} +0 -0
- /package/dist/{chunk-DQV3LUQL.js.map → chunk-CUDSA46O.js.map} +0 -0
- /package/dist/{chunk-PADG72LJ.js.map → chunk-DXDAQZ2K.js.map} +0 -0
- /package/dist/{chunk-EJCN5ATI.js.map → chunk-DYZR3Z6D.js.map} +0 -0
- /package/dist/{chunk-VN7IIQGS.js.map → chunk-E4KMY4KR.js.map} +0 -0
- /package/dist/{chunk-ZQKXRKMR.js.map → chunk-EFKZT6AY.js.map} +0 -0
- /package/dist/{chunk-DNRTKO2U.js.map → chunk-EJY3HJMT.js.map} +0 -0
- /package/dist/{chunk-4EDAHS6D.js.map → chunk-ERPBRXVZ.js.map} +0 -0
- /package/dist/{chunk-RPKJHYVE.js.map → chunk-FPEZ74F7.js.map} +0 -0
- /package/dist/{chunk-Z334TJRB.js.map → chunk-GF7RQWXC.js.map} +0 -0
- /package/dist/{chunk-6D7PZKCJ.js.map → chunk-GG4DN63W.js.map} +0 -0
- /package/dist/{chunk-OR7KEGLR.js.map → chunk-GTD4Q62X.js.map} +0 -0
- /package/dist/{chunk-QOMUE4FS.js.map → chunk-I5FLIB2J.js.map} +0 -0
- /package/dist/{chunk-2JL7WPXL.js.map → chunk-IVXVLDNT.js.map} +0 -0
- /package/dist/{chunk-EJLTNPRY.js.map → chunk-KHCUVYCY.js.map} +0 -0
- /package/dist/{chunk-QSIQVC32.js.map → chunk-KYTRF64R.js.map} +0 -0
- /package/dist/{chunk-LNFYISZ6.js.map → chunk-LX4TNSAE.js.map} +0 -0
- /package/dist/{chunk-H6ZXNH5I.js.map → chunk-MUJ4QFUQ.js.map} +0 -0
- /package/dist/{chunk-JSH2TV34.js.map → chunk-NA56NSKL.js.map} +0 -0
- /package/dist/{chunk-DFEYRCG2.js.map → chunk-NKB43PGI.js.map} +0 -0
- /package/dist/{chunk-DLQQU4EG.js.map → chunk-NS5ETDYD.js.map} +0 -0
- /package/dist/{chunk-BXOZALJ6.js.map → chunk-PB4WVYGC.js.map} +0 -0
- /package/dist/{chunk-Q35XGIEM.js.map → chunk-PBRCYYO7.js.map} +0 -0
- /package/dist/{chunk-QYN5PXDZ.js.map → chunk-PMVPCZJZ.js.map} +0 -0
- /package/dist/{chunk-3USQWITL.js.map → chunk-QI5RDDEL.js.map} +0 -0
- /package/dist/{chunk-I6OQCT3Y.js.map → chunk-QRWMS6SI.js.map} +0 -0
- /package/dist/{chunk-ITYAXU4S.js.map → chunk-R6UMNP5C.js.map} +0 -0
- /package/dist/{chunk-5W62VEFH.js.map → chunk-RWMSMTQP.js.map} +0 -0
- /package/dist/{chunk-FKJVEW6O.js.map → chunk-SYVGVK4S.js.map} +0 -0
- /package/dist/{chunk-GNKY5JEP.js.map → chunk-TSTI6A3A.js.map} +0 -0
- /package/dist/{chunk-ISFUFLFM.js.map → chunk-UCLMKNHA.js.map} +0 -0
- /package/dist/{chunk-ZMSZVISA.js.map → chunk-UFKPQBXH.js.map} +0 -0
- /package/dist/{chunk-B2OOJCUX.js.map → chunk-UGHCZS6K.js.map} +0 -0
- /package/dist/{chunk-RSLRGRBJ.js.map → chunk-V3KIRICY.js.map} +0 -0
- /package/dist/{chunk-MRJ43KSG.js.map → chunk-VPHSMJ2E.js.map} +0 -0
- /package/dist/{chunk-E2GYBTSP.js.map → chunk-VYEPACWY.js.map} +0 -0
- /package/dist/{chunk-A4HXWNST.js.map → chunk-X7LEDA74.js.map} +0 -0
- /package/dist/{chunk-KMAP7SNQ.js.map → chunk-XCB6QDL6.js.map} +0 -0
- /package/dist/{chunk-WDUG2CH3.js.map → chunk-YEQCMA5L.js.map} +0 -0
- /package/dist/{chunk-LZCSOUJ7.js.map → chunk-YFCEBB5P.js.map} +0 -0
- /package/dist/{chunk-IKXUG6KC.js.map → chunk-YXMHUBN2.js.map} +0 -0
- /package/dist/{chunk-GFNS3BHV.js.map → chunk-ZUPKNWRZ.js.map} +0 -0
- /package/dist/{chunk-GSTIBXBW.js.map → chunk-ZVPVTPGI.js.map} +0 -0
package/src/dedup/index.test.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { tmpdir } from "node:os";
|
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import test from "node:test";
|
|
6
6
|
|
|
7
|
-
import { findContradictions, findDuplicates } from "./index.js";
|
|
7
|
+
import { NEGATION_WORDS, findContradictions, findDuplicates } from "./index.js";
|
|
8
8
|
|
|
9
9
|
test("findContradictions detects can versus cannot statements", async () => {
|
|
10
10
|
const memoryDir = await mkdtemp(path.join(tmpdir(), "remnic-dedup-contradiction-"));
|
|
@@ -149,3 +149,67 @@ test("dedup scans reject symlinked category directories outside memoryDir", asyn
|
|
|
149
149
|
await rm(outsideDir, { recursive: true, force: true });
|
|
150
150
|
}
|
|
151
151
|
});
|
|
152
|
+
|
|
153
|
+
test("strip-negation pattern is derived from NEGATION_WORDS", async () => {
|
|
154
|
+
NEGATION_WORDS.add("seldom");
|
|
155
|
+
const memoryDir = await mkdtemp(path.join(tmpdir(), "remnic-dedup-negation-"));
|
|
156
|
+
|
|
157
|
+
try {
|
|
158
|
+
const factsDir = path.join(memoryDir, "facts");
|
|
159
|
+
await mkdir(factsDir);
|
|
160
|
+
await writeFile(
|
|
161
|
+
path.join(factsDir, "plain.md"),
|
|
162
|
+
["---", "id: mem-plain", "category: fact", "---", "The user enjoys morning runs."].join("\n"),
|
|
163
|
+
"utf8"
|
|
164
|
+
);
|
|
165
|
+
await writeFile(
|
|
166
|
+
path.join(factsDir, "seldom.md"),
|
|
167
|
+
["---", "id: mem-seldom", "category: fact", "---", "The user seldom enjoys morning runs."].join("\n"),
|
|
168
|
+
"utf8"
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
const result = findContradictions({ memoryDir, categories: ["facts"] });
|
|
172
|
+
|
|
173
|
+
// "high" requires the stripped texts to match exactly, which only happens
|
|
174
|
+
// when the strip pattern honors the word the test added to the set.
|
|
175
|
+
assert.equal(result.contradictions.length, 1);
|
|
176
|
+
assert.equal(result.contradictions[0]?.severity, "high");
|
|
177
|
+
assert.equal(result.contradictions[0]?.reason, "Negated version of similar content");
|
|
178
|
+
} finally {
|
|
179
|
+
NEGATION_WORDS.delete("seldom");
|
|
180
|
+
await rm(memoryDir, { recursive: true, force: true });
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
test("strip-negation cache rebuilds on same-size vocabulary swap", async () => {
|
|
185
|
+
const memoryDir = await mkdtemp(path.join(tmpdir(), "remnic-dedup-negation-swap-"));
|
|
186
|
+
|
|
187
|
+
try {
|
|
188
|
+
const factsDir = path.join(memoryDir, "facts");
|
|
189
|
+
await mkdir(factsDir);
|
|
190
|
+
await writeFile(
|
|
191
|
+
path.join(factsDir, "plain.md"),
|
|
192
|
+
["---", "id: mem-swap-plain", "category: fact", "---", "The user enjoys morning runs."].join("\n"),
|
|
193
|
+
"utf8"
|
|
194
|
+
);
|
|
195
|
+
await writeFile(
|
|
196
|
+
path.join(factsDir, "rarely.md"),
|
|
197
|
+
["---", "id: mem-swap-rarely", "category: fact", "---", "The user rarely enjoys morning runs."].join("\n"),
|
|
198
|
+
"utf8"
|
|
199
|
+
);
|
|
200
|
+
|
|
201
|
+
// Warm the pattern cache with the current vocabulary (no "rarely" yet).
|
|
202
|
+
assert.equal(findContradictions({ memoryDir, categories: ["facts"] }).contradictions.length, 0);
|
|
203
|
+
|
|
204
|
+
// Same-size swap: the size-keyed cache would keep serving the stale pattern.
|
|
205
|
+
NEGATION_WORDS.delete("never");
|
|
206
|
+
NEGATION_WORDS.add("rarely");
|
|
207
|
+
const result = findContradictions({ memoryDir, categories: ["facts"] });
|
|
208
|
+
assert.equal(result.contradictions.length, 1);
|
|
209
|
+
assert.equal(result.contradictions[0]?.severity, "high");
|
|
210
|
+
} finally {
|
|
211
|
+
NEGATION_WORDS.delete("rarely");
|
|
212
|
+
NEGATION_WORDS.add("never");
|
|
213
|
+
await rm(memoryDir, { recursive: true, force: true });
|
|
214
|
+
}
|
|
215
|
+
});
|
package/src/dedup/index.ts
CHANGED
|
@@ -245,7 +245,7 @@ function tokenizeForSimilarity(text: string): Set<string> {
|
|
|
245
245
|
|
|
246
246
|
// ── Contradiction detection ──────────────────────────────────────────────────
|
|
247
247
|
|
|
248
|
-
const NEGATION_WORDS = new Set([
|
|
248
|
+
export const NEGATION_WORDS = new Set([
|
|
249
249
|
"not",
|
|
250
250
|
"don't",
|
|
251
251
|
"doesn't",
|
|
@@ -317,9 +317,31 @@ function containsNegation(text: string): boolean {
|
|
|
317
317
|
return words.some((w) => NEGATION_WORDS.has(w));
|
|
318
318
|
}
|
|
319
319
|
|
|
320
|
+
function escapeRegExp(value: string): string {
|
|
321
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// Single vocabulary source: the strip pattern is derived from NEGATION_WORDS,
|
|
325
|
+
// never hand-maintained. Keyed on the alternation content so any vocabulary
|
|
326
|
+
// change (not just a size change) rebuilds the pattern.
|
|
327
|
+
let stripNegationPattern: RegExp | null = null;
|
|
328
|
+
let stripNegationPatternSource: string | null = null;
|
|
329
|
+
|
|
330
|
+
function stripNegationRegex(): RegExp {
|
|
331
|
+
const alternation = [...NEGATION_WORDS]
|
|
332
|
+
.sort((a, b) => b.length - a.length)
|
|
333
|
+
.map(escapeRegExp)
|
|
334
|
+
.join("|");
|
|
335
|
+
if (stripNegationPattern === null || stripNegationPatternSource !== alternation) {
|
|
336
|
+
stripNegationPattern = new RegExp(`\\b(?:${alternation})\\b`, "gi");
|
|
337
|
+
stripNegationPatternSource = alternation;
|
|
338
|
+
}
|
|
339
|
+
return stripNegationPattern;
|
|
340
|
+
}
|
|
341
|
+
|
|
320
342
|
function stripNegation(text: string): string {
|
|
321
343
|
return text
|
|
322
|
-
.replace(
|
|
344
|
+
.replace(stripNegationRegex(), "")
|
|
323
345
|
.replace(/\s+/g, " ")
|
|
324
346
|
.trim();
|
|
325
347
|
}
|
|
@@ -5,12 +5,12 @@ import { MeetingsBuilder, type MeetingDayData, type MeetingsDayBuildSummary, typ
|
|
|
5
5
|
import { DEFAULT_MEETINGS_CONFIG } from "./config.js";
|
|
6
6
|
import { MeetingRecordStore, type MeetingRecordFileIo } from "./store.js";
|
|
7
7
|
import type {
|
|
8
|
-
MeetingActivitySnapshot,
|
|
9
8
|
MeetingsConfig,
|
|
10
9
|
MeetingAppSpan,
|
|
11
10
|
MeetingAudioWindow,
|
|
12
11
|
MeetingRecord,
|
|
13
12
|
} from "./types.js";
|
|
13
|
+
import type { ActivitySnapshot } from "../activity/types.js";
|
|
14
14
|
import type { FusionConversationInput, FusionSegmentInput } from "../wearables/fusion/types.js";
|
|
15
15
|
import type { MeetingMemoryGenerator, MeetingMemoryOutcome } from "./memory-generator.js";
|
|
16
16
|
|
|
@@ -174,9 +174,25 @@ test("degradation — audio only yields a record with no screen-context section"
|
|
|
174
174
|
});
|
|
175
175
|
|
|
176
176
|
test("full — app+audio with activity fuses transcript and screen context", async () => {
|
|
177
|
-
const activity:
|
|
178
|
-
{
|
|
179
|
-
|
|
177
|
+
const activity: ActivitySnapshot[] = [
|
|
178
|
+
{
|
|
179
|
+
machine: "workstation",
|
|
180
|
+
capturedAtUtc: "2026-03-10T14:05:00.000Z",
|
|
181
|
+
app: "Preview",
|
|
182
|
+
windowTitle: "Q3-roadmap.pdf",
|
|
183
|
+
text: "",
|
|
184
|
+
textSource: "ax",
|
|
185
|
+
contentHash: "snap-1",
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
machine: "workstation",
|
|
189
|
+
capturedAtUtc: "2026-03-10T14:30:00.000Z",
|
|
190
|
+
app: "Zoom",
|
|
191
|
+
windowTitle: "",
|
|
192
|
+
text: "meeting chat",
|
|
193
|
+
textSource: "ax",
|
|
194
|
+
contentHash: "snap-2",
|
|
195
|
+
},
|
|
180
196
|
];
|
|
181
197
|
const { builder: b, store } = builder(
|
|
182
198
|
{
|
package/src/meetings/build.ts
CHANGED
|
@@ -22,10 +22,10 @@ import type {
|
|
|
22
22
|
MeetingMemoryOutcome,
|
|
23
23
|
MeetingsDayFactGenResult,
|
|
24
24
|
} from "./memory-generator.js";
|
|
25
|
+
import type { ActivitySnapshot } from "../activity/types.js";
|
|
25
26
|
import type { FusionConversationInput } from "../wearables/fusion/types.js";
|
|
26
27
|
import type {
|
|
27
28
|
DetectedMeeting,
|
|
28
|
-
MeetingActivitySnapshot,
|
|
29
29
|
MeetingRecord,
|
|
30
30
|
MeetingsConfig,
|
|
31
31
|
MeetingsDetectionInput,
|
|
@@ -38,7 +38,7 @@ export interface MeetingDayData {
|
|
|
38
38
|
/** Fusion conversation inputs across all wearable sources for the day. */
|
|
39
39
|
conversations: FusionConversationInput[];
|
|
40
40
|
/** Screen-activity snapshots for the day. */
|
|
41
|
-
activity?:
|
|
41
|
+
activity?: ActivitySnapshot[];
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
/** Supplies a day's signals. Backed by fixtures in tests; by the activity +
|
|
@@ -127,7 +127,9 @@ test("loadDayData assembles detection + activity for the requested day, keyed to
|
|
|
127
127
|
assert.equal(data.detection.appSpans[0]?.app, "Zoom");
|
|
128
128
|
assert.deepEqual(data.detection.audioWindows, [], "no wearable transcripts → no audio windows");
|
|
129
129
|
assert.equal(data.activity?.length, 2);
|
|
130
|
-
|
|
130
|
+
// Activity is the raw ActivitySnapshot list now — no rename mapper in between.
|
|
131
|
+
assert.equal(data.activity?.[0]?.windowTitle, "Standup");
|
|
132
|
+
assert.equal(data.activity?.[0]?.capturedAtUtc, "2026-03-10T14:00:00.000Z");
|
|
131
133
|
assert.equal(data.conversations.length, 0);
|
|
132
134
|
});
|
|
133
135
|
|
|
@@ -28,7 +28,6 @@ import type { FusionConversationInput, FusionSegmentInput } from "../wearables/f
|
|
|
28
28
|
import type { MeetingDayData, MeetingsDaySource } from "./build.js";
|
|
29
29
|
import { MeetingsInputError } from "./errors.js";
|
|
30
30
|
import type {
|
|
31
|
-
MeetingActivitySnapshot,
|
|
32
31
|
MeetingAppSpan,
|
|
33
32
|
MeetingAudioWindow,
|
|
34
33
|
MeetingsConfig,
|
|
@@ -97,15 +96,6 @@ export function storageWearableDayReader(store: WearableDayTranscriptStore): Mee
|
|
|
97
96
|
};
|
|
98
97
|
}
|
|
99
98
|
|
|
100
|
-
/** Map an activity snapshot to the redaction-safe screen snapshot fusion sees. */
|
|
101
|
-
function toMeetingActivitySnapshot(snapshot: ActivitySnapshot): MeetingActivitySnapshot {
|
|
102
|
-
const out: MeetingActivitySnapshot = { tsUtc: snapshot.capturedAtUtc, app: snapshot.app };
|
|
103
|
-
if (snapshot.windowTitle.length > 0) out.title = snapshot.windowTitle;
|
|
104
|
-
if (snapshot.browserUrl !== undefined && snapshot.browserUrl.length > 0) out.url = snapshot.browserUrl;
|
|
105
|
-
if (snapshot.text.length > 0) out.text = snapshot.text;
|
|
106
|
-
return out;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
99
|
/**
|
|
110
100
|
* Fallback dwell for a lone trailing meeting-app tick on a machine that
|
|
111
101
|
* captured no other snapshot to estimate a cadence from. Five minutes keeps
|
|
@@ -341,12 +331,11 @@ export class ActivityWearablesMeetingsDaySource implements MeetingsDaySource {
|
|
|
341
331
|
const snapshots = this.deps.activity
|
|
342
332
|
? await this.deps.activity.listSnapshotsForDay(null, startUtc, endUtc)
|
|
343
333
|
: [];
|
|
344
|
-
const activity = snapshots.map(toMeetingActivitySnapshot);
|
|
345
334
|
const appSpans = deriveAppSpans(snapshots, this.deps.config.appPatterns);
|
|
346
335
|
const bodies = await this.deps.wearables.readDayBodies(date);
|
|
347
336
|
const tzBySource = new Map(bodies.map((b) => [b.source, b.timezone ?? ""] as const));
|
|
348
337
|
const conversations = shiftFusionInputsToUtc(reconstructFusionInputs(date, bodies), tzBySource);
|
|
349
338
|
const audioWindows = buildAudioWindows(conversations);
|
|
350
|
-
return { detection: { date, appSpans, audioWindows }, conversations, activity };
|
|
339
|
+
return { detection: { date, appSpans, audioWindows }, conversations, activity: snapshots };
|
|
351
340
|
}
|
|
352
341
|
}
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import assert from "node:assert/strict";
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
2
4
|
import { test } from "node:test";
|
|
3
5
|
|
|
4
6
|
import { DEFAULT_MEETINGS_CONFIG } from "./config.js";
|
|
5
7
|
import { fuseMeeting } from "./fuse.js";
|
|
6
|
-
import type {
|
|
7
|
-
|
|
8
|
-
MeetingActivitySnapshot,
|
|
9
|
-
MeetingsConfig,
|
|
10
|
-
} from "./types.js";
|
|
8
|
+
import type { ActivitySnapshot } from "../activity/types.js";
|
|
9
|
+
import type { DetectedMeeting, MeetingsConfig } from "./types.js";
|
|
11
10
|
import type { FusionConversationInput, FusionSegmentInput } from "../wearables/fusion/types.js";
|
|
12
11
|
|
|
13
12
|
const DATE = "2026-03-10";
|
|
@@ -51,8 +50,17 @@ function conv(
|
|
|
51
50
|
return { source, conversationId: id, startIso, endIso, segments };
|
|
52
51
|
}
|
|
53
52
|
|
|
54
|
-
function snap(
|
|
55
|
-
|
|
53
|
+
function snap(
|
|
54
|
+
overrides: Partial<ActivitySnapshot> & { capturedAtUtc: string; app: string },
|
|
55
|
+
): ActivitySnapshot {
|
|
56
|
+
return {
|
|
57
|
+
machine: "workstation",
|
|
58
|
+
windowTitle: "",
|
|
59
|
+
text: "",
|
|
60
|
+
textSource: "ax",
|
|
61
|
+
contentHash: "snap-hash",
|
|
62
|
+
...overrides,
|
|
63
|
+
};
|
|
56
64
|
}
|
|
57
65
|
|
|
58
66
|
test("fusion prefers the higher-trust source in overlap regions and records corroboratedBy without duplicating text", () => {
|
|
@@ -97,10 +105,10 @@ test("screen context includes a >=20s dwell and omits a 5s alt-tab; meeting-app
|
|
|
97
105
|
]),
|
|
98
106
|
],
|
|
99
107
|
activity: [
|
|
100
|
-
snap({
|
|
101
|
-
snap({
|
|
102
|
-
snap({
|
|
103
|
-
snap({
|
|
108
|
+
snap({ capturedAtUtc: "2026-03-10T14:05:00.000Z", app: "Preview", windowTitle: "Q3-roadmap.pdf", text: "Q3 roadmap draft" }),
|
|
109
|
+
snap({ capturedAtUtc: "2026-03-10T14:05:25.000Z", app: "Notes", windowTitle: "scratch" }), // Preview dwell 25s → include; Notes starts
|
|
110
|
+
snap({ capturedAtUtc: "2026-03-10T14:05:30.000Z", app: "Preview", windowTitle: "Q3-roadmap.pdf" }), // Notes dwell 5s → exclude
|
|
111
|
+
snap({ capturedAtUtc: "2026-03-10T14:10:00.000Z", app: "Zoom", text: "Jane: agenda item one" }), // meeting-app → excerpt
|
|
104
112
|
],
|
|
105
113
|
},
|
|
106
114
|
config(),
|
|
@@ -122,9 +130,9 @@ test("context excerpts respect maxContextChars", () => {
|
|
|
122
130
|
meeting: meeting(),
|
|
123
131
|
conversations: [],
|
|
124
132
|
activity: [
|
|
125
|
-
snap({
|
|
126
|
-
snap({
|
|
127
|
-
snap({
|
|
133
|
+
snap({ capturedAtUtc: "2026-03-10T14:01:00.000Z", app: "Zoom", text: "aaaa" }),
|
|
134
|
+
snap({ capturedAtUtc: "2026-03-10T14:02:00.000Z", app: "Zoom", text: "bbbb" }),
|
|
135
|
+
snap({ capturedAtUtc: "2026-03-10T14:03:00.000Z", app: "Zoom", text: "cccc" }),
|
|
128
136
|
],
|
|
129
137
|
},
|
|
130
138
|
config({ maxContextChars: 8 }),
|
|
@@ -199,10 +207,51 @@ test("a lone trailing other-app snapshot is not inflated to the meeting end", ()
|
|
|
199
207
|
],
|
|
200
208
|
// A single Notes snapshot 1 min before the window end. With no later
|
|
201
209
|
// snapshot, the trailing run must NOT count dwell up to the meeting end.
|
|
202
|
-
activity: [snap({
|
|
210
|
+
activity: [snap({ capturedAtUtc: "2026-03-10T14:29:00.000Z", app: "Notes", windowTitle: "scratch" })],
|
|
203
211
|
},
|
|
204
212
|
config(),
|
|
205
213
|
);
|
|
206
214
|
assert.equal(fused.snapshotCount, 1);
|
|
207
215
|
assert.deepEqual(fused.screenContext, [], "a lone trailing snapshot has zero observed dwell");
|
|
208
216
|
});
|
|
217
|
+
|
|
218
|
+
test("fusion reads ActivitySnapshot fields — capturedAtUtc, browserUrl runs, windowTitle fallback", () => {
|
|
219
|
+
const fused = fuseMeeting(
|
|
220
|
+
{
|
|
221
|
+
meeting: meeting(),
|
|
222
|
+
conversations: [],
|
|
223
|
+
activity: [
|
|
224
|
+
snap({ capturedAtUtc: "2026-03-10T14:05:00.000Z", app: "Chrome", browserUrl: "https://github.com/org/repo/pull/1" }),
|
|
225
|
+
snap({ capturedAtUtc: "2026-03-10T14:06:00.000Z", app: "Chrome", browserUrl: "https://github.com/org/repo/pull/1" }),
|
|
226
|
+
// Different URL on the same app → a separate dwell run.
|
|
227
|
+
snap({ capturedAtUtc: "2026-03-10T14:06:30.000Z", app: "Chrome", browserUrl: "https://example.com/doc" }),
|
|
228
|
+
snap({ capturedAtUtc: "2026-03-10T14:07:00.000Z", app: "Chrome", browserUrl: "https://example.com/doc" }),
|
|
229
|
+
// Empty browserUrl falls back to the window title for key and label.
|
|
230
|
+
snap({ capturedAtUtc: "2026-03-10T14:07:30.000Z", app: "Chrome", windowTitle: "Spec v2" }),
|
|
231
|
+
snap({ capturedAtUtc: "2026-03-10T14:08:00.000Z", app: "Chrome", windowTitle: "Spec v2" }),
|
|
232
|
+
],
|
|
233
|
+
},
|
|
234
|
+
config(),
|
|
235
|
+
);
|
|
236
|
+
assert.equal(fused.snapshotCount, 6);
|
|
237
|
+
assert.deepEqual(
|
|
238
|
+
fused.screenContext.map((e) => e.label),
|
|
239
|
+
[
|
|
240
|
+
"Chrome: github.com/org/repo/pull/1",
|
|
241
|
+
"Chrome: example.com/doc",
|
|
242
|
+
"Chrome: Spec v2",
|
|
243
|
+
],
|
|
244
|
+
);
|
|
245
|
+
// Run dwell ends at the next different-key snapshot (90s, 60s) or, for the
|
|
246
|
+
// trailing run, at its own last snapshot (30s).
|
|
247
|
+
assert.deepEqual(fused.screenContext.map((e) => e.dwellSeconds), [90, 60, 30]);
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
test("MeetingActivitySnapshot and its rename mapper are gone", () => {
|
|
251
|
+
const here = import.meta.dirname;
|
|
252
|
+
for (const file of ["types.ts", "fuse.ts", "day-source.ts", "build.ts"]) {
|
|
253
|
+
const src = readFileSync(join(here, file), "utf8");
|
|
254
|
+
assert.ok(!src.includes("MeetingActivitySnapshot"), `${file} must not reference MeetingActivitySnapshot`);
|
|
255
|
+
assert.ok(!src.includes("toMeetingActivitySnapshot"), `${file} must not reference toMeetingActivitySnapshot`);
|
|
256
|
+
}
|
|
257
|
+
});
|
package/src/meetings/fuse.ts
CHANGED
|
@@ -23,9 +23,9 @@ import type {
|
|
|
23
23
|
FusionConversationInput,
|
|
24
24
|
FusionSegmentInput,
|
|
25
25
|
} from "../wearables/fusion/types.js";
|
|
26
|
+
import type { ActivitySnapshot } from "../activity/types.js";
|
|
26
27
|
import type {
|
|
27
28
|
FusedMeeting,
|
|
28
|
-
MeetingActivitySnapshot,
|
|
29
29
|
MeetingBuildInput,
|
|
30
30
|
MeetingScreenContextEvent,
|
|
31
31
|
MeetingsConfig,
|
|
@@ -119,20 +119,22 @@ function shortenUrl(url: string): string {
|
|
|
119
119
|
return url.replace(/^[a-z]+:\/\//i, "");
|
|
120
120
|
}
|
|
121
121
|
|
|
122
|
-
function contextLabel(snap:
|
|
123
|
-
if (snap.
|
|
124
|
-
|
|
122
|
+
function contextLabel(snap: ActivitySnapshot): string {
|
|
123
|
+
if (snap.browserUrl !== undefined && snap.browserUrl.length > 0) {
|
|
124
|
+
return `${snap.app}: ${shortenUrl(snap.browserUrl)}`;
|
|
125
|
+
}
|
|
126
|
+
if (snap.windowTitle.length > 0) return `${snap.app}: ${snap.windowTitle}`;
|
|
125
127
|
return snap.app;
|
|
126
128
|
}
|
|
127
129
|
|
|
128
130
|
/** True when a snapshot is the meeting app itself (captions/chat/participants),
|
|
129
131
|
* not other context. Matches the meeting's app or any configured app pattern. */
|
|
130
132
|
function isMeetingAppSnapshot(
|
|
131
|
-
snap:
|
|
133
|
+
snap: ActivitySnapshot,
|
|
132
134
|
meetingApp: string | undefined,
|
|
133
135
|
appPatterns: readonly string[],
|
|
134
136
|
): boolean {
|
|
135
|
-
const hay = `${snap.app} ${snap.
|
|
137
|
+
const hay = `${snap.app} ${snap.windowTitle} ${snap.browserUrl ?? ""}`.toLowerCase();
|
|
136
138
|
if (meetingApp !== undefined && meetingApp.length > 0 && hay.includes(meetingApp.toLowerCase())) {
|
|
137
139
|
return true;
|
|
138
140
|
}
|
|
@@ -159,21 +161,25 @@ interface ScreenContextResult {
|
|
|
159
161
|
* kept. Meeting-app snapshots never enter the timeline but feed excerpts.
|
|
160
162
|
*/
|
|
161
163
|
function buildScreenContext(
|
|
162
|
-
activity: readonly
|
|
164
|
+
activity: readonly ActivitySnapshot[],
|
|
163
165
|
windowStartMs: number,
|
|
164
166
|
windowEndMs: number,
|
|
165
167
|
meetingApp: string | undefined,
|
|
166
168
|
config: MeetingsConfig,
|
|
167
169
|
): ScreenContextResult {
|
|
168
170
|
const win = activity
|
|
169
|
-
.map((snap) => ({ snap, ms: Date.parse(snap.
|
|
171
|
+
.map((snap) => ({ snap, ms: Date.parse(snap.capturedAtUtc) }))
|
|
170
172
|
.filter((entry) => Number.isFinite(entry.ms) && entry.ms >= windowStartMs && entry.ms < windowEndMs)
|
|
171
173
|
.sort((a, b) => a.ms - b.ms || SORT_STR(a.snap.app, b.snap.app));
|
|
172
174
|
|
|
173
|
-
|
|
175
|
+
/** Run key detail: the URL when present, else the window title. */
|
|
176
|
+
const keyDetail = (snap: ActivitySnapshot): string =>
|
|
177
|
+
snap.browserUrl !== undefined && snap.browserUrl.length > 0 ? snap.browserUrl : snap.windowTitle;
|
|
178
|
+
|
|
179
|
+
const keyOf = (snap: ActivitySnapshot): string =>
|
|
174
180
|
isMeetingAppSnapshot(snap, meetingApp, config.appPatterns)
|
|
175
181
|
? MEETING_APP_KEY
|
|
176
|
-
: `${snap.app}\u0000${snap
|
|
182
|
+
: `${snap.app}\u0000${keyDetail(snap)}`;
|
|
177
183
|
|
|
178
184
|
const events: MeetingScreenContextEvent[] = [];
|
|
179
185
|
const includedText: string[] = [];
|
|
@@ -197,7 +203,7 @@ function buildScreenContext(
|
|
|
197
203
|
// Captions / chat / participant text visible on the meeting app itself.
|
|
198
204
|
for (let i = runStartIdx; i < runEndIdx; i++) {
|
|
199
205
|
const text = win[i]!.snap.text;
|
|
200
|
-
if (text
|
|
206
|
+
if (text.trim().length > 0) includedText.push(text.trim());
|
|
201
207
|
}
|
|
202
208
|
} else if (dwellSeconds >= config.contextDwellSeconds) {
|
|
203
209
|
events.push({
|
|
@@ -209,7 +215,7 @@ function buildScreenContext(
|
|
|
209
215
|
});
|
|
210
216
|
for (let i = runStartIdx; i < runEndIdx; i++) {
|
|
211
217
|
const text = win[i]!.snap.text;
|
|
212
|
-
if (text
|
|
218
|
+
if (text.trim().length > 0) includedText.push(text.trim());
|
|
213
219
|
}
|
|
214
220
|
}
|
|
215
221
|
runStartIdx = runEndIdx;
|
package/src/meetings/types.ts
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* timestamps are UTC ISO-8601; windows are half-open [startUtc, endUtc).
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
+
import type { ActivitySnapshot } from "../activity/types.js";
|
|
11
12
|
import type {
|
|
12
13
|
FusedSegment,
|
|
13
14
|
FusedSpeaker,
|
|
@@ -81,23 +82,6 @@ export interface MeetingsDetectionConfig {
|
|
|
81
82
|
mergeGapMinutes: number;
|
|
82
83
|
}
|
|
83
84
|
|
|
84
|
-
/**
|
|
85
|
-
* One screen-activity snapshot fed into meeting fusion (assembled from the
|
|
86
|
-
* activity store by a later wiring slice; injected directly in tests). Text is
|
|
87
|
-
* already post-redaction — meeting fusion never sees raw capture.
|
|
88
|
-
*/
|
|
89
|
-
export interface MeetingActivitySnapshot {
|
|
90
|
-
/** ISO 8601 UTC instant the snapshot was captured. */
|
|
91
|
-
tsUtc: string;
|
|
92
|
-
/** Foreground application label (e.g. "Preview", "Chrome", "Zoom"). */
|
|
93
|
-
app: string;
|
|
94
|
-
/** Window title, when known. */
|
|
95
|
-
title?: string;
|
|
96
|
-
/** Browser URL, when known. */
|
|
97
|
-
url?: string;
|
|
98
|
-
/** Extracted on-screen text excerpt, when known. */
|
|
99
|
-
text?: string;
|
|
100
|
-
}
|
|
101
85
|
|
|
102
86
|
/** One entry in a meeting's screen-context timeline (an other-app dwell). */
|
|
103
87
|
export interface MeetingScreenContextEvent {
|
|
@@ -144,8 +128,9 @@ export interface MeetingBuildInput {
|
|
|
144
128
|
* is fine (they are clipped in `fuseMeeting`).
|
|
145
129
|
*/
|
|
146
130
|
conversations: FusionConversationInput[];
|
|
147
|
-
/** Day screen-activity snapshots (filtered to the window in `fuseMeeting`).
|
|
148
|
-
|
|
131
|
+
/** Day screen-activity snapshots (filtered to the window in `fuseMeeting`).
|
|
132
|
+
* Already post-redaction — meeting fusion never sees raw capture. */
|
|
133
|
+
activity?: ActivitySnapshot[];
|
|
149
134
|
}
|
|
150
135
|
|
|
151
136
|
/** The fused, screen-aware view of a meeting produced by `fuseMeeting`. */
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Issue #2481 — the derived cache layers (episode map, rule memories) share
|
|
3
|
+
* one getVersionedCacheEntry read gate with the hot layer. These pin the
|
|
4
|
+
* per-layer contracts that must keep holding after that dedup: version-keyed
|
|
5
|
+
* round-trip, version-mismatch miss, keyId mismatch miss, and TTL expiry
|
|
6
|
+
* miss on EACH layer. (Hot-layer equivalents live in memory-cache-hot.test.ts.)
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import assert from "node:assert/strict";
|
|
10
|
+
import test from "node:test";
|
|
11
|
+
|
|
12
|
+
import {
|
|
13
|
+
clearMemoryCache,
|
|
14
|
+
getCachedEpisodeMap,
|
|
15
|
+
getCachedRuleMemories,
|
|
16
|
+
setCachedEpisodeMap,
|
|
17
|
+
setCachedRuleMemories,
|
|
18
|
+
} from "./memory-cache.js";
|
|
19
|
+
import type { MemoryFile } from "./types.js";
|
|
20
|
+
|
|
21
|
+
function mem(path: string, id: string, category = "fact", memoryKind?: string): MemoryFile {
|
|
22
|
+
return {
|
|
23
|
+
path,
|
|
24
|
+
frontmatter: { id, category, ...(memoryKind ? { memoryKind } : {}) },
|
|
25
|
+
content: "x",
|
|
26
|
+
} as unknown as MemoryFile;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const EPISODE = mem("a.md", "ep-1", "fact", "episode");
|
|
30
|
+
|
|
31
|
+
test("episode map: matching version hits; version or keyId mismatch misses", () => {
|
|
32
|
+
const dir = "/tmp/remnic-derived-ep-ver";
|
|
33
|
+
clearMemoryCache();
|
|
34
|
+
setCachedEpisodeMap(dir, [EPISODE], 7, true, "key:1");
|
|
35
|
+
const got = getCachedEpisodeMap(dir, 7, "key:1");
|
|
36
|
+
assert.ok(got, "expected a hit at the matching version and keyId");
|
|
37
|
+
assert.deepEqual([...got.keys()], ["ep-1"]);
|
|
38
|
+
assert.equal(getCachedEpisodeMap(dir, 8, "key:1"), null, "a bumped version must miss");
|
|
39
|
+
assert.equal(getCachedEpisodeMap(dir, 7, "key:2"), null, "a mismatched keyId must miss");
|
|
40
|
+
clearMemoryCache();
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test("episode map: TTL expiry misses; ttl=0 disables expiry", (t) => {
|
|
44
|
+
const dir = "/tmp/remnic-derived-ep-ttl";
|
|
45
|
+
clearMemoryCache();
|
|
46
|
+
t.mock.timers.enable({ apis: ["Date"] });
|
|
47
|
+
try {
|
|
48
|
+
setCachedEpisodeMap(dir, [EPISODE], 9);
|
|
49
|
+
assert.ok(getCachedEpisodeMap(dir, 9, "", 60_000), "fresh entry within TTL hits");
|
|
50
|
+
t.mock.timers.tick(120_000);
|
|
51
|
+
assert.equal(getCachedEpisodeMap(dir, 9, "", 60_000), null, "entry older than ttl is a miss");
|
|
52
|
+
setCachedEpisodeMap(dir, [EPISODE], 9);
|
|
53
|
+
t.mock.timers.tick(600_000);
|
|
54
|
+
assert.ok(getCachedEpisodeMap(dir, 9, "", 0), "ttl=0 disables expiry");
|
|
55
|
+
} finally {
|
|
56
|
+
t.mock.timers.reset();
|
|
57
|
+
clearMemoryCache();
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test("rule memories: matching version hits; version or keyId mismatch misses", () => {
|
|
62
|
+
const dir = "/tmp/remnic-derived-rule-ver";
|
|
63
|
+
clearMemoryCache();
|
|
64
|
+
setCachedRuleMemories(dir, [mem("r.md", "r-1", "rule"), mem("f.md", "f-1", "fact")], 7, true, "key:1");
|
|
65
|
+
const got = getCachedRuleMemories(dir, 7, "key:1");
|
|
66
|
+
assert.ok(got, "expected a hit at the matching version and keyId");
|
|
67
|
+
assert.deepEqual(got.all.map((m) => m.frontmatter.id), ["r-1"], "only rule-category memories in all");
|
|
68
|
+
assert.ok(got.byId.has("f-1"), "byId indexes every memory");
|
|
69
|
+
assert.equal(getCachedRuleMemories(dir, 8, "key:1"), null, "a bumped version must miss");
|
|
70
|
+
assert.equal(getCachedRuleMemories(dir, 7, "key:2"), null, "a mismatched keyId must miss");
|
|
71
|
+
clearMemoryCache();
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test("rule memories: TTL expiry misses; ttl=0 disables expiry", (t) => {
|
|
75
|
+
const dir = "/tmp/remnic-derived-rule-ttl";
|
|
76
|
+
clearMemoryCache();
|
|
77
|
+
t.mock.timers.enable({ apis: ["Date"] });
|
|
78
|
+
try {
|
|
79
|
+
setCachedRuleMemories(dir, [mem("r.md", "r-1", "rule")], 9);
|
|
80
|
+
assert.ok(getCachedRuleMemories(dir, 9, "", 60_000), "fresh entry within TTL hits");
|
|
81
|
+
t.mock.timers.tick(120_000);
|
|
82
|
+
assert.equal(getCachedRuleMemories(dir, 9, "", 60_000), null, "entry older than ttl is a miss");
|
|
83
|
+
setCachedRuleMemories(dir, [mem("r.md", "r-1", "rule")], 9);
|
|
84
|
+
t.mock.timers.tick(600_000);
|
|
85
|
+
assert.ok(getCachedRuleMemories(dir, 9, "", 0), "ttl=0 disables expiry");
|
|
86
|
+
} finally {
|
|
87
|
+
t.mock.timers.reset();
|
|
88
|
+
clearMemoryCache();
|
|
89
|
+
}
|
|
90
|
+
});
|