@remnic/core 9.3.624 → 9.3.625

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
package/src/cli.ts CHANGED
@@ -5,6 +5,7 @@ import { createHash } from "node:crypto";
5
5
  import type { Readable, Writable } from "node:stream";
6
6
  import type { Orchestrator } from "./orchestrator.js";
7
7
  import { ThreadingManager } from "./threading.js";
8
+ import { runWearablesCliCommand } from "./wearables/cli.js";
8
9
  import type {
9
10
  BehaviorSignalEvent,
10
11
  ContinuityIncidentRecord,
@@ -5017,6 +5018,192 @@ export function registerCli(
5017
5018
  }
5018
5019
  });
5019
5020
 
5021
+ // Wearable transcript sources (Limitless / Bee / Omi). Every
5022
+ // subcommand forwards to the shared runner in wearables/cli.ts so
5023
+ // this host CLI and the standalone `remnic` CLI never fork
5024
+ // behavior or formatting.
5025
+ {
5026
+ const wearablesCmd = cmd
5027
+ .command("wearables")
5028
+ .description(
5029
+ "Wearable transcript sources (Limitless / Bee / Omi): sync, transcripts, search, speakers, corrections",
5030
+ );
5031
+ const forwardWearables = async (argv: string[]): Promise<void> => {
5032
+ const code = await runWearablesCliCommand(
5033
+ orchestrator.getWearablesService(),
5034
+ argv,
5035
+ { stdout: process.stdout, stderr: process.stderr },
5036
+ );
5037
+ if (code !== 0) process.exitCode = code;
5038
+ };
5039
+ const stringOpt = (options: Record<string, unknown>, key: string, flag: string): string[] =>
5040
+ typeof options[key] === "string" && (options[key] as string).length > 0
5041
+ ? [flag, options[key] as string]
5042
+ : [];
5043
+
5044
+ wearablesCmd
5045
+ .command("status")
5046
+ .description("Show configured sources, connector availability, and last sync")
5047
+ .option("--json", "JSON output")
5048
+ .action(async (...args: unknown[]) => {
5049
+ const options = (args[0] ?? {}) as Record<string, unknown>;
5050
+ await forwardWearables(["status", ...(options.json === true ? ["--json"] : [])]);
5051
+ });
5052
+
5053
+ wearablesCmd
5054
+ .command("check <source>")
5055
+ .description("Verify credentials/connectivity for a source")
5056
+ .action(async (...args: unknown[]) => {
5057
+ await forwardWearables(["check", String(args[0] ?? "")]);
5058
+ });
5059
+
5060
+ wearablesCmd
5061
+ .command("sync")
5062
+ .description("Pull, clean, and store wearable transcripts (and trust-gated memories)")
5063
+ .option("--source <id>", "Only this source (default: all enabled)")
5064
+ .option("--date <date>", "Sync exactly this day (YYYY-MM-DD)")
5065
+ .option("--days <n>", "Lookback window in days ending today (default 2)")
5066
+ .option("--force-memories", "Re-run memory extraction for unchanged days")
5067
+ .option("--json", "JSON output")
5068
+ .action(async (...args: unknown[]) => {
5069
+ const options = (args[0] ?? {}) as Record<string, unknown>;
5070
+ await forwardWearables([
5071
+ "sync",
5072
+ ...stringOpt(options, "source", "--source"),
5073
+ ...stringOpt(options, "date", "--date"),
5074
+ ...stringOpt(options, "days", "--days"),
5075
+ ...(options.forceMemories === true ? ["--force-memories"] : []),
5076
+ ...(options.json === true ? ["--json"] : []),
5077
+ ]);
5078
+ });
5079
+
5080
+ wearablesCmd
5081
+ .command("transcript")
5082
+ .description("Print the stored transcript(s) for a day")
5083
+ .requiredOption("--date <date>", "Day to read (YYYY-MM-DD)")
5084
+ .option("--source <id>", "Scope to one source")
5085
+ .action(async (...args: unknown[]) => {
5086
+ const options = (args[0] ?? {}) as Record<string, unknown>;
5087
+ await forwardWearables([
5088
+ "transcript",
5089
+ ...stringOpt(options, "date", "--date"),
5090
+ ...stringOpt(options, "source", "--source"),
5091
+ ]);
5092
+ });
5093
+
5094
+ wearablesCmd
5095
+ .command("search <query>")
5096
+ .description("Search stored wearable transcripts")
5097
+ .option("--source <id>", "Source id filter")
5098
+ .option("--from <date>", "Inclusive start date (YYYY-MM-DD)")
5099
+ .option("--to <date>", "Inclusive end date (YYYY-MM-DD)")
5100
+ .option("--limit <n>", "Maximum results (default 10)")
5101
+ .option("--json", "JSON output")
5102
+ .action(async (...args: unknown[]) => {
5103
+ const query = typeof args[0] === "string" ? args[0] : "";
5104
+ const options = (args[1] ?? {}) as Record<string, unknown>;
5105
+ await forwardWearables([
5106
+ "search",
5107
+ query,
5108
+ ...stringOpt(options, "source", "--source"),
5109
+ ...stringOpt(options, "from", "--from"),
5110
+ ...stringOpt(options, "to", "--to"),
5111
+ ...stringOpt(options, "limit", "--limit"),
5112
+ ...(options.json === true ? ["--json"] : []),
5113
+ ]);
5114
+ });
5115
+
5116
+ wearablesCmd
5117
+ .command("memories")
5118
+ .description("List memories created from wearable transcripts")
5119
+ .option("--source <id>", "Source id filter")
5120
+ .option("--date <date>", "Transcript day filter (YYYY-MM-DD)")
5121
+ .option("--limit <n>", "Maximum results (default 50)")
5122
+ .option("--json", "JSON output")
5123
+ .action(async (...args: unknown[]) => {
5124
+ const options = (args[0] ?? {}) as Record<string, unknown>;
5125
+ await forwardWearables([
5126
+ "memories",
5127
+ ...stringOpt(options, "source", "--source"),
5128
+ ...stringOpt(options, "date", "--date"),
5129
+ ...stringOpt(options, "limit", "--limit"),
5130
+ ...(options.json === true ? ["--json"] : []),
5131
+ ]);
5132
+ });
5133
+
5134
+ const speakersCmd = wearablesCmd
5135
+ .command("speakers")
5136
+ .description("Manage the speaker registry");
5137
+ speakersCmd
5138
+ .command("list")
5139
+ .description("Show stored speaker mappings")
5140
+ .action(async () => forwardWearables(["speakers", "list"]));
5141
+ speakersCmd
5142
+ .command("self <name>")
5143
+ .description("Set the wearer's display name")
5144
+ .action(async (...args: unknown[]) =>
5145
+ forwardWearables(["speakers", "self", String(args[0] ?? "")]),
5146
+ );
5147
+ speakersCmd
5148
+ .command("set <source> <speakerKey> <name>")
5149
+ .description("Map a provider speaker label to a display name")
5150
+ .option("--self", "Mark this speaker as the wearer")
5151
+ .action(async (...args: unknown[]) => {
5152
+ const options = (args[3] ?? {}) as Record<string, unknown>;
5153
+ await forwardWearables([
5154
+ "speakers",
5155
+ "set",
5156
+ String(args[0] ?? ""),
5157
+ String(args[1] ?? ""),
5158
+ String(args[2] ?? ""),
5159
+ ...(options.self === true ? ["--self"] : []),
5160
+ ]);
5161
+ });
5162
+ speakersCmd
5163
+ .command("remove <source> <speakerKey>")
5164
+ .description("Remove a speaker mapping")
5165
+ .action(async (...args: unknown[]) =>
5166
+ forwardWearables([
5167
+ "speakers",
5168
+ "remove",
5169
+ String(args[0] ?? ""),
5170
+ String(args[1] ?? ""),
5171
+ ]),
5172
+ );
5173
+
5174
+ const correctionsCmd = wearablesCmd
5175
+ .command("corrections")
5176
+ .description("Manage user-specific transcript correction rules");
5177
+ correctionsCmd
5178
+ .command("list")
5179
+ .description("Show correction rules from config and state")
5180
+ .action(async () => forwardWearables(["corrections", "list"]));
5181
+ correctionsCmd
5182
+ .command("add <match> <replace>")
5183
+ .description("Add a correction rule (quote multi-word values)")
5184
+ .option("--regex", "Treat <match> as a regular expression")
5185
+ .option("--case-sensitive", "Match case-sensitively")
5186
+ .option("--source <id>", "Restrict the rule to one source")
5187
+ .action(async (...args: unknown[]) => {
5188
+ const options = (args[2] ?? {}) as Record<string, unknown>;
5189
+ await forwardWearables([
5190
+ "corrections",
5191
+ "add",
5192
+ String(args[0] ?? ""),
5193
+ String(args[1] ?? ""),
5194
+ ...(options.regex === true ? ["--regex"] : []),
5195
+ ...(options.caseSensitive === true ? ["--case-sensitive"] : []),
5196
+ ...stringOpt(options, "source", "--source"),
5197
+ ]);
5198
+ });
5199
+ correctionsCmd
5200
+ .command("remove <index>")
5201
+ .description("Remove a state correction rule by index")
5202
+ .action(async (...args: unknown[]) =>
5203
+ forwardWearables(["corrections", "remove", String(args[0] ?? "")]),
5204
+ );
5205
+ }
5206
+
5020
5207
  cmd
