@mongrov/analytics 0.6.0 → 0.8.0

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 (227) hide show
  1. package/README.md +23 -10
  2. package/UPGRADING.md +456 -0
  3. package/dist/core/device-events.d.ts +14 -0
  4. package/dist/core/device-events.d.ts.map +1 -0
  5. package/dist/core/device-events.js +13 -0
  6. package/dist/core/device-events.js.map +1 -0
  7. package/dist/core/errors.d.ts +1 -1
  8. package/dist/core/errors.d.ts.map +1 -1
  9. package/dist/core/errors.js.map +1 -1
  10. package/dist/core/extensions.d.ts +21 -4
  11. package/dist/core/extensions.d.ts.map +1 -1
  12. package/dist/core/extensions.js +21 -4
  13. package/dist/core/extensions.js.map +1 -1
  14. package/dist/core/factory.d.ts.map +1 -1
  15. package/dist/core/factory.js +125 -23
  16. package/dist/core/factory.js.map +1 -1
  17. package/dist/core/hooks.js +1 -1
  18. package/dist/core/hooks.js.map +1 -1
  19. package/dist/core/index.d.ts +13 -1
  20. package/dist/core/index.d.ts.map +1 -1
  21. package/dist/core/index.js +18 -0
  22. package/dist/core/index.js.map +1 -1
  23. package/dist/core/insight.d.ts +44 -0
  24. package/dist/core/insight.d.ts.map +1 -0
  25. package/dist/core/insight.js +62 -0
  26. package/dist/core/insight.js.map +1 -0
  27. package/dist/core/metric_metadata.d.ts +55 -0
  28. package/dist/core/metric_metadata.d.ts.map +1 -1
  29. package/dist/core/metric_metadata.js +35 -7
  30. package/dist/core/metric_metadata.js.map +1 -1
  31. package/dist/core/migrations.d.ts.map +1 -1
  32. package/dist/core/migrations.js +103 -2
  33. package/dist/core/migrations.js.map +1 -1
  34. package/dist/core/retention.d.ts +44 -13
  35. package/dist/core/retention.d.ts.map +1 -1
  36. package/dist/core/retention.js +49 -22
  37. package/dist/core/retention.js.map +1 -1
  38. package/dist/core/sampling.d.ts +70 -0
  39. package/dist/core/sampling.d.ts.map +1 -0
  40. package/dist/core/sampling.js +107 -0
  41. package/dist/core/sampling.js.map +1 -0
  42. package/dist/core/schemas.d.ts +73 -2
  43. package/dist/core/schemas.d.ts.map +1 -1
  44. package/dist/core/schemas.js +160 -5
  45. package/dist/core/schemas.js.map +1 -1
  46. package/dist/core/table_metadata.d.ts.map +1 -1
  47. package/dist/core/table_metadata.js +3 -0
  48. package/dist/core/table_metadata.js.map +1 -1
  49. package/dist/core/types.d.ts +45 -10
  50. package/dist/core/types.d.ts.map +1 -1
  51. package/dist/core/warehouse.d.ts +28 -0
  52. package/dist/core/warehouse.d.ts.map +1 -1
  53. package/dist/core/warehouse.js +44 -0
  54. package/dist/core/warehouse.js.map +1 -1
  55. package/dist/core/watermark-cache.d.ts +75 -0
  56. package/dist/core/watermark-cache.d.ts.map +1 -0
  57. package/dist/core/watermark-cache.js +0 -0
  58. package/dist/core/watermark-cache.js.map +1 -0
  59. package/dist/rules/__fakes__/fakeEngine.d.ts +2 -0
  60. package/dist/rules/__fakes__/fakeEngine.d.ts.map +1 -1
  61. package/dist/rules/__fakes__/fakeEngine.js +4 -0
  62. package/dist/rules/__fakes__/fakeEngine.js.map +1 -1
  63. package/dist/rules/baseline-reader.d.ts +55 -0
  64. package/dist/rules/baseline-reader.d.ts.map +1 -0
  65. package/dist/rules/baseline-reader.js +111 -0
  66. package/dist/rules/baseline-reader.js.map +1 -0
  67. package/dist/rules/compiler.d.ts +36 -1
  68. package/dist/rules/compiler.d.ts.map +1 -1
  69. package/dist/rules/compiler.js +185 -39
  70. package/dist/rules/compiler.js.map +1 -1
  71. package/dist/rules/defaults/luminx.d.ts.map +1 -1
  72. package/dist/rules/defaults/luminx.js +8 -0
  73. package/dist/rules/defaults/luminx.js.map +1 -1
  74. package/dist/rules/defaults/viva.d.ts.map +1 -1
  75. package/dist/rules/defaults/viva.js +7 -0
  76. package/dist/rules/defaults/viva.js.map +1 -1
  77. package/dist/rules/defaults/yogaring.d.ts.map +1 -1
  78. package/dist/rules/defaults/yogaring.js +7 -0
  79. package/dist/rules/defaults/yogaring.js.map +1 -1
  80. package/dist/rules/defaults/ziva.d.ts.map +1 -1
  81. package/dist/rules/defaults/ziva.js +100 -24
  82. package/dist/rules/defaults/ziva.js.map +1 -1
  83. package/dist/rules/evaluator.d.ts +14 -3
  84. package/dist/rules/evaluator.d.ts.map +1 -1
  85. package/dist/rules/evaluator.js +157 -5
  86. package/dist/rules/evaluator.js.map +1 -1
  87. package/dist/rules/factory.d.ts.map +1 -1
  88. package/dist/rules/factory.js +5 -1
  89. package/dist/rules/factory.js.map +1 -1
  90. package/dist/rules/index.d.ts +2 -1
  91. package/dist/rules/index.d.ts.map +1 -1
  92. package/dist/rules/index.js +2 -1
  93. package/dist/rules/index.js.map +1 -1
  94. package/dist/rules/schema.d.ts +75 -4
  95. package/dist/rules/schema.d.ts.map +1 -1
  96. package/dist/rules/schema.js +44 -0
  97. package/dist/rules/schema.js.map +1 -1
  98. package/dist/rules/types.d.ts +16 -1
  99. package/dist/rules/types.d.ts.map +1 -1
  100. package/dist/rules/validator.d.ts.map +1 -1
  101. package/dist/rules/validator.js +89 -0
  102. package/dist/rules/validator.js.map +1 -1
  103. package/dist/sync/baseline-compute.d.ts +63 -0
  104. package/dist/sync/baseline-compute.d.ts.map +1 -0
  105. package/dist/sync/baseline-compute.js +211 -0
  106. package/dist/sync/baseline-compute.js.map +1 -0
  107. package/dist/sync/events.d.ts +19 -5
  108. package/dist/sync/events.d.ts.map +1 -1
  109. package/dist/sync/events.js +17 -0
  110. package/dist/sync/events.js.map +1 -1
  111. package/dist/sync/factory.d.ts +54 -8
  112. package/dist/sync/factory.d.ts.map +1 -1
  113. package/dist/sync/factory.js +145 -46
  114. package/dist/sync/factory.js.map +1 -1
  115. package/dist/sync/fetcher.d.ts +15 -2
  116. package/dist/sync/fetcher.d.ts.map +1 -1
  117. package/dist/sync/fetcher.js +43 -7
  118. package/dist/sync/fetcher.js.map +1 -1
  119. package/dist/sync/flusher.d.ts +52 -2
  120. package/dist/sync/flusher.d.ts.map +1 -1
  121. package/dist/sync/flusher.js +78 -9
  122. package/dist/sync/flusher.js.map +1 -1
  123. package/dist/sync/index.d.ts +3 -1
  124. package/dist/sync/index.d.ts.map +1 -1
  125. package/dist/sync/index.js +1 -0
  126. package/dist/sync/index.js.map +1 -1
  127. package/dist/sync/mapper/activity.d.ts.map +1 -1
  128. package/dist/sync/mapper/activity.js +2 -1
  129. package/dist/sync/mapper/activity.js.map +1 -1
  130. package/dist/sync/mapper/firmware.d.ts +3 -12
  131. package/dist/sync/mapper/firmware.d.ts.map +1 -1
  132. package/dist/sync/mapper/firmware.js +2 -8
  133. package/dist/sync/mapper/firmware.js.map +1 -1
  134. package/dist/sync/mapper/ring-config.d.ts +76 -28
  135. package/dist/sync/mapper/ring-config.d.ts.map +1 -1
  136. package/dist/sync/mapper/ring-config.js +118 -35
  137. package/dist/sync/mapper/ring-config.js.map +1 -1
  138. package/dist/sync/mapper/schema.d.ts +669 -0
  139. package/dist/sync/mapper/schema.d.ts.map +1 -0
  140. package/dist/sync/mapper/schema.js +160 -0
  141. package/dist/sync/mapper/schema.js.map +1 -0
  142. package/dist/sync/mapper/sleep.d.ts +58 -13
  143. package/dist/sync/mapper/sleep.d.ts.map +1 -1
  144. package/dist/sync/mapper/sleep.js +141 -44
  145. package/dist/sync/mapper/sleep.js.map +1 -1
  146. package/dist/sync/mapper/types.d.ts +118 -29
  147. package/dist/sync/mapper/types.d.ts.map +1 -1
  148. package/dist/sync/pending_closes.js +2 -2
  149. package/dist/sync/pending_closes.js.map +1 -1
  150. package/dist/sync/pusher.js +2 -2
  151. package/dist/sync/pusher.js.map +1 -1
  152. package/dist/sync/scheduler.d.ts +7 -0
  153. package/dist/sync/scheduler.d.ts.map +1 -1
  154. package/dist/sync/scheduler.js +14 -0
  155. package/dist/sync/scheduler.js.map +1 -1
  156. package/dist/tools/__fakes__/engine.d.ts +2 -0
  157. package/dist/tools/__fakes__/engine.d.ts.map +1 -1
  158. package/dist/tools/__fakes__/engine.js +10 -0
  159. package/dist/tools/__fakes__/engine.js.map +1 -1
  160. package/dist/tools/authorize.d.ts +19 -12
  161. package/dist/tools/authorize.d.ts.map +1 -1
  162. package/dist/tools/authorize.js +31 -40
  163. package/dist/tools/authorize.js.map +1 -1
  164. package/dist/tools/factory.d.ts +2 -0
  165. package/dist/tools/factory.d.ts.map +1 -1
  166. package/dist/tools/factory.js +12 -1
  167. package/dist/tools/factory.js.map +1 -1
  168. package/dist/tools/formatters/copy-guidelines.d.ts +57 -0
  169. package/dist/tools/formatters/copy-guidelines.d.ts.map +1 -0
  170. package/dist/tools/formatters/copy-guidelines.js +110 -0
  171. package/dist/tools/formatters/copy-guidelines.js.map +1 -0
  172. package/dist/tools/formatters/index.d.ts +14 -0
  173. package/dist/tools/formatters/index.d.ts.map +1 -0
  174. package/dist/tools/formatters/index.js +14 -0
  175. package/dist/tools/formatters/index.js.map +1 -0
  176. package/dist/tools/formatters/spo2.d.ts +21 -0
  177. package/dist/tools/formatters/spo2.d.ts.map +1 -0
  178. package/dist/tools/formatters/spo2.js +79 -0
  179. package/dist/tools/formatters/spo2.js.map +1 -0
  180. package/dist/tools/{formatters.d.ts → formatters/text.d.ts} +1 -1
  181. package/dist/tools/formatters/text.d.ts.map +1 -0
  182. package/dist/tools/{formatters.js → formatters/text.js} +1 -1
  183. package/dist/tools/formatters/text.js.map +1 -0
  184. package/dist/tools/impls/activity.d.ts +2 -2
  185. package/dist/tools/impls/activity.js +7 -3
  186. package/dist/tools/impls/activity.js.map +1 -1
  187. package/dist/tools/impls/anomaly.d.ts +2 -2
  188. package/dist/tools/impls/anomaly.js +8 -4
  189. package/dist/tools/impls/anomaly.js.map +1 -1
  190. package/dist/tools/impls/compare.d.ts +2 -2
  191. package/dist/tools/impls/compare.js +8 -4
  192. package/dist/tools/impls/compare.js.map +1 -1
  193. package/dist/tools/impls/hrv.d.ts +2 -2
  194. package/dist/tools/impls/hrv.js +6 -2
  195. package/dist/tools/impls/hrv.js.map +1 -1
  196. package/dist/tools/impls/insights.d.ts +4 -4
  197. package/dist/tools/impls/insights.d.ts.map +1 -1
  198. package/dist/tools/impls/insights.js +10 -3
  199. package/dist/tools/impls/insights.js.map +1 -1
  200. package/dist/tools/impls/sleep.d.ts +2 -2
  201. package/dist/tools/impls/sleep.js +6 -2
  202. package/dist/tools/impls/sleep.js.map +1 -1
  203. package/dist/tools/impls/spo2.d.ts +52 -0
  204. package/dist/tools/impls/spo2.d.ts.map +1 -0
  205. package/dist/tools/impls/spo2.js +65 -0
  206. package/dist/tools/impls/spo2.js.map +1 -0
  207. package/dist/tools/index.d.ts +1 -0
  208. package/dist/tools/index.d.ts.map +1 -1
  209. package/dist/tools/index.js +4 -0
  210. package/dist/tools/index.js.map +1 -1
  211. package/dist/tools/mcp/guard.d.ts +28 -11
  212. package/dist/tools/mcp/guard.d.ts.map +1 -1
  213. package/dist/tools/mcp/guard.js +45 -15
  214. package/dist/tools/mcp/guard.js.map +1 -1
  215. package/dist/tools/mcp/index.d.ts +1 -1
  216. package/dist/tools/mcp/index.d.ts.map +1 -1
  217. package/dist/tools/mcp/index.js +1 -1
  218. package/dist/tools/mcp/index.js.map +1 -1
  219. package/dist/tools/mcp/server.d.ts.map +1 -1
  220. package/dist/tools/mcp/server.js +4 -0
  221. package/dist/tools/mcp/server.js.map +1 -1
  222. package/dist/tools/mcp/transports/http.d.ts.map +1 -1
  223. package/dist/tools/mcp/transports/http.js +4 -0
  224. package/dist/tools/mcp/transports/http.js.map +1 -1
  225. package/package.json +8 -5
  226. package/dist/tools/formatters.d.ts.map +0 -1
  227. package/dist/tools/formatters.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../src/tools/factory.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AASH,OAAO,KAAK,EACV,oBAAoB,EACpB,WAAW,EAEX,WAAW,EACZ,MAAM,SAAS,CAAA;AAChB,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAA;AAEjC,OAAO,EAGL,KAAK,qBAAqB,EAC3B,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAGL,KAAK,kBAAkB,EACxB,MAAM,iBAAiB,CAAA;AACxB,OAAO,EAGL,KAAK,iBAAiB,EACvB,MAAM,iBAAiB,CAAA;AACxB,OAAO,EAA6B,KAAK,WAAW,EAAE,MAAM,aAAa,CAAA;AACzE,OAAO,EAGL,KAAK,gBAAgB,EACtB,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAGL,KAAK,oBAAoB,EAC1B,MAAM,eAAe,CAAA;AAoBtB,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,UAAU,CAAC,OAAO,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAA;IAChD,eAAe,EAAE,UAAU,CAAC,OAAO,QAAQ,CAAC,oBAAoB,CAAC,CAAC,CAAA;IAClE,gBAAgB,EAAE,UAAU,CAAC,OAAO,QAAQ,CAAC,qBAAqB,CAAC,CAAC,CAAA;IACpE,YAAY,EAAE,UAAU,CAAC,OAAO,QAAQ,CAAC,iBAAiB,CAAC,CAAC,CAAA;IAC5D,aAAa,EAAE,UAAU,CAAC,OAAO,QAAQ,CAAC,kBAAkB,CAAC,CAAC,CAAA;IAC9D,WAAW,EAAE,UAAU,CAAC,OAAO,QAAQ,CAAC,gBAAgB,CAAC,CAAC,CAAA;CAC3D;AAED,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,gBAAgB,CAAA;IACvB,UAAU,EAAE,CAAC,GAAG,EAAE,WAAW,GAAG,IAAI,KAAK,IAAI,CAAA;IAC7C,WAAW,EAAE,WAAW,CAAA;IACxB,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAC3B;AAED,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,oBAAoB,GAC3B,oBAAoB,CAiGtB"}
