@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
@@ -22,7 +22,7 @@ import {
22
22
  import {
23
23
  CompoundingEngine,
24
24
  defaultTierMigrationCycleBudget
25
- } from "./chunk-2L54V4ZO.js";
25
+ } from "./chunk-UELS6WWF.js";
26
26
  import {
27
27
  SharedContextManager
28
28
  } from "./chunk-DRD2Q7HQ.js";
@@ -168,7 +168,7 @@ import {
168
168
  buildEntityRecallSection,
169
169
  entityRecentTranscriptLookbackHours,
170
170
  readRecentEntityTranscriptEntries
171
- } from "./chunk-BNW5NJJH.js";
171
+ } from "./chunk-LQYTQCXM.js";
172
172
  import {
173
173
  buildEventOrderRecallSection,
174
174
  shouldRecallEventOrderEvidence
@@ -203,7 +203,7 @@ import {
203
203
  materializeAfterSemanticConsolidation,
204
204
  parseConsolidationResponse,
205
205
  parseOperatorAwareConsolidationResponse
206
- } from "./chunk-QFQQFX2H.js";
206
+ } from "./chunk-3R2UZV3U.js";
207
207
  import {
208
208
  normalizeReplaySessionKey
209
209
  } from "./chunk-2PRQG7PV.js";
@@ -212,13 +212,13 @@ import {
212
212
  } from "./chunk-X6IRLNOO.js";
213
213
  import {
214
214
  searchVerifiedEpisodes
215
- } from "./chunk-R3OQGYOU.js";
215
+ } from "./chunk-P2D2MM47.js";
216
216
  import {
217
217
  ThreadingManager
218
218
  } from "./chunk-W4RVMTHR.js";
219
219
  import {
220
220
  searchVerifiedSemanticRules
221
- } from "./chunk-2UFQYU5F.js";
221
+ } from "./chunk-57QXN2CS.js";
222
222
  import {
223
223
  searchWorkProductLedgerEntries
224
224
  } from "./chunk-ZRWB5D4H.js";
@@ -237,7 +237,7 @@ import {
237
237
  } from "./chunk-CYEPCZN5.js";
238
238
  import {
239
239
  NamespaceStorageRouter
240
- } from "./chunk-W3BKVM64.js";
240
+ } from "./chunk-QDV6VAD4.js";
241
241
  import {
242
242
  namespaceIdentityFromToken
243
243
  } from "./chunk-ZFXCQPNO.js";
@@ -285,6 +285,17 @@ import {
285
285
  FallbackLlmClient,
286
286
  fallbackLlmRuntimeContextFromConfig
287
287
  } from "./chunk-KGIGRNR6.js";
288
+ import {
289
+ applyCorrections,
290
+ applyOffTheRecord,
291
+ compileCorrectionRule,
292
+ compileCorrectionRules,
293
+ compileRedactionPatterns,
294
+ correctionsFilePath,
295
+ loadCorrectionsFile,
296
+ redactText,
297
+ saveCorrectionsFile
298
+ } from "./chunk-NDAH7BJ5.js";
288
299
  import {
289
300
  buildRecallQueryPolicy
290
301
  } from "./chunk-LBJBNWS2.js";
@@ -306,6 +317,10 @@ import {
306
317
  isAbortError,
307
318
  throwIfAborted
308
319
  } from "./chunk-PVGDJXVK.js";
320
+ import {
321
+ WearablesInputError,
322
+ describeErrorForOperator
323
+ } from "./chunk-7WV3F5DQ.js";
309
324
  import {
310
325
  clusterByKey,
311
326
  combineNamespaces,
@@ -317,7 +332,7 @@ import {
317
332
  } from "./chunk-JGSKJHF7.js";
318
333
  import {
319
334
  buildXraySnapshot
320
- } from "./chunk-BPSGLMQ4.js";
335
+ } from "./chunk-YQNADJCT.js";
321
336
  import {
322
337
  objectiveStateStoreOverrideForNamespace,
323
338
  searchObjectiveStateSnapshots
@@ -338,11 +353,21 @@ import {
338
353
  ContentHashIndex,
339
354
  StorageManager,
340
355
  compareEntityTimestamps,
356
+ composeDayTranscriptBody,
357
+ composeDayTranscriptMeta,
341
358
  fingerprintEntityStructuredFacts,
359
+ hashTranscriptBody,
360
+ isValidTranscriptDate,
361
+ loadSpeakerRegistry,
342
362
  normalizeAttributePairs,
343
363
  normalizeEntityName,
344
- parseEntityFile
345
- } from "./chunk-PJGB7XRR.js";
364
+ parseDayTranscript,
365
+ parseEntityFile,
366
+ resolveSpeaker,
367
+ saveSpeakerRegistry,
368
+ serializeDayTranscript,
369
+ speakerRegistryKey
370
+ } from "./chunk-UGHUNQ74.js";
346
371
  import {
347
372
  attachCitation,
348
373
  hasCitationForTemplate,
@@ -350,7 +375,7 @@ import {
350
375
  } from "./chunk-J6A3CX5N.js";
351
376
  import {
352
377
  confidenceTier
353
- } from "./chunk-4R4KTDIE.js";
378
+ } from "./chunk-FPNQF475.js";
354
379
  import {
355
380
  isActiveMemoryStatus
356
381
  } from "./chunk-RULE4VG5.js";
@@ -395,7 +420,7 @@ import {
395
420
  } from "./chunk-AC5LO7IU.js";
396
421
 
397
422
  // src/orchestrator.ts
398
- import path2 from "path";
423
+ import path3 from "path";
399
424
  import os from "os";
400
425
  import { createHash, randomBytes } from "crypto";
401
426
  import { existsSync } from "fs";
@@ -937,6 +962,1300 @@ function getTaxonomyFilePath(memoryDir) {
937
962
  return path.join(memoryDir, TAXONOMY_DIR, TAXONOMY_FILE);
938
963
  }
939
964
 
