@remnic/core 9.3.624 → 9.3.626

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 (261) hide show
  1. package/dist/access-cli.js +18 -16
  2. package/dist/access-cli.js.map +1 -1
  3. package/dist/access-http.d.ts +12 -5
  4. package/dist/access-http.js +10 -9
  5. package/dist/access-mcp.d.ts +5 -5
  6. package/dist/access-mcp.js +8 -8
  7. package/dist/access-schema.d.ts +5 -5
  8. package/dist/{access-service-CBNEKjzN.d.ts → access-service-C_sfOHsX.d.ts} +26 -3
  9. package/dist/access-service.d.ts +5 -5
  10. package/dist/access-service.js +7 -7
  11. package/dist/action-confidence.d.ts +1 -1
  12. package/dist/active-memory-bridge.d.ts +1 -1
  13. package/dist/active-recall.d.ts +1 -1
  14. package/dist/active-recall.js +2 -1
  15. package/dist/active-recall.js.map +1 -1
  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 -4
  19. package/dist/briefing.d.ts +1 -1
  20. package/dist/briefing.js +3 -3
  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 -4
  27. package/dist/{chunk-7TPH6UZL.js → chunk-2RHI3FGV.js} +540 -17
  28. package/dist/chunk-2RHI3FGV.js.map +1 -0
  29. package/dist/{chunk-GYTVOLNX.js → chunk-3MNBW7R7.js} +2 -2
  30. package/dist/{chunk-QFQQFX2H.js → chunk-3R2UZV3U.js} +2 -2
  31. package/dist/{chunk-O4UNM6OR.js → chunk-532VCWYW.js} +2 -2
  32. package/dist/{chunk-2UFQYU5F.js → chunk-57QXN2CS.js} +2 -2
  33. package/dist/chunk-7WV3F5DQ.js +22 -0
  34. package/dist/chunk-7WV3F5DQ.js.map +1 -0
  35. package/dist/{chunk-RKW6QR7W.js → chunk-AZ4RI3QD.js} +1461 -78
  36. package/dist/chunk-AZ4RI3QD.js.map +1 -0
  37. package/dist/{chunk-KQFQ3IS5.js → chunk-F3FY3D3S.js} +43 -7
  38. package/dist/chunk-F3FY3D3S.js.map +1 -0
  39. package/dist/{chunk-4R4KTDIE.js → chunk-FPNQF475.js} +1 -1
  40. package/dist/chunk-FPNQF475.js.map +1 -0
  41. package/dist/{chunk-UGEBPVNI.js → chunk-GE7Q7KXP.js} +2 -2
  42. package/dist/{chunk-GLWW3EJQ.js → chunk-KB4MFBF5.js} +3 -3
  43. package/dist/{chunk-5GOMXHLC.js → chunk-KKTXCFD7.js} +255 -1
  44. package/dist/chunk-KKTXCFD7.js.map +1 -0
  45. package/dist/{chunk-FH3PPO42.js → chunk-KVFYTRMV.js} +2 -2
  46. package/dist/{chunk-BNW5NJJH.js → chunk-LQYTQCXM.js} +2 -2
  47. package/dist/{chunk-AYHXQR53.js → chunk-MVQN73GT.js} +2 -2
  48. package/dist/{chunk-ZZPIJPPD.js → chunk-N5RGXWLQ.js} +2 -2
  49. package/dist/chunk-NDAH7BJ5.js +213 -0
  50. package/dist/chunk-NDAH7BJ5.js.map +1 -0
  51. package/dist/{chunk-R3OQGYOU.js → chunk-P2D2MM47.js} +2 -2
  52. package/dist/{chunk-PSUB67YB.js → chunk-PW6GURU3.js} +2 -2
  53. package/dist/{chunk-W3BKVM64.js → chunk-QDV6VAD4.js} +2 -2
  54. package/dist/{chunk-3QSU4NFF.js → chunk-QHXW3LZV.js} +3 -3
  55. package/dist/{chunk-I6UCUHLK.js → chunk-SHV5Y2WU.js} +182 -3
  56. package/dist/chunk-SHV5Y2WU.js.map +1 -0
  57. package/dist/{chunk-OZXVGYGZ.js → chunk-STDAAGH7.js} +2 -2
  58. package/dist/{chunk-FMGWXIES.js → chunk-TZDSNIRO.js} +5 -5
  59. package/dist/{chunk-2L54V4ZO.js → chunk-UELS6WWF.js} +2 -2
  60. package/dist/{chunk-PJGB7XRR.js → chunk-UGHUNQ74.js} +502 -134
  61. package/dist/chunk-UGHUNQ74.js.map +1 -0
  62. package/dist/{chunk-FG76RDVI.js → chunk-Y3TMFC6I.js} +136 -4
  63. package/dist/chunk-Y3TMFC6I.js.map +1 -0
  64. package/dist/{chunk-BPSGLMQ4.js → chunk-YQNADJCT.js} +2 -2
  65. package/dist/{cli-Cw729yLf.d.ts → cli-EZv6YE6_.d.ts} +3 -3
  66. package/dist/cli.d.ts +6 -6
  67. package/dist/cli.js +23 -21
  68. package/dist/compounding/engine.d.ts +1 -1
  69. package/dist/compounding/engine.js +3 -3
  70. package/dist/compounding/preference-consolidator.d.ts +1 -1
  71. package/dist/compression-optimizer.d.ts +1 -1
  72. package/dist/config.d.ts +1 -1
  73. package/dist/config.js +2 -1
  74. package/dist/connectors/codex-materialize-runner.d.ts +1 -1
  75. package/dist/connectors/codex-materialize-runner.js +3 -3
  76. package/dist/connectors/codex-materialize.d.ts +1 -1
  77. package/dist/connectors/index.d.ts +1 -1
  78. package/dist/connectors/index.js +3 -3
  79. package/dist/consolidation-provenance-check.d.ts +1 -1
  80. package/dist/consolidation-undo.d.ts +1 -1
  81. package/dist/contradiction/index.d.ts +2 -2
  82. package/dist/conversation-index/backend.d.ts +1 -1
  83. package/dist/conversation-index/chunker.d.ts +1 -1
  84. package/dist/conversation-index/faiss-adapter.d.ts +1 -1
  85. package/dist/conversation-index/indexer.d.ts +1 -1
  86. package/dist/conversation-index/search.d.ts +1 -1
  87. package/dist/day-summary.d.ts +1 -1
  88. package/dist/delinearize.d.ts +1 -1
  89. package/dist/direct-answer-wiring.d.ts +1 -1
  90. package/dist/direct-answer.d.ts +1 -1
  91. package/dist/embedding-fallback.d.ts +1 -1
  92. package/dist/enrichment/index.d.ts +1 -1
  93. package/dist/entity-retrieval.d.ts +1 -1
  94. package/dist/entity-retrieval.js +3 -3
  95. package/dist/entity-schema.d.ts +1 -1
  96. package/dist/explicit-capture.d.ts +4 -4
  97. package/dist/extraction-judge-telemetry.d.ts +1 -1
  98. package/dist/extraction-judge-training.d.ts +1 -1
  99. package/dist/extraction-judge.d.ts +1 -1
  100. package/dist/extraction.d.ts +1 -1
  101. package/dist/fallback-llm.d.ts +1 -1
  102. package/dist/identity-continuity.d.ts +1 -1
  103. package/dist/importance.d.ts +1 -1
  104. package/dist/index.d.ts +307 -9
  105. package/dist/index.js +155 -29
  106. package/dist/index.js.map +1 -1
  107. package/dist/intent.d.ts +1 -1
  108. package/dist/lcm/engine.d.ts +1 -1
  109. package/dist/lcm/index.d.ts +1 -1
  110. package/dist/lcm/tools.d.ts +1 -1
  111. package/dist/lifecycle.d.ts +1 -1
  112. package/dist/live-connectors-runner.d.ts +1 -1
  113. package/dist/local-llm.d.ts +1 -1
  114. package/dist/maintenance/memory-governance.d.ts +1 -1
  115. package/dist/maintenance/memory-governance.js +3 -3
  116. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +3 -3
  117. package/dist/maintenance/rebuild-memory-projection.js +4 -4
  118. package/dist/mcp-memory-inspector-app.d.ts +5 -5
  119. package/dist/memory-action-policy.d.ts +1 -1
  120. package/dist/memory-cache.d.ts +1 -1
  121. package/dist/memory-lifecycle-ledger-utils.d.ts +1 -1
  122. package/dist/memory-projection-store.d.ts +1 -1
  123. package/dist/memory-provenance.d.ts +1 -1
  124. package/dist/memory-worth-outcomes.d.ts +1 -1
  125. package/dist/models-json.d.ts +1 -1
  126. package/dist/namespaces/migrate.d.ts +1 -1
  127. package/dist/namespaces/migrate.js +4 -4
  128. package/dist/namespaces/principal.d.ts +1 -1
  129. package/dist/namespaces/search.d.ts +1 -1
  130. package/dist/namespaces/storage.d.ts +1 -1
  131. package/dist/namespaces/storage.js +3 -3
  132. package/dist/native-knowledge.d.ts +1 -1
  133. package/dist/operator-toolkit.d.ts +1 -1
  134. package/dist/operator-toolkit.js +8 -7
  135. package/dist/{orchestrator-CqWOjfgl.d.ts → orchestrator-CEycaY3M.d.ts} +361 -4
  136. package/dist/orchestrator.d.ts +4 -4
  137. package/dist/orchestrator.js +13 -11
  138. package/dist/patterns-cli.d.ts +1 -1
  139. package/dist/policy-runtime.d.ts +1 -1
  140. package/dist/qmd-recall-cache.d.ts +1 -1
  141. package/dist/qmd.d.ts +1 -1
  142. package/dist/recall-disclosure-escalation.d.ts +1 -1
  143. package/dist/recall-explain-renderer.d.ts +1 -1
  144. package/dist/recall-explain-renderer.js +3 -3
  145. package/dist/recall-planner-llm.d.ts +1 -1
  146. package/dist/recall-state.d.ts +1 -1
  147. package/dist/recall-tag-filter.d.ts +1 -1
  148. package/dist/recall-xray-cli.d.ts +1 -1
  149. package/dist/recall-xray-cli.js +4 -4
  150. package/dist/recall-xray-renderer.d.ts +1 -1
  151. package/dist/recall-xray-renderer.js +3 -3
  152. package/dist/recall-xray.d.ts +1 -1
  153. package/dist/recall-xray.js +2 -2
  154. package/dist/resolve-auth-token.d.ts +1 -1
  155. package/dist/resume-bundles.js +3 -2
  156. package/dist/retrieval-agents.d.ts +1 -1
  157. package/dist/retrieval-tiers.d.ts +1 -1
  158. package/dist/routing/engine.d.ts +1 -1
  159. package/dist/routing/store.d.ts +1 -1
  160. package/dist/schemas.d.ts +10 -10
  161. package/dist/search/embed-helper.d.ts +1 -1
  162. package/dist/search/factory.d.ts +1 -1
  163. package/dist/search/index.d.ts +1 -1
  164. package/dist/search/lancedb-backend.d.ts +1 -1
  165. package/dist/search/meilisearch-backend.d.ts +1 -1
  166. package/dist/search/noop-backend.d.ts +1 -1
  167. package/dist/search/orama-backend.d.ts +1 -1
  168. package/dist/search/port.d.ts +1 -1
  169. package/dist/search/remote-backend.d.ts +1 -1
  170. package/dist/{semantic-SLAa_prH.d.ts → semantic-DJR8_DMQ.d.ts} +1 -1
  171. package/dist/{semantic-consolidation-4HkHWgeI.d.ts → semantic-consolidation-FbhPeJjB.d.ts} +1 -1
  172. package/dist/semantic-consolidation.d.ts +2 -2
  173. package/dist/semantic-consolidation.js +4 -4
  174. package/dist/semantic-rule-promotion.js +3 -3
  175. package/dist/semantic-rule-verifier.d.ts +1 -1
  176. package/dist/semantic-rule-verifier.js +3 -3
  177. package/dist/session-observer-bands.d.ts +1 -1
  178. package/dist/session-observer-state.d.ts +1 -1
  179. package/dist/shared-context/manager.d.ts +5 -5
  180. package/dist/signal.d.ts +1 -1
  181. package/dist/storage.d.ts +19 -1
  182. package/dist/storage.js +2 -2
  183. package/dist/summarizer.d.ts +1 -1
  184. package/dist/summary-snapshot.d.ts +1 -1
  185. package/dist/temporal-supersession.d.ts +1 -1
  186. package/dist/temporal-validity.d.ts +1 -1
  187. package/dist/threading.d.ts +1 -1
  188. package/dist/tier-migration.d.ts +1 -1
  189. package/dist/tier-routing.d.ts +1 -1
  190. package/dist/topics.d.ts +1 -1
  191. package/dist/transcript.d.ts +1 -1
  192. package/dist/types-D5VRAI04.d.ts +3134 -0
  193. package/dist/types.d.ts +3 -2862
  194. package/dist/types.js +1 -1
  195. package/dist/utility-runtime.d.ts +1 -1
  196. package/dist/verified-recall.js +3 -3
  197. package/package.json +1 -1
  198. package/src/access-http.ts +167 -0
  199. package/src/access-mcp.ts +198 -0
  200. package/src/access-service.ts +65 -0
  201. package/src/cli.ts +187 -0
  202. package/src/config.ts +7 -0
  203. package/src/index.ts +7 -0
  204. package/src/orchestrator.ts +42 -0
  205. package/src/storage.ts +106 -0
  206. package/src/types.ts +5 -0
  207. package/src/wearables/cleanup.test.ts +134 -0
  208. package/src/wearables/cleanup.ts +188 -0
  209. package/src/wearables/cli.test.ts +170 -0
  210. package/src/wearables/cli.ts +441 -0
  211. package/src/wearables/config.test.ts +143 -0
  212. package/src/wearables/config.ts +332 -0
  213. package/src/wearables/corrections.test.ts +118 -0
  214. package/src/wearables/corrections.ts +211 -0
  215. package/src/wearables/day-store.test.ts +143 -0
  216. package/src/wearables/day-store.ts +238 -0
  217. package/src/wearables/errors.test.ts +32 -0
  218. package/src/wearables/errors.ts +29 -0
  219. package/src/wearables/index.ts +114 -0
  220. package/src/wearables/memory-gen.test.ts +342 -0
  221. package/src/wearables/memory-gen.ts +413 -0
  222. package/src/wearables/pipeline.test.ts +608 -0
  223. package/src/wearables/pipeline.ts +519 -0
  224. package/src/wearables/redaction.test.ts +94 -0
  225. package/src/wearables/redaction.ts +156 -0
  226. package/src/wearables/registry.test.ts +62 -0
  227. package/src/wearables/registry.ts +133 -0
  228. package/src/wearables/service.test.ts +425 -0
  229. package/src/wearables/service.ts +691 -0
  230. package/src/wearables/speakers.test.ts +110 -0
  231. package/src/wearables/speakers.ts +174 -0
  232. package/src/wearables/storage-io.test.ts +105 -0
  233. package/src/wearables/sync-state.test.ts +134 -0
  234. package/src/wearables/sync-state.ts +186 -0
  235. package/src/wearables/types.ts +285 -0
  236. package/dist/chunk-4R4KTDIE.js.map +0 -1
  237. package/dist/chunk-5GOMXHLC.js.map +0 -1
  238. package/dist/chunk-7TPH6UZL.js.map +0 -1
  239. package/dist/chunk-FG76RDVI.js.map +0 -1
  240. package/dist/chunk-I6UCUHLK.js.map +0 -1
  241. package/dist/chunk-KQFQ3IS5.js.map +0 -1
  242. package/dist/chunk-PJGB7XRR.js.map +0 -1
  243. package/dist/chunk-RKW6QR7W.js.map +0 -1
  244. /package/dist/{chunk-GYTVOLNX.js.map → chunk-3MNBW7R7.js.map} +0 -0
  245. /package/dist/{chunk-QFQQFX2H.js.map → chunk-3R2UZV3U.js.map} +0 -0
  246. /package/dist/{chunk-O4UNM6OR.js.map → chunk-532VCWYW.js.map} +0 -0
  247. /package/dist/{chunk-2UFQYU5F.js.map → chunk-57QXN2CS.js.map} +0 -0
  248. /package/dist/{chunk-UGEBPVNI.js.map → chunk-GE7Q7KXP.js.map} +0 -0
  249. /package/dist/{chunk-GLWW3EJQ.js.map → chunk-KB4MFBF5.js.map} +0 -0
  250. /package/dist/{chunk-FH3PPO42.js.map → chunk-KVFYTRMV.js.map} +0 -0
  251. /package/dist/{chunk-BNW5NJJH.js.map → chunk-LQYTQCXM.js.map} +0 -0
  252. /package/dist/{chunk-AYHXQR53.js.map → chunk-MVQN73GT.js.map} +0 -0
  253. /package/dist/{chunk-ZZPIJPPD.js.map → chunk-N5RGXWLQ.js.map} +0 -0
  254. /package/dist/{chunk-R3OQGYOU.js.map → chunk-P2D2MM47.js.map} +0 -0
  255. /package/dist/{chunk-PSUB67YB.js.map → chunk-PW6GURU3.js.map} +0 -0
  256. /package/dist/{chunk-W3BKVM64.js.map → chunk-QDV6VAD4.js.map} +0 -0
  257. /package/dist/{chunk-3QSU4NFF.js.map → chunk-QHXW3LZV.js.map} +0 -0
  258. /package/dist/{chunk-OZXVGYGZ.js.map → chunk-STDAAGH7.js.map} +0 -0
  259. /package/dist/{chunk-FMGWXIES.js.map → chunk-TZDSNIRO.js.map} +0 -0
  260. /package/dist/{chunk-2L54V4ZO.js.map → chunk-UELS6WWF.js.map} +0 -0
  261. /package/dist/{chunk-BPSGLMQ4.js.map → chunk-YQNADJCT.js.map} +0 -0
