@remnic/core 9.3.629 → 9.3.631

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.
Files changed (221) hide show
  1. package/dist/access-cli.js +15 -13
  2. package/dist/access-cli.js.map +1 -1
  3. package/dist/access-http.d.ts +5 -4
  4. package/dist/access-http.js +7 -6
  5. package/dist/access-mcp.d.ts +5 -4
  6. package/dist/access-mcp.js +6 -5
  7. package/dist/{access-service-BdThkfIE.d.ts → access-service-C9_EpVHd.d.ts} +2 -2
  8. package/dist/access-service.d.ts +5 -4
  9. package/dist/access-service.js +5 -4
  10. package/dist/action-confidence.d.ts +1 -1
  11. package/dist/active-memory-bridge.d.ts +1 -1
  12. package/dist/active-recall.d.ts +1 -1
  13. package/dist/active-recall.js +1 -1
  14. package/dist/auto-sync-RFADEHIQ.js +75 -0
  15. package/dist/auto-sync-RFADEHIQ.js.map +1 -0
  16. package/dist/behavior-learner.d.ts +1 -1
  17. package/dist/behavior-signals.d.ts +1 -1
  18. package/dist/bootstrap.d.ts +4 -3
  19. package/dist/briefing.d.ts +1 -1
  20. package/dist/briefing.js +3 -2
  21. package/dist/buffer-surprise-report.d.ts +1 -1
  22. package/dist/buffer.d.ts +1 -1
  23. package/dist/calibration.d.ts +1 -1
  24. package/dist/causal-behavior.d.ts +1 -1
  25. package/dist/causal-consolidation.d.ts +1 -1
  26. package/dist/causal-consolidation.js +4 -3
  27. package/dist/causal-consolidation.js.map +1 -1
  28. package/dist/{chunk-532VCWYW.js → chunk-242XFZ36.js} +2 -2
  29. package/dist/{chunk-XXO5TI3B.js → chunk-32U3N7H5.js} +3 -3
  30. package/dist/{chunk-KB4MFBF5.js → chunk-3RDYU3JS.js} +3 -3
  31. package/dist/{chunk-57QXN2CS.js → chunk-4S3N6HFG.js} +2 -2
  32. package/dist/{chunk-OLNNOHBC.js → chunk-5PT5I6JQ.js} +20 -14
  33. package/dist/{chunk-OLNNOHBC.js.map → chunk-5PT5I6JQ.js.map} +1 -1
  34. package/dist/{chunk-GE7Q7KXP.js → chunk-7A2QKUUA.js} +2 -2
  35. package/dist/{chunk-KKTXCFD7.js → chunk-7H5WCPBS.js} +95 -11
  36. package/dist/{chunk-KKTXCFD7.js.map → chunk-7H5WCPBS.js.map} +1 -1
  37. package/dist/{chunk-3MNBW7R7.js → chunk-C4KKM62E.js} +2 -2
  38. package/dist/{chunk-NKCW223V.js → chunk-CMN5AWAZ.js} +2 -2
  39. package/dist/{chunk-JXHMAQYT.js → chunk-DOBJH4I6.js} +4 -4
  40. package/dist/{chunk-TZDSNIRO.js → chunk-IFVFQRZ2.js} +5 -5
  41. package/dist/{chunk-LQYTQCXM.js → chunk-JCLECECB.js} +2 -2
  42. package/dist/chunk-KVDUDYEN.js +1164 -0
  43. package/dist/chunk-KVDUDYEN.js.map +1 -0
  44. package/dist/{chunk-QDV6VAD4.js → chunk-LEG7XWS2.js} +2 -2
  45. package/dist/chunk-M7XQSUBB.js +280 -0
  46. package/dist/chunk-M7XQSUBB.js.map +1 -0
  47. package/dist/{chunk-N5RGXWLQ.js → chunk-PUEAEQSN.js} +2 -2
  48. package/dist/{chunk-UGHUNQ74.js → chunk-QYGIQ5NM.js} +212 -417
  49. package/dist/chunk-QYGIQ5NM.js.map +1 -0
  50. package/dist/{chunk-JKCDQBDW.js → chunk-UXFOGILU.js} +2 -2
  51. package/dist/{chunk-MVQN73GT.js → chunk-VTR3MNYF.js} +2 -2
  52. package/dist/{chunk-KVFYTRMV.js → chunk-W25I7G6U.js} +2 -2
  53. package/dist/{chunk-3GLCUPXP.js → chunk-WLZBVYC6.js} +192 -889
  54. package/dist/chunk-WLZBVYC6.js.map +1 -0
  55. package/dist/{chunk-3R2UZV3U.js → chunk-X7EJF46S.js} +2 -2
  56. package/dist/{chunk-54KDA6UK.js → chunk-XG4NAWAV.js} +3 -3
  57. package/dist/{chunk-P2D2MM47.js → chunk-YROCXMCK.js} +2 -2
  58. package/dist/{cli-DAsHklrf.d.ts → cli-CuVEQWKr.d.ts} +3 -3
  59. package/dist/cli.d.ts +6 -5
  60. package/dist/cli.js +18 -17
  61. package/dist/compounding/engine.d.ts +1 -1
  62. package/dist/compounding/engine.js +3 -2
  63. package/dist/compounding/preference-consolidator.d.ts +1 -1
  64. package/dist/compression-optimizer.d.ts +1 -1
  65. package/dist/config.d.ts +1 -1
  66. package/dist/config.js +1 -1
  67. package/dist/connectors/codex-materialize-runner.d.ts +1 -1
  68. package/dist/connectors/codex-materialize-runner.js +3 -2
  69. package/dist/connectors/codex-materialize.d.ts +1 -1
  70. package/dist/connectors/index.d.ts +1 -1
  71. package/dist/connectors/index.js +3 -2
  72. package/dist/consolidation-provenance-check.d.ts +1 -1
  73. package/dist/consolidation-undo.d.ts +1 -1
  74. package/dist/contradiction/index.d.ts +1 -1
  75. package/dist/contradiction/index.js +4 -4
  76. package/dist/conversation-index/backend.d.ts +1 -1
  77. package/dist/conversation-index/chunker.d.ts +1 -1
  78. package/dist/conversation-index/faiss-adapter.d.ts +1 -1
  79. package/dist/conversation-index/indexer.d.ts +1 -1
  80. package/dist/conversation-index/search.d.ts +1 -1
  81. package/dist/day-summary.d.ts +1 -1
  82. package/dist/delinearize.d.ts +1 -1
  83. package/dist/direct-answer-wiring.d.ts +1 -1
  84. package/dist/direct-answer.d.ts +1 -1
  85. package/dist/embedding-fallback.d.ts +1 -1
  86. package/dist/enrichment/index.d.ts +1 -1
  87. package/dist/entity-retrieval.d.ts +1 -1
  88. package/dist/entity-retrieval.js +3 -2
  89. package/dist/entity-schema.d.ts +1 -1
  90. package/dist/explicit-capture.d.ts +4 -3
  91. package/dist/extraction-judge-telemetry.d.ts +1 -1
  92. package/dist/extraction-judge-training.d.ts +1 -1
  93. package/dist/extraction-judge.d.ts +1 -1
  94. package/dist/extraction.d.ts +1 -1
  95. package/dist/fallback-llm.d.ts +1 -1
  96. package/dist/identity-continuity.d.ts +1 -1
  97. package/dist/importance.d.ts +1 -1
  98. package/dist/index.d.ts +8 -8
  99. package/dist/index.js +49 -45
  100. package/dist/index.js.map +1 -1
  101. package/dist/intent.d.ts +1 -1
  102. package/dist/lcm/engine.d.ts +1 -1
  103. package/dist/lcm/index.d.ts +1 -1
  104. package/dist/lcm/tools.d.ts +1 -1
  105. package/dist/lifecycle.d.ts +1 -1
  106. package/dist/live-connectors-runner.d.ts +1 -1
  107. package/dist/local-llm.d.ts +1 -1
  108. package/dist/maintenance/memory-governance.d.ts +1 -1
  109. package/dist/maintenance/memory-governance.js +3 -2
  110. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +3 -2
  111. package/dist/maintenance/rebuild-memory-projection.js +4 -3
  112. package/dist/mcp-memory-inspector-app.d.ts +5 -4
  113. package/dist/memory-action-policy.d.ts +1 -1
  114. package/dist/memory-cache.d.ts +1 -1
  115. package/dist/memory-lifecycle-ledger-utils.d.ts +1 -1
  116. package/dist/memory-projection-store.d.ts +1 -1
  117. package/dist/memory-provenance.d.ts +1 -1
  118. package/dist/memory-worth-outcomes.d.ts +1 -1
  119. package/dist/models-json.d.ts +1 -1
  120. package/dist/namespaces/migrate.d.ts +1 -1
  121. package/dist/namespaces/migrate.js +4 -3
  122. package/dist/namespaces/principal.d.ts +1 -1
  123. package/dist/namespaces/search.d.ts +1 -1
  124. package/dist/namespaces/storage.d.ts +1 -1
  125. package/dist/namespaces/storage.js +3 -2
  126. package/dist/native-knowledge.d.ts +1 -1
  127. package/dist/operator-toolkit.d.ts +1 -1
  128. package/dist/operator-toolkit.js +7 -6
  129. package/dist/{orchestrator-BexeSJ2j.d.ts → orchestrator-CoqytbK_.d.ts} +102 -10
  130. package/dist/orchestrator.d.ts +4 -3
  131. package/dist/orchestrator.js +12 -10
  132. package/dist/patterns-cli.d.ts +1 -1
  133. package/dist/policy-runtime.d.ts +1 -1
  134. package/dist/qmd-recall-cache.d.ts +1 -1
  135. package/dist/qmd.d.ts +1 -1
  136. package/dist/recall-disclosure-escalation.d.ts +1 -1
  137. package/dist/recall-explain-renderer.d.ts +1 -1
  138. package/dist/recall-planner-llm.d.ts +1 -1
  139. package/dist/recall-state.d.ts +1 -1
  140. package/dist/recall-tag-filter.d.ts +1 -1
  141. package/dist/recall-xray-cli.d.ts +1 -1
  142. package/dist/recall-xray-renderer.d.ts +1 -1
  143. package/dist/recall-xray.d.ts +1 -1
  144. package/dist/resolve-auth-token.d.ts +1 -1
  145. package/dist/resume-bundles.js +2 -2
  146. package/dist/retrieval-agents.d.ts +1 -1
  147. package/dist/retrieval-tiers.d.ts +1 -1
  148. package/dist/routing/engine.d.ts +1 -1
  149. package/dist/routing/store.d.ts +1 -1
  150. package/dist/search/embed-helper.d.ts +1 -1
  151. package/dist/search/factory.d.ts +1 -1
  152. package/dist/search/index.d.ts +1 -1
  153. package/dist/search/lancedb-backend.d.ts +1 -1
  154. package/dist/search/meilisearch-backend.d.ts +1 -1
  155. package/dist/search/noop-backend.d.ts +1 -1
  156. package/dist/search/orama-backend.d.ts +1 -1
  157. package/dist/search/port.d.ts +1 -1
  158. package/dist/search/remote-backend.d.ts +1 -1
  159. package/dist/{semantic-consolidation-PwkzNfdK.d.ts → semantic-consolidation-BPs6BURk.d.ts} +1 -1
  160. package/dist/semantic-consolidation.d.ts +2 -2
  161. package/dist/semantic-consolidation.js +4 -3
  162. package/dist/semantic-rule-promotion.js +3 -2
  163. package/dist/semantic-rule-verifier.d.ts +1 -1
  164. package/dist/semantic-rule-verifier.js +3 -2
  165. package/dist/session-observer-bands.d.ts +1 -1
  166. package/dist/session-observer-state.d.ts +1 -1
  167. package/dist/shared-context/manager.d.ts +1 -1
  168. package/dist/signal.d.ts +1 -1
  169. package/dist/storage.d.ts +38 -2
  170. package/dist/storage.js +6 -3
  171. package/dist/summarizer.d.ts +1 -1
  172. package/dist/summary-snapshot.d.ts +1 -1
  173. package/dist/temporal-supersession.d.ts +1 -1
  174. package/dist/temporal-validity.d.ts +1 -1
  175. package/dist/threading.d.ts +1 -1
  176. package/dist/tier-migration.d.ts +1 -1
  177. package/dist/tier-routing.d.ts +1 -1
  178. package/dist/topics.d.ts +1 -1
  179. package/dist/transcript.d.ts +1 -1
  180. package/dist/{types-BCF2wqKa.d.ts → types-CpMPD8xl.d.ts} +59 -11
  181. package/dist/types.d.ts +1 -1
  182. package/dist/utility-runtime.d.ts +1 -1
  183. package/dist/verified-recall.js +3 -2
  184. package/package.json +1 -1
  185. package/src/orchestrator.ts +74 -0
  186. package/src/storage.ts +100 -0
  187. package/src/wearables/auto-sync.test.ts +181 -0
  188. package/src/wearables/auto-sync.ts +129 -0
  189. package/src/wearables/cli.ts +6 -0
  190. package/src/wearables/config.test.ts +90 -11
  191. package/src/wearables/config.ts +113 -11
  192. package/src/wearables/memory-gen.test.ts +416 -1
  193. package/src/wearables/memory-gen.ts +381 -23
  194. package/src/wearables/pipeline.test.ts +396 -5
  195. package/src/wearables/pipeline.ts +174 -22
  196. package/src/wearables/service.test.ts +172 -0
  197. package/src/wearables/service.ts +84 -3
  198. package/src/wearables/storage-io.test.ts +81 -0
  199. package/src/wearables/trust.test.ts +123 -0
  200. package/src/wearables/trust.ts +168 -0
  201. package/src/wearables/types.ts +57 -9
  202. package/dist/chunk-3GLCUPXP.js.map +0 -1
  203. package/dist/chunk-UGHUNQ74.js.map +0 -1
  204. /package/dist/{chunk-532VCWYW.js.map → chunk-242XFZ36.js.map} +0 -0
  205. /package/dist/{chunk-XXO5TI3B.js.map → chunk-32U3N7H5.js.map} +0 -0
  206. /package/dist/{chunk-KB4MFBF5.js.map → chunk-3RDYU3JS.js.map} +0 -0
  207. /package/dist/{chunk-57QXN2CS.js.map → chunk-4S3N6HFG.js.map} +0 -0
  208. /package/dist/{chunk-GE7Q7KXP.js.map → chunk-7A2QKUUA.js.map} +0 -0
  209. /package/dist/{chunk-3MNBW7R7.js.map → chunk-C4KKM62E.js.map} +0 -0
  210. /package/dist/{chunk-NKCW223V.js.map → chunk-CMN5AWAZ.js.map} +0 -0
  211. /package/dist/{chunk-JXHMAQYT.js.map → chunk-DOBJH4I6.js.map} +0 -0
  212. /package/dist/{chunk-TZDSNIRO.js.map → chunk-IFVFQRZ2.js.map} +0 -0
  213. /package/dist/{chunk-LQYTQCXM.js.map → chunk-JCLECECB.js.map} +0 -0
  214. /package/dist/{chunk-QDV6VAD4.js.map → chunk-LEG7XWS2.js.map} +0 -0
  215. /package/dist/{chunk-N5RGXWLQ.js.map → chunk-PUEAEQSN.js.map} +0 -0
  216. /package/dist/{chunk-JKCDQBDW.js.map → chunk-UXFOGILU.js.map} +0 -0
  217. /package/dist/{chunk-MVQN73GT.js.map → chunk-VTR3MNYF.js.map} +0 -0
  218. /package/dist/{chunk-KVFYTRMV.js.map → chunk-W25I7G6U.js.map} +0 -0
  219. /package/dist/{chunk-3R2UZV3U.js.map → chunk-X7EJF46S.js.map} +0 -0
  220. /package/dist/{chunk-54KDA6UK.js.map → chunk-XG4NAWAV.js.map} +0 -0
  221. /package/dist/{chunk-P2D2MM47.js.map → chunk-YROCXMCK.js.map} +0 -0