965
+ // src/wearables/memory-gen.ts
966
+ function memoryStatusForMode(mode) {
967
+ return mode === "auto" ? "active" : "pending_review";
968
+ }
969
+ var WEARABLE_SOURCE_PREFIX = "wearable";
970
+ function wearableSourceLabel(sourceId) {
971
+ return `${WEARABLE_SOURCE_PREFIX}:${sourceId}`;
972
+ }
973
+ function wearableDayTag(date) {
974
+ return `wearable-day:${date}`;
975
+ }
976
+ var IMPORTANCE_RANK = {
977
+ trivial: 0,
978
+ low: 1,
979
+ normal: 2,
980
+ high: 3,
981
+ critical: 4
982
+ };
983
+ var MAX_EXTRACTION_CHUNK_CHARS = 6e3;
984
+ var MIN_CONVERSATION_CHARS = 80;
985
+ function buildExtractionTurns(sourceId, date, conversation, registry) {
986
+ const headerParts = [
987
+ `Wearable transcript (${sourceId}) \u2014 ${date}`,
988
+ conversation.title ? `"${conversation.title}"` : void 0,
989
+ conversation.location ? `at ${conversation.location}` : void 0
990
+ ].filter((part) => typeof part === "string");
991
+ const header = `[${headerParts.join(" \u2014 ")}]`;
992
+ const lines = [];
993
+ for (const segment of conversation.segments) {
994
+ const { label } = resolveSpeaker(sourceId, segment, registry);
995
+ lines.push(`${label}: ${segment.text}`);
996
+ }
997
+ const transcript = lines.join("\n");
998
+ if (transcript.trim().length < MIN_CONVERSATION_CHARS) return [];
999
+ const sessionKey = `wearables:${sourceId}:${date}:${conversation.id}`;
1000
+ const timestamp = conversation.startIso;
1001
+ const turns = [];
1002
+ let chunkLines = [];
1003
+ let chunkChars = 0;
1004
+ const flush = () => {
1005
+ if (chunkLines.length === 0) return;
1006
+ turns.push({
1007
+ role: "user",
1008
+ content: `${header}
1009
+ ${chunkLines.join("\n")}`,
1010
+ timestamp,
1011
+ sourceValidAt: timestamp,
1012
+ sessionKey
1013
+ });
1014
+ chunkLines = [];
1015
+ chunkChars = 0;
1016
+ };
1017
+ for (const line of transcript.split("\n")) {
1018
+ if (chunkChars + line.length + 1 > MAX_EXTRACTION_CHUNK_CHARS) flush();
1019
+ chunkLines.push(line);
1020
+ chunkChars += line.length + 1;
1021
+ }
1022
+ flush();
1023
+ return turns;
1024
+ }
1025
+ async function generateWearableMemories(sourceId, date, conversations, settings, registry, deps) {
1026
+ const result = {
1027
+ created: 0,
1028
+ skipped: 0,
1029
+ skippedByReason: {},
1030
+ warnings: []
1031
+ };
1032
+ if (settings.memoryMode === "off") return result;
1033
+ const skip = (reason, count = 1) => {
1034
+ result.skipped += count;
1035
+ result.skippedByReason[reason] = (result.skippedByReason[reason] ?? 0) + count;
1036
+ };
1037
+ const candidates = [];
1038
+ const seenContent = /* @__PURE__ */ new Set();
1039
+ for (const conversation of conversations) {
1040
+ const turns = buildExtractionTurns(sourceId, date, conversation, registry);
1041
+ if (turns.length === 0) continue;
1042
+ let extraction;
1043
+ try {
1044
+ extraction = await deps.extract(turns);
1045
+ } catch (err) {
1046
+ result.warnings.push(
1047
+ `extraction failed for ${sourceId}/${date} (conversation ${conversation.id}): ${describeErrorForOperator(err)} \u2014 the memory pass for this day retries on the next sync`
1048
+ );
1049
+ break;
1050
+ }
1051
+ for (const fact of extraction.facts) {
1052
+ const content = fact.content?.trim();
1053
+ if (!content) {
1054
+ skip("empty");
1055
+ continue;
1056
+ }
1057
+ if (fact.category === "procedure" || fact.category === "reasoning_trace") {
1058
+ skip("unsupported-category");
1059
+ continue;
1060
+ }
1061
+ if (typeof fact.confidence === "number" && fact.confidence < settings.minConfidence) {
1062
+ skip("below-confidence");
1063
+ continue;
1064
+ }
1065
+ const importance = scoreImportance(content, fact.category, fact.tags ?? []);
1066
+ if (IMPORTANCE_RANK[importance.level] < IMPORTANCE_RANK[settings.minImportance]) {
1067
+ skip("below-importance");
1068
+ continue;
1069
+ }
1070
+ const dedupKey = content.toLowerCase();
1071
+ if (seenContent.has(dedupKey)) {
1072
+ skip("duplicate-in-run");
1073
+ continue;
1074
+ }
1075
+ seenContent.add(dedupKey);
1076
+ candidates.push({ fact: { ...fact, content }, importance, conversation });
1077
+ }
1078
+ }
1079
+ const novel = [];
1080
+ for (const candidate of candidates) {
1081
+ if (await deps.writer.hasFactContentHash(candidate.fact.content)) {
1082
+ skip("duplicate-existing");
1083
+ continue;
1084
+ }
1085
+ novel.push(candidate);
1086
+ }
1087
+ novel.sort((a, b) => {
1088
+ if (a.importance.score > b.importance.score) return -1;
1089
+ if (a.importance.score < b.importance.score) return 1;
1090
+ if (a.fact.content < b.fact.content) return -1;
1091
+ if (a.fact.content > b.fact.content) return 1;
1092
+ return 0;
1093
+ });
1094
+ const cap = settings.maxMemoriesPerDay;
1095
+ const kept = cap > 0 ? novel.slice(0, cap) : novel;
1096
+ if (novel.length > kept.length) {
1097
+ skip("over-day-cap", novel.length - kept.length);
1098
+ }
1099
+ const status = memoryStatusForMode(settings.memoryMode);
1100
+ for (const candidate of kept) {
1101
+ const tags = [
1102
+ .../* @__PURE__ */ new Set([
1103
+ ...candidate.fact.tags ?? [],
1104
+ WEARABLE_SOURCE_PREFIX,
1105
+ wearableSourceLabel(sourceId),
1106
+ wearableDayTag(date)
1107
+ ])
1108
+ ];
1109
+ await deps.writer.writeMemory(candidate.fact.category, candidate.fact.content, {
1110
+ confidence: candidate.fact.confidence,
1111
+ tags,
1112
+ source: wearableSourceLabel(sourceId),
1113
+ importance: candidate.importance,
1114
+ validAt: candidate.conversation.startIso,
1115
+ structuredAttributes: {
1116
+ ...candidate.fact.structuredAttributes ?? {},
1117
+ wearableSource: sourceId,
1118
+ wearableDate: date,
1119
+ wearableConversationId: candidate.conversation.id
1120
+ },
1121
+ contentHashSource: candidate.fact.content,
1122
+ status
1123
+ });
1124
+ result.created += 1;
1125
+ }
1126
+ return result;
1127
+ }
1128
+ async function writeDailyDigestMemory(sourceId, date, conversations, settings, registry, writer) {
1129
+ if (settings.memoryMode === "off") return false;
1130
+ if (conversations.length === 0) return false;
1131
+ const lines = conversations.map((conversation) => {
1132
+ const title = conversation.title?.trim() || "Untitled conversation";
1133
+ const speakers = new Set(
1134
+ conversation.segments.map(
1135
+ (segment) => resolveSpeaker(sourceId, segment, registry).label
1136
+ )
1137
+ );
1138
+ return `- ${title} (${speakers.size} speaker${speakers.size === 1 ? "" : "s"})`;
1139
+ });
1140
+ const content = `Wearable day digest \u2014 ${sourceId}, ${date}: ${conversations.length} recorded conversation${conversations.length === 1 ? "" : "s"}.
1141
+ ` + lines.join("\n");
1142
+ if (await writer.hasFactContentHash(content)) return false;
1143
+ await writer.writeMemory("moment", content, {
1144
+ confidence: 0.9,
1145
+ tags: [
1146
+ WEARABLE_SOURCE_PREFIX,
1147
+ wearableSourceLabel(sourceId),
1148
+ wearableDayTag(date),
1149
+ "daily-digest"
1150
+ ],
1151
+ source: wearableSourceLabel(sourceId),
1152
+ importance: scoreImportance(content, "moment", ["daily-digest"]),
1153
+ validAt: `${date}T00:00:00.000Z`,
1154
+ structuredAttributes: {
1155
+ wearableSource: sourceId,
1156
+ wearableDate: date
1157
+ },
1158
+ contentHashSource: content,
1159
+ status: memoryStatusForMode(settings.memoryMode),
1160
+ memoryKind: "episode"
1161
+ });
1162
+ return true;
1163
+ }
1164
+ async function importNativeMemories(sourceId, memories, alreadyImportedIds, writer) {
1165
+ let imported = 0;
1166
+ const importedIds = [];
1167
+ const seenContent = /* @__PURE__ */ new Set();
1168
+ for (const memory of memories) {
1169
+ const content = memory.content?.trim();
1170
+ if (!content) continue;
1171
+ if (alreadyImportedIds.has(memory.id)) continue;
1172
+ if (seenContent.has(content) || await writer.hasFactContentHash(content)) {
1173
+ importedIds.push(memory.id);
1174
+ continue;
1175
+ }
1176
+ seenContent.add(content);
1177
+ await writer.writeMemory("fact", content, {
1178
+ confidence: 0.6,
1179
+ tags: [
1180
+ .../* @__PURE__ */ new Set([
1181
+ ...memory.tags ?? [],
1182
+ WEARABLE_SOURCE_PREFIX,
1183
+ wearableSourceLabel(sourceId),
1184
+ "native-import"
1185
+ ])
1186
+ ],
1187
+ source: `${wearableSourceLabel(sourceId)}:native`,
1188
+ importance: scoreImportance(content, "fact", memory.tags ?? []),
1189
+ validAt: memory.createdIso,
1190
+ structuredAttributes: {
1191
+ wearableSource: sourceId,
1192
+ wearableNativeId: memory.id
1193
+ },
1194
+ contentHashSource: content,
1195
+ status: "pending_review"
1196
+ });
1197
+ imported += 1;
1198
+ importedIds.push(memory.id);
1199
+ }
1200
+ return { imported, importedIds };
1201
+ }
1202
+
1203
+ // src/wearables/cleanup.ts
1204
+ var MERGE_GAP_MS = 3e4;
1205
+ var FILLER_TOKENS = ["um", "uh", "uhm", "umm", "uhh", "erm", "hmm", "mhm"];
1206
+ var FILLER_PATTERN = new RegExp(
1207
+ // Leading/trailing punctuation around the filler collapses with it so
1208
+ // "Um, so we should" -> "so we should" rather than ", so we should".
1209
+ `(?:^|\\s)(?:${FILLER_TOKENS.join("|")})[,.]?(?=\\s|$)`,
1210
+ "gi"
1211
+ );
1212
+ function cleanConversation(conversation, settings) {
1213
+ let segments = conversation.segments.map((segment) => ({ ...segment }));
1214
+ let droppedSegments = 0;
1215
+ let mergedSegments = 0;
1216
+ if (settings.stripFillers) {
1217
+ for (const segment of segments) {
1218
+ segment.text = stripFillerTokens(segment.text);
1219
+ }
1220
+ }
1221
+ if (settings.collapseRepeats) {
1222
+ for (const segment of segments) {
1223
+ segment.text = collapseImmediateRepeats(segment.text);
1224
+ }
1225
+ }
1226
+ for (const segment of segments) {
1227
+ segment.text = normalizeWhitespace(segment.text);
1228
+ }
1229
+ if (settings.dropLowQuality) {
1230
+ const kept = [];
1231
+ for (const segment of segments) {
1232
+ if (isLowQualitySegment(segment.text)) {
1233
+ droppedSegments += 1;
1234
+ } else {
1235
+ kept.push(segment);
1236
+ }
1237
+ }
1238
+ segments = kept;
1239
+ } else {
1240
+ const kept = segments.filter((segment) => segment.text.length > 0);
1241
+ droppedSegments += segments.length - kept.length;
1242
+ segments = kept;
1243
+ }
1244
+ if (settings.mergeSameSpeaker) {
1245
+ const merged = [];
1246
+ for (const segment of segments) {
1247
+ const previous = merged[merged.length - 1];
1248
+ if (previous && canMerge(previous, segment)) {
1249
+ previous.text = `${previous.text} ${segment.text}`.trim();
1250
+ if (segment.endIso) previous.endIso = segment.endIso;
1251
+ mergedSegments += 1;
1252
+ } else {
1253
+ merged.push(segment);
1254
+ }
1255
+ }
1256
+ segments = merged;
1257
+ }
1258
+ return {
1259
+ conversation: { ...conversation, segments },
1260
+ droppedSegments,
1261
+ mergedSegments
1262
+ };
1263
+ }
1264
+ function canMerge(previous, next) {
1265
+ if (previous.speakerKey !== next.speakerKey) return false;
1266
+ const previousEnd = previous.endIso ? Date.parse(previous.endIso) : NaN;
1267
+ const nextStart = next.startIso ? Date.parse(next.startIso) : NaN;
1268
+ if (Number.isNaN(previousEnd) || Number.isNaN(nextStart)) return true;
1269
+ return nextStart - previousEnd <= MERGE_GAP_MS;
1270
+ }
1271
+ function stripFillerTokens(text) {
1272
+ return normalizeWhitespace(text.replace(FILLER_PATTERN, " "));
1273
+ }
1274
+ function collapseImmediateRepeats(text) {
1275
+ const words = text.split(/\s+/).filter((word) => word.length > 0);
1276
+ if (words.length < 2) return text.trim();
1277
+ const out = [];
1278
+ let index = 0;
1279
+ while (index < words.length) {
1280
+ out.push(words[index]);
1281
+ index += 1;
1282
+ let matched = true;
1283
+ while (matched) {
1284
+ matched = false;
1285
+ for (let size = 4; size >= 1; size--) {
1286
+ if (out.length < size || index + size > words.length) continue;
1287
+ const tail = out.slice(-size).join(" ").toLowerCase();
1288
+ if (!/\p{L}/u.test(tail)) continue;
1289
+ const ahead = words.slice(index, index + size).join(" ").toLowerCase();
1290
+ if (tail === ahead) {
1291
+ index += size;
1292
+ matched = true;
1293
+ break;
1294
+ }
1295
+ }
1296
+ }
1297
+ }
1298
+ return out.join(" ");
1299
+ }
1300
+ function isLowQualitySegment(text) {
1301
+ const trimmed = text.trim();
1302
+ if (trimmed.length === 0) return true;
1303
+ if (/^(.)\1{4,}$/.test(trimmed)) return true;
1304
+ const letters = trimmed.replace(/[^\p{L}\p{N}]/gu, "");
1305
+ if (letters.length === 0) return true;
1306
+ if (trimmed.length >= 12 && letters.length / trimmed.length < 0.3) {
1307
+ return true;
1308
+ }
1309
+ const words = trimmed.toLowerCase().split(/\s+/);
1310
+ if (words.length >= 5) {
1311
+ const unique = new Set(words);
1312
+ if (unique.size === 1) return true;
1313
+ }
1314
+ return false;
1315
+ }
1316
+ function normalizeWhitespace(text) {
1317
+ return text.replace(/\s+/g, " ").trim();
1318
+ }
1319
+
1320
+ // src/wearables/sync-state.ts
1321
+ import { promises as fsPromises } from "fs";
1322
+ import * as path2 from "path";
1323
+ var MAX_TRACKED_NATIVE_IDS = 5e3;
1324
+ var MAX_TRACKED_DAY_HASHES = 800;
1325
+ function syncStateFilePath(memoryDir) {
1326
+ return path2.join(memoryDir, "state", "wearables", "sync.json");
1327
+ }
1328
+ function emptySyncState() {
1329
+ return { version: 1, sources: {} };
1330
+ }
1331
+ async function loadSyncState(memoryDir) {
1332
+ const filePath = syncStateFilePath(memoryDir);
1333
+ let raw;
1334
+ try {
1335
+ raw = await fsPromises.readFile(filePath, "utf-8");
1336
+ } catch (err) {
1337
+ if (err.code === "ENOENT") {
1338
+ return emptySyncState();
1339
+ }
1340
+ throw err;
1341
+ }
1342
+ let parsed;
1343
+ try {
1344
+ parsed = JSON.parse(raw);
1345
+ } catch {
1346
+ return emptySyncState();
1347
+ }
1348
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed) || typeof parsed.sources !== "object" || parsed.sources === null) {
1349
+ return emptySyncState();
1350
+ }
1351
+ return { version: 1, sources: parsed.sources };
1352
+ }
1353
+ async function saveSyncState(memoryDir, state) {
1354
+ const filePath = syncStateFilePath(memoryDir);
1355
+ await fsPromises.mkdir(path2.dirname(filePath), { recursive: true });
1356
+ const tmpPath = `${filePath}.tmp-${process.pid}-${Date.now().toString(36)}`;
1357
+ await fsPromises.writeFile(
1358
+ tmpPath,
1359
+ `${JSON.stringify(state, null, 2)}
1360
+ `,
1361
+ "utf-8"
1362
+ );
1363
+ try {
1364
+ await fsPromises.rename(tmpPath, filePath);
1365
+ } catch (err) {
1366
+ await fsPromises.unlink(tmpPath).catch(() => void 0);
1367
+ throw err;
1368
+ }
1369
+ }
1370
+ function updateSourceSyncState(state, sourceId, update) {
1371
+ const previous = state.sources[sourceId];
1372
+ const mergedHashes = {
1373
+ ...previous?.dayHashes ?? {},
1374
+ ...update.dayHashes
1375
+ };
1376
+ const hashKeys = Object.keys(mergedHashes).sort();
1377
+ while (hashKeys.length > MAX_TRACKED_DAY_HASHES) {
1378
+ const oldest = hashKeys.shift();
1379
+ if (oldest === void 0) break;
1380
+ delete mergedHashes[oldest];
1381
+ }
1382
+ const mergedMemoryHashes = {
1383
+ ...previous?.memoryDayHashes ?? {},
1384
+ ...update.memoryDayHashes ?? {}
1385
+ };
1386
+ for (const day of update.clearMemoryDays ?? []) {
1387
+ if (!(day in (update.memoryDayHashes ?? {}))) {
1388
+ delete mergedMemoryHashes[day];
1389
+ }
1390
+ }
1391
+ const memoryHashKeys = Object.keys(mergedMemoryHashes).sort();
1392
+ while (memoryHashKeys.length > MAX_TRACKED_DAY_HASHES) {
1393
+ const oldest = memoryHashKeys.shift();
1394
+ if (oldest === void 0) break;
1395
+ delete mergedMemoryHashes[oldest];
1396
+ }
1397
+ const mergedNativeIds = [
1398
+ ...previous?.importedNativeMemoryIds ?? [],
1399
+ ...update.importedNativeMemoryIds ?? []
1400
+ ];
1401
+ const dedupedNativeIds = [...new Set(mergedNativeIds)];
1402
+ const boundedNativeIds = dedupedNativeIds.length > MAX_TRACKED_NATIVE_IDS ? dedupedNativeIds.slice(dedupedNativeIds.length - MAX_TRACKED_NATIVE_IDS) : dedupedNativeIds;
1403
+ const sortedDays = [...update.days].sort();
1404
+ const latestDay = sortedDays[sortedDays.length - 1];
1405
+ const lastDateSynced = latestDay !== void 0 && (!previous || previous.lastDateSynced < latestDay) ? latestDay : previous?.lastDateSynced ?? latestDay ?? "";
1406
+ return {
1407
+ version: 1,
1408
+ sources: {
1409
+ ...state.sources,
1410
+ [sourceId]: {
1411
+ lastSyncAt: update.syncedAt,
1412
+ lastDateSynced,
1413
+ dayHashes: mergedHashes,
1414
+ memoryDayHashes: mergedMemoryHashes,
1415
+ importedNativeMemoryIds: boundedNativeIds
1416
+ }
1417
+ }
1418
+ };
1419
+ }
1420
+
1421
+ // src/wearables/pipeline.ts
1422
+ var MAX_PAGES_PER_DAY = 50;
1423
+ var MAX_NATIVE_PAGES = 20;
1424
+ var DEFAULT_SYNC_DAYS = 2;
1425
+ var MAX_SYNC_DAYS = 90;
1426
+ function dateInTimezone(date, timezone) {
1427
+ try {
1428
+ const parts = new Intl.DateTimeFormat("en-CA", {
1429
+ timeZone: timezone,
1430
+ year: "numeric",
1431
+ month: "2-digit",
1432
+ day: "2-digit"
1433
+ }).formatToParts(date);
1434
+ const get = (type) => parts.find((part) => part.type === type)?.value ?? "";
1435
+ return `${get("year")}-${get("month")}-${get("day")}`;
1436
+ } catch {
1437
+ return date.toISOString().slice(0, 10);
1438
+ }
1439
+ }
1440
+ function resolveSyncDates(options, timezone, now) {
1441
+ if (options.date !== void 0) {
1442
+ if (!isValidTranscriptDate(options.date)) {
1443
+ throw new WearablesInputError(
1444
+ `wearables sync: invalid date '${options.date}' \u2014 expected YYYY-MM-DD`
1445
+ );
1446
+ }
1447
+ return [options.date];
1448
+ }
1449
+ let days = DEFAULT_SYNC_DAYS;
1450
+ if (options.days !== void 0) {
1451
+ if (!Number.isFinite(options.days) || !Number.isInteger(options.days) || options.days < 1 || options.days > MAX_SYNC_DAYS) {
1452
+ throw new WearablesInputError(
1453
+ `wearables sync: invalid days '${options.days}' \u2014 expected an integer between 1 and ${MAX_SYNC_DAYS}`
1454
+ );
1455
+ }
1456
+ days = options.days;
1457
+ }
1458
+ const dates = [];
1459
+ let cursor = dateInTimezone(now, timezone);
1460
+ for (let count = 0; count < days; count++) {
1461
+ dates.unshift(cursor);
1462
+ cursor = previousIsoDate(cursor);
1463
+ }
1464
+ return dates;
1465
+ }
1466
+ function previousIsoDate(date) {
1467
+ const parsed = /* @__PURE__ */ new Date(`${date}T00:00:00Z`);
1468
+ parsed.setUTCDate(parsed.getUTCDate() - 1);
1469
+ return parsed.toISOString().slice(0, 10);
1470
+ }
1471
+ async function fetchAllConversationsForDate(connector, date, timezone, signal, warnings) {
1472
+ const conversations = [];
1473
+ let cursor = void 0;
1474
+ for (let page = 0; page < MAX_PAGES_PER_DAY; page++) {
1475
+ const result = await connector.fetchConversations({
1476
+ date,
1477
+ timezone,
1478
+ cursor,
1479
+ signal
1480
+ });
1481
+ conversations.push(...result.conversations);
1482
+ if (!result.nextCursor) return { conversations, partial: false };
1483
+ cursor = result.nextCursor;
1484
+ }
1485
+ warnings.push(
1486
+ `${connector.id}: stopped paginating ${date} after ${MAX_PAGES_PER_DAY} pages \u2014 day may be partially synced (every sync refetches and re-warns until the provider day fits the cap)`
1487
+ );
1488
+ return { conversations, partial: true };
1489
+ }
1490
+ var PARTIAL_DAY_MARKER = "\n*Note: pagination safety cap reached during sync \u2014 this day may be incomplete.*\n";
1491
+ function emptyDayBody(sourceId, date) {
1492
+ return `# ${sourceId} transcript \u2014 ${date}
1493
+
1494
+ _No storable conversation content for this day (all segments were elided or dropped)._
1495
+ `;
1496
+ }
1497
+ function cleanDay(raw, sourceId, settings, config, userRedaction, correctionRules) {
1498
+ const out = {
1499
+ conversations: [],
1500
+ segmentsKept: 0,
1501
+ segmentsDropped: 0,
1502
+ redactions: 0,
1503
+ correctionsApplied: 0
1504
+ };
1505
+ for (const conversation of raw) {
1506
+ let current = conversation;
1507
+ if (config.offTheRecordEnabled) {
1508
+ const otr = applyOffTheRecord(current);
1509
+ current = otr.conversation;
1510
+ out.segmentsDropped += otr.droppedSegments;
1511
+ }
1512
+ const cleaned = cleanConversation(current, settings.cleanup);
1513
+ current = cleaned.conversation;
1514
+ out.segmentsDropped += cleaned.droppedSegments;
1515
+ const segments = current.segments.map((segment) => {
1516
+ let text = segment.text;
1517
+ if (config.redactionEnabled) {
1518
+ const redacted = redactText(text, userRedaction);
1519
+ text = redacted.text;
1520
+ out.redactions += redacted.redactions;
1521
+ }
1522
+ const corrected = applyCorrections(text, correctionRules, sourceId);
1523
+ out.correctionsApplied += corrected.applied;
1524
+ return { ...segment, text: corrected.text };
1525
+ });
1526
+ current = { ...current, segments };
1527
+ if (current.segments.length > 0) {
1528
+ out.conversations.push(current);
1529
+ out.segmentsKept += current.segments.length;
1530
+ }
1531
+ }
1532
+ return out;
1533
+ }
1534
+ async function syncWearableSource(connector, settings, config, options, deps) {
1535
+ const now = deps.now ? deps.now() : /* @__PURE__ */ new Date();
1536
+ const timezone = config.timezone ?? defaultTimezone();
1537
+ const dates = resolveSyncDates(options, timezone, now);
1538
+ const summary = {
1539
+ source: connector.id,
1540
+ days: dates,
1541
+ conversations: 0,
1542
+ segmentsKept: 0,
1543
+ segmentsDropped: 0,
1544
+ redactions: 0,
1545
+ correctionsApplied: 0,
1546
+ transcriptsWritten: [],
1547
+ memoriesCreated: 0,
1548
+ memoriesSkipped: 0,
1549
+ nativeMemoriesImported: 0,
1550
+ warnings: []
1551
+ };
1552
+ const registry = await loadSpeakerRegistry(deps.memoryDir);
1553
+ const stateRules = await loadCorrectionsFile(deps.memoryDir);
1554
+ const correctionRules = [
1555
+ ...compileCorrectionRules(config.corrections, "wearables.corrections"),
1556
+ ...compileCorrectionRules(stateRules, "state corrections")
1557
+ ];
1558
+ const userRedaction = compileRedactionPatterns(config.redactionPatterns);
1559
+ let syncState = await loadSyncState(deps.memoryDir);
1560
+ const previousState = syncState.sources[connector.id];
1561
+ const dayHashes = {};
1562
+ const memoryDayHashes = {};
1563
+ const failedMemoryDays = [];
1564
+ const importedNativeIds = [];
1565
+ for (const date of dates) {
1566
+ const fetched = await fetchAllConversationsForDate(
1567
+ connector,
1568
+ date,
1569
+ timezone,
1570
+ options.signal,
1571
+ summary.warnings
1572
+ );
1573
+ const cleaned = cleanDay(
1574
+ fetched.conversations,
1575
+ connector.id,
1576
+ settings,
1577
+ config,
1578
+ userRedaction,
1579
+ correctionRules
1580
+ );
1581
+ summary.conversations += cleaned.conversations.length;
1582
+ summary.segmentsKept += cleaned.segmentsKept;
1583
+ summary.segmentsDropped += cleaned.segmentsDropped;
1584
+ summary.redactions += cleaned.redactions;
1585
+ summary.correctionsApplied += cleaned.correctionsApplied;
1586
+ if (fetched.conversations.length === 0) {
1587
+ const existing = await deps.readDayContentHash(connector.id, date);
1588
+ if (existing !== null) {
1589
+ summary.warnings.push(
1590
+ `${connector.id}: provider returned no conversations for ${date} but a stored transcript exists \u2014 leaving it in place; delete the day file manually if the recordings were intentionally removed upstream`
1591
+ );
1592
+ }
1593
+ continue;
1594
+ }
1595
+ const allElided = cleaned.conversations.length === 0;
1596
+ let body = allElided ? emptyDayBody(connector.id, date) : composeDayTranscriptBody(
1597
+ connector.id,
1598
+ date,
1599
+ timezone,
1600
+ cleaned.conversations,
1601
+ registry
1602
+ );
1603
+ if (fetched.partial && !allElided) {
1604
+ body += PARTIAL_DAY_MARKER;
1605
+ }
1606
+ const bodyHash = hashTranscriptBody(body);
1607
+ const existingHash = await deps.readDayContentHash(connector.id, date);
1608
+ const changed = existingHash !== bodyHash;
1609
+ const shouldWrite = changed && (!allElided || existingHash !== null);
1610
+ if (shouldWrite) {
1611
+ const meta = composeDayTranscriptMeta(
1612
+ connector.id,
1613
+ date,
1614
+ timezone,
1615
+ cleaned.conversations,
1616
+ registry,
1617
+ body,
1618
+ now.toISOString()
1619
+ );
1620
+ await deps.writeDayTranscript(
1621
+ connector.id,
1622
+ date,
1623
+ serializeDayTranscript(meta, body)
1624
+ );
1625
+ summary.transcriptsWritten.push(date);
1626
+ }
1627
+ dayHashes[date] = bodyHash;
1628
+ if (allElided) continue;
1629
+ const memoryPassComplete = previousState?.memoryDayHashes?.[date] === bodyHash;
1630
+ if (settings.memoryMode !== "off" && (changed || options.forceMemories === true || !memoryPassComplete)) {
1631
+ if (!deps.memoryGen) {
1632
+ summary.warnings.push(
1633
+ `${connector.id}: memoryMode is '${settings.memoryMode}' but no extraction engine is available in this context \u2014 transcripts synced, memories skipped`
1634
+ );
1635
+ } else {
1636
+ let passClean = false;
1637
+ try {
1638
+ const generated = await generateWearableMemories(
1639
+ connector.id,
1640
+ date,
1641
+ cleaned.conversations,
1642
+ settings,
1643
+ registry,
1644
+ deps.memoryGen
1645
+ );
1646
+ summary.memoriesCreated += generated.created;
1647
+ summary.memoriesSkipped += generated.skipped;
1648
+ summary.warnings.push(...generated.warnings);
1649
+ passClean = generated.warnings.length === 0;
1650
+ if (config.digestEnabled) {
1651
+ const wrote = await writeDailyDigestMemory(
1652
+ connector.id,
1653
+ date,
1654
+ cleaned.conversations,
1655
+ settings,
1656
+ registry,
1657
+ deps.memoryGen.writer
1658
+ );
1659
+ if (wrote) summary.memoriesCreated += 1;
1660
+ }
1661
+ } catch (err) {
1662
+ passClean = false;
1663
+ summary.warnings.push(
1664
+ `${connector.id}: memory pass failed for ${date}: ${describeErrorForOperator(err)} \u2014 retries on the next sync`
1665
+ );
1666
+ }
1667
+ if (passClean) {
1668
+ memoryDayHashes[date] = bodyHash;
1669
+ } else {
1670
+ failedMemoryDays.push(date);
1671
+ }
1672
+ }
1673
+ } else if (settings.memoryMode !== "off" && memoryPassComplete) {
1674
+ memoryDayHashes[date] = bodyHash;
1675
+ }
1676
+ }
1677
+ if (settings.importNativeMemories === "review" && typeof connector.fetchNativeMemories === "function") {
1678
+ if (!deps.memoryGen) {
1679
+ summary.warnings.push(
1680
+ `${connector.id}: importNativeMemories is enabled but no memory writer is available in this context`
1681
+ );
1682
+ } else {
1683
+ const alreadyImported = new Set(
1684
+ previousState?.importedNativeMemoryIds ?? []
1685
+ );
1686
+ let cursor = void 0;
1687
+ for (let page = 0; page < MAX_NATIVE_PAGES; page++) {
1688
+ const result = await connector.fetchNativeMemories({
1689
+ cursor,
1690
+ signal: options.signal
1691
+ });
1692
+ const imported = await importNativeMemories(
1693
+ connector.id,
1694
+ result.memories,
1695
+ alreadyImported,
1696
+ deps.memoryGen.writer
1697
+ );
1698
+ summary.nativeMemoriesImported += imported.imported;
1699
+ importedNativeIds.push(...imported.importedIds);
1700
+ for (const id of imported.importedIds) alreadyImported.add(id);
1701
+ if (!result.nextCursor) break;
1702
+ cursor = result.nextCursor;
1703
+ if (page === MAX_NATIVE_PAGES - 1) {
1704
+ summary.warnings.push(
1705
+ `${connector.id}: stopped native-memory import after ${MAX_NATIVE_PAGES} pages \u2014 remaining items import on the next sync`
1706
+ );
1707
+ }
1708
+ }
1709
+ }
1710
+ }
1711
+ if (summary.transcriptsWritten.length > 0 && deps.afterTranscriptsWritten) {
1712
+ try {
1713
+ await deps.afterTranscriptsWritten();
1714
+ } catch (err) {
1715
+ summary.warnings.push(
1716
+ `search reindex failed (transcripts are stored and will index on the next update): ${describeErrorForOperator(err)}`
1717
+ );
1718
+ }
1719
+ }
1720
+ syncState = updateSourceSyncState(syncState, connector.id, {
1721
+ syncedAt: now.toISOString(),
1722
+ days: dates,
1723
+ dayHashes,
1724
+ memoryDayHashes,
1725
+ clearMemoryDays: failedMemoryDays,
1726
+ importedNativeMemoryIds: importedNativeIds
1727
+ });
1728
+ await saveSyncState(deps.memoryDir, syncState);
1729
+ return summary;
1730
+ }
1731
+ function defaultTimezone() {
1732
+ try {
1733
+ return Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC";
1734
+ } catch {
1735
+ return "UTC";
1736
+ }
1737
+ }
1738
+
1739
+ // src/wearables/registry.ts
1740
+ var registrations = /* @__PURE__ */ new Map();
1741
+ function registerWearableConnector(registration) {
1742
+ if (!registration || typeof registration !== "object") {
1743
+ throw new Error("wearable connector registration must be an object");
1744
+ }
1745
+ if (typeof registration.id !== "string" || registration.id.trim().length === 0) {
1746
+ throw new Error("wearable connector id must be a non-empty string");
1747
+ }
1748
+ if (typeof registration.factory !== "function") {
1749
+ throw new Error(
1750
+ `wearable connector '${registration.id}' must provide a factory function`
1751
+ );
1752
+ }
1753
+ const key = registration.id.trim();
1754
+ if (registrations.has(key)) {
1755
+ throw new Error(`wearable connector '${key}' is already registered`);
1756
+ }
1757
+ registrations.set(key, { ...registration, id: key });
1758
+ }
1759
+ function getWearableConnector(id) {
1760
+ if (typeof id !== "string") return void 0;
1761
+ const key = id.trim();
1762
+ if (key.length === 0) return void 0;
1763
+ return registrations.get(key);
1764
+ }
1765
+ function listWearableConnectors() {
1766
+ return [...registrations.keys()];
1767
+ }
1768
+ function clearWearableConnectors() {
1769
+ registrations.clear();
1770
+ }
1771
+ var BUILT_IN_CONNECTOR_PACKAGES = [
1772
+ { id: "limitless", suffix: "connector-limitless" },
1773
+ { id: "bee", suffix: "connector-bee" },
1774
+ { id: "omi", suffix: "connector-omi" }
1775
+ ];
1776
+ var loadFailuresWarned = /* @__PURE__ */ new Set();
1777
+ async function ensureBuiltInWearableConnectors() {
1778
+ for (const entry of BUILT_IN_CONNECTOR_PACKAGES) {
1779
+ if (registrations.has(entry.id)) continue;
1780
+ const specifier = "@remnic/" + entry.suffix;
1781
+ let mod;
1782
+ try {
1783
+ mod = await import(specifier);
1784
+ } catch (err) {
1785
+ if (isModuleNotFound(err, specifier)) continue;
1786
+ if (!loadFailuresWarned.has(specifier)) {
1787
+ loadFailuresWarned.add(specifier);
1788
+ log.warn(
1789
+ `wearables: failed to load optional connector package ${specifier}: ${err instanceof Error ? err.message : String(err)}`
1790
+ );
1791
+ }
1792
+ continue;
1793
+ }
1794
+ const registration = mod.wearableConnectorRegistration;
1795
+ if (!registration) continue;
1796
+ try {
1797
+ registerWearableConnector(registration);
1798
+ } catch {
1799
+ }
1800
+ }
1801
+ }
1802
+ function isModuleNotFound(err, specifier) {
1803
+ if (!err || typeof err !== "object") return false;
1804
+ const code = err.code;
1805
+ if (code !== "ERR_MODULE_NOT_FOUND" && code !== "MODULE_NOT_FOUND") {
1806
+ return false;
1807
+ }
1808
+ const message = err.message;
1809
+ return typeof message === "string" && message.includes(specifier);
1810
+ }
1811
+
1812
+ // src/wearables/service.ts
1813
+ function createWearableMemoryWriter(storage) {
1814
+ return {
1815
+ writeMemory: storage.writeMemory.bind(storage),
1816
+ hasFactContentHash: async (content) => {
1817
+ if (await storage.hasFactContentHash(content)) return true;
1818
+ const needle = content.trim();
1819
+ const memories = await storage.readAllMemories();
1820
+ return memories.some(
1821
+ (memory) => typeof memory.frontmatter.source === "string" && memory.frontmatter.source.startsWith(`${WEARABLE_SOURCE_PREFIX}:`) && memory.content.trim() === needle
1822
+ );
1823
+ }
1824
+ };
1825
+ }
1826
+ var SOURCE_ID_PATTERN = /^[a-z][a-z0-9-]{0,63}$/;
1827
+ function assertValidSourceId(source) {
1828
+ if (!SOURCE_ID_PATTERN.test(source)) {
1829
+ throw new WearablesInputError(
1830
+ `invalid source id '${source}' \u2014 expected lowercase letters, digits, and dashes`
1831
+ );
1832
+ }
1833
+ }
1834
+ var TRANSCRIPT_SEARCH_DEFAULT_LIMIT = 10;
1835
+ var TRANSCRIPT_SEARCH_MAX_LIMIT = 50;
1836
+ var MEMORY_LIST_DEFAULT_LIMIT = 50;
1837
+ var MEMORY_LIST_MAX_LIMIT = 200;
1838
+ var WearablesService = class {
1839
+ constructor(deps) {
1840
+ this.deps = deps;
1841
+ }
1842
+ deps;
1843
+ get enabled() {
1844
+ return this.deps.config.enabled;
1845
+ }
1846
+ assertEnabled() {
1847
+ if (!this.deps.config.enabled) {
1848
+ throw new WearablesInputError(
1849
+ "wearables are not enabled \u2014 set `wearables.enabled: true` (and configure at least one source) in the plugin config"
1850
+ );
1851
+ }
1852
+ }
1853
+ timezone() {
1854
+ return this.deps.config.timezone ?? defaultTimezone();
1855
+ }
1856
+ enabledSources() {
1857
+ return Object.entries(this.deps.config.sources).filter(
1858
+ ([, settings]) => settings.enabled
1859
+ );
1860
+ }
1861
+ /** Status for every configured source (and connector availability). */
1862
+ async status() {
1863
+ await ensureBuiltInWearableConnectors();
1864
+ const storage = await this.deps.getStorage();
1865
+ const syncState = await loadSyncState(storage.dir);
1866
+ const sources = [];
1867
+ for (const [sourceId, settings] of Object.entries(this.deps.config.sources)) {
1868
+ const registration = getWearableConnector(sourceId);
1869
+ const days = await storage.listWearableTranscriptDays(sourceId).catch(() => []);
1870
+ const state = syncState.sources[sourceId];
1871
+ sources.push({
1872
+ source: sourceId,
1873
+ displayName: registration?.displayName ?? sourceId,
1874
+ enabled: settings.enabled,
1875
+ connectorInstalled: registration !== void 0,
1876
+ memoryMode: settings.memoryMode,
1877
+ lastSyncAt: state?.lastSyncAt ?? null,
1878
+ lastDateSynced: state?.lastDateSynced ?? null,
1879
+ transcriptDays: days.length
1880
+ });
1881
+ }
1882
+ return {
1883
+ enabled: this.deps.config.enabled,
1884
+ timezone: this.timezone(),
1885
+ sources,
1886
+ connectorsInstalled: listWearableConnectors()
1887
+ };
1888
+ }
1889
+ /** Run a sync for one source or all enabled sources. */
1890
+ async sync(options) {
1891
+ this.assertEnabled();
1892
+ await ensureBuiltInWearableConnectors();
1893
+ const storage = await this.deps.getStorage();
1894
+ let targets;
1895
+ if (options.source !== void 0) {
1896
+ assertValidSourceId(options.source);
1897
+ const settings = this.deps.config.sources[options.source];
1898
+ if (!settings) {
1899
+ throw new WearablesInputError(
1900
+ `unknown wearable source '${options.source}' \u2014 configured sources: ${Object.keys(this.deps.config.sources).join(", ") || "(none)"}`
1901
+ );
1902
+ }
1903
+ if (!settings.enabled) {
1904
+ throw new WearablesInputError(
1905
+ `wearable source '${options.source}' is configured but disabled \u2014 set wearables.sources.${options.source}.enabled: true`
1906
+ );
1907
+ }
1908
+ targets = [[options.source, settings]];
1909
+ } else {
1910
+ targets = this.enabledSources();
1911
+ if (targets.length === 0) {
1912
+ throw new WearablesInputError(
1913
+ "no wearable sources are enabled \u2014 configure wearables.sources.<id>.enabled: true"
1914
+ );
1915
+ }
1916
+ }
1917
+ const memoryGen = this.deps.extract ? {
1918
+ extract: this.deps.extract,
1919
+ writer: createWearableMemoryWriter(storage)
1920
+ } : null;
1921
+ const summaries = [];
1922
+ for (const [sourceId, settings] of targets) {
1923
+ const registration = getWearableConnector(sourceId);
1924
+ if (!registration) {
1925
+ throw new WearablesInputError(
1926
+ `wearable source '${sourceId}' is enabled but its connector package is not installed.
1927
+ Install it alongside Remnic:
1928
+ npm install @remnic/connector-${sourceId}`
1929
+ );
1930
+ }
1931
+ const connector = registration.factory({
1932
+ settings,
1933
+ timezone: this.timezone()
1934
+ });
1935
+ const summary = await syncWearableSource(
1936
+ connector,
1937
+ settings,
1938
+ this.deps.config,
1939
+ options,
1940
+ {
1941
+ memoryDir: storage.dir,
1942
+ readDayContentHash: async (source, date) => {
1943
+ const raw = await storage.readWearableDayTranscript(source, date);
1944
+ if (raw === null) return null;
1945
+ return parseDayTranscript(raw)?.meta.contentHash ?? null;
1946
+ },
1947
+ writeDayTranscript: (source, date, serialized) => storage.writeWearableDayTranscript(source, date, serialized),
1948
+ afterTranscriptsWritten: this.deps.reindexSearch,
1949
+ memoryGen
1950
+ }
1951
+ );
1952
+ summaries.push(summary);
1953
+ }
1954
+ return summaries;
1955
+ }
1956
+ /** Verify connectivity/credentials for one source. */
1957
+ async checkAuth(sourceId) {
1958
+ this.assertEnabled();
1959
+ await ensureBuiltInWearableConnectors();
1960
+ assertValidSourceId(sourceId);
1961
+ const settings = this.deps.config.sources[sourceId];
1962
+ if (!settings) {
1963
+ throw new WearablesInputError(`unknown wearable source '${sourceId}'`);
1964
+ }
1965
+ const registration = getWearableConnector(sourceId);
1966
+ if (!registration) {
1967
+ return {
1968
+ ok: false,
1969
+ detail: `connector package @remnic/connector-${sourceId} is not installed`
1970
+ };
1971
+ }
1972
+ const connector = registration.factory({
1973
+ settings,
1974
+ timezone: this.timezone()
1975
+ });
1976
+ try {
1977
+ return await connector.verifyAuth();
1978
+ } catch (err) {
1979
+ return {
1980
+ ok: false,
1981
+ detail: describeErrorForOperator(err)
1982
+ };
1983
+ }
1984
+ }
1985
+ /**
1986
+ * Full transcript(s) for a day. Without `source`, returns every
1987
+ * source that recorded that day, annotated with overlap hints.
1988
+ */
1989
+ async dayTranscript(date, sourceId) {
1990
+ if (!isValidTranscriptDate(date)) {
1991
+ throw new WearablesInputError(`invalid date '${date}' \u2014 expected YYYY-MM-DD`);
1992
+ }
1993
+ if (sourceId !== void 0) assertValidSourceId(sourceId);
1994
+ const storage = await this.deps.getStorage();
1995
+ const targets = sourceId !== void 0 ? [sourceId] : (await storage.listWearableTranscriptDays()).filter((entry) => entry.date === date).map((entry) => entry.source);
1996
+ const views = [];
1997
+ for (const source of [...new Set(targets)]) {
1998
+ const raw = await storage.readWearableDayTranscript(source, date);
1999
+ if (raw === null) continue;
2000
+ const parsed = parseDayTranscript(raw);
2001
+ views.push({
2002
+ source,
2003
+ date,
2004
+ meta: parsed?.meta ?? null,
2005
+ body: parsed?.body ?? raw,
2006
+ overlapsWith: []
2007
+ });
2008
+ }
2009
+ for (const view of views) {
2010
+ view.overlapsWith = views.map((other) => other.source).filter((other) => other !== view.source);
2011
+ }
2012
+ return views;
2013
+ }
2014
+ /** List days that have stored transcripts. */
2015
+ async listDays(sourceId) {
2016
+ if (sourceId !== void 0) assertValidSourceId(sourceId);
2017
+ const storage = await this.deps.getStorage();
2018
+ return storage.listWearableTranscriptDays(sourceId);
2019
+ }
2020
+ /**
2021
+ * Search stored transcripts. Uses the indexed backend when available
2022
+ * and falls back to a bounded substring scan otherwise — the two
2023
+ * paths are distinguishable in the result (`backend`) so callers can
2024
+ * tell "no hits" from "weaker search ran".
2025
+ */
2026
+ async searchTranscripts(query, options = {}) {
2027
+ const trimmed = query.trim();
2028
+ if (trimmed.length === 0) {
2029
+ throw new WearablesInputError("transcript search requires a non-empty query");
2030
+ }
2031
+ if (options.source !== void 0) assertValidSourceId(options.source);
2032
+ for (const [name, value] of [
2033
+ ["from", options.from],
2034
+ ["to", options.to]
2035
+ ]) {
2036
+ if (value !== void 0 && !isValidTranscriptDate(value)) {
2037
+ throw new WearablesInputError(`invalid ${name} date '${value}' \u2014 expected YYYY-MM-DD`);
2038
+ }
2039
+ }
2040
+ const limit = clampLimit(
2041
+ options.limit,
2042
+ TRANSCRIPT_SEARCH_DEFAULT_LIMIT,
2043
+ TRANSCRIPT_SEARCH_MAX_LIMIT,
2044
+ "limit"
2045
+ );
2046
+ const matchesScope = (source, date) => {
2047
+ if (options.source !== void 0 && source !== options.source) return false;
2048
+ if (options.from !== void 0 && date < options.from) return false;
2049
+ if (options.to !== void 0 && date > options.to) return false;
2050
+ return true;
2051
+ };
2052
+ if (this.deps.searchBackend) {
2053
+ const hits = await this.deps.searchBackend.search(trimmed, limit * 5);
2054
+ if (hits !== null) {
2055
+ const results2 = [];
2056
+ for (const hit of hits) {
2057
+ const located = locateTranscriptPath(hit.path);
2058
+ if (!located) continue;
2059
+ if (!matchesScope(located.source, located.date)) continue;
2060
+ results2.push({
2061
+ source: located.source,
2062
+ date: located.date,
2063
+ score: hit.score,
2064
+ snippet: hit.preview,
2065
+ backend: "indexed"
2066
+ });
2067
+ if (results2.length >= limit) break;
2068
+ }
2069
+ if (results2.length > 0) {
2070
+ return results2;
2071
+ }
2072
+ }
2073
+ }
2074
+ const storage = await this.deps.getStorage();
2075
+ const days = await storage.listWearableTranscriptDays(options.source);
2076
+ const needle = trimmed.toLowerCase();
2077
+ const results = [];
2078
+ for (const { source, date } of days) {
2079
+ if (!matchesScope(source, date)) continue;
2080
+ const raw = await storage.readWearableDayTranscript(source, date);
2081
+ if (raw === null) continue;
2082
+ const body = parseDayTranscript(raw)?.body ?? raw;
2083
+ const lower = body.toLowerCase();
2084
+ const index = lower.indexOf(needle);
2085
+ if (index === -1) continue;
2086
+ results.push({
2087
+ source,
2088
+ date,
2089
+ score: 0,
2090
+ snippet: extractSnippet(body, index, needle.length),
2091
+ backend: "scan"
2092
+ });
2093
+ if (results.length >= limit) break;
2094
+ }
2095
+ return results;
2096
+ }
2097
+ /**
2098
+ * Memories created from wearable transcripts, filterable by source
2099
+ * and/or day. Includes pending_review candidates — the whole point of
2100
+ * review mode is seeing what's queued.
2101
+ */
2102
+ async transcriptMemories(options = {}) {
2103
+ if (options.date !== void 0 && !isValidTranscriptDate(options.date)) {
2104
+ throw new WearablesInputError(`invalid date '${options.date}' \u2014 expected YYYY-MM-DD`);
2105
+ }
2106
+ if (options.source !== void 0) assertValidSourceId(options.source);
2107
+ const limit = clampLimit(
2108
+ options.limit,
2109
+ MEMORY_LIST_DEFAULT_LIMIT,
2110
+ MEMORY_LIST_MAX_LIMIT,
2111
+ "limit"
2112
+ );
2113
+ const storage = await this.deps.getStorage();
2114
+ const memories = await storage.readAllMemories();
2115
+ const results = [];
2116
+ for (const memory of memories) {
2117
+ const source = memory.frontmatter.source;
2118
+ if (typeof source !== "string" || !source.startsWith(`${WEARABLE_SOURCE_PREFIX}:`)) {
2119
+ continue;
2120
+ }
2121
+ const attrs = memory.frontmatter.structuredAttributes ?? {};
2122
+ const sourceId = attrs.wearableSource;
2123
+ if (options.source !== void 0) {
2124
+ if (sourceId !== options.source && source !== wearableSourceLabel(options.source) && source !== `${wearableSourceLabel(options.source)}:native`) {
2125
+ continue;
2126
+ }
2127
+ }
2128
+ if (options.date !== void 0 && attrs.wearableDate !== options.date) {
2129
+ continue;
2130
+ }
2131
+ results.push({
2132
+ id: memory.frontmatter.id,
2133
+ source: sourceId ?? source,
2134
+ date: attrs.wearableDate,
2135
+ conversationId: attrs.wearableConversationId,
2136
+ status: memory.frontmatter.status,
2137
+ content: memory.content,
2138
+ created: memory.frontmatter.created
2139
+ });
2140
+ }
2141
+ results.sort((a, b) => {
2142
+ if (a.created > b.created) return -1;
2143
+ if (a.created < b.created) return 1;
2144
+ if (a.id < b.id) return -1;
2145
+ if (a.id > b.id) return 1;
2146
+ return 0;
2147
+ });
2148
+ return results.slice(0, limit);
2149
+ }
2150
+ // -- speakers -------------------------------------------------------------
2151
+ async listSpeakers() {
2152
+ const storage = await this.deps.getStorage();
2153
+ return loadSpeakerRegistry(storage.dir);
2154
+ }
2155
+ async setSpeaker(sourceId, speakerKey, name, opts = {}) {
2156
+ if (typeof name !== "string" || name.trim().length === 0) {
2157
+ throw new WearablesInputError("speaker name must be a non-empty string");
2158
+ }
2159
+ if (typeof speakerKey !== "string" || speakerKey.trim().length === 0) {
2160
+ throw new WearablesInputError("speaker key must be a non-empty string");
2161
+ }
2162
+ const storage = await this.deps.getStorage();
2163
+ const registry = await loadSpeakerRegistry(storage.dir);
2164
+ registry.speakers[speakerRegistryKey(sourceId, speakerKey.trim())] = {
2165
+ name: name.trim(),
2166
+ ...opts.isSelf === true ? { isSelf: true } : {},
2167
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
2168
+ };
2169
+ await saveSpeakerRegistry(storage.dir, registry);
2170
+ return registry;
2171
+ }
2172
+ async setSelfName(name) {
2173
+ if (typeof name !== "string" || name.trim().length === 0) {
2174
+ throw new WearablesInputError("self name must be a non-empty string");
2175
+ }
2176
+ const storage = await this.deps.getStorage();
2177
+ const registry = await loadSpeakerRegistry(storage.dir);
2178
+ registry.selfName = name.trim();
2179
+ await saveSpeakerRegistry(storage.dir, registry);
2180
+ return registry;
2181
+ }
2182
+ async removeSpeaker(sourceId, speakerKey) {
2183
+ const storage = await this.deps.getStorage();
2184
+ const registry = await loadSpeakerRegistry(storage.dir);
2185
+ const key = speakerRegistryKey(sourceId, speakerKey.trim());
2186
+ if (!(key in registry.speakers)) {
2187
+ throw new WearablesInputError(`no speaker override stored for '${key}'`);
2188
+ }
2189
+ delete registry.speakers[key];
2190
+ await saveSpeakerRegistry(storage.dir, registry);
2191
+ return registry;
2192
+ }
2193
+ // -- corrections ----------------------------------------------------------
2194
+ async listCorrections() {
2195
+ const storage = await this.deps.getStorage();
2196
+ return {
2197
+ fromConfig: this.deps.config.corrections,
2198
+ fromState: await loadCorrectionsFile(storage.dir),
2199
+ stateFilePath: correctionsFilePath(storage.dir)
2200
+ };
2201
+ }
2202
+ async addCorrection(rule) {
2203
+ compileCorrectionRule(rule, "correction");
2204
+ const storage = await this.deps.getStorage();
2205
+ const rules = await loadCorrectionsFile(storage.dir);
2206
+ const duplicate = rules.some(
2207
+ (existing) => existing.match === rule.match && existing.replace === rule.replace && existing.regex === true === (rule.regex === true)
2208
+ );
2209
+ if (duplicate) {
2210
+ throw new WearablesInputError(
2211
+ `an identical correction rule already exists (match: ${JSON.stringify(rule.match)})`
2212
+ );
2213
+ }
2214
+ rules.push(rule);
2215
+ await saveCorrectionsFile(storage.dir, rules);
2216
+ }
2217
+ async removeCorrection(index) {
2218
+ if (!Number.isInteger(index) || index < 0) {
2219
+ throw new WearablesInputError(`invalid correction index '${index}'`);
2220
+ }
2221
+ const storage = await this.deps.getStorage();
2222
+ const rules = await loadCorrectionsFile(storage.dir);
2223
+ if (index >= rules.length) {
2224
+ throw new WearablesInputError(
2225
+ `correction index ${index} is out of range (have ${rules.length} state rule${rules.length === 1 ? "" : "s"})`
2226
+ );
2227
+ }
2228
+ const [removed] = rules.splice(index, 1);
2229
+ await saveCorrectionsFile(storage.dir, rules);
2230
+ return removed;
2231
+ }
2232
+ };
2233
+ function clampLimit(value, fallback, max, label) {
2234
+ if (value === void 0) return fallback;
2235
+ if (!Number.isFinite(value) || !Number.isInteger(value) || value < 1 || value > max) {
2236
+ throw new WearablesInputError(
2237
+ `invalid ${label} '${value}' \u2014 expected an integer between 1 and ${max}`
2238
+ );
2239
+ }
2240
+ return value;
2241
+ }
2242
+ function locateTranscriptPath(hitPath) {
2243
+ const normalized = hitPath.replace(/\\/g, "/");
2244
+ const match = normalized.match(
2245
+ /(?:^|\/)wearables\/([a-z][a-z0-9-]{0,63})\/(\d{4}-\d{2}-\d{2})\.md$/
2246
+ );
2247
+ if (!match) return null;
2248
+ if (!isValidTranscriptDate(match[2])) return null;
2249
+ return { source: match[1], date: match[2] };
2250
+ }
2251
+ function extractSnippet(body, index, matchLength) {
2252
+ const start = Math.max(0, index - 80);
2253
+ const end = Math.min(body.length, index + matchLength + 80);
2254
+ const prefix = start > 0 ? "\u2026" : "";
2255
+ const suffix = end < body.length ? "\u2026" : "";
2256
+ return `${prefix}${body.slice(start, end).replace(/\s+/g, " ").trim()}${suffix}`;
2257
+ }
2258
+
940
2259
  // src/orchestrator.ts
