@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
@@ -0,0 +1,156 @@
1
+ /**
2
+ * Wearable transcript redaction — privacy guard applied before any
3
+ * transcript text is persisted or fed to extraction.
4
+ *
5
+ * Always-on recorders capture things nobody intended to store: card
6
+ * numbers read aloud, SSNs dictated to a pharmacy line. Built-in
7
+ * patterns cover the unambiguous, high-sensitivity cases; users can add
8
+ * their own regexes via `wearables.redactionPatterns` (validated at
9
+ * config parse — invalid patterns are rejected loudly, never ignored).
10
+ *
11
+ * All built-in patterns are simple linear scans (no nested quantifiers)
12
+ * to stay safely outside polynomial-ReDoS territory.
13
+ */
14
+
15
+ import type { WearableConversation } from "./types.js";
16
+
17
+ export const REDACTION_PLACEHOLDER = "[redacted]";
18
+
19
+ /**
20
+ * Built-in patterns. Conservative by design — false positives erase
21
+ * real transcript content, so each pattern targets formats that are
22
+ * near-certain PII:
23
+ * - US SSN with separators (123-45-6789). Bare 9-digit runs are NOT
24
+ * matched (too many false positives: ids, tracking numbers).
25
+ * - Payment-card-like runs: 13–19 digits in groups separated by
26
+ * spaces/dashes (4111 1111 1111 1111) or contiguous 15–16 digits.
27
+ */
28
+ const BUILT_IN_PATTERNS: RegExp[] = [
29
+ /\b\d{3}-\d{2}-\d{4}\b/g,
30
+ // Starts and ENDS on a digit so a trailing separator is never
31
+ // consumed (replacing it would glue the placeholder to the next word).
32
+ /\b\d(?:[ -]?\d){12,18}\b/g,
33
+ ];
34
+
35
+ /** Minimum digit count before a digit-run is treated as a card number. */
36
+ const CARD_MIN_DIGITS = 13;
37
+
38
+ export interface RedactionResult {
39
+ text: string;
40
+ redactions: number;
41
+ }
42
+
43
+ export function redactText(
44
+ text: string,
45
+ userPatterns: RegExp[],
46
+ ): RedactionResult {
47
+ let redactions = 0;
48
+ let result = text;
49
+
50
+ // SSN pattern first (more specific than the digit-run pattern).
51
+ result = result.replace(BUILT_IN_PATTERNS[0], () => {
52
+ redactions += 1;
53
+ return REDACTION_PLACEHOLDER;
54
+ });
55
+
56
+ // Digit-run pattern with a post-match digit-count check so short
57
+ // grouped numbers ("call 555 0125 today") survive.
58
+ result = result.replace(BUILT_IN_PATTERNS[1], (match) => {
59
+ const digits = match.replace(/\D/g, "");
60
+ if (digits.length < CARD_MIN_DIGITS || digits.length > 19) {
61
+ return match;
62
+ }
63
+ redactions += 1;
64
+ return REDACTION_PLACEHOLDER;
65
+ });
66
+
67
+ for (const pattern of userPatterns) {
68
+ result = result.replace(pattern, () => {
69
+ redactions += 1;
70
+ return REDACTION_PLACEHOLDER;
71
+ });
72
+ }
73
+
74
+ return { text: result, redactions };
75
+ }
76
+
77
+ /**
78
+ * Compile user-supplied redaction patterns. Throws with a descriptive
79
+ * message on the first invalid pattern — config parsing surfaces this
80
+ * to the operator instead of silently skipping the rule.
81
+ */
82
+ export function compileRedactionPatterns(patterns: string[]): RegExp[] {
83
+ return patterns.map((pattern, index) => {
84
+ if (typeof pattern !== "string" || pattern.trim().length === 0) {
85
+ throw new Error(
86
+ `wearables.redactionPatterns[${index}] must be a non-empty string`,
87
+ );
88
+ }
89
+ if (pattern.length > 256) {
90
+ throw new Error(
91
+ `wearables.redactionPatterns[${index}] exceeds 256 characters — redaction patterns must stay short`,
92
+ );
93
+ }
94
+ try {
95
+ // Operator-supplied regexes from the operator's own config —
96
+ // length-capped above; never request input.
97
+ return new RegExp(pattern, "gi");
98
+ } catch (err) {
99
+ throw new Error(
100
+ `wearables.redactionPatterns[${index}] is not a valid regular expression: ${
101
+ err instanceof Error ? err.message : String(err)
102
+ }`,
103
+ );
104
+ }
105
+ });
106
+ }
107
+
108
+ const OFF_THE_RECORD = /\boff\s+the\s+record\b/i;
109
+ const BACK_ON_THE_RECORD = /\b(?:back\s+)?on\s+the\s+record\b/i;
110
+
111
+ export interface OffTheRecordResult {
112
+ conversation: WearableConversation;
113
+ droppedSegments: number;
114
+ }
115
+
116
+ /**
117
+ * Drop segments between a spoken "off the record" marker and the next
118
+ * "(back) on the record" marker (or conversation end). The marker
119
+ * segments themselves are kept, with the off-record span replaced by a
120
+ * visible placeholder so the transcript shows that content was elided
121
+ * by request rather than lost.
122
+ */
123
+ export function applyOffTheRecord(
124
+ conversation: WearableConversation,
125
+ ): OffTheRecordResult {
126
+ let offRecord = false;
127
+ let droppedSegments = 0;
128
+ const segments = [];
129
+ for (const segment of conversation.segments) {
130
+ if (!offRecord && OFF_THE_RECORD.test(segment.text)) {
131
+ offRecord = true;
132
+ segments.push({
133
+ ...segment,
134
+ text: "[off the record — segment elided]",
135
+ });
136
+ continue;
137
+ }
138
+ if (offRecord) {
139
+ if (BACK_ON_THE_RECORD.test(segment.text)) {
140
+ offRecord = false;
141
+ segments.push({
142
+ ...segment,
143
+ text: "[back on the record]",
144
+ });
145
+ } else {
146
+ droppedSegments += 1;
147
+ }
148
+ continue;
149
+ }
150
+ segments.push(segment);
151
+ }
152
+ return {
153
+ conversation: { ...conversation, segments },
154
+ droppedSegments,
155
+ };
156
+ }
@@ -0,0 +1,62 @@
1
+ import assert from "node:assert/strict";
2
+ import { afterEach, test } from "node:test";
3
+
4
+ import {
5
+ clearWearableConnectors,
6
+ ensureBuiltInWearableConnectors,
7
+ getWearableConnector,
8
+ listWearableConnectors,
9
+ registerWearableConnector,
10
+ } from "./registry.js";
11
+
12
+ afterEach(() => {
13
+ clearWearableConnectors();
14
+ });
15
+
16
+ function registration(id = "testsource") {
17
+ return {
18
+ id,
19
+ displayName: "Test Source",
20
+ factory: () => ({
21
+ id,
22
+ displayName: "Test Source",
23
+ verifyAuth: async () => ({ ok: true }),
24
+ fetchConversations: async () => ({ conversations: [], nextCursor: null }),
25
+ }),
26
+ };
27
+ }
28
+
29
+ test("register + get + list round-trip", () => {
30
+ registerWearableConnector(registration());
31
+ assert.ok(getWearableConnector("testsource"));
32
+ assert.deepEqual(listWearableConnectors(), ["testsource"]);
33
+ assert.equal(getWearableConnector("missing"), undefined);
34
+ assert.equal(getWearableConnector(""), undefined);
35
+ });
36
+
37
+ test("duplicate registration throws; ids are trimmed", () => {
38
+ registerWearableConnector(registration());
39
+ assert.throws(() => registerWearableConnector(registration()), /already registered/);
40
+ registerWearableConnector(registration(" spaced ") as never);
41
+ assert.ok(getWearableConnector("spaced"));
42
+ });
43
+
44
+ test("invalid registrations are rejected", () => {
45
+ assert.throws(
46
+ () => registerWearableConnector({ id: "", displayName: "x", factory: () => ({}) } as never),
47
+ /non-empty string/,
48
+ );
49
+ assert.throws(
50
+ () => registerWearableConnector({ id: "x", displayName: "x" } as never),
51
+ /factory function/,
52
+ );
53
+ });
54
+
55
+ test("ensureBuiltInWearableConnectors tolerates absent optional packages", async () => {
56
+ // In the core package's own test environment the connector packages
57
+ // may or may not be installed; the loader must succeed either way and
58
+ // never throw for a missing optional package.
59
+ await ensureBuiltInWearableConnectors();
60
+ await ensureBuiltInWearableConnectors();
61
+ assert.ok(Array.isArray(listWearableConnectors()));
62
+ });
@@ -0,0 +1,133 @@
1
+ /**
2
+ * Wearable connector registry.
3
+ *
4
+ * Concrete connectors live in optional à-la-carte packages and register
5
+ * a factory here (directly, or via the built-in loader below which
6
+ * dynamic-imports the known packages with computed specifiers so
7
+ * bundlers and the dts resolver never require them at build time).
8
+ */
9
+
10
+ import { log } from "../logger.js";
11
+ import type {
12
+ WearableSourceConnector,
13
+ WearableSourceSettings,
14
+ } from "./types.js";
15
+
16
+ export interface WearableConnectorFactoryOptions {
17
+ settings: WearableSourceSettings;
18
+ /** IANA timezone transcripts are bucketed in. */
19
+ timezone: string;
20
+ }
21
+
22
+ export type WearableConnectorFactory = (
23
+ options: WearableConnectorFactoryOptions,
24
+ ) => WearableSourceConnector;
25
+
26
+ export interface WearableConnectorRegistration {
27
+ id: string;
28
+ displayName: string;
29
+ factory: WearableConnectorFactory;
30
+ }
31
+
32
+ const registrations = new Map<string, WearableConnectorRegistration>();
33
+
34
+ export function registerWearableConnector(
35
+ registration: WearableConnectorRegistration,
36
+ ): void {
37
+ if (!registration || typeof registration !== "object") {
38
+ throw new Error("wearable connector registration must be an object");
39
+ }
40
+ if (
41
+ typeof registration.id !== "string" ||
42
+ registration.id.trim().length === 0
43
+ ) {
44
+ throw new Error("wearable connector id must be a non-empty string");
45
+ }
46
+ if (typeof registration.factory !== "function") {
47
+ throw new Error(
48
+ `wearable connector '${registration.id}' must provide a factory function`,
49
+ );
50
+ }
51
+ const key = registration.id.trim();
52
+ if (registrations.has(key)) {
53
+ throw new Error(`wearable connector '${key}' is already registered`);
54
+ }
55
+ registrations.set(key, { ...registration, id: key });
56
+ }
57
+
58
+ export function getWearableConnector(
59
+ id: string,
60
+ ): WearableConnectorRegistration | undefined {
61
+ if (typeof id !== "string") return undefined;
62
+ const key = id.trim();
63
+ if (key.length === 0) return undefined;
64
+ return registrations.get(key);
65
+ }
66
+
67
+ export function listWearableConnectors(): string[] {
68
+ return [...registrations.keys()];
69
+ }
70
+
71
+ /** Test-only: reset the registry between cases. */
72
+ export function clearWearableConnectors(): void {
73
+ registrations.clear();
74
+ }
75
+
76
+ /**
77
+ * Built-in connector packages, tried in order. Each entry is loaded via
78
+ * a computed-specifier dynamic import (see optional-bench.ts in
79
+ * @remnic/cli for the canonical à-la-carte pattern); a missing package
80
+ * is silently skipped, any other load error is logged once and skipped
81
+ * so one broken connector never takes down the others.
82
+ */
83
+ const BUILT_IN_CONNECTOR_PACKAGES: Array<{ id: string; suffix: string }> = [
84
+ { id: "limitless", suffix: "connector-limitless" },
85
+ { id: "bee", suffix: "connector-bee" },
86
+ { id: "omi", suffix: "connector-omi" },
87
+ ];
88
+
89
+ const loadFailuresWarned = new Set<string>();
90
+
91
+ export async function ensureBuiltInWearableConnectors(): Promise<void> {
92
+ for (const entry of BUILT_IN_CONNECTOR_PACKAGES) {
93
+ if (registrations.has(entry.id)) continue;
94
+ const specifier = "@remnic/" + entry.suffix;
95
+ let mod: { wearableConnectorRegistration?: WearableConnectorRegistration };
96
+ try {
97
+ mod = (await import(specifier)) as {
98
+ wearableConnectorRegistration?: WearableConnectorRegistration;
99
+ };
100
+ } catch (err) {
101
+ if (isModuleNotFound(err, specifier)) continue;
102
+ if (!loadFailuresWarned.has(specifier)) {
103
+ loadFailuresWarned.add(specifier);
104
+ log.warn(
105
+ `wearables: failed to load optional connector package ${specifier}: ${
106
+ err instanceof Error ? err.message : String(err)
107
+ }`,
108
+ );
109
+ }
110
+ continue;
111
+ }
112
+ const registration = mod.wearableConnectorRegistration;
113
+ if (!registration) continue;
114
+ try {
115
+ registerWearableConnector(registration);
116
+ } catch {
117
+ // Already registered by a direct import — fine.
118
+ }
119
+ }
120
+ }
121
+
122
+ function isModuleNotFound(err: unknown, specifier: string): boolean {
123
+ if (!err || typeof err !== "object") return false;
124
+ const code = (err as { code?: unknown }).code;
125
+ if (code !== "ERR_MODULE_NOT_FOUND" && code !== "MODULE_NOT_FOUND") {
126
+ return false;
127
+ }
128
+ const message = (err as { message?: unknown }).message;
129
+ // Only treat the failure as "optional package absent" when the error
130
+ // names the specifier we asked for — a transitive resolution failure
131
+ // inside an installed connector is a broken install worth surfacing.
132
+ return typeof message === "string" && message.includes(specifier);
133
+ }