@@ -90,6 +90,87 @@ test("transcript files never surface from readAllMemories", async () => {
90
90
  }
91
91
  });
92
92
 
93
+ test("promoteWearableMemory flips status, merges evidence, and updates confidence", async () => {
94
+ const { storage, dir } = makeStorage();
95
+ try {
96
+ const id = await storage.writeMemory("fact", "Launch moved to September twelfth.", {
97
+ confidence: 0.6,
98
+ source: "wearable:limitless",
99
+ status: "pending_review",
100
+ structuredAttributes: { wearableSource: "limitless", trustScore: "0.600" },
101
+ });
102
+ const promoted = await storage.promoteWearableMemory(
103
+ id,
104
+ { trustScore: "0.750", trustDecision: "promoted-by-corroboration" },
105
+ 0.75,
106
+ );
107
+ assert.equal(promoted, true);
108
+ const memory = (await storage.readAllMemories()).find(
109
+ (entry) => entry.frontmatter.id === id,
110
+ );
111
+ assert.ok(memory);
112
+ assert.equal(memory.frontmatter.status, "active");
113
+ assert.equal(memory.frontmatter.confidence, 0.75);
114
+ assert.equal(memory.frontmatter.structuredAttributes?.trustScore, "0.750");
115
+ assert.equal(
116
+ memory.frontmatter.structuredAttributes?.trustDecision,
117
+ "promoted-by-corroboration",
118
+ );
119
+
120
+ // Already-active rows are not re-promoted (operator decisions win).
121
+ assert.equal(await storage.promoteWearableMemory(id, {}, 0.9), false);
122
+ assert.equal(await storage.promoteWearableMemory("missing-id", {}), false);
123
+
124
+ const found = await storage.findWearableMemoryByContent(
125
+ "Launch moved to September twelfth.",
126
+ );
127
+ assert.equal(found?.id, id);
128
+ } finally {
129
+ rmSync(dir, { recursive: true, force: true });
130
+ }
131
+ });
132
+
133
+ test("demoteWearableMemory rejects only pending rows and merges evidence", async () => {
134
+ const { storage, dir } = makeStorage();
135
+ try {
136
+ const id = await storage.writeMemory("fact", "Vendor call moved the launch again.", {
137
+ confidence: 0.5,
138
+ source: "wearable:limitless",
139
+ status: "pending_review",
140
+ structuredAttributes: { wearableSource: "limitless", trustScore: "0.500" },
141
+ });
142
+ const demoted = await storage.demoteWearableMemory(id, {
143
+ trustScore: "0.310",
144
+ trustDecision: "demoted-by-rejection",
145
+ judgeVerdict: "reject",
146
+ });
147
+ assert.equal(demoted, true);
148
+ const memory = (await storage.readAllMemories()).find(
149
+ (entry) => entry.frontmatter.id === id,
150
+ );
151
+ assert.ok(memory);
152
+ assert.equal(memory.frontmatter.status, "rejected");
153
+ assert.equal(memory.frontmatter.structuredAttributes?.trustDecision, "demoted-by-rejection");
154
+ assert.equal(memory.frontmatter.structuredAttributes?.wearableSource, "limitless");
155
+
156
+ // Rejected rows are terminal for the wearable pipeline: no
157
+ // re-demote, no promote (operator surfaces own them from here).
158
+ assert.equal(await storage.demoteWearableMemory(id, {}), false);
159
+ assert.equal(await storage.promoteWearableMemory(id, {}, 0.9), false);
160
+ assert.equal(await storage.demoteWearableMemory("missing-id", {}), false);
161
+
162
+ // Active rows are never auto-demoted.
163
+ const activeId = await storage.writeMemory("fact", "Approved active row.", {
164
+ confidence: 0.9,
165
+ source: "wearable:limitless",
166
+ status: "active",
167
+ });
168
+ assert.equal(await storage.demoteWearableMemory(activeId, {}), false);
169
+ } finally {
170
+ rmSync(dir, { recursive: true, force: true });
171
+ }
172
+ });
173
+
93
174
  test("non-transcript files in the wearables tree are ignored by listing", async () => {
94
175
  const { storage, dir } = makeStorage();
95
176
  try {
@@ -0,0 +1,123 @@
1
+ import assert from "node:assert/strict";
2
+ import { test } from "node:test";
3
+
4
+ import {
5
+ computeTrustScore,
6
+ decideSmart,
7
+ findCorroboration,
8
+ tokenizeDayBody,
9
+ TRUST_CROSS_SOURCE_BOOST,
10
+ TRUST_JUDGE_ACCEPT_BOOST,
11
+ TRUST_SUPPORTING_MEMORY_BOOST,
12
+ } from "./trust.js";
13
+
14
+ const NO_EVIDENCE = { corroboratedBySources: [] };
15
+
16
+ test("trust = confidence x sourceTrust, clamped to [0,1]", () => {
17
+ assert.ok(
18
+ Math.abs(
19
+ computeTrustScore({ extractionConfidence: 0.9, sourceTrust: 0.8, evidence: NO_EVIDENCE }) -
20
+ 0.72,
21
+ ) < 1e-9,
22
+ );
23
+ // Missing confidence defaults to 0.7.
24
+ assert.equal(
25
+ computeTrustScore({ extractionConfidence: undefined, sourceTrust: 1, evidence: NO_EVIDENCE }),
26
+ 0.7,
27
+ );
28
+ // Boosts never push past 1.
29
+ assert.equal(
30
+ computeTrustScore({
31
+ extractionConfidence: 1,
32
+ sourceTrust: 1,
33
+ judgeVerdict: "accept",
34
+ evidence: { corroboratedBySources: ["bee"], supportingMemoryId: "m1" },
35
+ }),
36
+ 1,
37
+ );
38
+ });
39
+
40
+ test("judge accept and corroboration boosts stack as documented", () => {
41
+ const base = computeTrustScore({
42
+ extractionConfidence: 0.5,
43
+ sourceTrust: 0.8,
44
+ evidence: NO_EVIDENCE,
45
+ });
46
+ const judged = computeTrustScore({
47
+ extractionConfidence: 0.5,
48
+ sourceTrust: 0.8,
49
+ judgeVerdict: "accept",
50
+ evidence: NO_EVIDENCE,
51
+ });
52
+ const corroborated = computeTrustScore({
53
+ extractionConfidence: 0.5,
54
+ sourceTrust: 0.8,
55
+ judgeVerdict: "accept",
56
+ evidence: { corroboratedBySources: ["omi"], supportingMemoryId: "m1" },
57
+ });
58
+ assert.ok(Math.abs(judged - base - TRUST_JUDGE_ACCEPT_BOOST) < 1e-9);
59
+ assert.ok(
60
+ Math.abs(
61
+ corroborated -
62
+ judged -
63
+ TRUST_CROSS_SOURCE_BOOST -
64
+ TRUST_SUPPORTING_MEMORY_BOOST,
65
+ ) < 1e-9,
66
+ );
67
+ });
68
+
69
+ test("decideSmart: judge verdicts short-circuit; trust bands otherwise", () => {
70
+ const thresholds = { autoApproveTrust: 0.7, reviewTrust: 0.45 };
71
+ assert.equal(decideSmart(0.99, "reject", thresholds).outcome, "drop");
72
+ assert.equal(decideSmart(0.99, "defer", thresholds).outcome, "review");
73
+ assert.equal(decideSmart(0.7, "accept", thresholds).outcome, "active");
74
+ assert.equal(decideSmart(0.7, undefined, thresholds).outcome, "active");
75
+ assert.equal(decideSmart(0.5, undefined, thresholds).outcome, "review");
76
+ assert.equal(decideSmart(0.2, undefined, thresholds).outcome, "drop");
77
+ });
78
+
79
+ test("cross-source corroboration requires high token coverage", () => {
80
+ const beeDay = tokenizeDayBody(
81
+ "We agreed the product launch moves to September twelfth after the vendor call.",
82
+ );
83
+ const context = {
84
+ otherSourceDayTokens: new Map([["bee", beeDay]]),
85
+ existingMemories: [],
86
+ };
87
+ const corroborated = findCorroboration(
88
+ "Launch moved to September twelfth after vendor call.",
89
+ context,
90
+ );
91
+ assert.deepEqual(corroborated.corroboratedBySources, ["bee"]);
92
+
93
+ const unrelated = findCorroboration(
94
+ "Dentist appointment is on Thursday afternoon downtown.",
95
+ context,
96
+ );
97
+ assert.deepEqual(unrelated.corroboratedBySources, []);
98
+ });
99
+
100
+ test("existing-memory support sets supportingMemoryId", () => {
101
+ const context = {
102
+ otherSourceDayTokens: new Map<string, Set<string>>(),
103
+ existingMemories: [
104
+ { id: "fact-1", content: "User prefers the aisle seat on long flights." },
105
+ { id: "fact-2", content: "The launch moved to September twelfth, vendor informed." },
106
+ ],
107
+ };
108
+ const supported = findCorroboration(
109
+ "Launch moved to September twelfth and the vendor was informed.",
110
+ context,
111
+ );
112
+ assert.equal(supported.supportingMemoryId, "fact-2");
113
+ });
114
+
115
+ test("very short facts never corroborate (too little signal)", () => {
116
+ const context = {
117
+ otherSourceDayTokens: new Map([["bee", tokenizeDayBody("yes ok sure fine")]]),
118
+ existingMemories: [{ id: "m", content: "yes ok sure fine" }],
119
+ };
120
+ const evidence = findCorroboration("yes ok", context);
121
+ assert.deepEqual(evidence.corroboratedBySources, []);
122
+ assert.equal(evidence.supportingMemoryId, undefined);
123
+ });
@@ -0,0 +1,168 @@
1
+ /**
2
+ * Wearable memory trust scoring — the "smart" memoryMode engine.
3
+ *
4
+ * Grounded in how production memory systems handle noisy ingest:
5
+ *
6
+ * - **LLM-as-judge gating** (Remnic's own extraction judge,
7
+ * `judgeFactDurability`): an LLM verdict on durability decides
8
+ * accept / defer / reject before anything persists.
9
+ * - **Provenance priors**: each source carries a configurable trust
10
+ * prior (`sourceTrust`) — a noisy ASR channel contributes less
11
+ * confidence than a clean one (provenance-weighted fusion).
12
+ * - **Corroboration**: independent agreement raises trust. A fact
13
+ * supported by a second wearable that recorded the same day, or by
14
+ * an existing active memory, is far less likely to be an ASR
15
+ * artifact (ensemble agreement / self-consistency).
16
+ *
17
+ * The combined score maps to a three-way decision:
18
+ *
19
+ * trust >= autoApproveTrust -> written ACTIVE
20
+ * trust >= reviewTrust -> written pending_review
21
+ * below -> dropped
22
+ *
23
+ * with the judge able to short-circuit (reject -> drop,
24
+ * defer -> pending_review regardless of score). The score, verdict,
25
+ * and corroboration evidence persist on the memory (confidence +
26
+ * structuredAttributes + verificationState), so Remnic's existing
27
+ * trust machinery — memory-worth outcome counters, pattern
28
+ * reinforcement, temporal supersession, contradiction scans — keeps
29
+ * calibrating these memories after they land.
30
+ */
31
+
32
+ import {
33
+ countRecallTokenOverlap,
34
+ normalizeRecallTokens,
35
+ } from "../recall-tokenization.js";
36
+
37
+ /** Judge adjustments and corroboration boosts (documented, fixed). */
38
+ export const TRUST_JUDGE_ACCEPT_BOOST = 0.15;
39
+ export const TRUST_CROSS_SOURCE_BOOST = 0.15;
40
+ export const TRUST_SUPPORTING_MEMORY_BOOST = 0.1;
41
+
42
+ /** Minimum distinct tokens before similarity is meaningful. */
43
+ const MIN_FACT_TOKENS = 4;
44
+ /** Fact-token coverage required to call a day-text corroborating. */
45
+ const CROSS_SOURCE_COVERAGE = 0.6;
46
+ /** Fact-token coverage required to call an existing memory supporting. */
47
+ const MEMORY_SUPPORT_COVERAGE = 0.7;
48
+ /** Bound on existing memories scanned per fact batch. */
49
+ const MAX_MEMORIES_SCANNED = 5_000;
50
+
51
+ export interface TrustEvidence {
52
+ /** Other-source ids whose same-day content corroborates the fact. */
53
+ corroboratedBySources: string[];
54
+ /** Id of an existing active memory whose content supports the fact. */
55
+ supportingMemoryId?: string;
56
+ }
57
+
58
+ export interface TrustScoreInput {
59
+ /** Extraction confidence for the fact (defaults to 0.7 when absent). */
60
+ extractionConfidence: number | undefined;
61
+ /** Per-source trust prior from config (0..1). */
62
+ sourceTrust: number;
63
+ /** Judge verdict kind when a judge ran; undefined when unavailable. */
64
+ judgeVerdict?: "accept" | "reject" | "defer";
65
+ evidence: TrustEvidence;
66
+ }
67
+
68
+ export function computeTrustScore(input: TrustScoreInput): number {
69
+ const confidence =
70
+ typeof input.extractionConfidence === "number" &&
71
+ Number.isFinite(input.extractionConfidence)
72
+ ? Math.min(1, Math.max(0, input.extractionConfidence))
73
+ : 0.7;
74
+ let trust = confidence * Math.min(1, Math.max(0, input.sourceTrust));
75
+ if (input.judgeVerdict === "accept") trust += TRUST_JUDGE_ACCEPT_BOOST;
76
+ if (input.evidence.corroboratedBySources.length > 0) {
77
+ trust += TRUST_CROSS_SOURCE_BOOST;
78
+ }
79
+ if (input.evidence.supportingMemoryId !== undefined) {
80
+ trust += TRUST_SUPPORTING_MEMORY_BOOST;
81
+ }
82
+ return Math.min(1, Math.max(0, trust));
83
+ }
84
+
85
+ export interface CorroborationContext {
86
+ /**
87
+ * Same-day transcript bodies from OTHER sources, keyed by source id.
88
+ * Pre-tokenized once per sync (day bodies are large).
89
+ */
90
+ otherSourceDayTokens: Map<string, Set<string>>;
91
+ /** Existing active memories: id + content. */
92
+ existingMemories: Array<{ id: string; content: string }>;
93
+ }
94
+
95
+ /** Tokenize a day body once for repeated per-fact coverage checks. */
96
+ export function tokenizeDayBody(body: string): Set<string> {
97
+ return new Set(normalizeRecallTokens(body));
98
+ }
99
+
100
+ /**
101
+ * Find corroborating evidence for one fact. Deterministic and local:
102
+ * token-coverage similarity via the shared recall tokenizer — no LLM
103
+ * cost on the corroboration path.
104
+ */
105
+ export function findCorroboration(
106
+ factText: string,
107
+ context: CorroborationContext,
108
+ ): TrustEvidence {
109
+ const factTokens = normalizeRecallTokens(factText);
110
+ const evidence: TrustEvidence = { corroboratedBySources: [] };
111
+ if (factTokens.length < MIN_FACT_TOKENS) return evidence;
112
+ const factTokenSet = new Set(factTokens);
113
+
114
+ for (const [sourceId, dayTokens] of context.otherSourceDayTokens) {
115
+ let matches = 0;
116
+ for (const token of factTokenSet) {
117
+ if (dayTokens.has(token)) matches += 1;
118
+ }
119
+ if (matches / factTokenSet.size >= CROSS_SOURCE_COVERAGE) {
120
+ evidence.corroboratedBySources.push(sourceId);
121
+ }
122
+ }
123
+ evidence.corroboratedBySources.sort();
124
+
125
+ let scanned = 0;
126
+ for (const memory of context.existingMemories) {
127
+ if (scanned >= MAX_MEMORIES_SCANNED) break;
128
+ scanned += 1;
129
+ const matches = countRecallTokenOverlap(factTokenSet, memory.content);
130
+ if (matches / factTokenSet.size >= MEMORY_SUPPORT_COVERAGE) {
131
+ evidence.supportingMemoryId = memory.id;
132
+ break;
133
+ }
134
+ }
135
+ return evidence;
136
+ }
137
+
138
+ export interface SmartDecision {
139
+ outcome: "active" | "review" | "drop";
140
+ reason:
141
+ | "judge-rejected"
142
+ | "judge-deferred"
143
+ | "auto-approved"
144
+ | "queued-for-review"
145
+ | "below-trust";
146
+ trust: number;
147
+ }
148
+
149
+ /** Map judge verdict + trust score to the smart-mode decision. */
150
+ export function decideSmart(
151
+ trust: number,
152
+ judgeVerdict: "accept" | "reject" | "defer" | undefined,
153
+ thresholds: { autoApproveTrust: number; reviewTrust: number },
154
+ ): SmartDecision {
155
+ if (judgeVerdict === "reject") {
156
+ return { outcome: "drop", reason: "judge-rejected", trust };
157
+ }
158
+ if (judgeVerdict === "defer") {
159
+ return { outcome: "review", reason: "judge-deferred", trust };
160
+ }
161
+ if (trust >= thresholds.autoApproveTrust) {
162
+ return { outcome: "active", reason: "auto-approved", trust };
163
+ }
164
+ if (trust >= thresholds.reviewTrust) {
165
+ return { outcome: "review", reason: "queued-for-review", trust };
166
+ }
167
+ return { outcome: "drop", reason: "below-trust", trust };
168
+ }
@@ -143,32 +143,59 @@ export interface WearableSourceSettings {
143
143
  userId?: string;
144
144
  /**
145
145
  * Memory creation mode (trust gate):
146
+ * - "smart": DEFAULT. Fully automated trust pipeline: the
147
+ * extraction judge (LLM-as-judge) plus a per-source
148
+ * trust prior, cross-device corroboration, and
149
+ * existing-memory support combine into a trust score.
150
+ * High-trust facts are written active; borderline facts
151
+ * go to the review queue; low-trust/judge-rejected
152
+ * facts are dropped. See wearables/trust.ts.
146
153
  * - "off": transcripts only; never create memories.
147
- * - "review": extract candidate memories but write every one with
148
- * status "pending_review" so nothing enters active
149
- * recall without operator approval. Default.
150
- * - "auto": extracted memories that pass the quality gates are
151
- * written active, like live-conversation extraction.
154
+ * - "review": every extracted candidate is written
155
+ * "pending_review" nothing enters active recall
156
+ * without operator approval.
157
+ * - "auto": deterministic gates only; survivors written active
158
+ * (no judge, no trust scoring).
152
159
  */
153
160
  memoryMode: WearableMemoryMode;
161
+ /**
162
+ * Trust prior for this source's transcription quality (0..1).
163
+ * Multiplies extraction confidence in smart mode — lower it for a
164
+ * device that mis-transcribes often. Default 0.8.
165
+ */
166
+ sourceTrust: number;
167
+ /** Smart mode: trust at/above which facts are written active. */
168
+ autoApproveTrust: number;
169
+ /**
170
+ * Smart mode: trust at/above which borderline facts are queued for
171
+ * review instead of dropped. Must be below autoApproveTrust.
172
+ */
173
+ reviewTrust: number;
154
174
  /** Drop extracted facts below this confidence (0–1). */
155
175
  minConfidence: number;
156
176
  /** Drop extracted facts scored below this importance level. */
157
177
  minImportance: ImportanceLevel;
158
178
  /**
159
- * Cap on memories created per source per day. 0 disables the cap.
179
+ * Cap on memories created per source per day. 0 (the default)
180
+ * disables the cap — the smart trust pipeline is the quality gate,
181
+ * and a count cap would drop real memories on busy days.
160
182
  */
161
183
  maxMemoriesPerDay: number;
162
184
  /**
163
185
  * Import provider-extracted memories (Bee facts / Omi memories).
164
- * "review" lands them in the review queue; "off" (default) skips.
186
+ * - "smart": DEFAULT. Same trust pipeline as transcript facts
187
+ * (judge + trust prior + corroboration) with a slightly reduced
188
+ * prior — provider extraction quality is outside Remnic's
189
+ * control.
190
+ * - "review": always pending_review.
191
+ * - "off": skip.
165
192
  */
166
- importNativeMemories: "off" | "review";
193
+ importNativeMemories: "off" | "review" | "smart";
167
194
  /** Transcript cleanup toggles. */
168
195
  cleanup: WearableCleanupSettings;
169
196
  }
170
197
 
171
- export type WearableMemoryMode = "off" | "review" | "auto";
198
+ export type WearableMemoryMode = "off" | "review" | "auto" | "smart";
172
199
 
173
200
  export interface WearableCleanupSettings {
174
201
  /** Merge consecutive segments from the same speaker. */
@@ -225,6 +252,23 @@ export interface WearablesConfig {
225
252
  * Default false.
226
253
  */
227
254
  digestEnabled: boolean;
255
+ /**
256
+ * Periodically refresh transcripts in-process (long-lived hosts).
257
+ * Every tick re-syncs `autoSyncDays` ending today — existing day
258
+ * files included, so the current day keeps growing while the
259
+ * wearable records. Default true.
260
+ */
261
+ autoSyncEnabled: boolean;
262
+ /** Minutes between auto-sync ticks (1-1440). Default 15. */
263
+ autoSyncIntervalMinutes: number;
264
+ /** Rolling window (days ending today) per tick (1-90). Default 2. */
265
+ autoSyncDays: number;
266
+ /**
267
+ * Once-per-local-day deep pass window (days, 0-90) picking up late
268
+ * uploads and provider re-processing. 0 disables; otherwise must be
269
+ * >= autoSyncDays. Default 7.
270
+ */
271
+ autoSyncDeepDays: number;
228
272
  /** Correction rules from config (merged with CLI-managed rules). */
229
273
  corrections: WearableCorrectionRule[];
230
274
  /** Per-source settings, keyed by connector id. */
@@ -244,6 +288,10 @@ export interface WearableSyncSummary {
244
288
  /** Day files written (skipped-unchanged days are not listed). */
245
289
  transcriptsWritten: string[];
246
290
  memoriesCreated: number;
291
+ /** Earlier borderline writes promoted to active by new evidence. */
292
+ memoriesPromoted: number;
293
+ /** Earlier pending writes retired by a fresh judge-reject verdict. */
294
+ memoriesDemoted: number;
247
295
  memoriesSkipped: number;
248
296
  nativeMemoriesImported: number;
249
297
  /** Non-fatal warnings surfaced to the operator. */