@remnic/core 9.52.2 → 9.54.0
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 +22 -22
- package/dist/access-authorization-probe.d.ts +11 -11
- package/dist/access-authorization-probe.js +23 -23
- package/dist/access-boundary.d.ts +11 -11
- package/dist/access-boundary.js +22 -22
- 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 +22 -22
- 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.js +22 -22
- package/dist/access-http.d.ts +11 -11
- package/dist/access-http.js +29 -29
- package/dist/access-identity-continuity-surface.d.ts +9 -9
- package/dist/access-identity-continuity-surface.js +22 -22
- package/dist/access-lcm-surface.d.ts +11 -11
- package/dist/access-lcm-surface.js +22 -22
- package/dist/access-mcp.d.ts +11 -11
- package/dist/access-mcp.js +27 -27
- package/dist/access-memory-search-fanout.d.ts +2 -2
- package/dist/{access-namespace-preflight-Ds6CGlCP.d.ts → access-namespace-preflight-C7n4tgWC.d.ts} +1 -1
- package/dist/access-namespace-preflight.d.ts +3 -3
- package/dist/access-namespace-preflight.js +22 -22
- package/dist/access-observe-write-surface.d.ts +11 -11
- package/dist/access-observe-write-surface.js +22 -22
- package/dist/access-offline-manifest.d.ts +9 -9
- package/dist/access-offline-manifest.js +8 -4
- package/dist/access-operations-batch.js +24 -24
- package/dist/access-operations.d.ts +14 -14
- package/dist/access-operations.js +26 -26
- package/dist/access-recall-concurrency.d.ts +11 -11
- package/dist/access-recall-concurrency.js +22 -22
- package/dist/access-recall-response.d.ts +11 -11
- package/dist/access-recall-response.js +22 -22
- package/dist/access-recall-surface.d.ts +11 -11
- package/dist/access-recall-surface.js +22 -22
- package/dist/access-schema.d.ts +88 -88
- package/dist/{access-service-Cbk3bNbD.d.ts → access-service-CXvbxLK-.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 +22 -22
- package/dist/access-surface-catalog.d.ts +11 -11
- 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/active-recall.js +5 -5
- package/dist/ambient-provenance.d.ts +1 -1
- package/dist/artifact-search.d.ts +1 -1
- package/dist/{auto-sync-QFQGOEO2.js → auto-sync-TDHLZPVP.js} +7 -7
- 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.d.ts +2 -2
- package/dist/briefing.js +12 -12
- 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/bulk-import/index.d.ts +3 -3
- package/dist/calibration.d.ts +1 -1
- package/dist/capabilities.d.ts +1 -1
- package/dist/{catalog-D3B4RDy1.d.ts → catalog-BOOxl-I2.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 +13 -13
- package/dist/causal-trajectory-graph.d.ts +1 -1
- package/dist/{chunk-MLXZD4WM.js → chunk-2GFK22OZ.js} +2 -2
- package/dist/{chunk-RANKJI5T.js → chunk-3JLPGJ43.js} +2 -2
- package/dist/{chunk-O5FFBO2M.js → chunk-47P3RYZP.js} +70 -8
- package/dist/{chunk-O5FFBO2M.js.map → chunk-47P3RYZP.js.map} +1 -1
- package/dist/{chunk-X7XEDOSL.js → chunk-4IIFL7GC.js} +8 -8
- package/dist/chunk-5FVVY2EB.js +27 -0
- package/dist/chunk-5FVVY2EB.js.map +1 -0
- package/dist/{chunk-GXSOUIA4.js → chunk-5WQMORU5.js} +4 -4
- package/dist/{chunk-J272WKX2.js → chunk-6HPHEFXT.js} +2 -2
- package/dist/{chunk-B6LZ4IBH.js → chunk-6LAPLZOS.js} +2 -2
- package/dist/{chunk-WD5B5AIE.js → chunk-6R4G7N4S.js} +118 -173
- package/dist/chunk-6R4G7N4S.js.map +1 -0
- package/dist/{chunk-VXCNQYTC.js → chunk-APPFNGX6.js} +2 -2
- package/dist/{chunk-GXFSQ6SD.js → chunk-C4ZNGVK3.js} +2 -2
- package/dist/{chunk-JSEJH2YQ.js → chunk-CGVHPCP2.js} +2 -2
- package/dist/{chunk-BQIFZ2MP.js → chunk-CX5SW2EW.js} +41 -24
- package/dist/chunk-CX5SW2EW.js.map +1 -0
- package/dist/{chunk-RMHMUUBW.js → chunk-CYF6WVE7.js} +105 -49
- package/dist/{chunk-RMHMUUBW.js.map → chunk-CYF6WVE7.js.map} +1 -1
- package/dist/{chunk-TEMSWHQD.js → chunk-DZD576ZU.js} +5 -5
- package/dist/{chunk-AMTEYNYK.js → chunk-EVWWT3KE.js} +4 -4
- package/dist/{chunk-P2KBDJJ6.js → chunk-G2LUMAUF.js} +4 -3
- package/dist/{chunk-P2KBDJJ6.js.map → chunk-G2LUMAUF.js.map} +1 -1
- package/dist/{chunk-AXIDQG5A.js → chunk-G4T7L557.js} +5 -5
- package/dist/{chunk-7M6NYQWB.js → chunk-G7Z6EENO.js} +4 -4
- package/dist/{chunk-IARXXZU4.js → chunk-HOIRB77A.js} +99 -20
- package/dist/chunk-HOIRB77A.js.map +1 -0
- package/dist/{chunk-NDAH7BJ5.js → chunk-HVP33QVW.js} +103 -6
- package/dist/chunk-HVP33QVW.js.map +1 -0
- package/dist/{chunk-YXKVLYXU.js → chunk-HWRD23DW.js} +2 -2
- package/dist/{chunk-SOEN6KCF.js → chunk-IBKR2TFR.js} +2 -2
- package/dist/{chunk-R3DJVOSM.js → chunk-JDSVLVCG.js} +8 -1
- package/dist/chunk-JDSVLVCG.js.map +1 -0
- package/dist/{chunk-F4EU6VUC.js → chunk-JDZPNCMA.js} +2 -2
- package/dist/{chunk-D5YOZ66N.js → chunk-JV5YUBIZ.js} +20 -20
- package/dist/{chunk-DLG5V6ND.js → chunk-K4QS7LXU.js} +4 -7
- package/dist/chunk-K4QS7LXU.js.map +1 -0
- package/dist/{chunk-BAKXNQ3C.js → chunk-KRTY3ENI.js} +2 -2
- package/dist/{chunk-NK5RGJ65.js → chunk-L4DLLKPN.js} +2 -2
- package/dist/{chunk-VFSAWRMZ.js → chunk-LJS7KUPV.js} +2 -2
- package/dist/{chunk-JJRVPWFY.js → chunk-MLPTFODV.js} +166 -37
- package/dist/chunk-MLPTFODV.js.map +1 -0
- package/dist/{chunk-NOEMGE56.js → chunk-NH5IR2WM.js} +2 -2
- package/dist/{chunk-5V6IXHPI.js → chunk-NSVDUN76.js} +2 -2
- package/dist/{chunk-W7Y5JDVB.js → chunk-PDDTI7IC.js} +4 -4
- package/dist/{chunk-QINJITDS.js → chunk-PX2LSHYM.js} +2 -2
- package/dist/{chunk-HDIUYMSB.js → chunk-QWGE2PUH.js} +6 -6
- package/dist/{chunk-X57M2Q4I.js → chunk-RDJRFDJY.js} +3 -3
- package/dist/{chunk-RYLEDKQ2.js → chunk-RUMNY5DT.js} +3 -3
- package/dist/{chunk-BKIFTXHR.js → chunk-RZ4XGKOJ.js} +2 -2
- package/dist/{chunk-66SE3BNH.js → chunk-SUPEYLH2.js} +2 -2
- package/dist/{chunk-BF6SXNQJ.js → chunk-T2JHZWH6.js} +20 -20
- package/dist/{chunk-VHFXP27N.js → chunk-THPPCISA.js} +60 -23
- package/dist/chunk-THPPCISA.js.map +1 -0
- package/dist/{chunk-QPOHMO6M.js → chunk-UES4BHA2.js} +2 -2
- package/dist/{chunk-XAPMEU3K.js → chunk-UU7TRFNE.js} +2 -2
- package/dist/{chunk-QBSFOKY6.js → chunk-VOELBUS4.js} +4 -4
- package/dist/{chunk-2LY6GIIE.js → chunk-WDBX6VER.js} +2 -2
- package/dist/{chunk-BMEYMKWX.js → chunk-WM65LU67.js} +2 -2
- package/dist/{chunk-WLRD2TXF.js → chunk-WOPA3SGI.js} +4 -4
- package/dist/{chunk-5UZ6UCOZ.js → chunk-XUAMCLY4.js} +3 -3
- package/dist/{chunk-7HXZJAID.js → chunk-YGLY6QFV.js} +3 -3
- package/dist/{chunk-2OIQDTDC.js → chunk-ZPJ4QSGP.js} +2 -2
- package/dist/{chunk-3OY7KXHM.js → chunk-ZYLC7NFO.js} +3 -3
- package/dist/{cli-DMAFI0qt.d.ts → cli-DVUFIa5E.d.ts} +5 -5
- package/dist/cli.d.ts +13 -13
- package/dist/cli.js +42 -42
- package/dist/coding/pre-action-gate.d.ts +1 -1
- package/dist/compounding/engine.d.ts +2 -2
- package/dist/compounding/engine.js +13 -13
- 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 +5 -5
- package/dist/connectors/codex-materialize-runner.d.ts +1 -1
- package/dist/connectors/codex-materialize-runner.js +12 -12
- package/dist/connectors/codex-materialize.d.ts +1 -1
- package/dist/connectors/index.d.ts +1 -1
- package/dist/connectors/index.js +12 -12
- package/dist/consolidation-provenance-check.d.ts +2 -2
- package/dist/consolidation-undo.d.ts +2 -2
- package/dist/content-hash.d.ts +12 -2
- package/dist/content-hash.js +9 -3
- package/dist/contradiction/index.d.ts +2 -2
- package/dist/contradiction/index.js +3 -3
- package/dist/converge-config.d.ts +1 -1
- package/dist/convergence-refresh.d.ts +2 -2
- package/dist/convergence-refresh.js +14 -14
- 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 +14 -14
- 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-D63r24pH.d.ts → dependency-propagation-delivery-XzF76C9n.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 +12 -12
- package/dist/entity-schema.d.ts +1 -1
- package/dist/explicit-capture.d.ts +9 -9
- package/dist/explicit-capture.js +8 -8
- package/dist/external-wiki-access.d.ts +11 -11
- package/dist/external-wiki-access.js +23 -23
- 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-normalization.js +2 -2
- 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-source-grounding.js +3 -3
- package/dist/extraction.d.ts +1 -1
- package/dist/extraction.js +16 -16
- package/dist/fallback-llm.d.ts +1 -1
- package/dist/{forget-HDS4N6CG.js → forget-EHGGJZLX.js} +3 -2
- package/dist/{forget-HDS4N6CG.js.map → forget-EHGGJZLX.js.map} +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-construction.js +2 -2
- package/dist/harmonic-retrieval.js +4 -4
- 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 +777 -705
- package/dist/index.js +77 -67
- 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 +12 -12
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.d.ts +2 -2
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +12 -12
- package/dist/maintenance/rebuild-memory-projection.d.ts +2 -2
- package/dist/maintenance/rebuild-memory-projection.js +13 -13
- package/dist/{maintenance-kBII2qUI.d.ts → maintenance-BJzNIKBE.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-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 +13 -13
- package/dist/namespaces/principal.d.ts +1 -1
- package/dist/namespaces/search.d.ts +1 -1
- package/dist/namespaces/storage.d.ts +3 -3
- package/dist/namespaces/storage.js +12 -12
- 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 +15 -15
- package/dist/operator-doctor-replica.d.ts +1 -1
- package/dist/operator-toolkit.d.ts +3 -3
- package/dist/operator-toolkit.js +21 -21
- 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-DHy5LtTy.d.ts → orchestrator-BiXgBJBQ.d.ts} +8 -8
- package/dist/orchestrator.d.ts +9 -9
- package/dist/orchestrator.js +55 -55
- package/dist/patterns-cli.d.ts +1 -1
- package/dist/{pipeline-D21Rs6gW.d.ts → pipeline-sz_dcuLg.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/provenance.js +2 -2
- package/dist/{qmd-Dhle1P4X.d.ts → qmd-eAWObK4M.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/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 +4 -2
- package/dist/reconcile/manifest.js +7 -3
- 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/{resolution-MDPNOEG6.js → resolution-MMOD5WC4.js} +4 -4
- package/dist/resolve-auth-token.d.ts +1 -1
- package/dist/resume-bundles.js +6 -6
- 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/salvage-envelope.js +3 -3
- package/dist/schemas.d.ts +138 -138
- package/dist/{scope-profiles-NzGPDXLn.d.ts → scope-profiles-BBQsXuyj.d.ts} +1 -1
- package/dist/search/embed-helper.d.ts +1 -1
- package/dist/search/factory.d.ts +1 -1
- package/dist/search/index.d.ts +1 -1
- 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-DLDKvyy5.d.ts → semantic-consolidation-HD8RkzVA.d.ts} +1 -1
- package/dist/semantic-consolidation.d.ts +2 -2
- package/dist/semantic-consolidation.js +13 -13
- package/dist/semantic-rule-promotion.js +12 -12
- package/dist/semantic-rule-verifier.d.ts +1 -1
- package/dist/semantic-rule-verifier.js +12 -12
- package/dist/{service-PT2_z7xm.d.ts → service-DIrtHHEg.d.ts} +2 -2
- 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/source-agent-qualifier.d.ts +1 -1
- package/dist/source-agent-qualifier.js +14 -14
- package/dist/{storage-BmVudhk4.d.ts → storage-DqCzXi56.d.ts} +37 -33
- package/dist/storage.d.ts +2 -2
- package/dist/storage.js +14 -14
- package/dist/structured-attributes.d.ts +3 -1
- package/dist/structured-attributes.js +3 -1
- 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-CkiDUorT.d.ts → types-d_j4vC6N.d.ts} +26 -1
- package/dist/types.d.ts +1 -1
- package/dist/utility-runtime.d.ts +1 -1
- package/dist/verified-recall.js +12 -12
- package/dist/write-envelope.d.ts +1 -1
- package/dist/write-envelope.js +2 -2
- package/package.json +2 -2
- package/src/access-offline-manifest.ts +1 -0
- package/src/access-service-offline-file-content.test.ts +1 -0
- package/src/content-hash.ts +34 -1
- package/src/dedup/index.test.ts +56 -38
- package/src/dedup/index.ts +85 -38
- package/src/lifecycle/tombstones.test.ts +763 -16
- package/src/lifecycle/tombstones.ts +217 -50
- package/src/reconcile/manifest.test.ts +158 -1
- package/src/reconcile/manifest.ts +76 -22
- package/src/storage/content-hash-index.ts +56 -40
- package/src/storage/entity-ref-repair.ts +3 -3
- package/src/storage/memory-read-store.ts +9 -0
- package/src/storage/tombstone-blocked-capture-index.ts +4 -9
- package/src/storage-contract/content-hash.test.ts +13 -0
- package/src/storage-fact-hash-index-defer.test.ts +1 -1
- package/src/storage.ts +107 -93
- package/src/structured-attributes.ts +11 -0
- package/src/wearables/cleanup.test.ts +71 -0
- package/src/wearables/cleanup.ts +163 -16
- package/src/wearables/config.test.ts +62 -0
- package/src/wearables/config.ts +99 -5
- package/src/wearables/index.ts +10 -0
- package/src/wearables/pipeline.test.ts +36 -0
- package/src/wearables/pipeline.ts +18 -3
- package/src/wearables/redaction.test.ts +156 -0
- package/src/wearables/redaction.ts +102 -10
- package/src/wearables/text-language.ts +146 -0
- package/src/wearables/types.ts +26 -0
- package/dist/chunk-BQIFZ2MP.js.map +0 -1
- package/dist/chunk-DLG5V6ND.js.map +0 -1
- package/dist/chunk-IARXXZU4.js.map +0 -1
- package/dist/chunk-JJRVPWFY.js.map +0 -1
- package/dist/chunk-NDAH7BJ5.js.map +0 -1
- package/dist/chunk-R3DJVOSM.js.map +0 -1
- package/dist/chunk-VHFXP27N.js.map +0 -1
- package/dist/chunk-W5H6ZRPT.js +0 -14
- package/dist/chunk-W5H6ZRPT.js.map +0 -1
- package/dist/chunk-WD5B5AIE.js.map +0 -1
- /package/dist/{auto-sync-QFQGOEO2.js.map → auto-sync-TDHLZPVP.js.map} +0 -0
- /package/dist/{chunk-MLXZD4WM.js.map → chunk-2GFK22OZ.js.map} +0 -0
- /package/dist/{chunk-RANKJI5T.js.map → chunk-3JLPGJ43.js.map} +0 -0
- /package/dist/{chunk-X7XEDOSL.js.map → chunk-4IIFL7GC.js.map} +0 -0
- /package/dist/{chunk-GXSOUIA4.js.map → chunk-5WQMORU5.js.map} +0 -0
- /package/dist/{chunk-J272WKX2.js.map → chunk-6HPHEFXT.js.map} +0 -0
- /package/dist/{chunk-B6LZ4IBH.js.map → chunk-6LAPLZOS.js.map} +0 -0
- /package/dist/{chunk-VXCNQYTC.js.map → chunk-APPFNGX6.js.map} +0 -0
- /package/dist/{chunk-GXFSQ6SD.js.map → chunk-C4ZNGVK3.js.map} +0 -0
- /package/dist/{chunk-JSEJH2YQ.js.map → chunk-CGVHPCP2.js.map} +0 -0
- /package/dist/{chunk-TEMSWHQD.js.map → chunk-DZD576ZU.js.map} +0 -0
- /package/dist/{chunk-AMTEYNYK.js.map → chunk-EVWWT3KE.js.map} +0 -0
- /package/dist/{chunk-AXIDQG5A.js.map → chunk-G4T7L557.js.map} +0 -0
- /package/dist/{chunk-7M6NYQWB.js.map → chunk-G7Z6EENO.js.map} +0 -0
- /package/dist/{chunk-YXKVLYXU.js.map → chunk-HWRD23DW.js.map} +0 -0
- /package/dist/{chunk-SOEN6KCF.js.map → chunk-IBKR2TFR.js.map} +0 -0
- /package/dist/{chunk-F4EU6VUC.js.map → chunk-JDZPNCMA.js.map} +0 -0
- /package/dist/{chunk-D5YOZ66N.js.map → chunk-JV5YUBIZ.js.map} +0 -0
- /package/dist/{chunk-BAKXNQ3C.js.map → chunk-KRTY3ENI.js.map} +0 -0
- /package/dist/{chunk-NK5RGJ65.js.map → chunk-L4DLLKPN.js.map} +0 -0
- /package/dist/{chunk-VFSAWRMZ.js.map → chunk-LJS7KUPV.js.map} +0 -0
- /package/dist/{chunk-NOEMGE56.js.map → chunk-NH5IR2WM.js.map} +0 -0
- /package/dist/{chunk-5V6IXHPI.js.map → chunk-NSVDUN76.js.map} +0 -0
- /package/dist/{chunk-W7Y5JDVB.js.map → chunk-PDDTI7IC.js.map} +0 -0
- /package/dist/{chunk-QINJITDS.js.map → chunk-PX2LSHYM.js.map} +0 -0
- /package/dist/{chunk-HDIUYMSB.js.map → chunk-QWGE2PUH.js.map} +0 -0
- /package/dist/{chunk-X57M2Q4I.js.map → chunk-RDJRFDJY.js.map} +0 -0
- /package/dist/{chunk-RYLEDKQ2.js.map → chunk-RUMNY5DT.js.map} +0 -0
- /package/dist/{chunk-BKIFTXHR.js.map → chunk-RZ4XGKOJ.js.map} +0 -0
- /package/dist/{chunk-66SE3BNH.js.map → chunk-SUPEYLH2.js.map} +0 -0
- /package/dist/{chunk-BF6SXNQJ.js.map → chunk-T2JHZWH6.js.map} +0 -0
- /package/dist/{chunk-QPOHMO6M.js.map → chunk-UES4BHA2.js.map} +0 -0
- /package/dist/{chunk-XAPMEU3K.js.map → chunk-UU7TRFNE.js.map} +0 -0
- /package/dist/{chunk-QBSFOKY6.js.map → chunk-VOELBUS4.js.map} +0 -0
- /package/dist/{chunk-2LY6GIIE.js.map → chunk-WDBX6VER.js.map} +0 -0
- /package/dist/{chunk-BMEYMKWX.js.map → chunk-WM65LU67.js.map} +0 -0
- /package/dist/{chunk-WLRD2TXF.js.map → chunk-WOPA3SGI.js.map} +0 -0
- /package/dist/{chunk-5UZ6UCOZ.js.map → chunk-XUAMCLY4.js.map} +0 -0
- /package/dist/{chunk-7HXZJAID.js.map → chunk-YGLY6QFV.js.map} +0 -0
- /package/dist/{chunk-2OIQDTDC.js.map → chunk-ZPJ4QSGP.js.map} +0 -0
- /package/dist/{chunk-3OY7KXHM.js.map → chunk-ZYLC7NFO.js.map} +0 -0
- /package/dist/{resolution-MDPNOEG6.js.map → resolution-MMOD5WC4.js.map} +0 -0
package/src/storage.ts
CHANGED
|
@@ -13,7 +13,11 @@ import {
|
|
|
13
13
|
} from "node:fs/promises";
|
|
14
14
|
import { appendFileSync, createReadStream, mkdirSync, readFileSync, statSync } from "node:fs";
|
|
15
15
|
import { createHash } from "node:crypto";
|
|
16
|
-
import {
|
|
16
|
+
import {
|
|
17
|
+
computeContentHash,
|
|
18
|
+
computeLegacyContentHash,
|
|
19
|
+
normalizeContent,
|
|
20
|
+
} from "./content-hash.js";
|
|
17
21
|
import { raceAbort } from "./abort-error.js";
|
|
18
22
|
import { checkCorpusReadAbort, type CorpusReadOptions } from "./corpus-read-cancellation.js";
|
|
19
23
|
import { selectArtifactMatches, type ArtifactSearchOptions } from "./artifact-search.js";
|
|
@@ -115,7 +119,6 @@ import {
|
|
|
115
119
|
sortStructuredSectionsBySchema,
|
|
116
120
|
} from "./entity-schema.js";
|
|
117
121
|
import {
|
|
118
|
-
hasCitation,
|
|
119
122
|
hasCitationForTemplate,
|
|
120
123
|
stripCitationForTemplate,
|
|
121
124
|
DEFAULT_CITATION_FORMAT,
|
|
@@ -1058,7 +1061,7 @@ const FACT_HASH_INDEX_REBUILD_RETRY_BASE_MS = 50;
|
|
|
1058
1061
|
// the coding surfaces + wearable service). Imported here so internal callers
|
|
1059
1062
|
// (snapshotBeforeWrite, snapshotForProvenance) resolve, and re-exported to
|
|
1060
1063
|
// keep the public storage API stable for existing callers (wearables, dist).
|
|
1061
|
-
import { assemblePersistedBody, stripAttributesSuffix } from "./structured-attributes.js";
|
|
1064
|
+
import { assemblePersistedBody, storedContentIdentityCandidates, stripAttributesSuffix } from "./structured-attributes.js";
|
|
1062
1065
|
export { stripAttributesSuffix };
|
|
1063
1066
|
|
|
1064
1067
|
// `normalizeAttributePairs` moved to ./structured-attributes.ts (issue #1989
|
|
@@ -3009,22 +3012,51 @@ export class StorageManager extends TombstoneBlockedCaptureIndexHost {
|
|
|
3009
3012
|
}
|
|
3010
3013
|
|
|
3011
3014
|
private buildTombstoneStore(): TombstoneStore {
|
|
3015
|
+
let sourcePathsPromise: Promise<string[]> | undefined;
|
|
3016
|
+
let sourceContentsByIdPromise: Promise<Map<string, readonly string[]>> | undefined;
|
|
3012
3017
|
const options: TombstoneStoreOptions = {
|
|
3013
3018
|
enabled: this.tombstonesConfig.enabled,
|
|
3014
3019
|
semanticMatch: this.tombstonesConfig.semanticMatch,
|
|
3015
3020
|
semanticThreshold: this.tombstonesConfig.semanticThreshold,
|
|
3016
|
-
// Wire the SAME helpers the dedup index uses (rule 23 / checklist §13):
|
|
3017
|
-
// hashContent = ContentHashIndex.computeHash, normalizeText =
|
|
3018
|
-
// ContentHashIndex.normalizeContent. Importing them here (not copying)
|
|
3019
|
-
// guarantees the tombstone tiers can never drift from dedup.
|
|
3020
3021
|
hashContent: ContentHashIndex.computeHash,
|
|
3021
3022
|
normalizeText: ContentHashIndex.normalizeContent,
|
|
3023
|
+
sourceContentsForMemoryIds: async (sourceMemoryIds) => {
|
|
3024
|
+
const requested = new Set(sourceMemoryIds);
|
|
3025
|
+
const sourcePaths = await (sourcePathsPromise ??=
|
|
3026
|
+
this.memoryReadStore.collectTombstoneMigrationPaths());
|
|
3027
|
+
const directPaths = sourcePaths.filter((filePath) =>
|
|
3028
|
+
requested.has(path.basename(filePath, ".md"))
|
|
3029
|
+
);
|
|
3030
|
+
const contents = new Map<string, readonly string[]>();
|
|
3031
|
+
for (const memory of await this.readParsedMemoriesFromPaths(directPaths, 50)) {
|
|
3032
|
+
const id = memory.frontmatter.id;
|
|
3033
|
+
if (requested.has(id) && !contents.has(id)) {
|
|
3034
|
+
contents.set(id, this.storedContentIdentityCandidates(memory.content));
|
|
3035
|
+
}
|
|
3036
|
+
}
|
|
3037
|
+
if (contents.size === requested.size) return contents;
|
|
3038
|
+
sourceContentsByIdPromise ??= (async () => {
|
|
3039
|
+
const allContents = new Map<string, readonly string[]>();
|
|
3040
|
+
for (const memory of await this.readParsedMemoriesFromPaths(sourcePaths, 50)) {
|
|
3041
|
+
const id = memory.frontmatter.id;
|
|
3042
|
+
if (!allContents.has(id)) {
|
|
3043
|
+
allContents.set(id, this.storedContentIdentityCandidates(memory.content));
|
|
3044
|
+
}
|
|
3045
|
+
}
|
|
3046
|
+
return allContents;
|
|
3047
|
+
})();
|
|
3048
|
+
const allContents = await sourceContentsByIdPromise;
|
|
3049
|
+
for (const id of requested) {
|
|
3050
|
+
const content = allContents.get(id);
|
|
3051
|
+
if (content !== undefined) contents.set(id, content);
|
|
3052
|
+
}
|
|
3053
|
+
return contents;
|
|
3054
|
+
},
|
|
3022
3055
|
};
|
|
3023
3056
|
const io: TombstoneFileIo = {
|
|
3024
3057
|
read: (filePath) => this.readStorageSecureFile(filePath),
|
|
3025
3058
|
append: (filePath, content) => this.appendStorageSecureFile(filePath, content),
|
|
3026
3059
|
write: (filePath, content) => this.writeStorageSecureFile(filePath, content),
|
|
3027
|
-
// stat lets the store own its cross-process staleness probe (#1579).
|
|
3028
3060
|
stat: (filePath) => statSync(filePath),
|
|
3029
3061
|
};
|
|
3030
3062
|
return new TombstoneStore(this.tombstonesPath, this.tombstonesConfig.namespace, options, io);
|
|
@@ -3263,13 +3295,14 @@ export class StorageManager extends TombstoneBlockedCaptureIndexHost {
|
|
|
3263
3295
|
}
|
|
3264
3296
|
this.factHashIndexAuthoritative = false;
|
|
3265
3297
|
this.factHashIndexAuthoritativePromise = (async () => {
|
|
3266
|
-
//
|
|
3267
|
-
//
|
|
3268
|
-
//
|
|
3269
|
-
//
|
|
3270
|
-
//
|
|
3271
|
-
//
|
|
3272
|
-
//
|
|
3298
|
+
// ALWAYS rebuild the index from the durable corpus on first use per
|
|
3299
|
+
// process, preserving the #2016 crash/restart invariant. Markerless
|
|
3300
|
+
// legacy indexes additionally require body-derived hashes because their
|
|
3301
|
+
// frontmatter.contentHash values may use the old normalizer.
|
|
3302
|
+
//
|
|
3303
|
+
// The scan and publish run under the SAME per-index cross-process lock
|
|
3304
|
+
// used by reconcile saves, so migration cannot overwrite a peer's newer
|
|
3305
|
+
// additions with an unlocked write.
|
|
3273
3306
|
//
|
|
3274
3307
|
// PR #2016 (findings 1-2): bounded-retry the LOCKED rebuild so transient
|
|
3275
3308
|
// contention (a peer mid reconcile-save) clears within a short budget
|
|
@@ -3284,7 +3317,9 @@ export class StorageManager extends TombstoneBlockedCaptureIndexHost {
|
|
|
3284
3317
|
const baseMs = this.factHashIndexLockOptions.retryBaseMs ?? FACT_HASH_INDEX_REBUILD_RETRY_BASE_MS;
|
|
3285
3318
|
let published = false;
|
|
3286
3319
|
for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
|
|
3287
|
-
published = await factHashIndex.rebuildUnderLock(() =>
|
|
3320
|
+
published = await factHashIndex.rebuildUnderLock(() =>
|
|
3321
|
+
this.rebuildFactHashIndexFromCorpus(factHashIndex)
|
|
3322
|
+
);
|
|
3288
3323
|
if (published || attempt === maxAttempts - 1) break;
|
|
3289
3324
|
const wait = Math.min(baseMs * 2 ** attempt, CONTENT_HASH_INDEX_RETRY_MAX_DELAY_MS);
|
|
3290
3325
|
await new Promise<void>((resolve) => setTimeout(resolve, wait));
|
|
@@ -3346,17 +3381,16 @@ export class StorageManager extends TombstoneBlockedCaptureIndexHost {
|
|
|
3346
3381
|
// citation-strip reconstruction below, hashing the stored persist body
|
|
3347
3382
|
// (title + steps) exactly as buildProcedurePersistBody registered it.
|
|
3348
3383
|
if (inferMemoryStatus(memory.frontmatter, memory.path) !== "active") continue;
|
|
3349
|
-
const
|
|
3350
|
-
if (
|
|
3351
|
-
//
|
|
3352
|
-
// safely strip — skip rather than register a wrong hash. A
|
|
3353
|
-
// false-negative miss beats a wrong entry that would permanently
|
|
3354
|
-
// suppress legitimate duplicate writes (see corpusRegisteredHash).
|
|
3384
|
+
const hashes = this.corpusRegisteredHashes(memory);
|
|
3385
|
+
if (hashes.length === 0) {
|
|
3386
|
+
// Skip bodies whose configured citation cannot be stripped safely.
|
|
3355
3387
|
legacyRecovered++;
|
|
3356
3388
|
continue;
|
|
3357
3389
|
}
|
|
3358
|
-
|
|
3359
|
-
|
|
3390
|
+
for (const hash of hashes) {
|
|
3391
|
+
factHashIndex.addByHash(hash);
|
|
3392
|
+
if (memory.frontmatter.category === "fact") this.factOnlyHashes.add(hash);
|
|
3393
|
+
}
|
|
3360
3394
|
}
|
|
3361
3395
|
if (legacyRecovered > 0) {
|
|
3362
3396
|
log.info(
|
|
@@ -3364,39 +3398,28 @@ export class StorageManager extends TombstoneBlockedCaptureIndexHost {
|
|
|
3364
3398
|
);
|
|
3365
3399
|
}
|
|
3366
3400
|
}
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
|
|
3387
|
-
if (memory.frontmatter.contentHash) {
|
|
3388
|
-
return memory.frontmatter.contentHash;
|
|
3389
|
-
}
|
|
3390
|
-
const content = stripAttributesSuffix(memory.content);
|
|
3391
|
-
const stripped = stripCitationForTemplate(content, this.citationTemplate);
|
|
3392
|
-
if (stripped !== content) {
|
|
3393
|
-
return ContentHashIndex.computeHash(sanitizeMemoryContent(stripped).text);
|
|
3394
|
-
}
|
|
3395
|
-
if (!hasCitation(content)) {
|
|
3396
|
-
return ContentHashIndex.computeHash(sanitizeMemoryContent(content).text);
|
|
3397
|
-
}
|
|
3398
|
-
return null;
|
|
3401
|
+
private storedContentIdentityCandidates(content: string): string[] {
|
|
3402
|
+
return storedContentIdentityCandidates(content, (value) =>
|
|
3403
|
+
hasCitationForTemplate(value, this.citationTemplate) ? stripCitationForTemplate(value, this.citationTemplate) : value
|
|
3404
|
+
);
|
|
3405
|
+
}
|
|
3406
|
+
|
|
3407
|
+
/** Returns the current body identity while preserving explicit external identities. */
|
|
3408
|
+
private corpusRegisteredHashes(memory: MemoryFile): string[] {
|
|
3409
|
+
const persistedHash = memory.frontmatter.contentHash;
|
|
3410
|
+
const candidates = this.storedContentIdentityCandidates(memory.content)
|
|
3411
|
+
.map((content) => sanitizeMemoryContent(content).text);
|
|
3412
|
+
const currentHashes = candidates.map((content) => ContentHashIndex.computeHash(content));
|
|
3413
|
+
if (persistedHash && currentHashes.includes(persistedHash)) return [persistedHash];
|
|
3414
|
+
if (!persistedHash) return [currentHashes[0] ?? ContentHashIndex.computeHash("")];
|
|
3415
|
+
const legacyMatches = candidates.filter(
|
|
3416
|
+
(content) => computeLegacyContentHash(content) === persistedHash
|
|
3417
|
+
);
|
|
3418
|
+
return legacyMatches.length > 0
|
|
3419
|
+
? [...new Set(legacyMatches.map((content) => ContentHashIndex.computeHash(content)))]
|
|
3420
|
+
: [persistedHash];
|
|
3399
3421
|
}
|
|
3422
|
+
|
|
3400
3423
|
private get questionsDir(): string {
|
|
3401
3424
|
return path.join(this.baseDir, "questions");
|
|
3402
3425
|
}
|
|
@@ -3787,10 +3810,7 @@ export class StorageManager extends TombstoneBlockedCaptureIndexHost {
|
|
|
3787
3810
|
let tombstoneBlocked = false;
|
|
3788
3811
|
let gateRef: string | undefined;
|
|
3789
3812
|
let statusBeforeBlock: MemoryFrontmatter["status"];
|
|
3790
|
-
//
|
|
3791
|
-
// changed the final entityRef: a verdict is only valid for the ref it was
|
|
3792
|
-
// computed under, so reset and re-evaluate (parked mappings fall BACK to
|
|
3793
|
-
// the legacy claimant; entity-independent tiers re-block in the lookup).
|
|
3813
|
+
// Re-run after journal moves that change the final entityRef.
|
|
3794
3814
|
const applyTombstoneGate = async (): Promise<void> => {
|
|
3795
3815
|
if (tombstoneBlocked) {
|
|
3796
3816
|
if (fm.entityRef === gateRef) return;
|
|
@@ -3799,11 +3819,9 @@ export class StorageManager extends TombstoneBlockedCaptureIndexHost {
|
|
|
3799
3819
|
delete fm.blockedBy;
|
|
3800
3820
|
delete fm.tombstoneBlockTier;
|
|
3801
3821
|
}
|
|
3802
|
-
// Status semantics (thread ObteQ: pending_review candidates included,
|
|
3803
|
-
// terminal statuses respected) live in applyTombstoneResurrectionGate.
|
|
3804
3822
|
if (category !== "fact" || factHashSourceForTombstone === null) return;
|
|
3805
3823
|
const statusBefore = fm.status;
|
|
3806
|
-
const match = await this.entityRefRepair.gate(fm,
|
|
3824
|
+
const match = await this.entityRefRepair.gate(fm, sanitized.text, options.structuredAttributes);
|
|
3807
3825
|
if (match) {
|
|
3808
3826
|
gateRef = fm.entityRef;
|
|
3809
3827
|
statusBeforeBlock = statusBefore;
|
|
@@ -3980,34 +3998,28 @@ export class StorageManager extends TombstoneBlockedCaptureIndexHost {
|
|
|
3980
3998
|
for (const memory of existing) {
|
|
3981
3999
|
if (memory.frontmatter.category !== "fact") continue;
|
|
3982
4000
|
if (inferMemoryStatus(memory.frontmatter, memory.path) !== "active") continue;
|
|
3983
|
-
if (this.
|
|
4001
|
+
if (this.corpusRegisteredHashes(memory).includes(targetHash)) return true;
|
|
3984
4002
|
}
|
|
3985
4003
|
return false;
|
|
3986
4004
|
}
|
|
3987
4005
|
|
|
3988
|
-
private
|
|
3989
|
-
if (memory.frontmatter.category !== "fact") return
|
|
3990
|
-
|
|
3991
|
-
return memory.frontmatter.contentHash;
|
|
3992
|
-
}
|
|
3993
|
-
const configuredHashSource = stripCitationMarkersForHashRemoval(memory.content, this.citationTemplate);
|
|
3994
|
-
const hashSource =
|
|
3995
|
-
configuredHashSource !== memory.content
|
|
3996
|
-
? configuredHashSource
|
|
3997
|
-
: stripDefaultCitationMarkersWithoutRegex(memory.content);
|
|
3998
|
-
return ContentHashIndex.computeHash(sanitizeMemoryContent(hashSource).text);
|
|
4006
|
+
private factContentHashesForRemoval(memory: MemoryFile): string[] {
|
|
4007
|
+
if (memory.frontmatter.category !== "fact") return [];
|
|
4008
|
+
return this.corpusRegisteredHashes(memory);
|
|
3999
4009
|
}
|
|
4000
4010
|
|
|
4001
4011
|
private async addActiveFactContentHash(memory: MemoryFile): Promise<void> {
|
|
4002
4012
|
if (memory.frontmatter.category !== "fact") return;
|
|
4003
4013
|
if (inferMemoryStatus(memory.frontmatter, memory.path) !== "active") return;
|
|
4004
|
-
const
|
|
4005
|
-
if (
|
|
4014
|
+
const hashes = this.factContentHashesForRemoval(memory);
|
|
4015
|
+
if (hashes.length === 0) return;
|
|
4006
4016
|
|
|
4007
4017
|
await this.ensureFactHashIndexAuthoritative();
|
|
4008
4018
|
const factHashIndex = await this.getFactHashIndex();
|
|
4009
|
-
|
|
4010
|
-
|
|
4019
|
+
for (const hash of hashes) {
|
|
4020
|
+
factHashIndex.addByHash(hash);
|
|
4021
|
+
this.factOnlyHashes.add(hash);
|
|
4022
|
+
}
|
|
4011
4023
|
// PR #2016 thread SDzOP: flush through the SAME cross-process locked
|
|
4012
4024
|
// reconcile the write/batch/rebuild paths use, never the unlocked whole-file
|
|
4013
4025
|
// save() — an unlocked overwrite drops a concurrent extraction's appended
|
|
@@ -4045,16 +4057,19 @@ export class StorageManager extends TombstoneBlockedCaptureIndexHost {
|
|
|
4045
4057
|
private async syncFactHashIndexAfterRewrite(before: MemoryFile, after: MemoryFile): Promise<void> {
|
|
4046
4058
|
if (before.frontmatter.category !== "fact" && after.frontmatter.category !== "fact") return;
|
|
4047
4059
|
|
|
4048
|
-
const
|
|
4049
|
-
const
|
|
4060
|
+
const beforeHashes = this.factContentHashesForRemoval(before);
|
|
4061
|
+
const afterHashes = this.factContentHashesForRemoval(after);
|
|
4050
4062
|
const beforeStatus = inferMemoryStatus(before.frontmatter, before.path);
|
|
4051
4063
|
const afterStatus = inferMemoryStatus(after.frontmatter, after.path);
|
|
4052
|
-
|
|
4064
|
+
const hashesChanged =
|
|
4065
|
+
beforeHashes.length !== afterHashes.length ||
|
|
4066
|
+
beforeHashes.some((hash, index) => hash !== afterHashes[index]);
|
|
4067
|
+
if (!hashesChanged && beforeStatus === afterStatus) return;
|
|
4053
4068
|
|
|
4054
|
-
if (beforeStatus === "active" &&
|
|
4069
|
+
if (beforeStatus === "active" && beforeHashes.length > 0 && (hashesChanged || afterStatus !== "active")) {
|
|
4055
4070
|
await this.removeFactContentHashesForMemories([before]);
|
|
4056
4071
|
}
|
|
4057
|
-
if (afterStatus === "active" &&
|
|
4072
|
+
if (afterStatus === "active" && afterHashes.length > 0 && (hashesChanged || beforeStatus !== "active")) {
|
|
4058
4073
|
await this.addActiveFactContentHash(after);
|
|
4059
4074
|
}
|
|
4060
4075
|
}
|
|
@@ -4067,11 +4082,10 @@ export class StorageManager extends TombstoneBlockedCaptureIndexHost {
|
|
|
4067
4082
|
.map((memory) => memory.frontmatter.id)
|
|
4068
4083
|
.filter((id): id is string => typeof id === "string" && id.length > 0)
|
|
4069
4084
|
);
|
|
4070
|
-
const removedHashes = new
|
|
4085
|
+
const removedHashes = new Set<string>();
|
|
4071
4086
|
for (const memory of memories) {
|
|
4072
|
-
const hash
|
|
4073
|
-
|
|
4074
|
-
removedHashes.set(memory, hash);
|
|
4087
|
+
for (const hash of this.factContentHashesForRemoval(memory)) {
|
|
4088
|
+
removedHashes.add(hash);
|
|
4075
4089
|
}
|
|
4076
4090
|
}
|
|
4077
4091
|
if (removedHashes.size === 0) return;
|
|
@@ -4082,13 +4096,12 @@ export class StorageManager extends TombstoneBlockedCaptureIndexHost {
|
|
|
4082
4096
|
if (memory.frontmatter.category !== "fact") continue;
|
|
4083
4097
|
if (removedIds.has(memory.frontmatter.id)) continue;
|
|
4084
4098
|
if (inferMemoryStatus(memory.frontmatter, memory.path) !== "active") continue;
|
|
4085
|
-
const hash
|
|
4086
|
-
if (hash) {
|
|
4099
|
+
for (const hash of this.factContentHashesForRemoval(memory)) {
|
|
4087
4100
|
remainingActiveHashes.add(hash);
|
|
4088
4101
|
}
|
|
4089
4102
|
}
|
|
4090
4103
|
|
|
4091
|
-
for (const hash of removedHashes
|
|
4104
|
+
for (const hash of removedHashes) {
|
|
4092
4105
|
if (!remainingActiveHashes.has(hash)) {
|
|
4093
4106
|
factHashIndex.removeByHash(hash);
|
|
4094
4107
|
this.factOnlyHashes.delete(hash);
|
|
@@ -4117,8 +4130,9 @@ export class StorageManager extends TombstoneBlockedCaptureIndexHost {
|
|
|
4117
4130
|
*/
|
|
4118
4131
|
removeFactOnlyHashForMemory(memory: MemoryFile): void {
|
|
4119
4132
|
if (memory.frontmatter.category !== "fact") return;
|
|
4120
|
-
const hash
|
|
4121
|
-
|
|
4133
|
+
for (const hash of this.factContentHashesForRemoval(memory)) {
|
|
4134
|
+
this.factOnlyHashes.delete(hash);
|
|
4135
|
+
}
|
|
4122
4136
|
}
|
|
4123
4137
|
|
|
4124
4138
|
async isFactContentHashAuthoritative(): Promise<boolean> {
|
|
@@ -39,6 +39,17 @@ export function stripAttributesSuffix(content: string): string {
|
|
|
39
39
|
if (inner.includes("]") || inner.includes("\n")) return content.trim();
|
|
40
40
|
return trimmed.slice(0, markerIndex).trim();
|
|
41
41
|
}
|
|
42
|
+
|
|
43
|
+
/** Return stored-body identity forms, from raw semantic content to full body. */
|
|
44
|
+
export function storedContentIdentityCandidates(
|
|
45
|
+
content: string,
|
|
46
|
+
stripCitation: (value: string) => string,
|
|
47
|
+
): string[] {
|
|
48
|
+
const fullBody = content.trim();
|
|
49
|
+
const bodyWithoutAttributes = stripAttributesSuffix(fullBody);
|
|
50
|
+
const rawBody = stripCitation(bodyWithoutAttributes);
|
|
51
|
+
return [...new Set([rawBody, bodyWithoutAttributes, fullBody])];
|
|
52
|
+
}
|
|
42
53
|
// ---------------------------------------------------------------------------
|
|
43
54
|
// Persisted-body assembly (issue #1989 PR2)
|
|
44
55
|
// ---------------------------------------------------------------------------
|
|
@@ -188,3 +188,74 @@ test("preserveUtteranceBoundaries default (off) still collapses generic labels",
|
|
|
188
188
|
assert.equal(result.conversation.segments.length, 1);
|
|
189
189
|
assert.equal(result.conversation.segments[0].text, "First. Second.");
|
|
190
190
|
});
|
|
191
|
+
|
|
192
|
+
test("strips Japanese filler tokens that carry no word spaces", () => {
|
|
193
|
+
assert.equal(
|
|
194
|
+
stripFillerTokens("えーと、明日の会議は三時です"),
|
|
195
|
+
"明日の会議は三時です",
|
|
196
|
+
);
|
|
197
|
+
assert.equal(stripFillerTokens("あのー来週で"), "来週で");
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
test("keeps a meaning-bearing Japanese demonstrative", () => {
|
|
201
|
+
assert.equal(stripFillerTokens("あの会議は長かった"), "あの会議は長かった");
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
test("strips Korean and Cyrillic filler tokens as whole tokens", () => {
|
|
205
|
+
assert.equal(stripFillerTokens("음 회의는 세시입니다"), "회의는 세시입니다");
|
|
206
|
+
assert.equal(stripFillerTokens("эм встреча в три"), "встреча в три");
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
test("never strips a filler lookalike inside a longer token", () => {
|
|
210
|
+
assert.equal(stripFillerTokens("the umbrella is uhh wet"), "the umbrella is wet");
|
|
211
|
+
// "음" also occurs inside ordinary Korean words; only the whole token goes.
|
|
212
|
+
assert.equal(stripFillerTokens("다음 회의"), "다음 회의");
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
test("operator filler tokens apply to any script", () => {
|
|
216
|
+
assert.equal(stripFillerTokens("bueno vamos a empezar", ["bueno"]), "vamos a empezar");
|
|
217
|
+
assert.equal(stripFillerTokens("那个我们开始吧", ["那个"]), "我们开始吧");
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
test("cleanConversation strips configured filler tokens", () => {
|
|
221
|
+
const result = cleanConversation(
|
|
222
|
+
conversation([
|
|
223
|
+
{ speakerKey: "a", text: "えーと、予算を確認します" },
|
|
224
|
+
{ speakerKey: "b", text: "bueno, de acuerdo" },
|
|
225
|
+
]),
|
|
226
|
+
ALL_ON,
|
|
227
|
+
["bueno"],
|
|
228
|
+
);
|
|
229
|
+
assert.deepEqual(
|
|
230
|
+
result.conversation.segments.map((segment) => segment.text),
|
|
231
|
+
["予算を確認します", "de acuerdo"],
|
|
232
|
+
);
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
test("an Arabic filler followed by Arabic punctuation is stripped", () => {
|
|
236
|
+
assert.equal(stripFillerTokens("اه، نعم"), "نعم");
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
test("a longer operator token wins over a built-in prefix", () => {
|
|
240
|
+
assert.equal(stripFillerTokens("あのーさあ始めよう", ["あのーさあ"]), "始めよう");
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
test("matcher cache keys distinguish differently-shaped token lists", () => {
|
|
244
|
+
assert.equal(stripFillerTokens("uh,um is here", ["uh,um"]), "is here");
|
|
245
|
+
assert.equal(stripFillerTokens("uh,um is here", ["uh", "um"]), "uh,um is here");
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
test("a Hebrew operator token is a whole token, never a substring", () => {
|
|
249
|
+
// Hebrew spaces its words, so an inline match would strip the suffix of
|
|
250
|
+
// an ordinary word and corrupt the transcript.
|
|
251
|
+
assert.equal(stripFillerTokens("נראה טוב", ["אה"]), "נראה טוב");
|
|
252
|
+
assert.equal(stripFillerTokens("אה טוב", ["אה"]), "טוב");
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
test("a decomposed accented filler is stripped without rewriting the rest", () => {
|
|
256
|
+
const decomposed = "a\u0308hm weiter mit dem cafe\u0301";
|
|
257
|
+
const cleaned = stripFillerTokens(decomposed);
|
|
258
|
+
assert.equal(cleaned, "weiter mit dem cafe\u0301");
|
|
259
|
+
// The surviving text keeps the ASR's own bytes: no NFC rewrite.
|
|
260
|
+
assert.ok(cleaned.includes("\u0301"));
|
|
261
|
+
});
|
package/src/wearables/cleanup.ts
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* everything here is conservative and reversible by re-syncing.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
+
import { detectScriptHints, type ScriptHint } from "./text-language.js";
|
|
10
11
|
import type {
|
|
11
12
|
WearableCleanupSettings,
|
|
12
13
|
WearableConversation,
|
|
@@ -33,24 +34,155 @@ const MERGE_GAP_MS = 30_000;
|
|
|
33
34
|
const GENERIC_SPEAKER_PATTERN = /^unknown$/i;
|
|
34
35
|
|
|
35
36
|
/**
|
|
36
|
-
* Standalone filler tokens stripped when `stripFillers` is on
|
|
37
|
-
*
|
|
38
|
-
* inside "umbrella" is never touched.
|
|
39
|
-
*
|
|
37
|
+
* Standalone filler tokens stripped when `stripFillers` is on, for
|
|
38
|
+
* scripts that separate words with spaces. Matched case-insensitively
|
|
39
|
+
* as whole tokens — "um" inside "umbrella" is never touched.
|
|
40
|
+
* Deliberately short and low-risk: meaning-bearing hedges ("like",
|
|
41
|
+
* "well", Spanish "este", Arabic "يعني") are NOT stripped, and neither
|
|
42
|
+
* is "mm", which is also a unit of length.
|
|
40
43
|
*/
|
|
41
|
-
const
|
|
44
|
+
const TOKEN_FILLERS: Readonly<Partial<Record<ScriptHint, readonly string[]>>> = {
|
|
45
|
+
latin: [
|
|
46
|
+
"um",
|
|
47
|
+
"uh",
|
|
48
|
+
"uhm",
|
|
49
|
+
"umm",
|
|
50
|
+
"uhh",
|
|
51
|
+
"erm",
|
|
52
|
+
"hmm",
|
|
53
|
+
"mhm",
|
|
54
|
+
"ähm",
|
|
55
|
+
"äh",
|
|
56
|
+
"ehm",
|
|
57
|
+
"euh",
|
|
58
|
+
"mmm",
|
|
59
|
+
],
|
|
60
|
+
korean: ["음", "으음", "엄", "어어"],
|
|
61
|
+
arabic: ["امم", "اممم", "اه"],
|
|
62
|
+
cyrillic: ["эм", "ээ", "ммм"],
|
|
63
|
+
};
|
|
42
64
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
65
|
+
/**
|
|
66
|
+
* Filler tokens for scripts without word spaces. These are removed
|
|
67
|
+
* inline, so each entry must be unambiguous on its own: the elongated
|
|
68
|
+
* Japanese hesitation forms and the two Chinese hesitation particles
|
|
69
|
+
* qualify, while a bare 「あの」 ("that") does not (issue #2196).
|
|
70
|
+
*/
|
|
71
|
+
const INLINE_FILLERS: Readonly<Partial<Record<ScriptHint, readonly string[]>>> = {
|
|
72
|
+
japanese: ["えーと", "えっと", "ええと", "あのー", "あのう", "うーん"],
|
|
73
|
+
han: ["呃", "嗯"],
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Punctuation that trails a filler and collapses with it, so
|
|
78
|
+
* 「えーと、では」 becomes 「では」 rather than 「、では」. Arabic sentence
|
|
79
|
+
* punctuation is included: an Arabic filler is normally followed by
|
|
80
|
+
* `،`, and without it the token survives the pass.
|
|
81
|
+
*/
|
|
82
|
+
const TRAILING_FILLER_PUNCTUATION = "[,.、。،؛؟!?]?";
|
|
83
|
+
|
|
84
|
+
interface FillerMatchers {
|
|
85
|
+
token: RegExp | null;
|
|
86
|
+
inline: RegExp | null;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Compiled matchers are cached per (script hints + extra tokens) key.
|
|
91
|
+
* A day of transcripts is thousands of segments over a handful of
|
|
92
|
+
* scripts, so this compiles a few regexes instead of one per segment.
|
|
93
|
+
*/
|
|
94
|
+
const FILLER_MATCHER_CACHE = new Map<string, FillerMatchers>();
|
|
95
|
+
|
|
96
|
+
function escapeRegExp(value: string): string {
|
|
97
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Build one alternation from `tokens`, or `null` when none remain.
|
|
102
|
+
*
|
|
103
|
+
* Each token contributes BOTH its composed and its decomposed spelling,
|
|
104
|
+
* so `ähm` also matches an ASR that emits `a\u0308hm`. Matching both
|
|
105
|
+
* forms keeps the surrounding transcript byte-identical, which
|
|
106
|
+
* normalizing the text before stripping would not.
|
|
107
|
+
*
|
|
108
|
+
* JavaScript alternation is first-match, not longest-match, so the
|
|
109
|
+
* tokens are sorted longest-first. Without that a built-in prefix such
|
|
110
|
+
* as 「あのー」 would match ahead of a longer operator token that starts
|
|
111
|
+
* with it and leave the tail in the transcript. Ties break on the token
|
|
112
|
+
* itself so the pattern is stable across runs.
|
|
113
|
+
*/
|
|
114
|
+
function buildAlternation(
|
|
115
|
+
tokens: readonly string[],
|
|
116
|
+
shape: (pattern: string) => { source: string; flags: string },
|
|
117
|
+
): RegExp | null {
|
|
118
|
+
const forms = new Set<string>();
|
|
119
|
+
for (const token of tokens) {
|
|
120
|
+
forms.add(token.normalize("NFC"));
|
|
121
|
+
forms.add(token.normalize("NFD"));
|
|
122
|
+
}
|
|
123
|
+
const unique = [...forms].sort(
|
|
124
|
+
(left, right) => right.length - left.length || (left < right ? -1 : left > right ? 1 : 0),
|
|
125
|
+
);
|
|
126
|
+
if (unique.length === 0) return null;
|
|
127
|
+
const { source, flags } = shape(unique.map((token) => escapeRegExp(token)).join("|"));
|
|
128
|
+
return new RegExp(source, flags);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function buildFillerMatchers(
|
|
132
|
+
hints: readonly ScriptHint[],
|
|
133
|
+
extraTokens: readonly string[],
|
|
134
|
+
): FillerMatchers {
|
|
135
|
+
// Structural key: comma-joining collapses `["a,b"]` and `["a","b"]`
|
|
136
|
+
// onto one entry, and the first caller's regex would then clean the
|
|
137
|
+
// second caller's transcripts.
|
|
138
|
+
const key = JSON.stringify([hints, extraTokens]);
|
|
139
|
+
const cached = FILLER_MATCHER_CACHE.get(key);
|
|
140
|
+
if (cached) return cached;
|
|
141
|
+
|
|
142
|
+
const tokens: string[] = [];
|
|
143
|
+
const inline: string[] = [];
|
|
144
|
+
for (const hint of hints) {
|
|
145
|
+
tokens.push(...(TOKEN_FILLERS[hint] ?? []));
|
|
146
|
+
inline.push(...(INLINE_FILLERS[hint] ?? []));
|
|
147
|
+
}
|
|
148
|
+
// Operator tokens apply to every script: the operator knows their own
|
|
149
|
+
// language, and routing them by script hint would silently drop them.
|
|
150
|
+
for (const extra of extraTokens) {
|
|
151
|
+
const trimmed = extra.trim();
|
|
152
|
+
if (trimmed.length === 0) continue;
|
|
153
|
+
if (SPACE_DELIMITED_TOKEN.test(trimmed)) tokens.push(trimmed);
|
|
154
|
+
else inline.push(trimmed);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const matchers: FillerMatchers = {
|
|
158
|
+
token: buildAlternation(tokens, (pattern) => ({
|
|
159
|
+
source: `(?:^|\\s)(?:${pattern})${TRAILING_FILLER_PUNCTUATION}(?=\\s|$)`,
|
|
160
|
+
flags: "giu",
|
|
161
|
+
})),
|
|
162
|
+
inline: buildAlternation(inline, (pattern) => ({
|
|
163
|
+
source: `(?:${pattern})${TRAILING_FILLER_PUNCTUATION}`,
|
|
164
|
+
flags: "gu",
|
|
165
|
+
})),
|
|
166
|
+
};
|
|
167
|
+
FILLER_MATCHER_CACHE.set(key, matchers);
|
|
168
|
+
return matchers;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* A token whose first character belongs to a space-delimited script.
|
|
173
|
+
*
|
|
174
|
+
* Anything outside this set is removed INLINE, so a script that does
|
|
175
|
+
* space its words must appear here. Hebrew `אה` routed to the inline
|
|
176
|
+
* matcher would strip the suffix of `נראה` and corrupt the transcript.
|
|
177
|
+
*/
|
|
178
|
+
const SPACE_DELIMITED_TOKEN =
|
|
179
|
+
/^[\p{Script=Latin}\p{Script=Cyrillic}\p{Script=Greek}\p{Script=Hangul}\p{Script=Arabic}\p{Script=Hebrew}\p{N}]/u;
|
|
49
180
|
|
|
50
181
|
/** Apply configured cleanup passes to one conversation. */
|
|
51
182
|
export function cleanConversation(
|
|
52
183
|
conversation: WearableConversation,
|
|
53
184
|
settings: WearableCleanupSettings,
|
|
185
|
+
extraFillerTokens: readonly string[] = [],
|
|
54
186
|
): CleanupResult {
|
|
55
187
|
let segments = conversation.segments.map((segment) => ({ ...segment }));
|
|
56
188
|
let droppedSegments = 0;
|
|
@@ -61,7 +193,7 @@ export function cleanConversation(
|
|
|
61
193
|
|
|
62
194
|
if (settings.stripFillers) {
|
|
63
195
|
for (const segment of segments) {
|
|
64
|
-
segment.text = stripFillerTokens(segment.text);
|
|
196
|
+
segment.text = stripFillerTokens(segment.text, extraFillerTokens);
|
|
65
197
|
}
|
|
66
198
|
}
|
|
67
199
|
|
|
@@ -130,15 +262,30 @@ function canMerge(
|
|
|
130
262
|
const label = previous.speakerKey.trim();
|
|
131
263
|
if (label.length === 0 || GENERIC_SPEAKER_PATTERN.test(label)) return false;
|
|
132
264
|
}
|
|
133
|
-
const previousEnd = previous.endIso
|
|
134
|
-
|
|
265
|
+
const previousEnd = previous.endIso
|
|
266
|
+
? Date.parse(previous.endIso)
|
|
267
|
+
: Number.NaN;
|
|
268
|
+
const nextStart = next.startIso ? Date.parse(next.startIso) : Number.NaN;
|
|
135
269
|
// Without timestamps, adjacency is the only signal — still merge.
|
|
136
270
|
if (Number.isNaN(previousEnd) || Number.isNaN(nextStart)) return true;
|
|
137
271
|
return nextStart - previousEnd <= MERGE_GAP_MS;
|
|
138
272
|
}
|
|
139
273
|
|
|
140
|
-
|
|
141
|
-
|
|
274
|
+
/**
|
|
275
|
+
* Remove filler tokens from `text`. The built-in token set is chosen
|
|
276
|
+
* from the scripts present in the text, so a Japanese or Korean segment
|
|
277
|
+
* is cleaned by its own list instead of silently keeping every filler
|
|
278
|
+
* (issue #2196). Operator tokens apply to every script.
|
|
279
|
+
*/
|
|
280
|
+
export function stripFillerTokens(
|
|
281
|
+
text: string,
|
|
282
|
+
extraTokens: readonly string[] = [],
|
|
283
|
+
): string {
|
|
284
|
+
const matchers = buildFillerMatchers(detectScriptHints(text), extraTokens);
|
|
285
|
+
let result = text;
|
|
286
|
+
if (matchers.token) result = result.replace(matchers.token, " ");
|
|
287
|
+
if (matchers.inline) result = result.replace(matchers.inline, "");
|
|
288
|
+
return normalizeWhitespace(result);
|
|
142
289
|
}
|
|
143
290
|
|
|
144
291
|
/**
|