@@ -0,0 +1,413 @@
1
+ /**
2
+ * Wearable memory generation — trust-gated extraction from cleaned day
3
+ * transcripts.
4
+ *
5
+ * Wearable ASR quality varies wildly between providers and rooms, so
6
+ * unlike live-session extraction this path is gated per source:
7
+ *
8
+ * - memoryMode "off" -> never runs
9
+ * - memoryMode "review" -> candidates land with status
10
+ * "pending_review" (operator approves via
11
+ * the existing review-queue surfaces)
12
+ * - memoryMode "auto" -> candidates that pass every gate land
13
+ * active
14
+ *
15
+ * Deterministic gates (applied in order, all modes except "off"):
16
+ * 1. category gate — procedure / reasoning_trace candidates are
17
+ * skipped (they need richer persistence than
18
+ * this path provides)
19
+ * 2. confidence floor — `minConfidence`
20
+ * 3. importance floor — local `scoreImportance` >= `minImportance`
21
+ * 4. dedup — storage content-hash index + intra-run set
22
+ * 5. day cap — top `maxMemoriesPerDay` by importance score
23
+ * (0 disables the cap)
24
+ *
25
+ * The extraction engine itself is injected so this module stays free of
26
+ * LLM-client construction; callers hand in `orchestrator`-owned or
27
+ * standalone engines alike.
28
+ */
29
+
30
+ import { scoreImportance } from "../importance.js";
31
+ import { describeErrorForOperator } from "./errors.js";
32
+ import type {
33
+ BufferTurn,
34
+ ExtractedFact,
35
+ ExtractionResult,
36
+ ImportanceLevel,
37
+ ImportanceScore,
38
+ MemoryCategory,
39
+ MemoryStatus,
40
+ } from "../types.js";
41
+ import { resolveSpeaker, type SpeakerRegistry } from "./speakers.js";
42
+ import type {
43
+ WearableConversation,
44
+ WearableMemoryMode,
45
+ WearableNativeMemory,
46
+ WearableSourceSettings,
47
+ } from "./types.js";
48
+
49
+ /**
50
+ * Memory status used for a given memory mode. Least-privilege default:
51
+ * anything that is not explicitly "auto" lands in the review queue
52
+ * rather than active recall.
53
+ */
54
+ export function memoryStatusForMode(
55
+ mode: WearableMemoryMode,
56
+ ): Extract<MemoryStatus, "active" | "pending_review"> {
57
+ return mode === "auto" ? "active" : "pending_review";
58
+ }
59
+
60
+ /** Narrow writer interface satisfied by `StorageManager`. */
61
+ export interface WearableMemoryWriter {
62
+ writeMemory(
63
+ category: MemoryCategory,
64
+ content: string,
65
+ options: {
66
+ confidence?: number;
67
+ tags?: string[];
68
+ source?: string;
69
+ importance?: ImportanceScore;
70
+ validAt?: string;
71
+ structuredAttributes?: Record<string, string>;
72
+ contentHashSource?: string;
73
+ status?: MemoryStatus;
74
+ memoryKind?: "episode" | "note" | "box" | "dream" | "procedural";
75
+ },
76
+ ): Promise<string>;
77
+ hasFactContentHash(content: string): Promise<boolean>;
78
+ }
79
+
80
+ export interface WearableMemoryGenDeps {
81
+ extract(turns: BufferTurn[]): Promise<ExtractionResult>;
82
+ writer: WearableMemoryWriter;
83
+ }
84
+
85
+ export interface WearableMemoryGenResult {
86
+ created: number;
87
+ skipped: number;
88
+ skippedByReason: Record<string, number>;
89
+ /** Non-fatal problems (e.g. the extraction engine erroring). */
90
+ warnings: string[];
91
+ }
92
+
93
+ export const WEARABLE_SOURCE_PREFIX = "wearable";
94
+
95
+ export function wearableSourceLabel(sourceId: string): string {
96
+ return `${WEARABLE_SOURCE_PREFIX}:${sourceId}`;
97
+ }
98
+
99
+ export function wearableDayTag(date: string): string {
100
+ return `wearable-day:${date}`;
101
+ }
102
+
103
+ const IMPORTANCE_RANK: Record<ImportanceLevel, number> = {
104
+ trivial: 0,
105
+ low: 1,
106
+ normal: 2,
107
+ high: 3,
108
+ critical: 4,
109
+ };
110
+
111
+ /** Max characters of transcript per extraction turn. */
112
+ const MAX_EXTRACTION_CHUNK_CHARS = 6_000;
113
+ /** Skip extraction for conversations with less substance than this. */
114
+ const MIN_CONVERSATION_CHARS = 80;
115
+
116
+ /**
117
+ * Render a cleaned conversation into extraction-ready turns. Each turn
118
+ * carries a labeled multi-speaker transcript block; the wearer is
119
+ * marked "(you)" so first-person facts attribute correctly.
120
+ */
121
+ export function buildExtractionTurns(
122
+ sourceId: string,
123
+ date: string,
124
+ conversation: WearableConversation,
125
+ registry: SpeakerRegistry,
126
+ ): BufferTurn[] {
127
+ const headerParts = [
128
+ `Wearable transcript (${sourceId}) — ${date}`,
129
+ conversation.title ? `"${conversation.title}"` : undefined,
130
+ conversation.location ? `at ${conversation.location}` : undefined,
131
+ ].filter((part): part is string => typeof part === "string");
132
+ const header = `[${headerParts.join(" — ")}]`;
133
+
134
+ const lines: string[] = [];
135
+ for (const segment of conversation.segments) {
136
+ const { label } = resolveSpeaker(sourceId, segment, registry);
137
+ lines.push(`${label}: ${segment.text}`);
138
+ }
139
+ const transcript = lines.join("\n");
140
+ if (transcript.trim().length < MIN_CONVERSATION_CHARS) return [];
141
+
142
+ const sessionKey = `wearables:${sourceId}:${date}:${conversation.id}`;
143
+ const timestamp = conversation.startIso;
144
+ const turns: BufferTurn[] = [];
145
+ let chunkLines: string[] = [];
146
+ let chunkChars = 0;
147
+ const flush = () => {
148
+ if (chunkLines.length === 0) return;
149
+ turns.push({
150
+ role: "user",
151
+ content: `${header}\n${chunkLines.join("\n")}`,
152
+ timestamp,
153
+ sourceValidAt: timestamp,
154
+ sessionKey,
155
+ });
156
+ chunkLines = [];
157
+ chunkChars = 0;
158
+ };
159
+ for (const line of transcript.split("\n")) {
160
+ if (chunkChars + line.length + 1 > MAX_EXTRACTION_CHUNK_CHARS) flush();
161
+ chunkLines.push(line);
162
+ chunkChars += line.length + 1;
163
+ }
164
+ flush();
165
+ return turns;
166
+ }
167
+
168
+ interface GatedCandidate {
169
+ fact: ExtractedFact;
170
+ importance: ImportanceScore;
171
+ conversation: WearableConversation;
172
+ }
173
+
174
+ /**
175
+ * Run extraction + gates over a day's conversations and persist the
176
+ * survivors. Returns counts for the sync summary.
177
+ */
178
+ export async function generateWearableMemories(
179
+ sourceId: string,
180
+ date: string,
181
+ conversations: WearableConversation[],
182
+ settings: WearableSourceSettings,
183
+ registry: SpeakerRegistry,
184
+ deps: WearableMemoryGenDeps,
185
+ ): Promise<WearableMemoryGenResult> {
186
+ const result: WearableMemoryGenResult = {
187
+ created: 0,
188
+ skipped: 0,
189
+ skippedByReason: {},
190
+ warnings: [],
191
+ };
192
+ if (settings.memoryMode === "off") return result;
193
+
194
+ const skip = (reason: string, count = 1): void => {
195
+ result.skipped += count;
196
+ result.skippedByReason[reason] =
197
+ (result.skippedByReason[reason] ?? 0) + count;
198
+ };
199
+
200
+ const candidates: GatedCandidate[] = [];
201
+ const seenContent = new Set<string>();
202
+
203
+ for (const conversation of conversations) {
204
+ const turns = buildExtractionTurns(sourceId, date, conversation, registry);
205
+ if (turns.length === 0) continue;
206
+ let extraction: ExtractionResult;
207
+ try {
208
+ extraction = await deps.extract(turns);
209
+ } catch (err) {
210
+ // One failing extraction call almost always means every call will
211
+ // fail (missing key, provider outage) — stop hammering the engine
212
+ // and surface a single actionable warning instead of one per
213
+ // conversation. Candidates gathered before the failure still
214
+ // persist below.
215
+ result.warnings.push(
216
+ `extraction failed for ${sourceId}/${date} (conversation ${conversation.id}): ${describeErrorForOperator(err)} — the memory pass for this day retries on the next sync`,
217
+ );
218
+ break;
219
+ }
220
+ for (const fact of extraction.facts) {
221
+ const content = fact.content?.trim();
222
+ if (!content) {
223
+ skip("empty");
224
+ continue;
225
+ }
226
+ if (fact.category === "procedure" || fact.category === "reasoning_trace") {
227
+ skip("unsupported-category");
228
+ continue;
229
+ }
230
+ if (
231
+ typeof fact.confidence === "number" &&
232
+ fact.confidence < settings.minConfidence
233
+ ) {
234
+ skip("below-confidence");
235
+ continue;
236
+ }
237
+ const importance = scoreImportance(content, fact.category, fact.tags ?? []);
238
+ if (
239
+ IMPORTANCE_RANK[importance.level] <
240
+ IMPORTANCE_RANK[settings.minImportance]
241
+ ) {
242
+ skip("below-importance");
243
+ continue;
244
+ }
245
+ const dedupKey = content.toLowerCase();
246
+ if (seenContent.has(dedupKey)) {
247
+ skip("duplicate-in-run");
248
+ continue;
249
+ }
250
+ seenContent.add(dedupKey);
251
+ candidates.push({ fact: { ...fact, content }, importance, conversation });
252
+ }
253
+ }
254
+
255
+ // Drop candidates that already exist in storage BEFORE applying the
256
+ // day cap so duplicates never consume cap slots that novel,
257
+ // lower-scoring candidates should get (Codex P2 on PR #1458).
258
+ const novel: GatedCandidate[] = [];
259
+ for (const candidate of candidates) {
260
+ if (await deps.writer.hasFactContentHash(candidate.fact.content)) {
261
+ skip("duplicate-existing");
262
+ continue;
263
+ }
264
+ novel.push(candidate);
265
+ }
266
+
267
+ // Day cap: keep the most important candidates. Stable ordering —
268
+ // score desc, then content asc so equal scores compare 0-consistent.
269
+ novel.sort((a, b) => {
270
+ if (a.importance.score > b.importance.score) return -1;
271
+ if (a.importance.score < b.importance.score) return 1;
272
+ if (a.fact.content < b.fact.content) return -1;
273
+ if (a.fact.content > b.fact.content) return 1;
274
+ return 0;
275
+ });
276
+ const cap = settings.maxMemoriesPerDay;
277
+ const kept = cap > 0 ? novel.slice(0, cap) : novel;
278
+ if (novel.length > kept.length) {
279
+ skip("over-day-cap", novel.length - kept.length);
280
+ }
281
+
282
+ const status = memoryStatusForMode(settings.memoryMode);
283
+ for (const candidate of kept) {
284
+ const tags = [
285
+ ...new Set([
286
+ ...(candidate.fact.tags ?? []),
287
+ WEARABLE_SOURCE_PREFIX,
288
+ wearableSourceLabel(sourceId),
289
+ wearableDayTag(date),
290
+ ]),
291
+ ];
292
+ await deps.writer.writeMemory(candidate.fact.category, candidate.fact.content, {
293
+ confidence: candidate.fact.confidence,
294
+ tags,
295
+ source: wearableSourceLabel(sourceId),
296
+ importance: candidate.importance,
297
+ validAt: candidate.conversation.startIso,
298
+ structuredAttributes: {
299
+ ...(candidate.fact.structuredAttributes ?? {}),
300
+ wearableSource: sourceId,
301
+ wearableDate: date,
302
+ wearableConversationId: candidate.conversation.id,
303
+ },
304
+ contentHashSource: candidate.fact.content,
305
+ status,
306
+ });
307
+ result.created += 1;
308
+ }
309
+ return result;
310
+ }
311
+
312
+ /**
313
+ * Write one compact daily-digest memory summarizing the day's recorded
314
+ * conversations. Deterministic (no LLM): titles, time ranges, speaker
315
+ * counts. Gated by `wearables.digestEnabled`.
316
+ */
317
+ export async function writeDailyDigestMemory(
318
+ sourceId: string,
319
+ date: string,
320
+ conversations: WearableConversation[],
321
+ settings: WearableSourceSettings,
322
+ registry: SpeakerRegistry,
323
+ writer: WearableMemoryWriter,
324
+ ): Promise<boolean> {
325
+ if (settings.memoryMode === "off") return false;
326
+ if (conversations.length === 0) return false;
327
+ const lines = conversations.map((conversation) => {
328
+ const title = conversation.title?.trim() || "Untitled conversation";
329
+ const speakers = new Set(
330
+ conversation.segments.map(
331
+ (segment) => resolveSpeaker(sourceId, segment, registry).label,
332
+ ),
333
+ );
334
+ return `- ${title} (${speakers.size} speaker${speakers.size === 1 ? "" : "s"})`;
335
+ });
336
+ const content =
337
+ `Wearable day digest — ${sourceId}, ${date}: ` +
338
+ `${conversations.length} recorded conversation${conversations.length === 1 ? "" : "s"}.\n` +
339
+ lines.join("\n");
340
+ if (await writer.hasFactContentHash(content)) return false;
341
+ await writer.writeMemory("moment", content, {
342
+ confidence: 0.9,
343
+ tags: [
344
+ WEARABLE_SOURCE_PREFIX,
345
+ wearableSourceLabel(sourceId),
346
+ wearableDayTag(date),
347
+ "daily-digest",
348
+ ],
349
+ source: wearableSourceLabel(sourceId),
350
+ importance: scoreImportance(content, "moment", ["daily-digest"]),
351
+ validAt: `${date}T00:00:00.000Z`,
352
+ structuredAttributes: {
353
+ wearableSource: sourceId,
354
+ wearableDate: date,
355
+ },
356
+ contentHashSource: content,
357
+ status: memoryStatusForMode(settings.memoryMode),
358
+ memoryKind: "episode",
359
+ });
360
+ return true;
361
+ }
362
+
363
+ /**
364
+ * Import provider-extracted memories (Bee facts, Omi memories) into the
365
+ * review queue. Always `pending_review` regardless of memoryMode — the
366
+ * provider's extraction quality is outside Remnic's control.
367
+ */
368
+ export async function importNativeMemories(
369
+ sourceId: string,
370
+ memories: WearableNativeMemory[],
371
+ alreadyImportedIds: ReadonlySet<string>,
372
+ writer: WearableMemoryWriter,
373
+ ): Promise<{ imported: number; importedIds: string[] }> {
374
+ let imported = 0;
375
+ const importedIds: string[] = [];
376
+ const seenContent = new Set<string>();
377
+ for (const memory of memories) {
378
+ const content = memory.content?.trim();
379
+ if (!content) continue;
380
+ if (alreadyImportedIds.has(memory.id)) continue;
381
+ // Intra-run + cross-run dedup: the storage hash index only learns a
382
+ // fact after its write lands, so same-content items within one page
383
+ // batch need the local set.
384
+ if (seenContent.has(content) || (await writer.hasFactContentHash(content))) {
385
+ importedIds.push(memory.id);
386
+ continue;
387
+ }
388
+ seenContent.add(content);
389
+ await writer.writeMemory("fact", content, {
390
+ confidence: 0.6,
391
+ tags: [
392
+ ...new Set([
393
+ ...(memory.tags ?? []),
394
+ WEARABLE_SOURCE_PREFIX,
395
+ wearableSourceLabel(sourceId),
396
+ "native-import",
397
+ ]),
398
+ ],
399
+ source: `${wearableSourceLabel(sourceId)}:native`,
400
+ importance: scoreImportance(content, "fact", memory.tags ?? []),
401
+ validAt: memory.createdIso,
402
+ structuredAttributes: {
403
+ wearableSource: sourceId,
404
+ wearableNativeId: memory.id,
405
+ },
406
+ contentHashSource: content,
407
+ status: "pending_review",
408
+ });
409
+ imported += 1;
410
+ importedIds.push(memory.id);
411
+ }
412
+ return { imported, importedIds };
413
+ }