@remnic/core 9.22.0 → 9.22.2
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 +15 -12
- package/dist/access-admin-ops-surface.js +25 -24
- package/dist/access-audit.js +2 -2
- package/dist/access-boundary.d.ts +13 -10
- package/dist/access-boundary.js +25 -24
- package/dist/access-cli.js +84 -82
- package/dist/access-cli.js.map +1 -1
- package/dist/access-http.d.ts +16 -9
- package/dist/access-http.js +33 -32
- package/dist/access-identity-continuity-surface.d.ts +9 -7
- package/dist/access-identity-continuity-surface.js +25 -24
- package/dist/access-lcm-surface.d.ts +12 -9
- package/dist/access-lcm-surface.js +25 -24
- package/dist/access-mcp-output-schemas.js +1 -1
- package/dist/access-mcp.d.ts +22 -10
- package/dist/access-mcp.js +31 -30
- package/dist/access-memory-search-fanout.d.ts +2 -2
- package/dist/{access-namespace-preflight-tmwQgCT6.d.ts → access-namespace-preflight-CmErR03h.d.ts} +1 -1
- package/dist/access-namespace-preflight.d.ts +3 -3
- package/dist/access-namespace-preflight.js +25 -24
- package/dist/access-observe-write-surface.d.ts +12 -9
- package/dist/access-observe-write-surface.js +25 -24
- package/dist/access-operations-batch.js +27 -26
- package/dist/access-operations.d.ts +16 -13
- package/dist/access-operations.js +28 -27
- package/dist/access-recall-concurrency.d.ts +12 -9
- package/dist/access-recall-concurrency.js +25 -24
- package/dist/access-recall-response.d.ts +12 -9
- package/dist/access-recall-response.js +25 -24
- package/dist/access-recall-surface.d.ts +14 -11
- package/dist/access-recall-surface.js +25 -24
- package/dist/access-schema.d.ts +76 -76
- package/dist/{access-service-BtxW52vv.d.ts → access-service-CKEROeFP.d.ts} +22 -11
- package/dist/access-service.d.ts +13 -10
- package/dist/access-service.js +25 -24
- package/dist/access-surface-catalog.d.ts +12 -9
- package/dist/access-surface-catalog.js +6 -0
- package/dist/access-surface-catalog.js.map +1 -1
- package/dist/access-wearables-meetings-surface.d.ts +78 -0
- package/dist/access-wearables-meetings-surface.js +22 -0
- package/dist/access-wearables-meetings-surface.js.map +1 -0
- 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/{auto-sync-UPTUVE4X.js → auto-sync-5OE5QOZB.js} +3 -3
- package/dist/behavior-learner.d.ts +1 -1
- package/dist/behavior-signals.d.ts +1 -1
- package/dist/bootstrap.d.ts +9 -7
- package/dist/briefing.d.ts +2 -2
- package/dist/briefing.js +5 -5
- package/dist/buffer-surprise-report.d.ts +1 -1
- package/dist/buffer.d.ts +2 -2
- 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/{catalog-iWvWz61I.d.ts → catalog-BFTVo2Ez.d.ts} +1 -1
- package/dist/causal-behavior.d.ts +1 -1
- package/dist/causal-behavior.js +2 -2
- package/dist/causal-chain.js +2 -2
- package/dist/causal-consolidation.d.ts +1 -1
- package/dist/causal-consolidation.js +9 -9
- package/dist/causal-retrieval.js +2 -2
- package/dist/causal-trajectory-graph.d.ts +1 -1
- package/dist/causal-trajectory.js +1 -1
- package/dist/{chunk-GF22HWS2.js → chunk-2PHX3I5C.js} +7 -7
- package/dist/{chunk-JFDEYXF7.js → chunk-3NAHZQU3.js} +2 -2
- package/dist/{chunk-XBRPEGXF.js → chunk-3UOSVTFR.js} +5 -5
- package/dist/{chunk-TTKWTT72.js → chunk-3ZW6ZQO6.js} +3 -3
- package/dist/{chunk-UV72DEZD.js → chunk-4B77XSG5.js} +5 -5
- package/dist/{chunk-IC2TCZJ2.js → chunk-57QKLRBV.js} +3 -1
- package/dist/chunk-57QKLRBV.js.map +1 -0
- package/dist/{chunk-LMHDFOVV.js → chunk-5CPI2QWG.js} +3 -3
- package/dist/{chunk-P72WZMWQ.js → chunk-5FZQ74K5.js} +2 -2
- package/dist/{chunk-BRT7KEXU.js → chunk-6TWXQ4E2.js} +5 -5
- package/dist/{chunk-CV6ZYXK3.js → chunk-7I7AYIUQ.js} +3 -3
- package/dist/{chunk-TVVEYCNW.js → chunk-7K5Q6COX.js} +4 -4
- package/dist/{chunk-MONWCSVL.js → chunk-7RAJ3GXO.js} +2 -1
- package/dist/chunk-7RAJ3GXO.js.map +1 -0
- package/dist/{chunk-SQEDDWAM.js → chunk-A2RFKL3V.js} +5 -34
- package/dist/chunk-A2RFKL3V.js.map +1 -0
- package/dist/{chunk-JN57HZCD.js → chunk-ATEAL5EI.js} +6 -6
- package/dist/{chunk-4PYLBXX6.js → chunk-AYAROCWD.js} +2 -2
- package/dist/chunk-BLDBT6HZ.js +39 -0
- package/dist/chunk-BLDBT6HZ.js.map +1 -0
- package/dist/{chunk-NB2P47N7.js → chunk-CUUY5CC4.js} +6 -6
- package/dist/{chunk-KL5Y6PEV.js → chunk-E3ZMFFQZ.js} +2 -2
- package/dist/{chunk-55FDQYPS.js → chunk-ES5IHQ6M.js} +2 -2
- package/dist/{chunk-N47ZWQBO.js → chunk-G2NSSQES.js} +2 -2
- package/dist/{chunk-GSWPU5CR.js → chunk-GD3QOBMB.js} +2 -2
- package/dist/{chunk-6ANPNUVL.js → chunk-IIXLB5E7.js} +58 -36
- package/dist/chunk-IIXLB5E7.js.map +1 -0
- package/dist/{chunk-SACPRJ7F.js → chunk-JNIJBXVK.js} +2 -2
- package/dist/{chunk-W3XT32W7.js → chunk-JVGUY2AU.js} +23 -13
- package/dist/chunk-JVGUY2AU.js.map +1 -0
- package/dist/{chunk-DGLON3AL.js → chunk-KNRG6ZGJ.js} +6 -6
- package/dist/{chunk-GEUDRENF.js → chunk-MMTG3SCJ.js} +2 -2
- package/dist/{chunk-WZFAPHIK.js → chunk-MU7ZHSZE.js} +2 -2
- package/dist/{chunk-W4FBGYGK.js → chunk-NRV4Y3PM.js} +1 -1
- package/dist/{chunk-QTO2FGBT.js → chunk-OUW4PAY4.js} +191 -117
- package/dist/chunk-OUW4PAY4.js.map +1 -0
- package/dist/{chunk-KQ2XCQYN.js → chunk-P2BLHUL2.js} +2 -2
- package/dist/{chunk-ACGGTPU4.js → chunk-QCKQEBIY.js} +17 -9
- package/dist/chunk-QCKQEBIY.js.map +1 -0
- package/dist/{chunk-6L775BUB.js → chunk-QE63OHLB.js} +2 -2
- package/dist/{chunk-ONHRRJZM.js → chunk-QJAQNXWI.js} +6 -6
- package/dist/{chunk-EQYBHXRR.js → chunk-ROUL7RZU.js} +385 -59
- package/dist/chunk-ROUL7RZU.js.map +1 -0
- package/dist/{chunk-N6M7MRVN.js → chunk-RT4TSR2C.js} +9034 -8726
- package/dist/chunk-RT4TSR2C.js.map +1 -0
- package/dist/{chunk-6NZ7U4R4.js → chunk-SCOT5DTA.js} +1 -1
- package/dist/chunk-SCOT5DTA.js.map +1 -0
- package/dist/{chunk-6RYWNBSA.js → chunk-SVZGA2MK.js} +2 -2
- package/dist/chunk-SYOIVK7K.js +63 -0
- package/dist/chunk-SYOIVK7K.js.map +1 -0
- package/dist/{chunk-DM5A4ZFL.js → chunk-TWCOYET2.js} +11 -1
- package/dist/chunk-TWCOYET2.js.map +1 -0
- package/dist/{chunk-7S5OMI76.js → chunk-WS32QRLO.js} +109 -44
- package/dist/chunk-WS32QRLO.js.map +1 -0
- package/dist/{chunk-OCMLGND7.js → chunk-WWZ35GWW.js} +5 -5
- package/dist/{chunk-QMGIQUBG.js → chunk-XWPFZX6S.js} +503 -139
- package/dist/chunk-XWPFZX6S.js.map +1 -0
- package/dist/{chunk-A5N6ISRK.js → chunk-ZN4UZWMT.js} +2 -2
- package/dist/{chunk-7M6APKJL.js → chunk-ZYU7ZFLT.js} +2 -2
- package/dist/{cli-D9vXgWwM.d.ts → cli-BrwT9YMD.d.ts} +5 -5
- package/dist/cli.d.ts +14 -11
- package/dist/cli.js +57 -56
- package/dist/compounding/engine.d.ts +2 -2
- package/dist/compounding/engine.js +5 -5
- 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/config.js +2 -2
- package/dist/connectors/codex-materialize-runner.d.ts +1 -1
- package/dist/connectors/codex-materialize-runner.js +5 -5
- package/dist/connectors/codex-materialize.d.ts +1 -1
- package/dist/connectors/index.d.ts +1 -1
- package/dist/connectors/index.js +5 -5
- package/dist/consolidation-provenance-check.d.ts +2 -2
- package/dist/consolidation-undo.d.ts +2 -2
- package/dist/contradiction/index.d.ts +2 -2
- 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/day-summary.d.ts +1 -1
- 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/enrichment/index.d.ts +1 -1
- package/dist/entity-retrieval.d.ts +2 -2
- package/dist/entity-retrieval.js +5 -5
- package/dist/entity-schema.d.ts +1 -1
- package/dist/explicit-capture.d.ts +9 -7
- package/dist/extraction-error-classification.d.ts +22 -0
- package/dist/extraction-error-classification.js +11 -0
- package/dist/extraction-error-classification.js.map +1 -0
- 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.d.ts +3 -17
- package/dist/extraction.js +5 -3
- package/dist/fallback-llm.d.ts +1 -1
- package/dist/{first-start-migration-LEXL3XYL.js → first-start-migration-BKNGOFED.js} +4 -4
- 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-snapshot.d.ts +1 -1
- package/dist/graph.d.ts +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 +713 -923
- package/dist/index.js +176 -140
- package/dist/intent.d.ts +1 -1
- package/dist/lcm/engine.d.ts +1 -1
- package/dist/lcm/engine.js +3 -3
- package/dist/lcm/index.d.ts +1 -1
- package/dist/lcm/index.js +11 -11
- 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 +6 -6
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.d.ts +2 -2
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +5 -5
- package/dist/maintenance/rebuild-memory-projection.d.ts +2 -2
- package/dist/maintenance/rebuild-memory-projection.js +7 -7
- package/dist/maintenance-C98njYb8.d.ts +366 -0
- package/dist/mcp-memory-inspector-app.d.ts +12 -9
- package/dist/mcp-read-only-tools.js +1 -1
- 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 +1 -1
- package/dist/memory-provenance.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 +6 -6
- package/dist/namespaces/storage.d.ts +3 -3
- package/dist/namespaces/storage.js +5 -5
- package/dist/native-knowledge.d.ts +1 -1
- package/dist/offline-sync-impression-drain.d.ts +1 -1
- package/dist/operator-toolkit.d.ts +2 -2
- package/dist/operator-toolkit.js +18 -18
- package/dist/orchestration/compression-guideline-coordinator.d.ts +2 -2
- package/dist/orchestration/maintenance.d.ts +6 -223
- package/dist/orchestration/maintenance.js +12 -12
- package/dist/{orchestrator-DB93S48I.d.ts → orchestrator-C9ZVppNh.d.ts} +12 -481
- package/dist/orchestrator.d.ts +10 -8
- package/dist/orchestrator.js +84 -82
- package/dist/patterns-cli.d.ts +1 -1
- package/dist/{pipeline-DmGc-H66.d.ts → pipeline-id_-sqYm.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/{qmd-cJT0crkF.d.ts → qmd-R6XaQOZK.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 +4 -4
- 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-explain-renderer.js +3 -3
- 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-cli.js +4 -4
- package/dist/recall-xray-renderer.d.ts +1 -1
- package/dist/recall-xray-renderer.js +3 -3
- package/dist/recall-xray.d.ts +1 -1
- package/dist/recall-xray.js +2 -2
- 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/resolve-auth-token.d.ts +1 -1
- package/dist/resume-bundles.js +4 -4
- 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/schemas.d.ts +76 -76
- package/dist/{scope-profiles-KG2nODX7.d.ts → scope-profiles-EhyuAKkd.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 +5 -5
- package/dist/search/index.d.ts +1 -1
- package/dist/search/index.js +9 -9
- 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-BaZlg0D8.d.ts → semantic-consolidation-B2xo26UQ.d.ts} +1 -1
- package/dist/semantic-consolidation.d.ts +2 -2
- package/dist/semantic-consolidation.js +7 -7
- package/dist/semantic-rule-promotion.js +5 -5
- package/dist/semantic-rule-verifier.d.ts +1 -1
- package/dist/semantic-rule-verifier.js +6 -6
- package/dist/service-DgFWlZ-r.d.ts +835 -0
- 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 +9 -9
- package/dist/signal.d.ts +1 -1
- package/dist/{storage-BKhHHnRo.d.ts → storage-XGawwqXE.d.ts} +131 -2
- package/dist/storage.d.ts +2 -2
- package/dist/storage.js +4 -4
- package/dist/summarizer.d.ts +1 -1
- package/dist/summary-snapshot.d.ts +1 -1
- 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/types.d.ts +66 -66
- package/dist/trust-score-stage.d.ts +1 -1
- package/dist/trust-score.d.ts +1 -1
- package/dist/{types-GBQZuT7l.d.ts → types-Bbs0qz6b.d.ts} +2 -3
- package/dist/types.d.ts +1 -1
- package/dist/types.js +1 -1
- package/dist/utility-runtime.d.ts +1 -1
- package/dist/verified-recall.js +6 -6
- package/dist/write-envelope.d.ts +1 -1
- package/package.json +2 -2
- package/src/access-boundary.ts +3 -0
- package/src/access-http.test.ts +63 -0
- package/src/access-http.ts +26 -37
- package/src/access-mcp-output-schemas.ts +10 -0
- package/src/access-mcp.ts +4 -121
- package/src/access-operations-batch.ts +10 -5
- package/src/access-service.ts +43 -46
- package/src/access-surface-catalog.ts +6 -0
- package/src/access-wearables-meetings-surface.ts +139 -0
- package/src/activity/digest.ts +1 -1
- package/src/activity/runner.test.ts +37 -0
- package/src/activity/runner.ts +12 -0
- package/src/activity/scheduler.test.ts +1 -0
- package/src/activity/sync-registration.ts +13 -0
- package/src/cli/meetings-commands.ts +70 -0
- package/src/cli.ts +7 -6
- package/src/extraction-error-classification.ts +52 -0
- package/src/extraction.ts +2 -50
- package/src/mcp-read-only-tools.ts +2 -0
- package/src/meetings/build-memory.test.ts +364 -0
- package/src/meetings/build-scheduler.test.ts +55 -0
- package/src/meetings/build-scheduler.ts +96 -0
- package/src/meetings/day-source.test.ts +275 -0
- package/src/meetings/day-source.ts +352 -0
- package/src/meetings/http-glue.ts +126 -0
- package/src/meetings/index.ts +5 -4
- package/src/meetings/manual-sync-flush.test.ts +72 -0
- package/src/meetings/mcp-tools.ts +65 -0
- package/src/meetings/memory-gen.test.ts +498 -0
- package/src/meetings/memory-gen.ts +549 -0
- package/src/meetings/memory-generator.ts +5 -0
- package/src/meetings/namespace-isolation.test.ts +159 -0
- package/src/meetings/service.test.ts +195 -0
- package/src/meetings/service.ts +157 -0
- package/src/meetings/summary-extractor.test.ts +287 -0
- package/src/meetings/summary-extractor.ts +264 -0
- package/src/orchestration/maintenance.ts +8 -0
- package/src/orchestration/orchestrator-init.ts +6 -1
- package/src/orchestration/workspace-ops.ts +172 -8
- package/src/orchestrator.ts +12 -13
- package/src/storage.ts +21 -0
- package/src/testing/subjects/meetings-namespace-harness.ts +159 -0
- package/src/testing/subjects/meetings-namespace.test.ts +93 -0
- package/src/types.ts +1 -2
- package/src/wearables/cli.ts +2 -1
- package/src/wearables/index.ts +1 -0
- package/src/wearables/mcp-tools.ts +143 -0
- package/src/wearables/registry.test.ts +11 -0
- package/src/wearables/registry.ts +13 -0
- package/src/wearables/service.test.ts +62 -1
- package/src/wearables/service.ts +30 -2
- package/src/wearables/transcript-search-namespace-isolation.test.ts +98 -0
- package/dist/chunk-6ANPNUVL.js.map +0 -1
- package/dist/chunk-6NZ7U4R4.js.map +0 -1
- package/dist/chunk-7S5OMI76.js.map +0 -1
- package/dist/chunk-ACGGTPU4.js.map +0 -1
- package/dist/chunk-DM5A4ZFL.js.map +0 -1
- package/dist/chunk-EQYBHXRR.js.map +0 -1
- package/dist/chunk-IC2TCZJ2.js.map +0 -1
- package/dist/chunk-MONWCSVL.js.map +0 -1
- package/dist/chunk-N6M7MRVN.js.map +0 -1
- package/dist/chunk-QMGIQUBG.js.map +0 -1
- package/dist/chunk-QTO2FGBT.js.map +0 -1
- package/dist/chunk-SQEDDWAM.js.map +0 -1
- package/dist/chunk-W3XT32W7.js.map +0 -1
- /package/dist/{auto-sync-UPTUVE4X.js.map → auto-sync-5OE5QOZB.js.map} +0 -0
- /package/dist/{chunk-GF22HWS2.js.map → chunk-2PHX3I5C.js.map} +0 -0
- /package/dist/{chunk-JFDEYXF7.js.map → chunk-3NAHZQU3.js.map} +0 -0
- /package/dist/{chunk-XBRPEGXF.js.map → chunk-3UOSVTFR.js.map} +0 -0
- /package/dist/{chunk-TTKWTT72.js.map → chunk-3ZW6ZQO6.js.map} +0 -0
- /package/dist/{chunk-UV72DEZD.js.map → chunk-4B77XSG5.js.map} +0 -0
- /package/dist/{chunk-LMHDFOVV.js.map → chunk-5CPI2QWG.js.map} +0 -0
- /package/dist/{chunk-P72WZMWQ.js.map → chunk-5FZQ74K5.js.map} +0 -0
- /package/dist/{chunk-BRT7KEXU.js.map → chunk-6TWXQ4E2.js.map} +0 -0
- /package/dist/{chunk-CV6ZYXK3.js.map → chunk-7I7AYIUQ.js.map} +0 -0
- /package/dist/{chunk-TVVEYCNW.js.map → chunk-7K5Q6COX.js.map} +0 -0
- /package/dist/{chunk-JN57HZCD.js.map → chunk-ATEAL5EI.js.map} +0 -0
- /package/dist/{chunk-4PYLBXX6.js.map → chunk-AYAROCWD.js.map} +0 -0
- /package/dist/{chunk-NB2P47N7.js.map → chunk-CUUY5CC4.js.map} +0 -0
- /package/dist/{chunk-KL5Y6PEV.js.map → chunk-E3ZMFFQZ.js.map} +0 -0
- /package/dist/{chunk-55FDQYPS.js.map → chunk-ES5IHQ6M.js.map} +0 -0
- /package/dist/{chunk-N47ZWQBO.js.map → chunk-G2NSSQES.js.map} +0 -0
- /package/dist/{chunk-GSWPU5CR.js.map → chunk-GD3QOBMB.js.map} +0 -0
- /package/dist/{chunk-SACPRJ7F.js.map → chunk-JNIJBXVK.js.map} +0 -0
- /package/dist/{chunk-DGLON3AL.js.map → chunk-KNRG6ZGJ.js.map} +0 -0
- /package/dist/{chunk-GEUDRENF.js.map → chunk-MMTG3SCJ.js.map} +0 -0
- /package/dist/{chunk-WZFAPHIK.js.map → chunk-MU7ZHSZE.js.map} +0 -0
- /package/dist/{chunk-W4FBGYGK.js.map → chunk-NRV4Y3PM.js.map} +0 -0
- /package/dist/{chunk-KQ2XCQYN.js.map → chunk-P2BLHUL2.js.map} +0 -0
- /package/dist/{chunk-6L775BUB.js.map → chunk-QE63OHLB.js.map} +0 -0
- /package/dist/{chunk-ONHRRJZM.js.map → chunk-QJAQNXWI.js.map} +0 -0
- /package/dist/{chunk-6RYWNBSA.js.map → chunk-SVZGA2MK.js.map} +0 -0
- /package/dist/{chunk-OCMLGND7.js.map → chunk-WWZ35GWW.js.map} +0 -0
- /package/dist/{chunk-A5N6ISRK.js.map → chunk-ZN4UZWMT.js.map} +0 -0
- /package/dist/{chunk-7M6APKJL.js.map → chunk-ZYU7ZFLT.js.map} +0 -0
- /package/dist/{first-start-migration-LEXL3XYL.js.map → first-start-migration-BKNGOFED.js.map} +0 -0
|
@@ -0,0 +1,549 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Meeting memory generation (issue #1900, Phase 4).
|
|
3
|
+
*
|
|
4
|
+
* Layer 1 — the deterministic meeting EPISODE: one recall-anchor memory per
|
|
5
|
+
* meeting ("what meetings did I have Tuesday?"), always written when
|
|
6
|
+
* `meetings.enabled`, no LLM. Title, time span, attendees, and source list are
|
|
7
|
+
* a pure function of the record, so an unchanged meeting is skipped by content
|
|
8
|
+
* hash. Reuses the shared sealed-envelope composer + secure write path
|
|
9
|
+
* (`composeMemoryEnvelope` / `writeSealedMemory`) — no bespoke memory format.
|
|
10
|
+
*
|
|
11
|
+
* The LLM summary/facts layer (decisions + commitments through the existing
|
|
12
|
+
* trust pipeline) builds on this module in the trust-gated slice; it reuses the
|
|
13
|
+
* same writer interface and the wearables judge/trust machinery rather than
|
|
14
|
+
* forking it.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { scoreImportance } from "../importance.js";
|
|
18
|
+
import { composeMemoryEnvelope } from "../write-envelope.js";
|
|
19
|
+
import { log } from "../logger.js";
|
|
20
|
+
import { stripAttributesSuffix } from "../structured-attributes.js";
|
|
21
|
+
import type { MemoryWriteResult } from "../index.js";
|
|
22
|
+
import type { MemoryCategory, MemoryStatus } from "../types.js";
|
|
23
|
+
import {
|
|
24
|
+
type MeetingEpisodeGenResult,
|
|
25
|
+
type MeetingMemoryGenerator,
|
|
26
|
+
type MeetingMemoryOutcome,
|
|
27
|
+
type MeetingMemoryWriter,
|
|
28
|
+
type MeetingsDayFactGenResult,
|
|
29
|
+
} from "./memory-generator.js";
|
|
30
|
+
import { computeTrustScore, decideSmart, type TrustEvidence } from "../wearables/trust.js";
|
|
31
|
+
import { describeErrorForOperator } from "../wearables/errors.js";
|
|
32
|
+
import type { MeetingRecord, MeetingsConfig } from "./types.js";
|
|
33
|
+
|
|
34
|
+
/** Frontmatter `source` prefix for meeting-derived memories. */
|
|
35
|
+
export const MEETING_SOURCE_PREFIX = "meeting";
|
|
36
|
+
/** `source: meeting:<id>` — walks memory → record → day transcripts. */
|
|
37
|
+
export function meetingSourceLabel(id: string): string {
|
|
38
|
+
return `${MEETING_SOURCE_PREFIX}:${id}`;
|
|
39
|
+
}
|
|
40
|
+
/** `meeting-day:<date>` tag for day-scoped recall ("meetings on Tuesday"). */
|
|
41
|
+
export function meetingDayTag(date: string): string {
|
|
42
|
+
return `meeting-day:${date}`;
|
|
43
|
+
}
|
|
44
|
+
/** Render the deterministic episode content for a meeting. Pure. */
|
|
45
|
+
export function composeMeetingEpisodeContent(record: MeetingRecord): string {
|
|
46
|
+
const clock = (iso: string): string => {
|
|
47
|
+
const ms = Date.parse(iso);
|
|
48
|
+
return Number.isNaN(ms) ? iso : new Date(ms).toISOString().slice(11, 16);
|
|
49
|
+
};
|
|
50
|
+
const app = record.app !== undefined && record.app.length > 0 ? ` (${record.app})` : "";
|
|
51
|
+
const title = record.title !== undefined && record.title.length > 0 ? ` · ${record.title}` : "";
|
|
52
|
+
const attendees = record.attendees.length > 0 ? record.attendees.join(", ") : "no attendees resolved";
|
|
53
|
+
const sources = record.sources.length > 0 ? record.sources.join(", ") : "none";
|
|
54
|
+
return (
|
|
55
|
+
`Meeting ${record.id}${title} — ${record.date} ${clock(record.startUtc)}–${clock(record.endUtc)} UTC${app}.\n` +
|
|
56
|
+
`Attendees: ${attendees}.\n` +
|
|
57
|
+
`Transcript sources: ${sources}.`
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Write the deterministic episode memory for one meeting. Idempotent: skips
|
|
63
|
+
* when an identical episode already exists. Returns true when a new episode was
|
|
64
|
+
* written. The episode is `active` (a recall anchor, not a trust-gated claim).
|
|
65
|
+
*/
|
|
66
|
+
export async function writeMeetingEpisodeMemory(
|
|
67
|
+
record: MeetingRecord,
|
|
68
|
+
writer: MeetingMemoryWriter,
|
|
69
|
+
): Promise<boolean> {
|
|
70
|
+
const content = composeMeetingEpisodeContent(record);
|
|
71
|
+
if (await writer.hasMemoryFromSource(meetingSourceLabel(record.id), content)) return false;
|
|
72
|
+
|
|
73
|
+
const structuredAttributes: Record<string, string> = {
|
|
74
|
+
meetingId: record.id,
|
|
75
|
+
meetingDate: record.date,
|
|
76
|
+
transcriptSources: record.sources.join(","),
|
|
77
|
+
};
|
|
78
|
+
if (record.app !== undefined) structuredAttributes.meetingApp = record.app;
|
|
79
|
+
|
|
80
|
+
const envelope = composeMemoryEnvelope(
|
|
81
|
+
{
|
|
82
|
+
content,
|
|
83
|
+
category: "moment",
|
|
84
|
+
confidence: 0.9,
|
|
85
|
+
tags: [MEETING_SOURCE_PREFIX, meetingDayTag(record.date)],
|
|
86
|
+
validAt: record.startUtc,
|
|
87
|
+
structuredAttributes,
|
|
88
|
+
},
|
|
89
|
+
{ source: meetingSourceLabel(record.id) },
|
|
90
|
+
{ salvage: true },
|
|
91
|
+
);
|
|
92
|
+
if (envelope.salvageNotes.length > 0) {
|
|
93
|
+
log.warn(`meeting episode write salvaged invalid fields: ${envelope.salvageNotes.join("; ")}`);
|
|
94
|
+
}
|
|
95
|
+
await writer.writeSealedMemory(envelope, {
|
|
96
|
+
importance: scoreImportance(content, "moment", [MEETING_SOURCE_PREFIX]),
|
|
97
|
+
contentHashSource: content,
|
|
98
|
+
status: "active",
|
|
99
|
+
memoryKind: "episode",
|
|
100
|
+
});
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** Write one deterministic episode per record, idempotently. */
|
|
105
|
+
export async function generateMeetingEpisodes(
|
|
106
|
+
records: readonly MeetingRecord[],
|
|
107
|
+
writer: MeetingMemoryWriter,
|
|
108
|
+
): Promise<MeetingEpisodeGenResult> {
|
|
109
|
+
let written = 0;
|
|
110
|
+
let skipped = 0;
|
|
111
|
+
for (const record of records) {
|
|
112
|
+
if (await writeMeetingEpisodeMemory(record, writer)) written++;
|
|
113
|
+
else skipped++;
|
|
114
|
+
}
|
|
115
|
+
return { written, skipped };
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// ---------------------------------------------------------------------------
|
|
119
|
+
// Layer 2 — trust-gated LLM summary + facts (decisions, commitments, questions)
|
|
120
|
+
// ---------------------------------------------------------------------------
|
|
121
|
+
|
|
122
|
+
/** One LLM-extracted meeting claim. */
|
|
123
|
+
export interface MeetingFactCandidate {
|
|
124
|
+
/** Claim text (a decision, commitment, or open question). */
|
|
125
|
+
content: string;
|
|
126
|
+
/** Memory category — commitments carry the decay-aware `commitment` category. */
|
|
127
|
+
category: Extract<MemoryCategory, "fact" | "decision" | "commitment">;
|
|
128
|
+
/** Extractor confidence (0..1); defaults to 0.7 downstream when absent. */
|
|
129
|
+
confidence?: number;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/** LLM extractor over a meeting's fused transcript + screen context. Injected so
|
|
133
|
+
* `off` mode provably never invokes it and tests stay deterministic. */
|
|
134
|
+
export interface MeetingSummaryExtractor {
|
|
135
|
+
extract(input: {
|
|
136
|
+
record: MeetingRecord;
|
|
137
|
+
transcriptText: string;
|
|
138
|
+
screenContextText: string;
|
|
139
|
+
}): Promise<{ summary: string; candidates: MeetingFactCandidate[] }>;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/** Optional durability judge; verdicts align with the shared trust pipeline. */
|
|
143
|
+
export interface MeetingSummaryJudge {
|
|
144
|
+
judge(candidates: readonly MeetingFactCandidate[]): Promise<Array<"accept" | "reject" | "defer">>;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export interface MeetingSummaryDeps {
|
|
148
|
+
extractor: MeetingSummaryExtractor;
|
|
149
|
+
judge?: MeetingSummaryJudge;
|
|
150
|
+
writer: MeetingMemoryWriter;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export interface MeetingSummaryResult {
|
|
154
|
+
/** False when summaryMode is `off` (the extractor is never called). */
|
|
155
|
+
llmInvoked: boolean;
|
|
156
|
+
/** Facts written active. */
|
|
157
|
+
active: number;
|
|
158
|
+
/** Facts written to the review queue (pending_review). */
|
|
159
|
+
review: number;
|
|
160
|
+
/** Candidates dropped below the trust bar or judge-rejected. */
|
|
161
|
+
dropped: number;
|
|
162
|
+
/** Candidates/summary skipped because an identical memory already existed
|
|
163
|
+
* for this meeting (idempotent rebuild). */
|
|
164
|
+
skipped: number;
|
|
165
|
+
/** True when a non-empty summary memory was written. */
|
|
166
|
+
summaryWritten: boolean;
|
|
167
|
+
/** Facts the #1579 tombstone chokepoint downgraded to pending_review (no
|
|
168
|
+
* active copy). Counted here, NOT in `active`/`review`, so callers never
|
|
169
|
+
* report a blocked write as a successfully written fact (#1645). */
|
|
170
|
+
tombstoneBlocked: number;
|
|
171
|
+
/** Non-fatal degradation notes (e.g. the durability judge threw and the
|
|
172
|
+
* pass continued on trust bands alone). */
|
|
173
|
+
warnings: readonly string[];
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/** Render the fused transcript as `**Speaker** [HH:MM]: text` for the prompt. */
|
|
177
|
+
function renderTranscriptText(record: MeetingRecord): string {
|
|
178
|
+
return record.transcript
|
|
179
|
+
.map((seg) => {
|
|
180
|
+
const ms = seg.startIso === undefined ? NaN : Date.parse(seg.startIso);
|
|
181
|
+
const clock = Number.isNaN(ms) ? "--:--" : new Date(ms).toISOString().slice(11, 16);
|
|
182
|
+
return `**${seg.speaker}** [${clock}]: ${seg.text}`;
|
|
183
|
+
})
|
|
184
|
+
.join("\n");
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/** Render the screen-context timeline + excerpts for the prompt. */
|
|
188
|
+
function renderScreenContextText(record: MeetingRecord): string {
|
|
189
|
+
const lines = record.screenContext.map((e) => `[${e.clock}] ${e.label}`);
|
|
190
|
+
if (record.contextExcerpts.length > 0) lines.push(...record.contextExcerpts);
|
|
191
|
+
return lines.join("\n");
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
async function writeMeetingFact(
|
|
195
|
+
record: MeetingRecord,
|
|
196
|
+
candidate: MeetingFactCandidate,
|
|
197
|
+
status: Extract<MemoryStatus, "active" | "pending_review">,
|
|
198
|
+
writer: MeetingMemoryWriter,
|
|
199
|
+
): Promise<MemoryWriteResult> {
|
|
200
|
+
const structuredAttributes: Record<string, string> = {
|
|
201
|
+
meetingId: record.id,
|
|
202
|
+
meetingDate: record.date,
|
|
203
|
+
transcriptSources: record.sources.join(","),
|
|
204
|
+
};
|
|
205
|
+
if (record.app !== undefined) structuredAttributes.meetingApp = record.app;
|
|
206
|
+
const envelope = composeMemoryEnvelope(
|
|
207
|
+
{
|
|
208
|
+
content: candidate.content,
|
|
209
|
+
category: candidate.category,
|
|
210
|
+
confidence: candidate.confidence,
|
|
211
|
+
tags: [MEETING_SOURCE_PREFIX, meetingDayTag(record.date), candidate.category],
|
|
212
|
+
validAt: record.startUtc,
|
|
213
|
+
structuredAttributes,
|
|
214
|
+
},
|
|
215
|
+
{ source: meetingSourceLabel(record.id) },
|
|
216
|
+
{ salvage: true },
|
|
217
|
+
);
|
|
218
|
+
if (envelope.salvageNotes.length > 0) {
|
|
219
|
+
log.warn(`meeting fact write salvaged invalid fields: ${envelope.salvageNotes.join("; ")}`);
|
|
220
|
+
}
|
|
221
|
+
return writer.writeSealedMemory(envelope, {
|
|
222
|
+
importance: scoreImportance(candidate.content, candidate.category, [MEETING_SOURCE_PREFIX]),
|
|
223
|
+
contentHashSource: candidate.content,
|
|
224
|
+
status,
|
|
225
|
+
memoryKind: "note",
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/** Confidence assigned to the synthesized meeting-summary fact. */
|
|
230
|
+
const SUMMARY_FACT_CONFIDENCE = 0.8;
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Route one meeting claim (candidate or the synthesized summary) to active /
|
|
234
|
+
* review / drop through the SAME trust decision. `review` mode never
|
|
235
|
+
* auto-actives (queue everything survivable); `smart` mode uses the shared
|
|
236
|
+
* band decision. A judge reject drops in both. Used for BOTH the extracted
|
|
237
|
+
* candidates and the summary text so a rejected/low-trust meeting can never
|
|
238
|
+
* push its summary active behind the gate's back.
|
|
239
|
+
*/
|
|
240
|
+
function resolveMeetingFactOutcome(
|
|
241
|
+
config: MeetingsConfig,
|
|
242
|
+
trust: number,
|
|
243
|
+
judgeVerdict: "accept" | "reject" | "defer" | undefined,
|
|
244
|
+
): "active" | "review" | "drop" {
|
|
245
|
+
if (config.summaryMode === "review") {
|
|
246
|
+
return judgeVerdict === "reject" ? "drop" : "review";
|
|
247
|
+
}
|
|
248
|
+
return decideSmart(trust, judgeVerdict, {
|
|
249
|
+
autoApproveTrust: config.autoApproveTrust,
|
|
250
|
+
reviewTrust: config.reviewTrust,
|
|
251
|
+
}).outcome;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Layer 2: extract a trust-gated summary + facts (decisions/commitments/open
|
|
256
|
+
* questions) for one meeting and persist the survivors. Reuses the shared trust
|
|
257
|
+
* pipeline (`computeTrustScore` + `decideSmart`), NOT a parallel scorer.
|
|
258
|
+
*
|
|
259
|
+
* - `off` → returns immediately; the extractor is NEVER invoked (episode-only).
|
|
260
|
+
* - `review`→ every candidate lands in the review queue (pending_review).
|
|
261
|
+
* - `smart` → judge verdict + trust bands route each candidate to active /
|
|
262
|
+
* review / drop; corroboration (>= 2 transcript sources) boosts trust.
|
|
263
|
+
*/
|
|
264
|
+
export async function generateMeetingSummaryFacts(
|
|
265
|
+
record: MeetingRecord,
|
|
266
|
+
config: MeetingsConfig,
|
|
267
|
+
deps: MeetingSummaryDeps,
|
|
268
|
+
): Promise<MeetingSummaryResult> {
|
|
269
|
+
const empty: MeetingSummaryResult = {
|
|
270
|
+
llmInvoked: false,
|
|
271
|
+
active: 0,
|
|
272
|
+
review: 0,
|
|
273
|
+
dropped: 0,
|
|
274
|
+
skipped: 0,
|
|
275
|
+
summaryWritten: false,
|
|
276
|
+
tombstoneBlocked: 0,
|
|
277
|
+
warnings: [],
|
|
278
|
+
};
|
|
279
|
+
if (config.summaryMode === "off") return empty;
|
|
280
|
+
if (record.transcript.length === 0) return empty; // nothing to summarize (no audio)
|
|
281
|
+
|
|
282
|
+
const { summary, candidates } = await deps.extractor.extract({
|
|
283
|
+
record,
|
|
284
|
+
transcriptText: renderTranscriptText(record),
|
|
285
|
+
screenContextText: renderScreenContextText(record),
|
|
286
|
+
});
|
|
287
|
+
const warnings: string[] = [];
|
|
288
|
+
const result: MeetingSummaryResult = { ...empty, llmInvoked: true, warnings };
|
|
289
|
+
|
|
290
|
+
// The durability judge is optional and external — a throw (provider outage,
|
|
291
|
+
// timeout, malformed response) must DEGRADE this pass, not abort the whole day
|
|
292
|
+
// build. Fall back to no verdicts so the trust bands still route each candidate
|
|
293
|
+
// (review mode → review; smart mode → trust-band decision) (#1900 round 2).
|
|
294
|
+
let verdicts: Array<"accept" | "reject" | "defer"> | undefined;
|
|
295
|
+
if (deps.judge) {
|
|
296
|
+
try {
|
|
297
|
+
verdicts = await deps.judge.judge(candidates);
|
|
298
|
+
} catch (err) {
|
|
299
|
+
warnings.push(
|
|
300
|
+
`meeting summary judge unavailable: ${describeErrorForOperator(err)} — trust scoring continued without judge verdicts`,
|
|
301
|
+
);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
// Corroboration: a meeting covered by >= 2 transcript sources corroborates its
|
|
305
|
+
// claims (record.corroboratedBy already lists the out-voted sources).
|
|
306
|
+
const evidence: TrustEvidence = { corroboratedBySources: [...record.corroboratedBy] };
|
|
307
|
+
|
|
308
|
+
const source = meetingSourceLabel(record.id);
|
|
309
|
+
for (let i = 0; i < candidates.length; i++) {
|
|
310
|
+
const candidate = candidates[i]!;
|
|
311
|
+
const judgeVerdict = verdicts?.[i];
|
|
312
|
+
// Idempotent rebuild: an identical claim already persisted for THIS meeting
|
|
313
|
+
// must not be written a second time (facts are not in the fact-hash index).
|
|
314
|
+
if (await deps.writer.hasMemoryFromSource(source, candidate.content)) {
|
|
315
|
+
result.skipped++;
|
|
316
|
+
continue;
|
|
317
|
+
}
|
|
318
|
+
const trust = computeTrustScore({
|
|
319
|
+
extractionConfidence: candidate.confidence,
|
|
320
|
+
sourceTrust: config.sourceTrust,
|
|
321
|
+
judgeVerdict,
|
|
322
|
+
evidence,
|
|
323
|
+
});
|
|
324
|
+
const outcome = resolveMeetingFactOutcome(config, trust, judgeVerdict);
|
|
325
|
+
if (outcome === "drop") {
|
|
326
|
+
result.dropped++;
|
|
327
|
+
continue;
|
|
328
|
+
}
|
|
329
|
+
const status = outcome === "active" ? "active" : "pending_review";
|
|
330
|
+
const write = await writeMeetingFact(record, candidate, status, deps.writer);
|
|
331
|
+
if (write.tombstoneBlocked) {
|
|
332
|
+
// #1645: the tombstone chokepoint downgraded this fact to pending_review
|
|
333
|
+
// (no active copy). Count it as blocked, NEVER as the active/review write
|
|
334
|
+
// we requested, so the day summary never overstates persisted facts.
|
|
335
|
+
result.tombstoneBlocked++;
|
|
336
|
+
} else if (status === "active") {
|
|
337
|
+
result.active++;
|
|
338
|
+
} else {
|
|
339
|
+
result.review++;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// The synthesized summary is a meeting claim too: gate it through the SAME
|
|
344
|
+
// trust decision so a meeting whose candidates were all rejected / below the
|
|
345
|
+
// bar cannot slip its summary in as an active fact (it was force-active before).
|
|
346
|
+
const summaryText = summary.trim();
|
|
347
|
+
if (summaryText.length > 0) {
|
|
348
|
+
if (await deps.writer.hasMemoryFromSource(source, summaryText)) {
|
|
349
|
+
result.skipped++;
|
|
350
|
+
} else {
|
|
351
|
+
const summaryTrust = computeTrustScore({
|
|
352
|
+
extractionConfidence: SUMMARY_FACT_CONFIDENCE,
|
|
353
|
+
sourceTrust: config.sourceTrust,
|
|
354
|
+
evidence,
|
|
355
|
+
});
|
|
356
|
+
const outcome = resolveMeetingFactOutcome(config, summaryTrust, undefined);
|
|
357
|
+
if (outcome === "drop") {
|
|
358
|
+
result.dropped++;
|
|
359
|
+
} else {
|
|
360
|
+
const write = await writeMeetingFact(
|
|
361
|
+
record,
|
|
362
|
+
{ content: summaryText, category: "fact", confidence: SUMMARY_FACT_CONFIDENCE },
|
|
363
|
+
outcome === "active" ? "active" : "pending_review",
|
|
364
|
+
deps.writer,
|
|
365
|
+
);
|
|
366
|
+
// #1645: a tombstone-blocked summary landed pending_review (no active
|
|
367
|
+
// copy) — record it as blocked, not as a written summary, so the day
|
|
368
|
+
// aggregate never reports a summary that was actually gated out.
|
|
369
|
+
if (write.tombstoneBlocked) result.tombstoneBlocked++;
|
|
370
|
+
else result.summaryWritten = true;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
return result;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* Storage capabilities `createMeetingMemoryWriter` needs, satisfied structurally
|
|
379
|
+
* by `StorageManager`. Kept narrow (the same rationale as the wearables writer)
|
|
380
|
+
* so this module performs NO direct fs and never touches the secure-store key.
|
|
381
|
+
*/
|
|
382
|
+
export interface MeetingMemoryStorageIo {
|
|
383
|
+
writeSealedMemory: MeetingMemoryWriter["writeSealedMemory"];
|
|
384
|
+
readAllMemories(): Promise<
|
|
385
|
+
Array<{
|
|
386
|
+
path: string;
|
|
387
|
+
frontmatter: { id: string; source: string; status?: string };
|
|
388
|
+
content: string;
|
|
389
|
+
}>
|
|
390
|
+
>;
|
|
391
|
+
invalidateMemory(id: string): Promise<boolean>;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* Build the meeting memory writer used by production builds. Mirrors
|
|
396
|
+
* `createWearableMemoryWriter`: the storage fact-hash index only covers
|
|
397
|
+
* category "fact", so meeting episodes (`moment`) and decisions/commitments
|
|
398
|
+
* dedup by a bounded, source-scoped content scan over `meeting:<id>`-tagged
|
|
399
|
+
* memories — without it, a forced/retried day re-writes identical episodes and
|
|
400
|
+
* facts (issue #1900).
|
|
401
|
+
*/
|
|
402
|
+
export function createMeetingMemoryWriter(storage: MeetingMemoryStorageIo): MeetingMemoryWriter {
|
|
403
|
+
return {
|
|
404
|
+
writeSealedMemory: storage.writeSealedMemory.bind(storage),
|
|
405
|
+
hasMemoryFromSource: async (source: string, content: string): Promise<boolean> => {
|
|
406
|
+
// Stored bodies carry the "[Attributes: ...]" enrichment suffix; callers
|
|
407
|
+
// pass raw text — strip on BOTH sides so attribute-bearing memories match.
|
|
408
|
+
const needle = stripAttributesSuffix(content);
|
|
409
|
+
const memories = await storage.readAllMemories();
|
|
410
|
+
return memories.some(
|
|
411
|
+
(memory) =>
|
|
412
|
+
memory.frontmatter.source === source &&
|
|
413
|
+
memory.frontmatter.status !== "rejected" &&
|
|
414
|
+
stripAttributesSuffix(memory.content) === needle,
|
|
415
|
+
);
|
|
416
|
+
},
|
|
417
|
+
retireMemoriesFromSource: async (source: string): Promise<number> => {
|
|
418
|
+
const memories = await storage.readAllMemories();
|
|
419
|
+
let retired = 0;
|
|
420
|
+
for (const memory of memories) {
|
|
421
|
+
if (memory.frontmatter.source !== source) continue;
|
|
422
|
+
if (await storage.invalidateMemory(memory.frontmatter.id)) retired++;
|
|
423
|
+
}
|
|
424
|
+
return retired;
|
|
425
|
+
},
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
/**
|
|
430
|
+
* Fold the per-record trust-gated summary/fact results into one day aggregate.
|
|
431
|
+
* The deterministic builder used to own this loop; it now lives behind the
|
|
432
|
+
* memory seam so the engine never calls `generateMeetingSummaryFacts`.
|
|
433
|
+
*/
|
|
434
|
+
async function generateMeetingDayFacts(
|
|
435
|
+
records: readonly MeetingRecord[],
|
|
436
|
+
config: MeetingsConfig,
|
|
437
|
+
writer: MeetingMemoryWriter,
|
|
438
|
+
summaryDeps: { extractor: MeetingSummaryExtractor; judge?: MeetingSummaryJudge },
|
|
439
|
+
warnings: string[],
|
|
440
|
+
): Promise<MeetingsDayFactGenResult> {
|
|
441
|
+
const agg: MeetingsDayFactGenResult = {
|
|
442
|
+
llmInvoked: false,
|
|
443
|
+
active: 0,
|
|
444
|
+
review: 0,
|
|
445
|
+
dropped: 0,
|
|
446
|
+
skipped: 0,
|
|
447
|
+
summariesWritten: 0,
|
|
448
|
+
};
|
|
449
|
+
for (const record of records) {
|
|
450
|
+
const result = await generateMeetingSummaryFacts(record, config, {
|
|
451
|
+
extractor: summaryDeps.extractor,
|
|
452
|
+
...(summaryDeps.judge !== undefined ? { judge: summaryDeps.judge } : {}),
|
|
453
|
+
writer,
|
|
454
|
+
});
|
|
455
|
+
if (result.llmInvoked) agg.llmInvoked = true;
|
|
456
|
+
agg.active += result.active;
|
|
457
|
+
agg.review += result.review;
|
|
458
|
+
agg.dropped += result.dropped;
|
|
459
|
+
agg.skipped += result.skipped;
|
|
460
|
+
if (result.summaryWritten) agg.summariesWritten++;
|
|
461
|
+
if (result.tombstoneBlocked > 0) {
|
|
462
|
+
agg.tombstoneBlocked = (agg.tombstoneBlocked ?? 0) + result.tombstoneBlocked;
|
|
463
|
+
}
|
|
464
|
+
if (result.warnings.length > 0) warnings.push(...result.warnings);
|
|
465
|
+
}
|
|
466
|
+
return agg;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
/**
|
|
470
|
+
* Concrete `MeetingMemoryGenerator` for the builder's memory seam (issue #1900
|
|
471
|
+
* decouple). Implements `onRecordsBuilt` by driving the EXISTING episode/fact/
|
|
472
|
+
* retract logic behind the interface the deterministic engine depends on, and
|
|
473
|
+
* routes each built id by the partition the engine computed from the store's
|
|
474
|
+
* per-record write result (round 2):
|
|
475
|
+
*
|
|
476
|
+
* - `removedIds` — retire the deleted meetings' memories so recall never
|
|
477
|
+
* surfaces a meeting `show <id>` can no longer load;
|
|
478
|
+
* - `updatedIds` — the record changed under a kept id: retract the prior
|
|
479
|
+
* episode/summary for `meeting:<id>` FIRST, then regenerate from the new
|
|
480
|
+
* record, so recall is refreshed (not duplicated, not left stale);
|
|
481
|
+
* - `unchangedIds` — the stored record was byte-identical this rebuild: SKIP
|
|
482
|
+
* all generation for it (no episode probe, no extractor call, no rewrite) so
|
|
483
|
+
* an idempotent rebuild fires zero LLM calls and writes nothing;
|
|
484
|
+
* - new ids (built minus unchanged minus updated) — generate fresh.
|
|
485
|
+
*
|
|
486
|
+
* `reindexNeeded` fires when the regenerated records wrote new episodes/facts;
|
|
487
|
+
* the builder additionally reindexes on removed/built counts, so a retract-only
|
|
488
|
+
* rebuild still reindexes.
|
|
489
|
+
*
|
|
490
|
+
* `config` is captured here (not passed through `onRecordsBuilt`) because the
|
|
491
|
+
* seam input carries record partitions, while the summary/facts layer needs
|
|
492
|
+
* `summaryMode` and the trust bands.
|
|
493
|
+
*/
|
|
494
|
+
export function createMeetingMemoryGenerator(
|
|
495
|
+
writer: MeetingMemoryWriter,
|
|
496
|
+
config: MeetingsConfig,
|
|
497
|
+
summaryDeps?: { extractor: MeetingSummaryExtractor; judge?: MeetingSummaryJudge },
|
|
498
|
+
): MeetingMemoryGenerator {
|
|
499
|
+
return {
|
|
500
|
+
async onRecordsBuilt({ built, removedIds, unchangedIds, updatedIds }): Promise<MeetingMemoryOutcome> {
|
|
501
|
+
// Retract deleted meetings' memories (stale removals).
|
|
502
|
+
for (const removedId of removedIds) {
|
|
503
|
+
await writer.retireMemoriesFromSource(meetingSourceLabel(removedId));
|
|
504
|
+
}
|
|
505
|
+
// Retract the prior memories of records that changed under a kept id, so the
|
|
506
|
+
// regeneration below REFRESHES the meeting (never leaves stale episodes/facts
|
|
507
|
+
// beside the new ones). The updated records are still in `built` and regenerate.
|
|
508
|
+
for (const updatedId of updatedIds) {
|
|
509
|
+
await writer.retireMemoriesFromSource(meetingSourceLabel(updatedId));
|
|
510
|
+
}
|
|
511
|
+
// Generate changed + new records always. An unchanged record regenerates ONLY
|
|
512
|
+
// when its deterministic episode memory is ABSENT: a prior build persisted the
|
|
513
|
+
// record but its generation threw before the memories landed, so the store now
|
|
514
|
+
// reports the record in unchangedIds (contentHash identical) and a plain skip
|
|
515
|
+
// would strand the meeting with zero memories forever. "Unchanged" means skip
|
|
516
|
+
// only when the memories already exist. The episode is the sentinel — it is
|
|
517
|
+
// always written first (deterministic, no LLM), so an absent episode means the
|
|
518
|
+
// source is unwritten. Unchanged records whose episode already exists still skip,
|
|
519
|
+
// so an idempotent rebuild fires zero LLM calls and writes nothing.
|
|
520
|
+
const unchanged = new Set(unchangedIds);
|
|
521
|
+
const toGenerate: MeetingRecord[] = [];
|
|
522
|
+
for (const record of built) {
|
|
523
|
+
if (!unchanged.has(record.id)) {
|
|
524
|
+
toGenerate.push(record);
|
|
525
|
+
continue;
|
|
526
|
+
}
|
|
527
|
+
const episodePresent = await writer.hasMemoryFromSource(
|
|
528
|
+
meetingSourceLabel(record.id),
|
|
529
|
+
composeMeetingEpisodeContent(record),
|
|
530
|
+
);
|
|
531
|
+
if (!episodePresent) toGenerate.push(record);
|
|
532
|
+
}
|
|
533
|
+
const warnings: string[] = [];
|
|
534
|
+
const outcome: MeetingMemoryOutcome = { reindexNeeded: false, warnings };
|
|
535
|
+
if (toGenerate.length > 0) {
|
|
536
|
+
outcome.episodes = await generateMeetingEpisodes(toGenerate, writer);
|
|
537
|
+
if (summaryDeps !== undefined && config.summaryMode !== "off") {
|
|
538
|
+
outcome.facts = await generateMeetingDayFacts(toGenerate, config, writer, summaryDeps, warnings);
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
const episodesWrote = (outcome.episodes?.written ?? 0) > 0;
|
|
542
|
+
const factsWrote =
|
|
543
|
+
outcome.facts !== undefined &&
|
|
544
|
+
outcome.facts.active + outcome.facts.review + outcome.facts.summariesWritten > 0;
|
|
545
|
+
outcome.reindexNeeded = episodesWrote || factsWrote;
|
|
546
|
+
return outcome;
|
|
547
|
+
},
|
|
548
|
+
};
|
|
549
|
+
}
|
|
@@ -69,6 +69,11 @@ export interface MeetingsDayFactGenResult {
|
|
|
69
69
|
skipped: number;
|
|
70
70
|
/** Count of records whose summary memory was written. */
|
|
71
71
|
summariesWritten: number;
|
|
72
|
+
/** Facts the #1579 tombstone chokepoint downgraded to pending_review (no
|
|
73
|
+
* active copy). Surfaced distinctly from `active`/`review` so callers never
|
|
74
|
+
* count a blocked write as a successfully written active/review fact (#1645).
|
|
75
|
+
* Optional so the seam's canned outcomes stay valid when unset. */
|
|
76
|
+
tombstoneBlocked?: number;
|
|
72
77
|
}
|
|
73
78
|
|
|
74
79
|
/**
|