1
+ {"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../src/tools/factory.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AASH,OAAO,KAAK,EACV,oBAAoB,EACpB,WAAW,EAEX,WAAW,EACZ,MAAM,SAAS,CAAA;AAChB,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAA;AAEjC,OAAO,EAGL,KAAK,qBAAqB,EAC3B,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAGL,KAAK,kBAAkB,EACxB,MAAM,iBAAiB,CAAA;AACxB,OAAO,EAGL,KAAK,iBAAiB,EACvB,MAAM,iBAAiB,CAAA;AACxB,OAAO,EAA6B,KAAK,WAAW,EAAE,MAAM,aAAa,CAAA;AACzE,OAAO,EAA+B,KAAK,YAAY,EAAE,MAAM,cAAc,CAAA;AAC7E,OAAO,EAGL,KAAK,gBAAgB,EACtB,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAGL,KAAK,oBAAoB,EAC1B,MAAM,eAAe,CAAA;AAuBtB,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,UAAU,CAAC,OAAO,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAA;IAChD,eAAe,EAAE,UAAU,CAAC,OAAO,QAAQ,CAAC,oBAAoB,CAAC,CAAC,CAAA;IAClE,gBAAgB,EAAE,UAAU,CAAC,OAAO,QAAQ,CAAC,qBAAqB,CAAC,CAAC,CAAA;IACpE,YAAY,EAAE,UAAU,CAAC,OAAO,QAAQ,CAAC,iBAAiB,CAAC,CAAC,CAAA;IAC5D,aAAa,EAAE,UAAU,CAAC,OAAO,QAAQ,CAAC,kBAAkB,CAAC,CAAC,CAAA;IAC9D,OAAO,EAAE,UAAU,CAAC,OAAO,QAAQ,CAAC,YAAY,CAAC,CAAC,CAAA;IAClD,WAAW,EAAE,UAAU,CAAC,OAAO,QAAQ,CAAC,gBAAgB,CAAC,CAAC,CAAA;CAC3D;AAED,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,gBAAgB,CAAA;IACvB,UAAU,EAAE,CAAC,GAAG,EAAE,WAAW,GAAG,IAAI,KAAK,IAAI,CAAA;IAC7C,WAAW,EAAE,WAAW,CAAA;IACxB,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAC3B;AAED,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,oBAAoB,GAC3B,oBAAoB,CAwGtB"}
@@ -26,6 +26,7 @@ import { getActivityTotal, getActivityTotalInputSchema, } from './impls/activity
26
26
  import { detectAnomaly, detectAnomalyInputSchema, } from './impls/anomaly';