941
2260
  function dedupeEntitySynthesisEvidenceEntries(entries) {
942
2261
  const dedupedEvidenceEntries = [];
@@ -1072,7 +2391,7 @@ async function qmdStartupCollectionCheckWithTimeout(promise, controller, label)
1072
2391
  return await Promise.race([checkedPromise, timeoutPromise]);
1073
2392
  }
1074
2393
  function defaultWorkspaceDir() {
1075
- return path2.join(os.homedir(), ".openclaw", "workspace");
2394
+ return path3.join(os.homedir(), ".openclaw", "workspace");
1076
2395
  }
1077
2396
  function sanitizeSessionKeyForFilename(sessionKey) {
1078
2397
  const readable = sessionKey.replace(/[^a-zA-Z0-9._-]/g, "_");
@@ -1381,11 +2700,11 @@ function mergeGraphExpandedResults(primary, expanded) {
1381
2700
  return Array.from(mergedByPath.values());
1382
2701
  }
1383
2702
  function graphPathRelativeToStorage(storageDir, candidatePath) {
1384
- const absolutePath = path2.isAbsolute(candidatePath) ? candidatePath : path2.resolve(storageDir, candidatePath);
1385
- const rel = path2.relative(storageDir, absolutePath);
2703
+ const absolutePath = path3.isAbsolute(candidatePath) ? candidatePath : path3.resolve(storageDir, candidatePath);
2704
+ const rel = path3.relative(storageDir, absolutePath);
1386
2705
  if (!rel || rel === ".") return null;
1387
2706
  if (rel.startsWith("..")) return null;
1388
- return rel.split(path2.sep).join("/");
2707
+ return rel.split(path3.sep).join("/");
1389
2708
  }
1390
2709
  function normalizeGraphActivationScore(score) {
1391
2710
  const bounded = Number.isFinite(score) && score > 0 ? score : 0;
@@ -1527,7 +2846,7 @@ function buildMemoryPathById(allMemsForGraph, storageDir) {
1527
2846
  for (const mem of allMemsForGraph ?? []) {
1528
2847
  const id = mem.frontmatter.id;
1529
2848
  if (!id) continue;
1530
- pathById.set(id, path2.relative(storageDir, mem.path));
2849
+ pathById.set(id, path3.relative(storageDir, mem.path));
1531
2850
  }
1532
2851
  return pathById;
1533
2852
  }
@@ -1535,7 +2854,7 @@ function appendMemoryToGraphContext(options) {
1535
2854
  if (!Array.isArray(options.allMemsForGraph)) return;
1536
2855
  const nowIso = (/* @__PURE__ */ new Date()).toISOString();
1537
2856
  options.allMemsForGraph.push({
1538
- path: path2.join(options.storageDir, options.memoryRelPath),
2857
+ path: path3.join(options.storageDir, options.memoryRelPath),
1539
2858
  content: options.content,
1540
2859
  frontmatter: {
1541
2860
  id: options.memoryId,
@@ -1555,16 +2874,16 @@ function resolvePersistedMemoryRelativePath(options) {
1555
2874
  const persisted = options.pathById.get(options.memoryId);
1556
2875
  if (persisted) return persisted;
1557
2876
  if (options.category === "correction") {
1558
- return path2.join("corrections", `${options.memoryId}.md`);
2877
+ return path3.join("corrections", `${options.memoryId}.md`);
1559
2878
  }
1560
2879
  const subtree = options.category === "procedure" ? "procedures" : options.category === "reasoning_trace" ? "reasoning-traces" : "facts";
1561
2880
  const idParts = options.memoryId.split("-");
1562
2881
  const maybeTimestamp = Number(idParts[1]);
1563
2882
  if (Number.isFinite(maybeTimestamp) && maybeTimestamp > 0) {
1564
2883
  const day = new Date(maybeTimestamp).toISOString().slice(0, 10);
1565
- return path2.join(subtree, day, `${options.memoryId}.md`);
2884
+ return path3.join(subtree, day, `${options.memoryId}.md`);
1566
2885
  }
1567
- return path2.join(subtree, `${options.memoryId}.md`);
2886
+ return path3.join(subtree, `${options.memoryId}.md`);
1568
2887
  }
1569
2888
  var Orchestrator = class _Orchestrator {
1570
2889
  storage;
@@ -1683,6 +3002,7 @@ var Orchestrator = class _Orchestrator {
1683
3002
  consolidationInFlight = false;
1684
3003
  consolidationObservers = /* @__PURE__ */ new Set();
1685
3004
  qmdMaintenanceTimer = null;
3005
+ wearablesServiceInstance = null;
1686
3006
  qmdMaintenancePending = false;
1687
3007
  qmdMaintenanceInFlight = false;
1688
3008
  lastQmdEmbedAtMs = 0;
@@ -2001,7 +3321,7 @@ var Orchestrator = class _Orchestrator {
2001
3321
  this.config = config;
2002
3322
  this.profiler = new ProfilingCollector({
2003
3323
  enabled: config.profilingEnabled,
2004
- storageDir: config.profilingStorageDir || path2.join(config.memoryDir, "profiling"),
3324
+ storageDir: config.profilingStorageDir || path3.join(config.memoryDir, "profiling"),
2005
3325
  maxTraces: config.profilingMaxTraces
2006
3326
  });
2007
3327
  this.storageRouter = new NamespaceStorageRouter(config);
@@ -2036,7 +3356,7 @@ var Orchestrator = class _Orchestrator {
2036
3356
  this.compounding = config.compoundingEnabled ? new CompoundingEngine(config, this.storage) : void 0;
2037
3357
  this.buffer = new SmartBuffer(config, this.storage);
2038
3358
  this.transcript = new TranscriptManager(config);
2039
- this.conversationIndexDir = path2.join(
3359
+ this.conversationIndexDir = path3.join(
2040
3360
  config.memoryDir,
2041
3361
  "conversation-index",
2042
3362
  "chunks"
@@ -2093,7 +3413,7 @@ var Orchestrator = class _Orchestrator {
2093
3413
  this.modelRegistry
2094
3414
  );
2095
3415
  this.threading = new ThreadingManager(
2096
- path2.join(config.memoryDir, "threads"),
3416
+ path3.join(config.memoryDir, "threads"),
2097
3417
  config.threadingGapMinutes
2098
3418
  );
2099
3419
  this.tmtBuilder = new TmtBuilder(config.memoryDir, {
@@ -2376,7 +3696,7 @@ var Orchestrator = class _Orchestrator {
2376
3696
  const files = await readdir(wsDir).catch(() => []);
2377
3697
  for (const f of files) {
2378
3698
  if (!f.startsWith(".compaction-reset-signal-")) continue;
2379
- const fp = path2.join(wsDir, f);
3699
+ const fp = path3.join(wsDir, f);
2380
3700
  const s = await stat(fp).catch(() => null);
2381
3701
  if (s && Date.now() - s.mtimeMs >= COMPACTION_SIGNAL_MAX_AGE_MS) {
2382
3702
  await unlink(fp).catch(() => {
@@ -2738,7 +4058,7 @@ var Orchestrator = class _Orchestrator {
2738
4058
  */
2739
4059
  async autoRegisterDaySummaryCron() {
2740
4060
  const home = resolveHomeDir();
2741
- const jobsPath = path2.join(home, ".openclaw", "cron", "jobs.json");
4061
+ const jobsPath = path3.join(home, ".openclaw", "cron", "jobs.json");
2742
4062
  try {
2743
4063
  if (!existsSync(jobsPath)) {
2744
4064
  log.debug(
@@ -2762,7 +4082,7 @@ var Orchestrator = class _Orchestrator {
2762
4082
  }
2763
4083
  async autoRegisterNightlyGovernanceCron() {
2764
4084
  const home = resolveHomeDir();
2765
- const jobsPath = path2.join(home, ".openclaw", "cron", "jobs.json");
4085
+ const jobsPath = path3.join(home, ".openclaw", "cron", "jobs.json");
2766
4086
  try {
2767
4087
  if (!existsSync(jobsPath)) {
2768
4088
  log.debug("nightly governance cron: jobs.json not found, skipping auto-register");
@@ -2784,7 +4104,7 @@ var Orchestrator = class _Orchestrator {
2784
4104
  }
2785
4105
  async autoRegisterProceduralMiningCron() {
2786
4106
  const home = resolveHomeDir();
2787
- const jobsPath = path2.join(home, ".openclaw", "cron", "jobs.json");
4107
+ const jobsPath = path3.join(home, ".openclaw", "cron", "jobs.json");
2788
4108
  try {
2789
4109
  if (!existsSync(jobsPath)) {
2790
4110
  log.debug("procedural mining cron: jobs.json not found, skipping auto-register");
@@ -2804,7 +4124,7 @@ var Orchestrator = class _Orchestrator {
2804
4124
  }
2805
4125
  async autoRegisterContradictionScanCron() {
2806
4126
  const home = resolveHomeDir();
2807
- const jobsPath = path2.join(home, ".openclaw", "cron", "jobs.json");
4127
+ const jobsPath = path3.join(home, ".openclaw", "cron", "jobs.json");
2808
4128
  try {
2809
4129
  if (!existsSync(jobsPath)) {
2810
4130
  log.debug("contradiction scan cron: jobs.json not found, skipping auto-register");
@@ -2824,7 +4144,7 @@ var Orchestrator = class _Orchestrator {
2824
4144
  }
2825
4145
  async autoRegisterPatternReinforcementCron() {
2826
4146
  const home = resolveHomeDir();
2827
- const jobsPath = path2.join(home, ".openclaw", "cron", "jobs.json");
4147
+ const jobsPath = path3.join(home, ".openclaw", "cron", "jobs.json");
2828
4148
  try {
2829
4149
  if (!existsSync(jobsPath)) {
2830
4150
  log.debug("pattern reinforcement cron: jobs.json not found, skipping auto-register");
@@ -2886,7 +4206,7 @@ var Orchestrator = class _Orchestrator {
2886
4206
  }
2887
4207
  async autoRegisterGraphEdgeDecayCron() {
2888
4208
  const home = resolveHomeDir();
2889
- const jobsPath = path2.join(home, ".openclaw", "cron", "jobs.json");
4209
+ const jobsPath = path3.join(home, ".openclaw", "cron", "jobs.json");
2890
4210
  try {
2891
4211
  if (!existsSync(jobsPath)) {
2892
4212
  log.debug("graph edge decay cron: jobs.json not found, skipping auto-register");
@@ -2943,15 +4263,15 @@ ${doc.content}` : doc.content,
2943
4263
  this.lastFileHygieneRunAtMs = now;
2944
4264
  if (hygiene.rotateEnabled) {
2945
4265
  for (const rel of hygiene.rotatePaths) {
2946
- const abs = path2.isAbsolute(rel) ? rel : path2.join(this.config.workspaceDir, rel);
4266
+ const abs = path3.isAbsolute(rel) ? rel : path3.join(this.config.workspaceDir, rel);
2947
4267
  try {
2948
4268
  const raw = await readFile2(abs, "utf-8");
2949
4269
  if (raw.length > hygiene.rotateMaxBytes) {
2950
- const archiveDir = path2.join(
4270
+ const archiveDir = path3.join(
2951
4271
  this.config.workspaceDir,
2952
4272
  hygiene.archiveDir
2953
4273
  );
2954
- const base = path2.basename(abs);
4274
+ const base = path3.basename(abs);
2955
4275
  const prefix = base.toUpperCase().replace(/\.MD$/i, "").replace(/[^A-Z0-9]+/g, "-") || "FILE";
2956
4276
  const { newContent } = await rotateMarkdownFileToArchive({
2957
4277
  filePath: abs,
@@ -2976,8 +4296,8 @@ ${doc.content}` : doc.content,
2976
4296
  log.warn(w.message);
2977
4297
  }
2978
4298
  if (hygiene.warningsLogEnabled && warnings.length > 0) {
2979
- const fp = path2.join(this.config.memoryDir, hygiene.warningsLogPath);
2980
- await mkdir2(path2.dirname(fp), { recursive: true });
4299
+ const fp = path3.join(this.config.memoryDir, hygiene.warningsLogPath);
4300
+ await mkdir2(path3.dirname(fp), { recursive: true });
2981
4301
  const stamp = (/* @__PURE__ */ new Date()).toISOString();
2982
4302
  const block = `
2983
4303
 
@@ -3446,16 +4766,16 @@ ${evidenceText}`
3446
4766
  const datesToScan = [yesterday, utcToday].filter(
3447
4767
  (v, i, a) => a.indexOf(v) === i
3448
4768
  );
3449
- const factsBaseDir = path2.join(storage.dir, "facts");
4769
+ const factsBaseDir = path3.join(storage.dir, "facts");
3450
4770
  const MAX_CHARS = 1e5;
3451
4771
  const facts = [];
3452
4772
  for (const date of datesToScan) {
3453
- const factsDir = path2.join(factsBaseDir, date);
4773
+ const factsDir = path3.join(factsBaseDir, date);
3454
4774
  try {
3455
4775
  const entries = await readdir(factsDir, { withFileTypes: true });
3456
4776
  for (const entry of entries) {
3457
4777
  if (!entry.name.endsWith(".md")) continue;
3458
- const fullPath = path2.join(factsDir, entry.name);
4778
+ const fullPath = path3.join(factsDir, entry.name);
3459
4779
  try {
3460
4780
  const raw = await readFile2(fullPath, "utf-8");
3461
4781
  const fmMatch = raw.match(/^---\n([\s\S]*?)\n---\n?([\s\S]*)$/);
@@ -3471,7 +4791,7 @@ ${evidenceText}`
3471
4791
  facts.push({
3472
4792
  path: fullPath,
3473
4793
  frontmatter: {
3474
- id: fm.id || path2.basename(entry.name, ".md"),
4794
+ id: fm.id || path3.basename(entry.name, ".md"),
3475
4795
  category: fm.category || "fact",
3476
4796
  created: fm.created || "unknown",
3477
4797
  updated: fm.updated || fm.created || "unknown",
@@ -3492,13 +4812,13 @@ ${evidenceText}`
3492
4812
  (a, b) => a.frontmatter.created < b.frontmatter.created ? -1 : 1
3493
4813
  );
3494
4814
  const hourlySummaries = [];
3495
- const hourlyBaseDir = path2.join(storage.dir, "summaries", "hourly");
4815
+ const hourlyBaseDir = path3.join(storage.dir, "summaries", "hourly");
3496
4816
  try {
3497
4817
  const sessionKeys = await readdir(hourlyBaseDir, { withFileTypes: true });
3498
4818
  for (const sk of sessionKeys) {
3499
4819
  if (!sk.isDirectory()) continue;
3500
4820
  for (const date of datesToScan) {
3501
- const summaryFile = path2.join(hourlyBaseDir, sk.name, `${date}.md`);
4821
+ const summaryFile = path3.join(hourlyBaseDir, sk.name, `${date}.md`);
3502
4822
  try {
3503
4823
  const raw = await readFile2(summaryFile, "utf-8");
3504
4824
  if (raw.trim().length > 0) {
@@ -3596,7 +4916,7 @@ ${evidenceText}`
3596
4916
  }
3597
4917
  async getLastGraphRecallSnapshot(namespace) {
3598
4918
  const storage = await this.getStorage(namespace);
3599
- const snapshotPath = path2.join(
4919
+ const snapshotPath = path3.join(
3600
4920
  storage.dir,
3601
4921
  "state",
3602
4922
  "last_graph_recall.json"
@@ -3635,7 +4955,7 @@ ${evidenceText}`
3635
4955
  }
3636
4956
  async getLastIntentSnapshot(namespace) {
3637
4957
  const storage = await this.getStorage(namespace);
3638
- const snapshotPath = path2.join(storage.dir, "state", "last_intent.json");
4958
+ const snapshotPath = path3.join(storage.dir, "state", "last_intent.json");
3639
4959
  try {
3640
4960
  const raw = await readFile2(snapshotPath, "utf-8");
3641
4961
  const parsed = JSON.parse(raw);
@@ -3668,7 +4988,7 @@ ${evidenceText}`
3668
4988
  }
3669
4989
  async getLastQmdRecallSnapshot(namespace) {
3670
4990
  const storage = await this.getStorage(namespace);
3671
- const snapshotPath = path2.join(
4991
+ const snapshotPath = path3.join(
3672
4992
  storage.dir,
3673
4993
  "state",
3674
4994
  "last_qmd_recall.json"
@@ -3821,7 +5141,7 @@ ${r.snippet.trim()}
3821
5141
  const entries = await readdir(dir, { withFileTypes: true });
3822
5142
  let total = 0;
3823
5143
  for (const entry of entries) {
3824
- const fullPath = path2.join(dir, entry.name);
5144
+ const fullPath = path3.join(dir, entry.name);
3825
5145
  if (entry.isDirectory()) {
3826
5146
  total += await this.countConversationChunkDocs(fullPath);
3827
5147
  continue;
@@ -4717,7 +6037,7 @@ ${r.snippet.trim()}
4717
6037
  0
4718
6038
  );
4719
6039
  seedPaths.push(
4720
- ...seedRelativePaths.map((rel) => path2.join(storage.dir, rel))
6040
+ ...seedRelativePaths.map((rel) => path3.join(storage.dir, rel))
4721
6041
  );
4722
6042
  const seedSet = new Set(seedRelativePaths);
4723
6043
  const expanded = await this.graphIndexFor(storage).spreadingActivation(
@@ -4728,7 +6048,7 @@ ${r.snippet.trim()}
4728
6048
  if (expanded.length === 0) continue;
4729
6049
  for (const candidate of expanded.slice(0, perNamespaceExpandedCap)) {
4730
6050
  if (seedSet.has(candidate.path)) continue;
4731
- const memoryPath = path2.resolve(storage.dir, candidate.path);
6051
+ const memoryPath = path3.resolve(storage.dir, candidate.path);
4732
6052
  const memory = await storage.readMemoryByPath(memoryPath);
4733
6053
  if (!memory) continue;
4734
6054
  if (isArtifactMemoryPath(memory.path)) continue;
@@ -4752,7 +6072,7 @@ ${r.snippet.trim()}
4752
6072
  path: memory.path,
4753
6073
  score,
4754
6074
  namespace,
4755
- seed: path2.resolve(storage.dir, candidate.seed),
6075
+ seed: path3.resolve(storage.dir, candidate.seed),
4756
6076
  hopDepth: candidate.hopDepth,
4757
6077
  decayedWeight: candidate.decayedWeight,
4758
6078
  graphType: candidate.graphType,
@@ -4773,12 +6093,12 @@ ${r.snippet.trim()}
4773
6093
  }
4774
6094
  async recordLastGraphRecallSnapshot(options) {
4775
6095
  try {
4776
- const snapshotPath = path2.join(
6096
+ const snapshotPath = path3.join(
4777
6097
  options.storage.dir,
4778
6098
  "state",
4779
6099
  "last_graph_recall.json"
4780
6100
  );
4781
- await mkdir2(path2.dirname(snapshotPath), { recursive: true });
6101
+ await mkdir2(path3.dirname(snapshotPath), { recursive: true });
4782
6102
  const now = (/* @__PURE__ */ new Date()).toISOString();
4783
6103
  const totalSeedCount = options.seedPaths.length;
4784
6104
  const totalExpandedCount = options.expandedPaths.length;
@@ -4812,12 +6132,12 @@ ${r.snippet.trim()}
4812
6132
  }
4813
6133
  async recordLastIntentSnapshot(options) {
4814
6134
  try {
4815
- const snapshotPath = path2.join(
6135
+ const snapshotPath = path3.join(
4816
6136
  options.storage.dir,
4817
6137
  "state",
4818
6138
  "last_intent.json"
4819
6139
  );
4820
- await mkdir2(path2.dirname(snapshotPath), { recursive: true });
6140
+ await mkdir2(path3.dirname(snapshotPath), { recursive: true });
4821
6141
  await writeFile2(
4822
6142
  snapshotPath,
4823
6143
  JSON.stringify(options.snapshot, null, 2),
@@ -4829,12 +6149,12 @@ ${r.snippet.trim()}
4829
6149
  }
4830
6150
  async recordLastQmdRecallSnapshot(options) {
4831
6151
  try {
4832
- const snapshotPath = path2.join(
6152
+ const snapshotPath = path3.join(
4833
6153
  options.storage.dir,
4834
6154
  "state",
4835
6155
  "last_qmd_recall.json"
4836
6156
  );
4837
- await mkdir2(path2.dirname(snapshotPath), { recursive: true });
6157
+ await mkdir2(path3.dirname(snapshotPath), { recursive: true });
4838
6158
  await writeFile2(
4839
6159
  snapshotPath,
4840
6160
  JSON.stringify(options.snapshot, null, 2),
@@ -4849,8 +6169,8 @@ ${r.snippet.trim()}
4849
6169
  const stateDir = await this.resolveStateDirForNamespace(
4850
6170
  options.namespace
4851
6171
  );
4852
- const snapshotPath = path2.join(stateDir, "last_intent.json");
4853
- await mkdir2(path2.dirname(snapshotPath), { recursive: true });
6172
+ const snapshotPath = path3.join(stateDir, "last_intent.json");
6173
+ await mkdir2(path3.dirname(snapshotPath), { recursive: true });
4854
6174
  await writeFile2(
4855
6175
  snapshotPath,
4856
6176
  JSON.stringify(options.snapshot, null, 2),
@@ -4862,12 +6182,12 @@ ${r.snippet.trim()}
4862
6182
  }
4863
6183
  async resolveStateDirForNamespace(namespace) {
4864
6184
  if (!this.config.namespacesEnabled) {
4865
- return path2.join(this.config.memoryDir, "state");
6185
+ return path3.join(this.config.memoryDir, "state");
4866
6186
  }
4867
6187
  if (namespace !== this.config.defaultNamespace) {
4868
- return path2.join(this.config.memoryDir, "namespaces", namespace, "state");
6188
+ return path3.join(this.config.memoryDir, "namespaces", namespace, "state");
4869
6189
  }
4870
- const candidate = path2.join(
6190
+ const candidate = path3.join(
4871
6191
  this.config.memoryDir,
4872
6192
  "namespaces",
4873
6193
  this.config.defaultNamespace
@@ -4875,11 +6195,11 @@ ${r.snippet.trim()}
4875
6195
  try {
4876
6196
  const candidateStat = await stat(candidate);
4877
6197
  if (candidateStat.isDirectory()) {
4878
- return path2.join(candidate, "state");
6198
+ return path3.join(candidate, "state");
4879
6199
  }
4880
6200
  } catch {
4881
6201
  }
4882
- return path2.join(this.config.memoryDir, "state");
6202
+ return path3.join(this.config.memoryDir, "state");
4883
6203
  }
4884
6204
  buildGraphRecallRankedResults(results, sourceLabelResolver, limit = 64) {
4885
6205
  return results.slice(0, limit).map((result) => ({
@@ -5265,7 +6585,7 @@ ${r.snippet.trim()}
5265
6585
  const graphExpandedResultPaths = /* @__PURE__ */ new Set();
5266
6586
  const graphSourceLabelsForPath = (resultPath) => {
5267
6587
  const labels = [];
5268
- const normalizedPath = resultPath.split(path2.sep).join("/");
6588
+ const normalizedPath = resultPath.split(path3.sep).join("/");
5269
6589
  const isEntityPath = normalizedPath.startsWith("entities/") || normalizedPath.includes("/entities/");
5270
6590
  if (graphBaselinePaths.has(resultPath)) labels.push("baseline");
5271
6591
  if (graphExpandedResultPaths.has(resultPath))
@@ -6580,11 +7900,11 @@ ${formatted}`;
6580
7900
  if (!this.config.compactionResetEnabled) return null;
6581
7901
  const workspaceDir = compactionWorkspaceDir || this.config.workspaceDir || defaultWorkspaceDir();
6582
7902
  const safeSessionKey = sanitizeSessionKeyForFilename(effectiveSessionKey);
6583
- const signalPath = path2.join(
7903
+ const signalPath = path3.join(
6584
7904
  workspaceDir,
6585
7905
  `.compaction-reset-signal-${safeSessionKey}`
6586
7906
  );
6587
- const bootPath = path2.join(workspaceDir, "BOOT.md");
7907
+ const bootPath = path3.join(workspaceDir, "BOOT.md");
6588
7908
  try {
6589
7909
  const signalStat = await stat(signalPath).catch(() => null);
6590
7910
  if (!signalStat) return null;
@@ -8447,6 +9767,41 @@ _Context: ${topQuestion.context}_`
8447
9767
  bulkImportWriteNamespace() {
8448
9768
  return this.config.defaultNamespace;
8449
9769
  }
9770
+ /**
9771
+ * Lazily-constructed wearables service (Limitless / Bee / Omi
9772
+ * transcript ingestion). All wearables surfaces — CLI, MCP tools,
9773
+ * HTTP routes — share this one instance so sync state, search, and
9774
+ * memory writes stay consistent. Writes are pinned to the same
9775
+ * deterministic namespace bulk-import uses.
9776
+ */
9777
+ getWearablesService() {
9778
+ if (!this.wearablesServiceInstance) {
9779
+ this.wearablesServiceInstance = new WearablesService({
9780
+ config: this.config.wearables,
9781
+ getStorage: async () => await this.getStorageForNamespace(this.bulkImportWriteNamespace()),
9782
+ extract: (turns) => this.extraction.extract(turns),
9783
+ searchBackend: {
9784
+ search: async (query, maxResults) => {
9785
+ if (!this.qmd.isAvailable()) return null;
9786
+ try {
9787
+ const results = await this.qmd.search(query, void 0, maxResults);
9788
+ return results.map((result) => ({
9789
+ path: result.path,
9790
+ score: result.score,
9791
+ preview: result.snippet
9792
+ }));
9793
+ } catch {
9794
+ return null;
9795
+ }
9796
+ }
9797
+ },
9798
+ reindexSearch: async () => {
9799
+ await this.qmd.update();
9800
+ }
9801
+ });
9802
+ }
9803
+ return this.wearablesServiceInstance;
9804
+ }
8450
9805
  /**
8451
9806
  * Ingest a batch of bulk-import turns (#460). Like ingestReplayBatch, this
8452
9807
  * normalizes user/assistant turns into the extraction buffer and awaits
@@ -9015,7 +10370,7 @@ ${normalized}`).digest("hex");
9015
10370
  );
9016
10371
  this.tierMigrationInFlight = true;
9017
10372
  try {
9018
- const coldStorage = new StorageManager(path2.join(storage.dir, "cold"));
10373
+ const coldStorage = new StorageManager(path3.join(storage.dir, "cold"));
9019
10374
  const [hotMemories, coldMemories] = await Promise.all([
9020
10375
  storage.readAllMemories(),
9021
10376
  coldStorage.readAllMemories()
@@ -10218,7 +11573,7 @@ ${normalized}`).digest("hex");
10218
11573
  const allMems = allMemsForGraph ?? [];
10219
11574
  for (const m of allMems) {
10220
11575
  if (m.frontmatter.entityRef === entityRef) {
10221
- const rel = path2.relative(storage.dir, m.path);
11576
+ const rel = path3.relative(storage.dir, m.path);
10222
11577
  if (rel !== memoryRelPath) entitySiblings.push(rel);
10223
11578
  }
10224
11579
  }
@@ -10518,7 +11873,7 @@ ${normalized}`).digest("hex");
10518
11873
  }
10519
11874
  if (this.config.semanticConsolidationEnabled) {
10520
11875
  try {
10521
- const stateFilePath = path2.join(
11876
+ const stateFilePath = path3.join(
10522
11877
  this.config.memoryDir,
10523
11878
  "state",
10524
11879
  "semantic-consolidation-last-run.json"
@@ -10566,7 +11921,7 @@ ${normalized}`).digest("hex");
10566
11921
  );
10567
11922
  }
10568
11923
  if (semResult.errors === 0 || semResult.memoriesArchived > 0) {
10569
- const stateDir = path2.join(this.config.memoryDir, "state");
11924
+ const stateDir = path3.join(this.config.memoryDir, "state");
10570
11925
  await mkdir2(stateDir, { recursive: true });
10571
11926
  await writeFile2(
10572
11927
  stateFilePath,
@@ -11061,12 +12416,12 @@ ${texts.map((t, i) => `[${i + 1}] ${t}`).join("\n\n")}`;
11061
12416
  protectedCategories: this.config.lifecycleProtectedCategories
11062
12417
  }
11063
12418
  };
11064
- const metricsPath = path2.join(
12419
+ const metricsPath = path3.join(
11065
12420
  storage.dir,
11066
12421
  "state",
11067
12422
  "lifecycle-metrics.json"
11068
12423
  );
11069
- await mkdir2(path2.dirname(metricsPath), { recursive: true });
12424
+ await mkdir2(path3.dirname(metricsPath), { recursive: true });
11070
12425
  await writeFile2(metricsPath, JSON.stringify(metrics, null, 2), "utf-8");
11071
12426
  }
11072
12427
  /**
@@ -11594,7 +12949,7 @@ ${lines.join("\n\n")}`;
11594
12949
  nsMap = buildMemoryWorthCounterMap(memories);
11595
12950
  this.memoryWorthCounterCache.set(ns, { at: nowMs, counters: nsMap });
11596
12951
  }
11597
- for (const [path3, c] of nsMap) counters.set(path3, c);
12952
+ for (const [path4, c] of nsMap) counters.set(path4, c);
11598
12953
  } catch (err) {
11599
12954
  log.debug("memory-worth: failed to read namespace, skipping", {
11600
12955
  namespace: ns,
@@ -11765,12 +13120,12 @@ ${lines.join("\n\n")}`;
11765
13120
  */
11766
13121
  semanticDedupScopeFor(targetStorage) {
11767
13122
  if (!this.config.namespacesEnabled) return {};
11768
- const memoryDir = path2.resolve(this.config.memoryDir);
11769
- const storageDir = path2.resolve(targetStorage.dir);
13123
+ const memoryDir = path3.resolve(this.config.memoryDir);
13124
+ const storageDir = path3.resolve(targetStorage.dir);
11770
13125
  if (storageDir === memoryDir) {
11771
13126
  return { pathExcludePrefixes: ["namespaces/"] };
11772
13127
  }
11773
- let rel = path2.relative(memoryDir, storageDir);
13128
+ let rel = path3.relative(memoryDir, storageDir);
11774
13129
  if (!rel || rel.startsWith("..")) {
11775
13130
  log.debug(
11776
13131
  `semantic dedup: target storage dir ${storageDir} is outside memoryDir ${memoryDir}; scoping lookup to absolute path prefix`
@@ -11789,7 +13144,7 @@ ${lines.join("\n\n")}`;
11789
13144
  if (hits.length === 0) return [];
11790
13145
  const results = [];
11791
13146
  for (const hit of hits) {
11792
- const fullPath = path2.isAbsolute(hit.path) ? hit.path : path2.join(this.config.memoryDir, hit.path);
13147
+ const fullPath = path3.isAbsolute(hit.path) ? hit.path : path3.join(this.config.memoryDir, hit.path);
11793
13148
  const memory = await this.storage.readMemoryByPath(fullPath);
11794
13149
  if (!memory) continue;
11795
13150
  results.push({
@@ -12432,8 +13787,8 @@ ${lines.join("\n\n")}`;
12432
13787
  }
12433
13788
  namespaceFromStorageDir(storageDir) {
12434
13789
  if (!this.config.namespacesEnabled) return this.config.defaultNamespace;
12435
- const resolvedStorageDir = path2.resolve(storageDir);
12436
- const resolvedMemoryDir = path2.resolve(this.config.memoryDir);
13790
+ const resolvedStorageDir = path3.resolve(storageDir);
13791
+ const resolvedMemoryDir = path3.resolve(this.config.memoryDir);
12437
13792
  if (resolvedStorageDir === resolvedMemoryDir)
12438
13793
  return this.config.defaultNamespace;
12439
13794
  const m = resolvedStorageDir.match(/[\\/]namespaces[\\/]([^\\/]+)$/);
@@ -12473,6 +13828,34 @@ export {
12473
13828
  saveTaxonomy,
12474
13829
  getTaxonomyDir,
12475
13830
  getTaxonomyFilePath,
13831
+ memoryStatusForMode,
13832
+ WEARABLE_SOURCE_PREFIX,
13833
+ wearableSourceLabel,
13834
+ wearableDayTag,
13835
+ buildExtractionTurns,
13836
+ generateWearableMemories,
13837
+ writeDailyDigestMemory,
13838
+ importNativeMemories,
13839
+ cleanConversation,
13840
+ stripFillerTokens,
13841
+ collapseImmediateRepeats,
13842
+ isLowQualitySegment,
13843
+ syncStateFilePath,
13844
+ emptySyncState,
13845
+ loadSyncState,
13846
+ saveSyncState,
13847
+ updateSourceSyncState,
13848
+ dateInTimezone,
13849
+ resolveSyncDates,
13850
+ syncWearableSource,
13851
+ defaultTimezone,
13852
+ registerWearableConnector,
13853
+ getWearableConnector,
13854
+ listWearableConnectors,
13855
+ clearWearableConnectors,
13856
+ ensureBuiltInWearableConnectors,
13857
+ WearablesService,
13858
+ locateTranscriptPath,
12476
13859
  dedupeEntitySynthesisEvidenceEntries,
12477
13860
  defaultWorkspaceDir,
12478
13861
  sanitizeSessionKeyForFilename,
@@ -12502,4 +13885,4 @@ export {
12502
13885
  resolvePersistedMemoryRelativePath,
12503
13886
  Orchestrator
12504
13887
  };
12505
- //# sourceMappingURL=chunk-RKW6QR7W.js.map
13888
+ //# sourceMappingURL=chunk-AZ4RI3QD.js.map