@remnic/core 9.5.0 → 9.6.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 +6 -6
- package/dist/access-admin-ops-surface.js +12 -12
- package/dist/access-boundary.d.ts +6 -6
- package/dist/access-boundary.js +13 -13
- package/dist/access-cli.js +36 -36
- package/dist/access-http.d.ts +6 -6
- package/dist/access-http.js +17 -17
- package/dist/access-identity-continuity-surface.d.ts +5 -5
- package/dist/access-identity-continuity-surface.js +12 -12
- package/dist/access-lcm-surface.d.ts +6 -6
- package/dist/access-lcm-surface.js +12 -12
- package/dist/access-mcp.d.ts +6 -6
- package/dist/access-mcp.js +16 -16
- package/dist/access-observe-write-surface.d.ts +6 -6
- package/dist/access-observe-write-surface.js +12 -12
- package/dist/access-operations-batch.js +14 -14
- package/dist/access-operations.d.ts +7 -7
- package/dist/access-operations.js +15 -15
- package/dist/access-recall-surface.d.ts +6 -6
- package/dist/access-recall-surface.js +12 -12
- package/dist/access-schema.d.ts +4 -4
- package/dist/{access-service-DZX0sYAv.d.ts → access-service-CXJaql3A.d.ts} +4 -4
- package/dist/access-service.d.ts +6 -6
- package/dist/access-service.js +12 -12
- package/dist/access-surface-catalog.d.ts +6 -6
- 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 +1 -1
- package/dist/{auto-sync-G6IU7L6C.js → auto-sync-4A5YBWYO.js} +3 -3
- package/dist/behavior-learner.d.ts +1 -1
- package/dist/behavior-signals.d.ts +1 -1
- package/dist/bootstrap.d.ts +5 -5
- package/dist/briefing.d.ts +2 -2
- package/dist/briefing.js +4 -4
- package/dist/buffer-surprise-report.d.ts +1 -1
- package/dist/buffer.d.ts +2 -2
- package/dist/calibration.d.ts +1 -1
- package/dist/capabilities.d.ts +1 -1
- package/dist/{catalog-DoH2b0OR.d.ts → catalog-C9cu67Ig.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 +5 -5
- package/dist/causal-trajectory-graph.d.ts +1 -1
- package/dist/{chunk-H5PFR5ZT.js → chunk-23JFRB73.js} +2 -2
- package/dist/{chunk-BGMSRN6I.js → chunk-3XIY7MDQ.js} +3 -3
- package/dist/{chunk-L2QJQSHL.js → chunk-4GNEDXDI.js} +2 -2
- package/dist/{chunk-MFDJ63U5.js → chunk-4LV23CHK.js} +2 -2
- package/dist/{chunk-AER6MT24.js → chunk-6SXVCD7W.js} +2 -2
- package/dist/{chunk-2SFEFR5I.js → chunk-72WG5QKN.js} +2 -2
- package/dist/{chunk-7BAJAXGB.js → chunk-7MW3CVLD.js} +2 -2
- package/dist/{chunk-SNNIUDRS.js → chunk-AYZJID4S.js} +2 -2
- package/dist/{chunk-M7XQSUBB.js → chunk-B3ABVQ36.js} +116 -9
- package/dist/chunk-B3ABVQ36.js.map +1 -0
- package/dist/{chunk-LNHJ32NK.js → chunk-BEM4D2QG.js} +3 -3
- package/dist/{chunk-AOYJVCFN.js → chunk-CYWA2CXR.js} +2 -2
- package/dist/{chunk-2JFDHI2K.js → chunk-DEG5ULFJ.js} +2 -2
- package/dist/{chunk-L6V3WJ55.js → chunk-DS7MM2ES.js} +3 -3
- package/dist/{chunk-CXEL3Y22.js → chunk-FKCRGLRD.js} +184 -29
- package/dist/{chunk-CXEL3Y22.js.map → chunk-FKCRGLRD.js.map} +1 -1
- package/dist/{chunk-TI7IHDBQ.js → chunk-H2DXDQCQ.js} +5 -5
- package/dist/{chunk-KMF67QDQ.js → chunk-HLN7RROI.js} +4 -4
- package/dist/{chunk-D2XYYU5Q.js → chunk-J3XHPFEU.js} +2 -2
- package/dist/{chunk-ODSYJXDU.js → chunk-JSNVWNGX.js} +2 -2
- package/dist/{chunk-7Z2DZNWN.js → chunk-KL4UAXUY.js} +43 -2
- package/dist/{chunk-7Z2DZNWN.js.map → chunk-KL4UAXUY.js.map} +1 -1
- package/dist/{chunk-4WNLSSHL.js → chunk-LADFWYHY.js} +4 -4
- package/dist/{chunk-UBGAOYBP.js → chunk-LR5DZ56F.js} +2 -2
- package/dist/{chunk-5K5VEDFY.js → chunk-N3JDIRBW.js} +2 -2
- package/dist/{chunk-2B7DP7AP.js → chunk-P454KXD6.js} +93 -15
- package/dist/chunk-P454KXD6.js.map +1 -0
- package/dist/{chunk-VWEWK2BM.js → chunk-PCLIWBGD.js} +3 -3
- package/dist/{chunk-TV7C3HCB.js → chunk-PNLFN2PZ.js} +2 -2
- package/dist/{chunk-STCCFQU5.js → chunk-PVUJ6WVV.js} +6 -6
- package/dist/{chunk-ITWTKT2R.js → chunk-RGK27ELN.js} +2 -2
- package/dist/{chunk-II6WHCCW.js → chunk-SIRPLF54.js} +2 -2
- package/dist/{chunk-UDCDGP6A.js → chunk-SVOHGUMI.js} +1188 -112
- package/dist/chunk-SVOHGUMI.js.map +1 -0
- package/dist/{chunk-NFTABV56.js → chunk-TLUDFPZL.js} +2 -2
- package/dist/{chunk-I74ZEBMA.js → chunk-UFUKHGZD.js} +4 -4
- package/dist/{chunk-L4UZBJNM.js → chunk-UTRJFDP2.js} +2 -2
- package/dist/{chunk-5GPPACXK.js → chunk-VSMRDBFT.js} +2 -1
- package/dist/{chunk-SNBEVJBX.js → chunk-XCL57M6Q.js} +3 -3
- package/dist/{chunk-DZSAXE3T.js → chunk-Y7UNNAZD.js} +2 -2
- package/dist/{chunk-XN3TUUX2.js → chunk-YQB32INI.js} +2 -2
- package/dist/{cli-1dghPCO2.d.ts → cli-CqdPA6z9.d.ts} +3 -3
- package/dist/cli.d.ts +7 -7
- package/dist/cli.js +29 -29
- package/dist/compounding/engine.d.ts +2 -2
- package/dist/compounding/engine.js +4 -4
- 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 +1 -1
- package/dist/connectors/codex-materialize-runner.d.ts +1 -1
- package/dist/connectors/codex-materialize-runner.js +4 -4
- package/dist/connectors/codex-materialize.d.ts +1 -1
- package/dist/connectors/index.d.ts +1 -1
- package/dist/connectors/index.js +4 -4
- 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 +4 -4
- package/dist/entity-schema.d.ts +1 -1
- package/dist/explicit-capture.d.ts +5 -5
- 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 +1 -1
- package/dist/fallback-llm.d.ts +1 -1
- package/dist/graph-dashboard-diff.d.ts +1 -1
- package/dist/graph-dashboard-key.d.ts +1 -1
- package/dist/graph-dashboard-parser.d.ts +1 -1
- package/dist/graph-edge-reinforcement.d.ts +1 -1
- package/dist/graph-snapshot.d.ts +1 -1
- package/dist/graph.d.ts +1 -1
- package/dist/identity-continuity.d.ts +1 -1
- package/dist/importance.d.ts +1 -1
- package/dist/index.d.ts +268 -17
- package/dist/index.js +80 -38
- 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 +4 -4
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +4 -4
- package/dist/maintenance/rebuild-memory-projection.js +5 -5
- package/dist/mcp-memory-inspector-app.d.ts +6 -6
- 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 +11 -11
- package/dist/namespaces/principal.d.ts +1 -1
- package/dist/namespaces/search.d.ts +1 -1
- package/dist/namespaces/search.js +7 -7
- package/dist/namespaces/storage.d.ts +3 -3
- package/dist/namespaces/storage.js +4 -4
- package/dist/native-knowledge.d.ts +1 -1
- package/dist/operator-toolkit.d.ts +2 -2
- package/dist/operator-toolkit.js +15 -15
- package/dist/orchestration/compression-guideline-coordinator.d.ts +2 -2
- package/dist/orchestration/maintenance.d.ts +2 -2
- package/dist/orchestration/maintenance.js +6 -6
- package/dist/{orchestrator-Dh3WSW_O.d.ts → orchestrator-CWFrT8AF.d.ts} +91 -60
- package/dist/orchestrator.d.ts +5 -5
- package/dist/orchestrator.js +36 -36
- package/dist/patterns-cli.d.ts +1 -1
- package/dist/policy-runtime.d.ts +1 -1
- package/dist/provenance.d.ts +1 -1
- package/dist/{qmd-DoUVxqND.d.ts → qmd--fnPMK60.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-disclosure-escalation.d.ts +1 -1
- package/dist/recall-explain-renderer.d.ts +1 -1
- package/dist/recall-planner-llm.d.ts +1 -1
- package/dist/recall-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/resolve-auth-token.d.ts +1 -1
- package/dist/resume-bundles.js +2 -2
- 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/schemas.d.ts +22 -22
- package/dist/search/document-scanner.js +2 -2
- package/dist/search/embed-helper.d.ts +1 -1
- package/dist/search/factory.d.ts +1 -1
- package/dist/search/factory.js +6 -6
- package/dist/search/index.d.ts +1 -1
- package/dist/search/index.js +6 -6
- package/dist/search/lancedb-backend.d.ts +1 -1
- package/dist/search/lancedb-backend.js +3 -3
- package/dist/search/meilisearch-backend.d.ts +1 -1
- package/dist/search/meilisearch-backend.js +3 -3
- package/dist/search/noop-backend.d.ts +1 -1
- package/dist/search/orama-backend.d.ts +1 -1
- package/dist/search/orama-backend.js +3 -3
- package/dist/search/port.d.ts +1 -1
- package/dist/search/remote-backend.d.ts +1 -1
- package/dist/{semantic-consolidation-Dof1865o.d.ts → semantic-consolidation-O5lMZ4LG.d.ts} +1 -1
- package/dist/semantic-consolidation.d.ts +2 -2
- package/dist/semantic-consolidation.js +5 -5
- package/dist/semantic-rule-promotion.js +4 -4
- package/dist/semantic-rule-verifier.d.ts +1 -1
- package/dist/semantic-rule-verifier.js +4 -4
- package/dist/session-observer-bands.d.ts +1 -1
- package/dist/session-observer-state.d.ts +1 -1
- package/dist/shared-context/manager.d.ts +1 -1
- package/dist/signal.d.ts +1 -1
- package/dist/storage-C1maCNbc.d.ts +1591 -0
- package/dist/storage.d.ts +5 -1239
- package/dist/storage.js +3 -3
- 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-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 +12 -12
- package/dist/trust-score-stage.d.ts +1 -1
- package/dist/trust-score.d.ts +1 -1
- package/dist/{types-C7HUgOuA.d.ts → types-CGgcNWCG.d.ts} +31 -1
- package/dist/types.d.ts +1 -1
- package/dist/utility-runtime.d.ts +1 -1
- package/dist/verified-recall.js +4 -4
- package/package.json +2 -2
- package/src/cli-wearables-fuse.test.ts +197 -0
- package/src/cli.ts +25 -0
- package/src/storage.ts +16 -0
- package/src/wearables/cli.test.ts +102 -0
- package/src/wearables/cli.ts +65 -0
- package/src/wearables/config.test.ts +32 -0
- package/src/wearables/config.ts +52 -0
- package/src/wearables/day-store.test.ts +246 -0
- package/src/wearables/day-store.ts +272 -3
- package/src/wearables/fusion/cluster.ts +230 -0
- package/src/wearables/fusion/fuse.ts +218 -0
- package/src/wearables/fusion/fusion.test.ts +2575 -0
- package/src/wearables/fusion/index.ts +50 -0
- package/src/wearables/fusion/reconcile.ts +831 -0
- package/src/wearables/fusion/reconstruct.ts +251 -0
- package/src/wearables/fusion/store.ts +480 -0
- package/src/wearables/fusion/types.ts +228 -0
- package/src/wearables/index.ts +41 -0
- package/src/wearables/service.test.ts +1030 -2
- package/src/wearables/service.ts +258 -16
- package/src/wearables/speakers.test.ts +128 -0
- package/src/wearables/speakers.ts +58 -4
- package/src/wearables/storage-io.test.ts +146 -0
- package/src/wearables/types.ts +31 -0
- package/dist/chunk-2B7DP7AP.js.map +0 -1
- package/dist/chunk-M7XQSUBB.js.map +0 -1
- package/dist/chunk-UDCDGP6A.js.map +0 -1
- /package/dist/{auto-sync-G6IU7L6C.js.map → auto-sync-4A5YBWYO.js.map} +0 -0
- /package/dist/{chunk-H5PFR5ZT.js.map → chunk-23JFRB73.js.map} +0 -0
- /package/dist/{chunk-BGMSRN6I.js.map → chunk-3XIY7MDQ.js.map} +0 -0
- /package/dist/{chunk-L2QJQSHL.js.map → chunk-4GNEDXDI.js.map} +0 -0
- /package/dist/{chunk-MFDJ63U5.js.map → chunk-4LV23CHK.js.map} +0 -0
- /package/dist/{chunk-AER6MT24.js.map → chunk-6SXVCD7W.js.map} +0 -0
- /package/dist/{chunk-2SFEFR5I.js.map → chunk-72WG5QKN.js.map} +0 -0
- /package/dist/{chunk-7BAJAXGB.js.map → chunk-7MW3CVLD.js.map} +0 -0
- /package/dist/{chunk-SNNIUDRS.js.map → chunk-AYZJID4S.js.map} +0 -0
- /package/dist/{chunk-LNHJ32NK.js.map → chunk-BEM4D2QG.js.map} +0 -0
- /package/dist/{chunk-AOYJVCFN.js.map → chunk-CYWA2CXR.js.map} +0 -0
- /package/dist/{chunk-2JFDHI2K.js.map → chunk-DEG5ULFJ.js.map} +0 -0
- /package/dist/{chunk-L6V3WJ55.js.map → chunk-DS7MM2ES.js.map} +0 -0
- /package/dist/{chunk-TI7IHDBQ.js.map → chunk-H2DXDQCQ.js.map} +0 -0
- /package/dist/{chunk-KMF67QDQ.js.map → chunk-HLN7RROI.js.map} +0 -0
- /package/dist/{chunk-D2XYYU5Q.js.map → chunk-J3XHPFEU.js.map} +0 -0
- /package/dist/{chunk-ODSYJXDU.js.map → chunk-JSNVWNGX.js.map} +0 -0
- /package/dist/{chunk-4WNLSSHL.js.map → chunk-LADFWYHY.js.map} +0 -0
- /package/dist/{chunk-UBGAOYBP.js.map → chunk-LR5DZ56F.js.map} +0 -0
- /package/dist/{chunk-5K5VEDFY.js.map → chunk-N3JDIRBW.js.map} +0 -0
- /package/dist/{chunk-VWEWK2BM.js.map → chunk-PCLIWBGD.js.map} +0 -0
- /package/dist/{chunk-TV7C3HCB.js.map → chunk-PNLFN2PZ.js.map} +0 -0
- /package/dist/{chunk-STCCFQU5.js.map → chunk-PVUJ6WVV.js.map} +0 -0
- /package/dist/{chunk-ITWTKT2R.js.map → chunk-RGK27ELN.js.map} +0 -0
- /package/dist/{chunk-II6WHCCW.js.map → chunk-SIRPLF54.js.map} +0 -0
- /package/dist/{chunk-NFTABV56.js.map → chunk-TLUDFPZL.js.map} +0 -0
- /package/dist/{chunk-I74ZEBMA.js.map → chunk-UFUKHGZD.js.map} +0 -0
- /package/dist/{chunk-L4UZBJNM.js.map → chunk-UTRJFDP2.js.map} +0 -0
- /package/dist/{chunk-5GPPACXK.js.map → chunk-VSMRDBFT.js.map} +0 -0
- /package/dist/{chunk-SNBEVJBX.js.map → chunk-XCL57M6Q.js.map} +0 -0
- /package/dist/{chunk-DZSAXE3T.js.map → chunk-Y7UNNAZD.js.map} +0 -0
- /package/dist/{chunk-XN3TUUX2.js.map → chunk-YQB32INI.js.map} +0 -0
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wearable cross-source fusion — day-level orchestrator.
|
|
3
|
+
*
|
|
4
|
+
* Takes every enabled source's normalized conversations for one day,
|
|
5
|
+
* clusters them across sources, reconciles each cluster, and attaches a
|
|
6
|
+
* stable content-hash id so re-running over unchanged inputs produces a
|
|
7
|
+
* byte-identical artifact (idempotent — no duplicate files).
|
|
8
|
+
*
|
|
9
|
+
* Deterministic end to end: no timestamps from "now", no LLM, no
|
|
10
|
+
* randomness. The only time-derived value is the `fusedAt` frontmatter
|
|
11
|
+
* field added at storage time (excluded from the content hash).
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { createHash } from "node:crypto";
|
|
15
|
+
import { clusterConversations } from "./cluster.js";
|
|
16
|
+
import {
|
|
17
|
+
DEFAULT_SOURCE_TRUST,
|
|
18
|
+
DEFAULT_WINDOW_TOLERANCE_MS,
|
|
19
|
+
fuseCluster,
|
|
20
|
+
type FuseClusterResult,
|
|
21
|
+
} from "./reconcile.js";
|
|
22
|
+
import { DEFAULT_PROXIMITY_GAP_MS } from "./cluster.js";
|
|
23
|
+
import type {
|
|
24
|
+
FusionConversationInput,
|
|
25
|
+
FusionDayResult,
|
|
26
|
+
FusionOptions,
|
|
27
|
+
FusedConversationProvenance,
|
|
28
|
+
FusedWearableConversation,
|
|
29
|
+
} from "./types.js";
|
|
30
|
+
|
|
31
|
+
const FUSION_ID_PREFIX = "fusion";
|
|
32
|
+
/**
|
|
33
|
+
* Fusion algorithm/schema version. Folded into the day content hash (the
|
|
34
|
+
* idempotency key) so a change to the clustering, reconciliation, or
|
|
35
|
+
* reconstruction algorithm invalidates cached artifacts even when the raw
|
|
36
|
+
* inputs and fusion config are byte-identical — pre-existing `_fusion/`
|
|
37
|
+
* files are regenerated rather than served stale. Bump this whenever the
|
|
38
|
+
* fusion algorithm meaningfully changes (this PR changed it repeatedly).
|
|
39
|
+
* The per-conversation id (`canonicalInputsKey`) is intentionally NOT
|
|
40
|
+
* folded with the version, so a conversation keeps a stable identity.
|
|
41
|
+
*/
|
|
42
|
+
export const FUSION_ALGO_VERSION = "2026-07-12-1";
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Canonical, stable serialization of a set of fusion inputs — the base
|
|
46
|
+
* for the per-conversation id. Input-only so a conversation keeps a
|
|
47
|
+
* stable identity across re-runs regardless of input order. The day
|
|
48
|
+
* content hash additionally folds the effective fusion config (see
|
|
49
|
+
* `canonicalDayKey`) so a config change invalidates the cached artifact.
|
|
50
|
+
*/
|
|
51
|
+
function canonicalInputsKey(
|
|
52
|
+
date: string,
|
|
53
|
+
inputs: readonly FusionConversationInput[],
|
|
54
|
+
): string {
|
|
55
|
+
const sorted = [...inputs].sort((a, b) => {
|
|
56
|
+
if (a.source !== b.source) return a.source < b.source ? -1 : 1;
|
|
57
|
+
if (a.conversationId !== b.conversationId) {
|
|
58
|
+
return a.conversationId < b.conversationId ? -1 : 1;
|
|
59
|
+
}
|
|
60
|
+
return 0;
|
|
61
|
+
});
|
|
62
|
+
// Serialize the structured tuples UNAMBIGUOUSLY before hashing. The old
|
|
63
|
+
// `|`/newline-delimited form could collide: a speaker label or segment
|
|
64
|
+
// text containing `|` (or a newline plus a forged `seg|…`/`conv|…`
|
|
65
|
+
// prefix) could make two DISTINCT inputs serialize to identical bytes,
|
|
66
|
+
// hashing alike — so fuseDay wrongly took the idempotent-skip path and
|
|
67
|
+
// kept serving a stale artifact. JSON string quoting makes every field
|
|
68
|
+
// boundary unforgeable, and the positional array order is the
|
|
69
|
+
// deterministic key order (no object keys to sort).
|
|
70
|
+
const record = [
|
|
71
|
+
date,
|
|
72
|
+
sorted.map((conv) => [
|
|
73
|
+
conv.source,
|
|
74
|
+
conv.conversationId,
|
|
75
|
+
conv.startIso,
|
|
76
|
+
conv.endIso ?? "",
|
|
77
|
+
conv.title ?? "",
|
|
78
|
+
conv.summary ?? "",
|
|
79
|
+
conv.segments.map((segment) => [
|
|
80
|
+
segment.speaker,
|
|
81
|
+
segment.isSelf ? "self" : "other",
|
|
82
|
+
segment.startIso ?? "",
|
|
83
|
+
segment.endIso ?? "",
|
|
84
|
+
segment.text,
|
|
85
|
+
]),
|
|
86
|
+
]),
|
|
87
|
+
];
|
|
88
|
+
return createHash("sha256").update(JSON.stringify(record), "utf-8").digest("hex");
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Effective-config fingerprint folded into the day content hash so a
|
|
93
|
+
* change to any clustering/reconciliation knob (proximity gap, window
|
|
94
|
+
* tolerance, or per-source trust) invalidates the cached artifact.
|
|
95
|
+
* Defaults are resolved so an explicit default hashes identically to an
|
|
96
|
+
* omission, keeping the skip-unchanged path deterministic. Only sources
|
|
97
|
+
* that actually contribute to the day are included, so a trust change
|
|
98
|
+
* for an absent source cannot spuriously trigger a rebuild.
|
|
99
|
+
*/
|
|
100
|
+
function configFingerprint(
|
|
101
|
+
options: FusionOptions,
|
|
102
|
+
sources: readonly string[],
|
|
103
|
+
): string {
|
|
104
|
+
const proximityGapMs = options.proximityGapMs ?? DEFAULT_PROXIMITY_GAP_MS;
|
|
105
|
+
const windowToleranceMs =
|
|
106
|
+
options.windowToleranceMs ?? DEFAULT_WINDOW_TOLERANCE_MS;
|
|
107
|
+
const trustMap = options.sourceTrust ?? {};
|
|
108
|
+
// JSON form so a source id (or future field) containing `|` cannot forge
|
|
109
|
+
// a trust-entry boundary and collide two distinct effective configs into
|
|
110
|
+
// one day hash.
|
|
111
|
+
const trustEntries = [...new Set(sources)]
|
|
112
|
+
.sort()
|
|
113
|
+
.map((source) => [source, trustMap[source] ?? DEFAULT_SOURCE_TRUST]);
|
|
114
|
+
return JSON.stringify([proximityGapMs, windowToleranceMs, trustEntries]);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Day idempotency key: the input-only hash combined with the effective
|
|
119
|
+
* fusion-config fingerprint AND the fusion algorithm version. Same inputs +
|
|
120
|
+
* same config + same version => same hash (no duplicate rewrite); a config
|
|
121
|
+
* change OR an algorithm-version bump => new hash => rebuild. The version
|
|
122
|
+
* is what forces regeneration when the ALGORITHM itself changes but the
|
|
123
|
+
* inputs and config are byte-identical (issue #1849).
|
|
124
|
+
*/
|
|
125
|
+
export function canonicalDayKey(
|
|
126
|
+
date: string,
|
|
127
|
+
inputs: readonly FusionConversationInput[],
|
|
128
|
+
options: FusionOptions,
|
|
129
|
+
algoVersion: string = FUSION_ALGO_VERSION,
|
|
130
|
+
): string {
|
|
131
|
+
const inputsKey = canonicalInputsKey(date, inputs);
|
|
132
|
+
const fingerprint = configFingerprint(
|
|
133
|
+
options,
|
|
134
|
+
inputs.map((conv) => conv.source),
|
|
135
|
+
);
|
|
136
|
+
return createHash("sha256")
|
|
137
|
+
.update(inputsKey)
|
|
138
|
+
.update("\n")
|
|
139
|
+
.update(fingerprint)
|
|
140
|
+
.update("\n")
|
|
141
|
+
.update(algoVersion)
|
|
142
|
+
.digest("hex");
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function buildProvenance(
|
|
146
|
+
cluster: readonly FusionConversationInput[],
|
|
147
|
+
proximityGapMs: number,
|
|
148
|
+
windowToleranceMs: number,
|
|
149
|
+
): FusedConversationProvenance {
|
|
150
|
+
return {
|
|
151
|
+
contributions: cluster.map((conv) => ({
|
|
152
|
+
source: conv.source,
|
|
153
|
+
conversationId: conv.conversationId,
|
|
154
|
+
startIso: conv.startIso,
|
|
155
|
+
...(conv.endIso !== undefined ? { endIso: conv.endIso } : {}),
|
|
156
|
+
segmentCount: conv.segments.length,
|
|
157
|
+
})),
|
|
158
|
+
proximityGapMs,
|
|
159
|
+
windowToleranceMs,
|
|
160
|
+
method: "time-proximity",
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Fuse every source's conversations for one day into a stable result.
|
|
166
|
+
* Empty input yields an empty (but well-formed) result so callers can
|
|
167
|
+
* treat "nothing to fuse" uniformly.
|
|
168
|
+
*/
|
|
169
|
+
export function fuseDay(
|
|
170
|
+
date: string,
|
|
171
|
+
inputs: readonly FusionConversationInput[],
|
|
172
|
+
options: FusionOptions = {},
|
|
173
|
+
): FusionDayResult {
|
|
174
|
+
const proximityGapMs = options.proximityGapMs ?? DEFAULT_PROXIMITY_GAP_MS;
|
|
175
|
+
const windowToleranceMs =
|
|
176
|
+
options.windowToleranceMs ?? DEFAULT_WINDOW_TOLERANCE_MS;
|
|
177
|
+
|
|
178
|
+
const clusters = clusterConversations(inputs, proximityGapMs);
|
|
179
|
+
const conversations: FusedWearableConversation[] = clusters.map((cluster) => {
|
|
180
|
+
const fused: FuseClusterResult = fuseCluster(cluster, {
|
|
181
|
+
...options,
|
|
182
|
+
windowToleranceMs,
|
|
183
|
+
});
|
|
184
|
+
const id = `${FUSION_ID_PREFIX}-${canonicalInputsKey(date, cluster).slice(0, 24)}`;
|
|
185
|
+
const result: FusedWearableConversation = {
|
|
186
|
+
id,
|
|
187
|
+
date,
|
|
188
|
+
startIso: fused.startIso,
|
|
189
|
+
sources: fused.sources,
|
|
190
|
+
speakers: fused.speakers,
|
|
191
|
+
segments: fused.segments,
|
|
192
|
+
disagreements: fused.disagreements,
|
|
193
|
+
provenance: buildProvenance(cluster, proximityGapMs, windowToleranceMs),
|
|
194
|
+
};
|
|
195
|
+
if (fused.endIso !== undefined) result.endIso = fused.endIso;
|
|
196
|
+
if (fused.title !== undefined) result.title = fused.title;
|
|
197
|
+
if (fused.summary !== undefined) result.summary = fused.summary;
|
|
198
|
+
return result;
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
const sourcesSeen = new Set<string>();
|
|
202
|
+
const sources: string[] = [];
|
|
203
|
+
for (const conv of conversations) {
|
|
204
|
+
for (const source of conv.sources) {
|
|
205
|
+
if (!sourcesSeen.has(source)) {
|
|
206
|
+
sourcesSeen.add(source);
|
|
207
|
+
sources.push(source);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return {
|
|
213
|
+
date,
|
|
214
|
+
conversations,
|
|
215
|
+
sources,
|
|
216
|
+
contentHash: canonicalDayKey(date, inputs, options),
|
|
217
|
+
};
|
|
218
|
+
}
|