5021
5208
  .command("benchmark-status")
5022
5209
  .description("Show benchmark/evaluation harness status, benchmark packs, and latest run summary")
package/src/config.ts CHANGED
@@ -35,6 +35,7 @@ import { expandTildePath } from "./utils/path.js";
35
35
  // lives in connectors/coerce.ts (a tiny, dependency-free module) so neither
36
36
  // config.ts → connectors/index.ts nor the reverse circular import arises.
37
37
  import { coerceBool, coerceInstallExtension, coerceNumber } from "./connectors/coerce.js";
38
+ import { parseWearablesConfig } from "./wearables/config.js";
38
39
 
39
40
  const DEFAULT_MEMORY_DIR = path.join(
40
41
  resolveHomeDir(),
@@ -1142,6 +1143,11 @@ export function parseConfig(raw: unknown): PluginConfig {
1142
1143
  recallMaxProcedures,
1143
1144
  };
1144
1145
 
1146
+ // Wearable transcript ingestion (Limitless / Bee / Omi). Parsing is
1147
+ // delegated to the wearables module; it follows the same loud-reject
1148
+ // conventions as the `procedural` block above.
1149
+ const wearables = parseWearablesConfig(cfg.wearables);
1150
+
1145
1151
  // Coding-agent project/branch scoping (issue #569)
1146
1152
  const rawCodingMode =
1147
1153
  cfg.codingMode && typeof cfg.codingMode === "object" && !Array.isArray(cfg.codingMode)
@@ -2018,6 +2024,7 @@ export function parseConfig(raw: unknown): PluginConfig {
2018
2024
  dreaming,
2019
2025
  dreamsPhases,
2020
2026
  procedural,
2027
+ wearables,
2021
2028
  // At-rest encryption (issue #690 PR 3/4)
2022
2029
  // coerceBool handles CLI string inputs: `--config secureStoreEnabled=true`
2023
2030
  // arrives as the string "true" which `=== true` would reject (CLAUDE.md #36).
package/src/index.ts CHANGED
@@ -1038,6 +1038,13 @@ export {
1038
1038
  type BulkImportCliCommandOptions,
1039
1039
  } from "./cli.js";
1040
1040
 
1041
+ // ---------------------------------------------------------------------------
1042
+ // Wearable transcript subsystem (Limitless / Bee / Omi connectors).
1043
+ // Connector packages import the registry + types from here.
1044
+ // ---------------------------------------------------------------------------
1045
+
1046
+ export * from "./wearables/index.js";
1047
+
1041
1048
  // ---------------------------------------------------------------------------
1042
1049
  // Shared importer base (issue #568)
1043
1050
  // ---------------------------------------------------------------------------
@@ -262,6 +262,7 @@ import {
262
262
  } from "./native-knowledge.js";
263
263
  import { normalizeReplaySessionKey, type ReplayTurn } from "./replay/types.js";
264
264
  import type { ImportTurn } from "./bulk-import/types.js";
265
+ import { WearablesService } from "./wearables/service.js";
265
266
  import {
266
267
  type AgentPersonaModelConfig,
267
268
  confidenceTier,
@@ -1694,6 +1695,7 @@ export class Orchestrator {
1694
1695
  (observation: ConsolidationObservation) => Promise<void> | void
1695
1696
  >();
1696
1697
  private qmdMaintenanceTimer: NodeJS.Timeout | null = null;
1698
+ private wearablesServiceInstance: WearablesService | null = null;
1697
1699
  private qmdMaintenancePending = false;
1698
1700
  private qmdMaintenanceInFlight = false;
1699
1701
  private lastQmdEmbedAtMs = 0;
@@ -10765,6 +10767,46 @@ export class Orchestrator {
10765
10767
  return this.config.defaultNamespace;
10766
10768
  }
10767
10769
 
10770
+ /**
10771
+ * Lazily-constructed wearables service (Limitless / Bee / Omi
10772
+ * transcript ingestion). All wearables surfaces — CLI, MCP tools,
10773
+ * HTTP routes — share this one instance so sync state, search, and
10774
+ * memory writes stay consistent. Writes are pinned to the same
10775
+ * deterministic namespace bulk-import uses.
10776
+ */
10777
+ getWearablesService(): WearablesService {
10778
+ if (!this.wearablesServiceInstance) {
10779
+ this.wearablesServiceInstance = new WearablesService({
10780
+ config: this.config.wearables,
10781
+ getStorage: async () =>
10782
+ await this.getStorageForNamespace(this.bulkImportWriteNamespace()),
10783
+ extract: (turns) => this.extraction.extract(turns),
10784
+ searchBackend: {
10785
+ search: async (query, maxResults) => {
10786
+ if (!this.qmd.isAvailable()) return null;
10787
+ try {
10788
+ const results = await this.qmd.search(query, undefined, maxResults);
10789
+ return results.map((result) => ({
10790
+ path: result.path,
10791
+ score: result.score,
10792
+ preview: result.snippet,
10793
+ }));
10794
+ } catch {
10795
+ // Backend hiccup → tell the service "unavailable" so it
10796
+ // runs its bounded scan fallback instead of returning a
10797
+ // silent empty result (CLAUDE.md rule 34).
10798
+ return null;
10799
+ }
10800
+ },
10801
+ },
10802
+ reindexSearch: async () => {
10803
+ await this.qmd.update();
10804
+ },
10805
+ });
10806
+ }
10807
+ return this.wearablesServiceInstance;
10808
+ }
10809
+
10768
10810
  /**
10769
10811
  * Ingest a batch of bulk-import turns (#460). Like ingestReplayBatch, this
10770
10812
  * normalizes user/assistant turns into the extraction buffer and awaits
package/src/storage.ts CHANGED
@@ -8,6 +8,7 @@ import { getCachedEntities, invalidateCachedEntities, setCachedEntities } from "
8
8
  import { rotateMarkdownFileToArchive } from "./hygiene.js";
9
9
  import { sanitizeMemoryContent } from "./sanitize.js";
10
10
  import { createVersion as createPageVersion, type VersioningConfig, type VersionTrigger } from "./page-versioning.js";
11
+ import { isValidTranscriptDate, WEARABLES_DIR_NAME } from "./wearables/day-store.js";
11
12
  import {
12
13
  SecureStoreLockedError,
13
14
  MAGIC_HEADER_SIZE,
@@ -2490,6 +2491,111 @@ export class StorageManager {
2490
2491
  return this.readSharedVersion("artifact-write", StorageManager.artifactWriteVersionByDir);
2491
2492
  }
2492
2493
 
2494
+ // -------------------------------------------------------------------------
2495
+ // Wearable day transcripts
2496
+ //
2497
+ // Stored under `<baseDir>/wearables/<source>/<YYYY-MM-DD>.md` — outside
2498
+ // the memory scan roots (never surfaces as a memory) but inside the QMD
2499
+ // collection root (full-text searchable). IO lives here so transcripts
2500
+ // inherit the same encrypted-at-rest + atomic-write semantics as
2501
+ // memories.
2502
+ // -------------------------------------------------------------------------
2503
+
2504
+ private get wearablesDir(): string {
2505
+ return path.join(this.baseDir, WEARABLES_DIR_NAME);
2506
+ }
2507
+
2508
+ /**
2509
+ * Resolve the on-disk path for a source/day transcript. Throws on
2510
+ * malformed inputs — source ids and dates reach this from CLI/MCP/HTTP
2511
+ * surfaces and must never become path traversal.
2512
+ */
2513
+ wearableTranscriptPath(sourceId: string, date: string): string {
2514
+ if (typeof sourceId !== "string" || !/^[a-z][a-z0-9-]{0,63}$/.test(sourceId)) {
2515
+ throw new Error(
2516
+ `invalid wearable source id '${String(sourceId)}' — expected lowercase letters, digits, and dashes`,
2517
+ );
2518
+ }
2519
+ if (!isValidTranscriptDate(date)) {
2520
+ throw new Error(
2521
+ `invalid wearable transcript date '${String(date)}' — expected YYYY-MM-DD`,
2522
+ );
2523
+ }
2524
+ return path.join(this.wearablesDir, sourceId, `${date}.md`);
2525
+ }
2526
+
2527
+ async writeWearableDayTranscript(
2528
+ sourceId: string,
2529
+ date: string,
2530
+ serialized: string,
2531
+ ): Promise<void> {
2532
+ const targetPath = this.wearableTranscriptPath(sourceId, date);
2533
+ // writeMaybeEncryptedFile handles mkdir + atomic temp→rename.
2534
+ await writeMaybeEncryptedFile(targetPath, serialized, this.resolveWriteKey(), {}, this.baseDir);
2535
+ }
2536
+
2537
+ /** Read a stored day transcript; null when the day has no file. */
2538
+ async readWearableDayTranscript(
2539
+ sourceId: string,
2540
+ date: string,
2541
+ ): Promise<string | null> {
2542
+ const targetPath = this.wearableTranscriptPath(sourceId, date);
2543
+ try {
2544
+ return await readMaybeEncryptedFile(targetPath, this._secureStoreKey, this.baseDir);
2545
+ } catch (err) {
2546
+ if ((err as NodeJS.ErrnoException).code === "ENOENT") return null;
2547
+ throw err;
2548
+ }
2549
+ }
2550
+
2551
+ /**
2552
+ * List stored transcript days, newest first, optionally scoped to one
2553
+ * source. Non-transcript files in the tree are ignored.
2554
+ */
2555
+ async listWearableTranscriptDays(
2556
+ sourceId?: string,
2557
+ ): Promise<Array<{ source: string; date: string }>> {
2558
+ const days: Array<{ source: string; date: string }> = [];
2559
+ let sources: string[];
2560
+ if (sourceId !== undefined) {
2561
+ sources = [sourceId];
2562
+ } else {
2563
+ try {
2564
+ const entries = await readdir(this.wearablesDir, { withFileTypes: true });
2565
+ sources = entries
2566
+ .filter((entry) => entry.isDirectory())
2567
+ .map((entry) => entry.name);
2568
+ } catch (err) {
2569
+ if ((err as NodeJS.ErrnoException).code === "ENOENT") return [];
2570
+ throw err;
2571
+ }
2572
+ }
2573
+ for (const source of sources) {
2574
+ if (!/^[a-z][a-z0-9-]{0,63}$/.test(source)) continue;
2575
+ let entries: string[];
2576
+ try {
2577
+ entries = await readdir(path.join(this.wearablesDir, source));
2578
+ } catch (err) {
2579
+ if ((err as NodeJS.ErrnoException).code === "ENOENT") continue;
2580
+ throw err;
2581
+ }
2582
+ for (const entry of entries) {
2583
+ if (!entry.endsWith(".md")) continue;
2584
+ const date = entry.slice(0, -3);
2585
+ if (!isValidTranscriptDate(date)) continue;
2586
+ days.push({ source, date });
2587
+ }
2588
+ }
2589
+ days.sort((a, b) => {
2590
+ if (a.date > b.date) return -1;
2591
+ if (a.date < b.date) return 1;
2592
+ if (a.source < b.source) return -1;
2593
+ if (a.source > b.source) return 1;
2594
+ return 0;
2595
+ });
2596
+ return days;
2597
+ }
2598
+
2493
2599
  private get factsDir(): string {
2494
2600
  return path.join(this.baseDir, "facts");
2495
2601
  }
package/src/types.ts CHANGED
@@ -871,6 +871,11 @@ export interface PluginConfig {
871
871
  */
872
872
  dreamsPhases: DreamsPhasesConfig;
873
873
  procedural: ProceduralConfig;
874
+ /**
875
+ * Wearable transcript ingestion (Limitless / Bee / Omi connectors).
876
+ * Disabled by default; see docs/wearables.md.
877
+ */
878
+ wearables: import("./wearables/types.js").WearablesConfig;
874
879
  /**
875
880
  * At-rest encryption configuration (issue #690 PR 3/4).
876
881
  *
@@ -0,0 +1,134 @@
1
+ import assert from "node:assert/strict";
2
+ import { test } from "node:test";
3
+
4
+ import {
5
+ cleanConversation,
6
+ collapseImmediateRepeats,
7
+ isLowQualitySegment,
8
+ stripFillerTokens,
9
+ } from "./cleanup.js";
10
+ import type { WearableCleanupSettings, WearableConversation } from "./types.js";
11
+
12
+ const ALL_ON: WearableCleanupSettings = {
13
+ mergeSameSpeaker: true,
14
+ stripFillers: true,
15
+ collapseRepeats: true,
16
+ dropLowQuality: true,
17
+ };
18
+
19
+ function conversation(
20
+ segments: Array<{ speakerKey: string; text: string; startIso?: string; endIso?: string }>,
21
+ ): WearableConversation {
22
+ return {
23
+ id: "c1",
24
+ source: "testsource",
25
+ startIso: "2026-06-10T10:00:00Z",
26
+ segments,
27
+ };
28
+ }
29
+
30
+ test("stripFillerTokens removes standalone fillers but not words containing them", () => {
31
+ assert.equal(stripFillerTokens("Um, so we should ship it"), "so we should ship it");
32
+ assert.equal(stripFillerTokens("Grab the umbrella, uh, before noon"), "Grab the umbrella, before noon");
33
+ assert.equal(stripFillerTokens("hmm"), "");
34
+ });
35
+
36
+ test("collapseImmediateRepeats collapses word and phrase stutters fully", () => {
37
+ assert.equal(collapseImmediateRepeats("I I I think so"), "I think so");
38
+ assert.equal(collapseImmediateRepeats("we should we should go"), "we should go");
39
+ assert.equal(collapseImmediateRepeats("that's a really really really good point"), "that's a really good point");
40
+ });
41
+
42
+ test("collapseImmediateRepeats never collapses pure digit sequences", () => {
43
+ assert.equal(collapseImmediateRepeats("call 555 555 1234 now"), "call 555 555 1234 now");
44
+ });
45
+
46
+ test("isLowQualitySegment flags garbage and keeps real speech", () => {
47
+ assert.equal(isLowQualitySegment("aaaaaaaaaa"), true);
48
+ assert.equal(isLowQualitySegment("%$#@! ---- ////"), true);
49
+ assert.equal(isLowQualitySegment("yeah yeah yeah yeah yeah"), true);
50
+ assert.equal(isLowQualitySegment("Let's review the budget tomorrow."), false);
51
+ assert.equal(isLowQualitySegment("ok"), false);
52
+ });
53
+
54
+ test("merges consecutive same-speaker segments within the gap", () => {
55
+ const result = cleanConversation(
56
+ conversation([
57
+ {
58
+ speakerKey: "a",
59
+ text: "First part.",
60
+ startIso: "2026-06-10T10:00:00Z",
61
+ endIso: "2026-06-10T10:00:05Z",
62
+ },
63
+ {
64
+ speakerKey: "a",
65
+ text: "Second part.",
66
+ startIso: "2026-06-10T10:00:10Z",
67
+ endIso: "2026-06-10T10:00:15Z",
68
+ },
69
+ { speakerKey: "b", text: "Reply.", startIso: "2026-06-10T10:00:20Z" },
70
+ ]),
71
+ ALL_ON,
72
+ );
73
+ assert.equal(result.conversation.segments.length, 2);
74
+ assert.equal(result.conversation.segments[0].text, "First part. Second part.");
75
+ assert.equal(result.conversation.segments[0].endIso, "2026-06-10T10:00:15Z");
76
+ assert.equal(result.mergedSegments, 1);
77
+ });
78
+
79
+ test("does not merge across a long silence gap", () => {
80
+ const result = cleanConversation(
81
+ conversation([
82
+ {
83
+ speakerKey: "a",
84
+ text: "Before lunch.",
85
+ startIso: "2026-06-10T10:00:00Z",
86
+ endIso: "2026-06-10T10:00:05Z",
87
+ },
88
+ {
89
+ speakerKey: "a",
90
+ text: "After lunch.",
91
+ startIso: "2026-06-10T11:30:00Z",
92
+ endIso: "2026-06-10T11:30:05Z",
93
+ },
94
+ ]),
95
+ ALL_ON,
96
+ );
97
+ assert.equal(result.conversation.segments.length, 2);
98
+ });
99
+
100
+ test("drops low-quality segments and counts them", () => {
101
+ const result = cleanConversation(
102
+ conversation([
103
+ { speakerKey: "a", text: "Real sentence about plans." },
104
+ { speakerKey: "a", text: "zzzzzzzzz" },
105
+ ]),
106
+ { ...ALL_ON, mergeSameSpeaker: false },
107
+ );
108
+ assert.equal(result.conversation.segments.length, 1);
109
+ assert.equal(result.droppedSegments, 1);
110
+ });
111
+
112
+ test("respects disabled passes", () => {
113
+ const result = cleanConversation(
114
+ conversation([
115
+ { speakerKey: "a", text: "Um, well well well" },
116
+ { speakerKey: "a", text: "Um, again" },
117
+ ]),
118
+ {
119
+ mergeSameSpeaker: false,
120
+ stripFillers: false,
121
+ collapseRepeats: false,
122
+ dropLowQuality: false,
123
+ },
124
+ );
125
+ assert.equal(result.conversation.segments.length, 2);
126
+ assert.equal(result.conversation.segments[0].text, "Um, well well well");
127
+ });
128
+
129
+ test("input conversation is not mutated", () => {
130
+ const input = conversation([{ speakerKey: "a", text: "Um, hello there" }]);
131
+ const before = JSON.stringify(input);
132
+ cleanConversation(input, ALL_ON);
133
+ assert.equal(JSON.stringify(input), before);
134
+ });