27
27
  import { compareTrend, compareTrendInputSchema, } from './impls/compare';
28
28
  import { getHRV, getHRVInputSchema } from './impls/hrv';
29
+ import { getSpO2, getSpO2InputSchema } from './impls/spo2';
29
30
  import { getInsights, getInsightsInputSchema, } from './impls/insights';
30
31
  import { getSleepSummary, getSleepSummaryInputSchema, } from './impls/sleep';
31
32
  const DESCRIPTIONS = {
@@ -42,8 +43,11 @@ const DESCRIPTIONS = {
42
43
  + 'in a user\'s metric over the last lookbackDays. Args: userId, metric '
43
44
  + '(hrv_ms|sleep_total_minutes|activity_steps), lookbackDays (7..90), '
44
45
  + 'stddevThreshold (1..4, default 2).',
46
+ getSpO2: 'Return nightly blood-oxygen averages during sleep for a user, '
47
+ + 'with how many brief low moments occurred each night and how it '
48
+ + 'compares to their usual range. Args: userId, days (1..90).',
45
49
  getInsights: 'Return recent AI-generated insights for a user. Args: userId, '
46
- + 'days (1..30, default 7), optional severity (info|warn|critical).',
50
+ + 'days (1..30, default 7), optional severity (info|warn|urgent).',
47
51
  };
48
52
  export function createAnalyticsTools(config) {
49
53
  const budget = {
@@ -80,6 +84,13 @@ export function createAnalyticsTools(config) {
80
84
  clock: config.clock,
81
85
  };
82
86
  const tools = {
87
+ getSpO2: makeTool({
88
+ ...shared,
89
+ name: 'getSpO2',
90
+ description: DESCRIPTIONS.getSpO2,
91
+ inputSchema: getSpO2InputSchema,
92
+ impl: getSpO2,
93
+ }),
83
94
  getHRV: makeTool({
84
95
  ...shared,
85
96
  name: 'getHRV',
@@ -1 +1 @@
1
- {"version":3,"file":"factory.js","sourceRoot":"","sources":["../../src/tools/factory.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"factory.js","sourceRoot":"","sources":["../../src/tools/factory.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,57 @@
1
+ /**
2
+ * User-language guardrails for tool output (Sprint 5 §5 / T-29, Ziva #2,
3
+ * principle 37).
4
+ *
5
+ * Tool output goes into an LLM's context, and the LLM will parrot whatever
6
+ * register it finds there. If a formatter says "2 desaturation events", the
7
+ * assistant tells the user they had two desaturation events — which is
8
+ * clinical language Ziva is not licensed to speak and the user did not ask
9
+ * for. The fix is upstream of the model: never put the word in the context.
10
+ *
11
+ * This is a runtime guard, not a lint rule, because the failure mode is a
12
+ * formatter interpolating a value into a template that happens to read
13
+ * clinically. `assertNoBanTerms` runs on every formatter's return path and
14
+ * throws — a tool call that fails loudly is recoverable; one that quietly
15
+ * teaches the model to talk like a doctor is not.
16
+ */
17
+ /**
18
+ * Vocabulary that must never reach the model.
19
+ *
20
+ * Matching is prefix-based on word boundaries, so listing `desaturation`
21
+ * also catches `desaturations` and `desaturating`. Inflections are still
22
+ * listed explicitly where they read differently, for documentation value.
23
+ */
24
+ export declare const BANNED_MEDICAL_VOCABULARY: readonly ["desaturation", "desaturated", "desaturating", "hypoxia", "hypoxic", "hypoxemia", "apnea", "apneic", "arrhythmia", "arrhythmic", "bradycardia", "tachycardia", "ischemia", "ischemic"];
25
+ export type BannedTerm = (typeof BANNED_MEDICAL_VOCABULARY)[number];
26
+ /**
27
+ * What to write instead. Keys are the clinical phrasing a formatter author
28
+ * might reach for; values are the register Ziva actually speaks.
29
+ *
30
+ * Ordered longest-first at use time so multi-word phrases win over their
31
+ * constituent words.
32
+ */
33
+ export declare const PREFERRED_LANGUAGE: Record<string, string>;
34
+ /** Thrown when a formatter emits banned vocabulary. */
35
+ export declare class FormatterCopyError extends Error {
36
+ readonly formatterName: string;
37
+ readonly found: string[];
38
+ constructor(formatterName: string, found: string[]);
39
+ }
40
+ /** Every banned term present in `text`, in list order. Empty when clean. */
41
+ export declare function findBanTerms(text: string): string[];
42
+ /**
43
+ * Guardrail called from every formatter's return path.
44
+ *
45
+ * @throws `FormatterCopyError` when any banned term appears.
46
+ */
47
+ export declare function assertNoBanTerms(text: string, formatterName: string): void;
48
+ /**
49
+ * Best-effort rewrite of clinical phrasing into Ziva's register.
50
+ *
51
+ * A convenience for formatter authors, NOT a substitute for the guard —
52
+ * it only knows the phrases in `PREFERRED_LANGUAGE`, so
53
+ * `assertNoBanTerms` still runs afterwards and still throws on anything
54
+ * it missed.
55
+ */
56
+ export declare function applyPreferredLanguage(text: string): string;
57
+ //# sourceMappingURL=copy-guidelines.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"copy-guidelines.d.ts","sourceRoot":"","sources":["../../../src/tools/formatters/copy-guidelines.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH;;;;;;GAMG;AACH,eAAO,MAAM,yBAAyB,kMAe5B,CAAA;AAEV,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,yBAAyB,CAAC,CAAC,MAAM,CAAC,CAAA;AAEnE;;;;;;GAMG;AACH,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAYrD,CAAA;AAED,uDAAuD;AACvD,qBAAa,kBAAmB,SAAQ,KAAK;IAC3C,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAA;IAC9B,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,CAAA;gBAEZ,aAAa,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE;CAUnD;AAWD,4EAA4E;AAC5E,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAEnD;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,IAAI,CAK1E;AAED;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAY3D"}
@@ -0,0 +1,110 @@
1
+ /**
2
+ * User-language guardrails for tool output (Sprint 5 §5 / T-29, Ziva #2,
3
+ * principle 37).
4
+ *
5
+ * Tool output goes into an LLM's context, and the LLM will parrot whatever
6
+ * register it finds there. If a formatter says "2 desaturation events", the
7
+ * assistant tells the user they had two desaturation events — which is
8
+ * clinical language Ziva is not licensed to speak and the user did not ask
9
+ * for. The fix is upstream of the model: never put the word in the context.
10
+ *
11
+ * This is a runtime guard, not a lint rule, because the failure mode is a
12
+ * formatter interpolating a value into a template that happens to read
13
+ * clinically. `assertNoBanTerms` runs on every formatter's return path and
14
+ * throws — a tool call that fails loudly is recoverable; one that quietly
15
+ * teaches the model to talk like a doctor is not.
16
+ */
17
+ /**
18
+ * Vocabulary that must never reach the model.
19
+ *
20
+ * Matching is prefix-based on word boundaries, so listing `desaturation`
21
+ * also catches `desaturations` and `desaturating`. Inflections are still
22
+ * listed explicitly where they read differently, for documentation value.
23
+ */
24
+ export const BANNED_MEDICAL_VOCABULARY = [
25
+ 'desaturation',
26
+ 'desaturated',
27
+ 'desaturating',
28
+ 'hypoxia',
29
+ 'hypoxic',
30
+ 'hypoxemia',
31
+ 'apnea',
32
+ 'apneic',
33
+ 'arrhythmia',
34
+ 'arrhythmic',
35
+ 'bradycardia',
36
+ 'tachycardia',
37
+ 'ischemia',
38
+ 'ischemic',
39
+ ];
40
+ /**
41
+ * What to write instead. Keys are the clinical phrasing a formatter author
42
+ * might reach for; values are the register Ziva actually speaks.
43
+ *
44
+ * Ordered longest-first at use time so multi-word phrases win over their
45
+ * constituent words.
46
+ */
47
+ export const PREFERRED_LANGUAGE = {
48
+ 'oxygen desaturation': 'oxygen dip',
49
+ 'desaturation event': 'brief low moment',
50
+ 'desaturation events': 'brief low moments',
51
+ 'desaturations': 'brief low moments',
52
+ 'desaturation': 'brief low moment',
53
+ 'hypoxemia': 'low oxygen',
54
+ 'hypoxia': 'low oxygen',
55
+ 'apnea': 'pauses in breathing',
56
+ 'bradycardia': 'slow heart rate',
57
+ 'tachycardia': 'fast heart rate',
58
+ 'arrhythmia': 'irregular heartbeat',
59
+ };
60
+ /** Thrown when a formatter emits banned vocabulary. */
61
+ export class FormatterCopyError extends Error {
62
+ constructor(formatterName, found) {
63
+ super(`Formatter '${formatterName}' emitted banned medical vocabulary: `
64
+ + `${found.join(', ')}. Use PREFERRED_LANGUAGE or rewrite — this text `
65
+ + `goes into an LLM's context and the model will repeat it.`);
66
+ this.name = 'FormatterCopyError';
67
+ this.formatterName = formatterName;
68
+ this.found = found;
69
+ }
70
+ }
71
+ /**
72
+ * Compiled matchers. Leading `\b` only: a trailing boundary would let
73
+ * `desaturations` slip past a `desaturation` entry, and over-blocking is
74
+ * the right bias for a guardrail on health copy.
75
+ */
76
+ const MATCHERS = BANNED_MEDICAL_VOCABULARY.map(term => ({ term, re: new RegExp(`\\b${term}`, 'iu') }));
77
+ /** Every banned term present in `text`, in list order. Empty when clean. */
78
+ export function findBanTerms(text) {
79
+ return MATCHERS.filter(m => m.re.test(text)).map(m => m.term);
80
+ }
81
+ /**
82
+ * Guardrail called from every formatter's return path.
83
+ *
84
+ * @throws `FormatterCopyError` when any banned term appears.
85
+ */
86
+ export function assertNoBanTerms(text, formatterName) {
87
+ const found = findBanTerms(text);
88
+ if (found.length > 0) {
89
+ throw new FormatterCopyError(formatterName, found);
90
+ }
91
+ }
92
+ /**
93
+ * Best-effort rewrite of clinical phrasing into Ziva's register.
94
+ *
95
+ * A convenience for formatter authors, NOT a substitute for the guard —
96
+ * it only knows the phrases in `PREFERRED_LANGUAGE`, so
97
+ * `assertNoBanTerms` still runs afterwards and still throws on anything
98
+ * it missed.
99
+ */
100
+ export function applyPreferredLanguage(text) {
101
+ // Longest first, so 'oxygen desaturation' is replaced before
102
+ // 'desaturation' can consume half of it.
103
+ const phrases = Object.keys(PREFERRED_LANGUAGE).sort((a, b) => b.length - a.length);
104
+ let out = text;
105
+ for (const phrase of phrases) {
106
+ out = out.replace(new RegExp(`\\b${phrase}\\b`, 'giu'), PREFERRED_LANGUAGE[phrase]);
107
+ }
108
+ return out;
109
+ }
110
+ //# sourceMappingURL=copy-guidelines.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"copy-guidelines.js","sourceRoot":"","sources":["../../../src/tools/formatters/copy-guidelines.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG;IACvC,cAAc;IACd,aAAa;IACb,cAAc;IACd,SAAS;IACT,SAAS;IACT,WAAW;IACX,OAAO;IACP,QAAQ;IACR,YAAY;IACZ,YAAY;IACZ,aAAa;IACb,aAAa;IACb,UAAU;IACV,UAAU;CACF,CAAA;AAIV;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAA2B;IACxD,qBAAqB,EAAE,YAAY;IACnC,oBAAoB,EAAE,kBAAkB;IACxC,qBAAqB,EAAE,mBAAmB;IAC1C,eAAe,EAAE,mBAAmB;IACpC,cAAc,EAAE,kBAAkB;IAClC,WAAW,EAAE,YAAY;IACzB,SAAS,EAAE,YAAY;IACvB,OAAO,EAAE,qBAAqB;IAC9B,aAAa,EAAE,iBAAiB;IAChC,aAAa,EAAE,iBAAiB;IAChC,YAAY,EAAE,qBAAqB;CACpC,CAAA;AAED,uDAAuD;AACvD,MAAM,OAAO,kBAAmB,SAAQ,KAAK;IAI3C,YAAY,aAAqB,EAAE,KAAe;QAChD,KAAK,CACH,cAAc,aAAa,uCAAuC;cAChE,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,kDAAkD;cACrE,0DAA0D,CAC7D,CAAA;QACD,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAA;QAChC,IAAI,CAAC,aAAa,GAAG,aAAa,CAAA;QAClC,IAAI,CAAC,KAAK,GAAG,KAAK,CAAA;IACpB,CAAC;CACF;AAED;;;;GAIG;AACH,MAAM,QAAQ,GAAmC,yBAAyB,CAAC,GAAG,CAC5E,IAAI,CAAC,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,MAAM,CAAC,MAAM,IAAI,EAAE,EAAE,IAAI,CAAC,EAAE,CAAC,CACvD,CAAA;AAED,4EAA4E;AAC5E,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,OAAO,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAA;AAC/D,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAAY,EAAE,aAAqB;IAClE,MAAM,KAAK,GAAG,YAAY,CAAC,IAAI,CAAC,CAAA;IAChC,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACrB,MAAM,IAAI,kBAAkB,CAAC,aAAa,EAAE,KAAK,CAAC,CAAA;IACpD,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,sBAAsB,CAAC,IAAY;IACjD,6DAA6D;IAC7D,yCAAyC;IACzC,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC,CAAA;IACnF,IAAI,GAAG,GAAG,IAAI,CAAA;IACd,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,GAAG,GAAG,GAAG,CAAC,OAAO,CACf,IAAI,MAAM,CAAC,MAAM,MAAM,KAAK,EAAE,KAAK,CAAC,EACpC,kBAAkB,CAAC,MAAM,CAAC,CAC3B,CAAA;IACH,CAAC;IACD,OAAO,GAAG,CAAA;AACZ,CAAC"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Formatter surface for `@mongrov/analytics/tools`.
3
+ *
4
+ * Two concerns, kept separate:
5
+ *
6
+ * - `text.ts` — mechanical string/number helpers (byte counts, deltas).
7
+ * - `copy-guidelines.ts` — the user-language guardrail (principle 37).
8
+ * Every formatter calls `assertNoBanTerms` on its return path.
9
+ *
10
+ * Re-exported here so call sites keep importing from `'../formatters'`.
11
+ */
12
+ export { deltaPct, formatBytes, popStddev } from './text';
13
+ export { applyPreferredLanguage, assertNoBanTerms, BANNED_MEDICAL_VOCABULARY, type BannedTerm, findBanTerms, FormatterCopyError, PREFERRED_LANGUAGE, } from './copy-guidelines';
14
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/tools/formatters/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAA;AACzD,OAAO,EACL,sBAAsB,EACtB,gBAAgB,EAChB,yBAAyB,EACzB,KAAK,UAAU,EACf,YAAY,EACZ,kBAAkB,EAClB,kBAAkB,GACnB,MAAM,mBAAmB,CAAA"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Formatter surface for `@mongrov/analytics/tools`.
3
+ *
4
+ * Two concerns, kept separate:
5
+ *
6
+ * - `text.ts` — mechanical string/number helpers (byte counts, deltas).
7
+ * - `copy-guidelines.ts` — the user-language guardrail (principle 37).
8
+ * Every formatter calls `assertNoBanTerms` on its return path.
9
+ *
10
+ * Re-exported here so call sites keep importing from `'../formatters'`.
11
+ */
12
+ export { deltaPct, formatBytes, popStddev } from './text';
13
+ export { applyPreferredLanguage, assertNoBanTerms, BANNED_MEDICAL_VOCABULARY, findBanTerms, FormatterCopyError, PREFERRED_LANGUAGE, } from './copy-guidelines';
14
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/tools/formatters/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAA;AACzD,OAAO,EACL,sBAAsB,EACtB,gBAAgB,EAChB,yBAAyB,EAEzB,YAAY,EACZ,kBAAkB,EAClB,kBAAkB,GACnB,MAAM,mBAAmB,CAAA"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * `getSpO2` formatter (Sprint 5 T-31, Ziva #2).
3
+ *
4
+ * The reference implementation for principle 37. The underlying data has a
5
+ * field counting readings below 90% during sleep; the clinical word for
6
+ * that is on the banned list, so this formatter has to say what happened
7
+ * without reaching for it:
8
+ *
9
+ * "A couple of brief low moments during sleep."
10
+ *
11
+ * not
12
+ *
13
+ * "2 desaturation events. minSpo2=87."
14
+ *
15
+ * The second version is not just colder — it is the version the assistant
16
+ * repeats back to the user, which is how clinical vocabulary leaks into a
17
+ * consumer wellness product.
18
+ */
19
+ import type { SpO2BaselineRow, SpO2NightRow } from '../impls/spo2';
20
+ export declare function formatSpO2(nights: readonly SpO2NightRow[], baseline: SpO2BaselineRow | null, days: number): string;
21
+ //# sourceMappingURL=spo2.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spo2.d.ts","sourceRoot":"","sources":["../../../src/tools/formatters/spo2.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAmClE,wBAAgB,UAAU,CACxB,MAAM,EAAE,SAAS,YAAY,EAAE,EAC/B,QAAQ,EAAE,eAAe,GAAG,IAAI,EAChC,IAAI,EAAE,MAAM,GACX,MAAM,CAqCR"}
@@ -0,0 +1,79 @@
1
+ /**
2
+ * `getSpO2` formatter (Sprint 5 T-31, Ziva #2).
3
+ *
4
+ * The reference implementation for principle 37. The underlying data has a
5
+ * field counting readings below 90% during sleep; the clinical word for
6
+ * that is on the banned list, so this formatter has to say what happened
7
+ * without reaching for it:
8
+ *
9
+ * "A couple of brief low moments during sleep."
10
+ *
11
+ * not
12
+ *
13
+ * "2 desaturation events. minSpo2=87."
14
+ *
15
+ * The second version is not just colder — it is the version the assistant
16
+ * repeats back to the user, which is how clinical vocabulary leaks into a
17
+ * consumer wellness product.
18
+ */
19
+ import { assertNoBanTerms } from './copy-guidelines';
20
+ const FORMATTER_NAME = 'getSpO2';
21
+ /**
22
+ * Small counts read better as words, and vague is honest here — the exact
23
+ * count of dips is not a number the user should be doing arithmetic on.
24
+ */
25
+ function describeLowMoments(count) {
26
+ if (count === 0)
27
+ return 'No brief low moments';
28
+ if (count === 1)
29
+ return 'One brief low moment';
30
+ if (count === 2)
31
+ return 'A couple of brief low moments';
32
+ if (count <= 5)
33
+ return 'A few brief low moments';
34
+ return 'Several brief low moments';
35
+ }
36
+ /** "in your usual range" needs a usual range to compare against. */
37
+ function describeRange(avg, baseline) {
38
+ if (!baseline) {
39
+ // Below 20 days of data. Saying nothing is better than implying a
40
+ // reference range we do not have.
41
+ return 'Still learning your usual range.';
42
+ }
43
+ const lo = Math.round(baseline.p10);
44
+ const hi = Math.round(baseline.p50 + (baseline.p50 - baseline.p10));
45
+ const within = avg >= baseline.p10;
46
+ return within
47
+ ? `Overall in your usual range (typically ${lo}–${hi}%).`
48
+ : `A little below your usual range (typically ${lo}–${hi}%).`;
49
+ }
50
+ export function formatSpO2(nights, baseline, days) {
51
+ if (nights.length === 0) {
52
+ const empty = days === 1
53
+ ? 'No sleep readings for last night yet.'
54
+ : `No sleep readings in the last ${days} days yet.`;
55
+ assertNoBanTerms(empty, FORMATTER_NAME);
56
+ return empty;
57
+ }
58
+ const latest = nights[nights.length - 1];
59
+ const lines = [];
60
+ if (nights.length === 1) {
61
+ lines.push('Last night:');
62
+ }
63
+ else {
64
+ lines.push(`Last ${nights.length} nights:`);
65
+ }
66
+ lines.push(` Average oxygen: ${Math.round(latest.avg_spo2)}%. `
67
+ + `${describeLowMoments(latest.low_moment_count)} during sleep.`);
68
+ if (nights.length > 1) {
69
+ const avgAll = nights.reduce((a, n) => a + n.avg_spo2, 0) / nights.length;
70
+ lines.push(` Across those nights: ${Math.round(avgAll)}% on average.`);
71
+ }
72
+ lines.push(` ${describeRange(latest.avg_spo2, baseline)}`);
73
+ const text = lines.join('\n');
74
+ // Runtime guard, not just a test: a future edit that interpolates a
75
+ // clinical phrase fails the tool call rather than teaching the model.
76
+ assertNoBanTerms(text, FORMATTER_NAME);
77
+ return text;
78
+ }
79
+ //# sourceMappingURL=spo2.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spo2.js","sourceRoot":"","sources":["../../../src/tools/formatters/spo2.ts"],"names":[],"mappings":"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"}
@@ -14,4 +14,4 @@ export declare function formatBytes(s: string): number;
14
14
  export declare function deltaPct(current: number, prior: number): string;
15
15
  /** Population stddev; returns 0 when the sample is empty. */
16
16
  export declare function popStddev(values: number[]): number;
17
- //# sourceMappingURL=formatters.d.ts.map
17
+ //# sourceMappingURL=text.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"text.d.ts","sourceRoot":"","sources":["../../../src/tools/formatters/text.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,wBAAgB,WAAW,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAE7C;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAI/D;AAED,6DAA6D;AAC7D,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,CAMlD"}
@@ -27,4 +27,4 @@ export function popStddev(values) {
27
27
  const variance = values.reduce((a, v) => a + (v - mean) ** 2, 0) / values.length;
28
28
  return Math.sqrt(variance);
29
29
  }
30
- //# sourceMappingURL=formatters.js.map
30
+ //# sourceMappingURL=text.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"text.js","sourceRoot":"","sources":["../../../src/tools/formatters/text.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,UAAU,WAAW,CAAC,CAAS;IACnC,OAAO,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,UAAU,CAAA;AAC/C,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,QAAQ,CAAC,OAAe,EAAE,KAAa;IACrD,IAAI,KAAK,KAAK,CAAC;QAAE,OAAO,KAAK,CAAA;IAC7B,MAAM,GAAG,GAAG,CAAC,CAAC,OAAO,GAAG,KAAK,CAAC,GAAG,KAAK,CAAC,GAAG,GAAG,CAAA;IAC7C,OAAO,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAA;AACnD,CAAC;AAED,6DAA6D;AAC7D,MAAM,UAAU,SAAS,CAAC,MAAgB;IACxC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,CAAC,CAAA;IACjC,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,CAAA;IAC9D,MAAM,QAAQ,GACV,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,CAAA;IACnE,OAAO,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;AAC5B,CAAC"}
@@ -4,11 +4,11 @@ export declare const getActivityTotalInputSchema: z.ZodObject<{
4
4
  userId: z.ZodString;
5
5
  days: z.ZodNumber;
6
6
  }, "strip", z.ZodTypeAny, {
7
- days: number;
8
7
  userId: string;
9
- }, {
10
8
  days: number;
9
+ }, {
11
10
  userId: string;
11
+ days: number;
12
12
  }>;
13
13
  export type GetActivityTotalInput = z.infer<typeof getActivityTotalInputSchema>;
14
14
  export declare const getActivityTotal: ToolImpl<GetActivityTotalInput>;
@@ -1,5 +1,5 @@
1
1
  import { z } from 'zod';
2
- import { formatBytes } from '../formatters';
2
+ import { assertNoBanTerms, formatBytes } from '../formatters';
3
3
  export const getActivityTotalInputSchema = z.object({
4
4
  userId: z.string(),
5
5
  days: z.number().int().min(1).max(90),
@@ -12,14 +12,14 @@ export const getActivityTotal = async (input, ctx) => {
12
12
  days: input.days,
13
13
  };
14
14
  const steps = await ctx.analytics.execute(`SELECT date_trunc('day', ts)::VARCHAR AS day, SUM(steps)::INTEGER AS steps
15
- FROM activity
15
+ FROM v_activity
16
16
  WHERE user_id = $userId AND brand = $brand AND family_id = $familyId
17
17
  AND ts >= now() - INTERVAL ($days) DAY
18
18
  GROUP BY 1 ORDER BY 1`, params);
19
19
  const buckets = await ctx.analytics.execute(`SELECT date_trunc('day', ts)::VARCHAR AS day,
20
20
  SUM(calories)::DOUBLE AS calories,
21
21
  SUM(distance_km)::DOUBLE AS distance_km
22
- FROM activity_bucket
22
+ FROM v_activity_bucket
23
23
  WHERE user_id = $userId AND brand = $brand AND family_id = $familyId
24
24
  AND ts >= now() - INTERVAL ($days) DAY
25
25
  GROUP BY 1 ORDER BY 1`, params);
@@ -56,6 +56,10 @@ export const getActivityTotal = async (input, ctx) => {
56
56
  return finalize(text, orderedDays.length);
57
57
  };
58
58
  function finalize(text, rowCount) {
59
+ // principle 37 — every formatter's return path is guarded, not
60
+ // just the SpO2 one. Tool text lands in an LLM's context, and the
61
+ // model repeats whatever register it finds there.
62
+ assertNoBanTerms(text, 'getActivityTotal');
59
63
  return { text, rowCount, bytes: formatBytes(text) };
60
64
  }
61
65
  //# sourceMappingURL=activity.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"activity.js","sourceRoot":"","sources":["../../../src/tools/impls/activity.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"activity.js","sourceRoot":"","sources":["../../../src/tools/impls/activity.ts"],"names":[],"mappings":"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"}
@@ -6,13 +6,13 @@ export declare const detectAnomalyInputSchema: z.ZodObject<{
6
6
  lookbackDays: z.ZodNumber;
7
7
  stddevThreshold: z.ZodDefault<z.ZodNumber>;
8
8
  }, "strip", z.ZodTypeAny, {
9
- metric: "hrv_ms" | "activity_steps" | "sleep_total_minutes";
10
9
  userId: string;
10
+ metric: "hrv_ms" | "activity_steps" | "sleep_total_minutes";
11
11
  lookbackDays: number;
12
12
  stddevThreshold: number;
13
13
  }, {
14
- metric: "hrv_ms" | "activity_steps" | "sleep_total_minutes";
15
14
  userId: string;
15
+ metric: "hrv_ms" | "activity_steps" | "sleep_total_minutes";
16
16
  lookbackDays: number;
17
17
  stddevThreshold?: number | undefined;
18
18
  }>;
@@ -1,5 +1,5 @@
1
1
  import { z } from 'zod';
2
- import { formatBytes, popStddev } from '../formatters';
2
+ import { assertNoBanTerms, formatBytes, popStddev } from '../formatters';
3
3
  export const detectAnomalyInputSchema = z.object({
4
4
  userId: z.string(),
5
5
  metric: z.enum(['hrv_ms', 'sleep_total_minutes', 'activity_steps']),
@@ -9,7 +9,7 @@ export const detectAnomalyInputSchema = z.object({
9
9
  const METRIC_SPECS = {
10
10
  hrv_ms: {
11
11
  sql: lookbackDays => `SELECT date_trunc('day', ts)::VARCHAR AS day, AVG(hrv_ms)::DOUBLE AS value
12
- FROM hrv
12
+ FROM v_hrv
13
13
  WHERE user_id = $userId AND brand = $brand AND family_id = $familyId
14
14
  AND hrv_ms IS NOT NULL
15
15
  AND ts >= now() - INTERVAL (${lookbackDays}) DAY
@@ -19,7 +19,7 @@ const METRIC_SPECS = {
19
19
  },
20
20
  sleep_total_minutes: {
21
21
  sql: lookbackDays => `SELECT night_of::VARCHAR AS day, SUM(total_minutes)::DOUBLE AS value
22
- FROM sleep_session
22
+ FROM v_sleep_session
23
23
  WHERE user_id = $userId AND brand = $brand AND family_id = $familyId
24
24
  AND ts_start >= now() - INTERVAL (${lookbackDays}) DAY
25
25
  GROUP BY night_of ORDER BY night_of`,
@@ -28,7 +28,7 @@ const METRIC_SPECS = {
28
28
  },
29
29
  activity_steps: {
30
30
  sql: lookbackDays => `SELECT date_trunc('day', ts)::VARCHAR AS day, SUM(steps)::DOUBLE AS value
31
- FROM activity
31
+ FROM v_activity
32
32
  WHERE user_id = $userId AND brand = $brand AND family_id = $familyId
33
33
  AND ts >= now() - INTERVAL (${lookbackDays}) DAY
34
34
  GROUP BY 1 ORDER BY 1`,
@@ -65,6 +65,10 @@ export const detectAnomaly = async (input, ctx) => {
65
65
  return finalize(text, outliers.length);
66
66
  };
67
67
  function finalize(text, rowCount) {
68
+ // principle 37 — every formatter's return path is guarded, not
69
+ // just the SpO2 one. Tool text lands in an LLM's context, and the
70
+ // model repeats whatever register it finds there.
71
+ assertNoBanTerms(text, 'detectAnomaly');
68
72
  return { text, rowCount, bytes: formatBytes(text) };
69
73
  }
70
74
  //# sourceMappingURL=anomaly.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"anomaly.js","sourceRoot":"","sources":["../../../src/tools/impls/anomaly.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"anomaly.js","sourceRoot":"","sources":["../../../src/tools/impls/anomaly.ts"],"names":[],"mappings":"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"}
@@ -6,13 +6,13 @@ export declare const compareTrendInputSchema: z.ZodObject<{
6
6
  currentWindowDays: z.ZodNumber;
7
7
  priorWindowDays: z.ZodNumber;
8
8
  }, "strip", z.ZodTypeAny, {
9
- metric: "hrv_ms" | "activity_steps" | "sleep_total_minutes";
10
9
  userId: string;
10
+ metric: "hrv_ms" | "activity_steps" | "sleep_total_minutes";
11
11
  currentWindowDays: number;
12
12
  priorWindowDays: number;
13
13
  }, {
14
- metric: "hrv_ms" | "activity_steps" | "sleep_total_minutes";
15
14
  userId: string;
15
+ metric: "hrv_ms" | "activity_steps" | "sleep_total_minutes";
16
16
  currentWindowDays: number;
17
17
  priorWindowDays: number;
18
18
  }>;
@@ -1,5 +1,5 @@
1
1
  import { z } from 'zod';
2
- import { deltaPct, formatBytes } from '../formatters';
2
+ import { assertNoBanTerms, deltaPct, formatBytes } from '../formatters';
3
3
  export const compareTrendInputSchema = z.object({
4
4
  userId: z.string(),
5
5
  metric: z.enum(['hrv_ms', 'sleep_total_minutes', 'activity_steps']),
@@ -9,7 +9,7 @@ export const compareTrendInputSchema = z.object({
9
9
  const METRIC_SPECS = {
10
10
  hrv_ms: {
11
11
  sql: (windowDays, offsetDays) => `SELECT AVG(hrv_ms)::DOUBLE AS value
12
- FROM hrv
12
+ FROM v_hrv
13
13
  WHERE user_id = $userId AND brand = $brand AND family_id = $familyId
14
14
  AND hrv_ms IS NOT NULL
15
15
  AND ts >= now() - INTERVAL (${windowDays + offsetDays}) DAY
@@ -19,7 +19,7 @@ const METRIC_SPECS = {
19
19
  },
20
20
  sleep_total_minutes: {
21
21
  sql: (windowDays, offsetDays) => `SELECT AVG(total_minutes)::DOUBLE AS value
22
- FROM sleep_session
22
+ FROM v_sleep_session
23
23
  WHERE user_id = $userId AND brand = $brand AND family_id = $familyId
24
24
  AND ts_start >= now() - INTERVAL (${windowDays + offsetDays}) DAY
25
25
  AND ts_start < now() - INTERVAL (${offsetDays}) DAY`,
@@ -29,7 +29,7 @@ const METRIC_SPECS = {
29
29
  activity_steps: {
30
30
  sql: (windowDays, offsetDays) => `SELECT AVG(daily_steps)::DOUBLE AS value FROM (
31
31
  SELECT date_trunc('day', ts) AS day, SUM(steps) AS daily_steps
32
- FROM activity
32
+ FROM v_activity
33
33
  WHERE user_id = $userId AND brand = $brand AND family_id = $familyId
34
34
  AND ts >= now() - INTERVAL (${windowDays + offsetDays}) DAY
35
35
  AND ts < now() - INTERVAL (${offsetDays}) DAY
@@ -63,6 +63,10 @@ function fmt(v) {
63
63
  return v == null ? 'n/a' : v.toFixed(1);
64
64
  }
65
65
  function finalize(text, rowCount) {
66
+ // principle 37 — every formatter's return path is guarded, not
67
+ // just the SpO2 one. Tool text lands in an LLM's context, and the
68
+ // model repeats whatever register it finds there.
69
+ assertNoBanTerms(text, 'compareTrend');
66
70
  return { text, rowCount, bytes: formatBytes(text) };
67
71
  }
68
72
  //# sourceMappingURL=compare.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"compare.js","sourceRoot":"","sources":["../../../src/tools/impls/compare.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"compare.js","sourceRoot":"","sources":["../../../src/tools/impls/compare.ts"],"names":[],"mappings":"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"}