agent-bober 0.17.1 → 0.18.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 (355) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/CHANGELOG.md +15 -0
  3. package/README.md +158 -5
  4. package/dist/chat/answerer.d.ts +17 -0
  5. package/dist/chat/answerer.d.ts.map +1 -0
  6. package/dist/chat/answerer.js +52 -0
  7. package/dist/chat/answerer.js.map +1 -0
  8. package/dist/chat/approval-cursor.d.ts +25 -0
  9. package/dist/chat/approval-cursor.d.ts.map +1 -0
  10. package/dist/chat/approval-cursor.js +61 -0
  11. package/dist/chat/approval-cursor.js.map +1 -0
  12. package/dist/chat/approval-reader.d.ts +11 -0
  13. package/dist/chat/approval-reader.d.ts.map +1 -0
  14. package/dist/chat/approval-reader.js +20 -0
  15. package/dist/chat/approval-reader.js.map +1 -0
  16. package/dist/chat/careful-sidecar.d.ts +15 -0
  17. package/dist/chat/careful-sidecar.d.ts.map +1 -0
  18. package/dist/chat/careful-sidecar.js +39 -0
  19. package/dist/chat/careful-sidecar.js.map +1 -0
  20. package/dist/chat/chat-session.d.ts +129 -0
  21. package/dist/chat/chat-session.d.ts.map +1 -0
  22. package/dist/chat/chat-session.js +470 -0
  23. package/dist/chat/chat-session.js.map +1 -0
  24. package/dist/chat/completion-tailer.d.ts +27 -0
  25. package/dist/chat/completion-tailer.d.ts.map +1 -0
  26. package/dist/chat/completion-tailer.js +187 -0
  27. package/dist/chat/completion-tailer.js.map +1 -0
  28. package/dist/chat/conversation-store.d.ts +22 -0
  29. package/dist/chat/conversation-store.d.ts.map +1 -0
  30. package/dist/chat/conversation-store.js +66 -0
  31. package/dist/chat/conversation-store.js.map +1 -0
  32. package/dist/chat/cursor-store.d.ts +23 -0
  33. package/dist/chat/cursor-store.d.ts.map +1 -0
  34. package/dist/chat/cursor-store.js +42 -0
  35. package/dist/chat/cursor-store.js.map +1 -0
  36. package/dist/chat/pid-sidecar.d.ts +22 -0
  37. package/dist/chat/pid-sidecar.d.ts.map +1 -0
  38. package/dist/chat/pid-sidecar.js +42 -0
  39. package/dist/chat/pid-sidecar.js.map +1 -0
  40. package/dist/chat/roster-reader.d.ts +16 -0
  41. package/dist/chat/roster-reader.d.ts.map +1 -0
  42. package/dist/chat/roster-reader.js +39 -0
  43. package/dist/chat/roster-reader.js.map +1 -0
  44. package/dist/chat/run-spawner.d.ts +71 -0
  45. package/dist/chat/run-spawner.d.ts.map +1 -0
  46. package/dist/chat/run-spawner.js +121 -0
  47. package/dist/chat/run-spawner.js.map +1 -0
  48. package/dist/chat/slash-commands.d.ts +36 -0
  49. package/dist/chat/slash-commands.d.ts.map +1 -0
  50. package/dist/chat/slash-commands.js +133 -0
  51. package/dist/chat/slash-commands.js.map +1 -0
  52. package/dist/chat/steer-cleanup.d.ts +12 -0
  53. package/dist/chat/steer-cleanup.d.ts.map +1 -0
  54. package/dist/chat/steer-cleanup.js +60 -0
  55. package/dist/chat/steer-cleanup.js.map +1 -0
  56. package/dist/chat/turn-classifier.d.ts +43 -0
  57. package/dist/chat/turn-classifier.d.ts.map +1 -0
  58. package/dist/chat/turn-classifier.js +150 -0
  59. package/dist/chat/turn-classifier.js.map +1 -0
  60. package/dist/cli/commands/blackboard.d.ts +33 -0
  61. package/dist/cli/commands/blackboard.d.ts.map +1 -0
  62. package/dist/cli/commands/blackboard.js +114 -0
  63. package/dist/cli/commands/blackboard.js.map +1 -0
  64. package/dist/cli/commands/chat.d.ts +13 -0
  65. package/dist/cli/commands/chat.d.ts.map +1 -0
  66. package/dist/cli/commands/chat.js +43 -0
  67. package/dist/cli/commands/chat.js.map +1 -0
  68. package/dist/cli/commands/evolve.d.ts +18 -0
  69. package/dist/cli/commands/evolve.d.ts.map +1 -0
  70. package/dist/cli/commands/evolve.js +72 -0
  71. package/dist/cli/commands/evolve.js.map +1 -0
  72. package/dist/cli/commands/facts.d.ts +15 -0
  73. package/dist/cli/commands/facts.d.ts.map +1 -0
  74. package/dist/cli/commands/facts.js +213 -0
  75. package/dist/cli/commands/facts.js.map +1 -0
  76. package/dist/cli/commands/medical.d.ts +24 -0
  77. package/dist/cli/commands/medical.d.ts.map +1 -0
  78. package/dist/cli/commands/medical.js +132 -0
  79. package/dist/cli/commands/medical.js.map +1 -0
  80. package/dist/cli/commands/memory.d.ts +2 -1
  81. package/dist/cli/commands/memory.d.ts.map +1 -1
  82. package/dist/cli/commands/memory.js +94 -7
  83. package/dist/cli/commands/memory.js.map +1 -1
  84. package/dist/cli/commands/plan.d.ts.map +1 -1
  85. package/dist/cli/commands/plan.js +29 -3
  86. package/dist/cli/commands/plan.js.map +1 -1
  87. package/dist/cli/commands/replay.d.ts +17 -0
  88. package/dist/cli/commands/replay.d.ts.map +1 -0
  89. package/dist/cli/commands/replay.js +261 -0
  90. package/dist/cli/commands/replay.js.map +1 -0
  91. package/dist/cli/commands/run.d.ts +9 -0
  92. package/dist/cli/commands/run.d.ts.map +1 -1
  93. package/dist/cli/commands/run.js +27 -1
  94. package/dist/cli/commands/run.js.map +1 -1
  95. package/dist/cli/commands/sprint.d.ts.map +1 -1
  96. package/dist/cli/commands/sprint.js +17 -3
  97. package/dist/cli/commands/sprint.js.map +1 -1
  98. package/dist/cli/index.js +12 -0
  99. package/dist/cli/index.js.map +1 -1
  100. package/dist/config/defaults.d.ts.map +1 -1
  101. package/dist/config/defaults.js +8 -0
  102. package/dist/config/defaults.js.map +1 -1
  103. package/dist/config/role-providers.d.ts +1 -1
  104. package/dist/config/role-providers.d.ts.map +1 -1
  105. package/dist/config/role-providers.js +6 -1
  106. package/dist/config/role-providers.js.map +1 -1
  107. package/dist/config/schema.d.ts +467 -222
  108. package/dist/config/schema.d.ts.map +1 -1
  109. package/dist/config/schema.js +25 -0
  110. package/dist/config/schema.js.map +1 -1
  111. package/dist/contracts/eval-result.d.ts +84 -84
  112. package/dist/contracts/spec.d.ts +28 -28
  113. package/dist/contracts/sprint-contract.d.ts +4 -4
  114. package/dist/fleet/critic-deep.d.ts +53 -0
  115. package/dist/fleet/critic-deep.d.ts.map +1 -0
  116. package/dist/fleet/critic-deep.js +199 -0
  117. package/dist/fleet/critic-deep.js.map +1 -0
  118. package/dist/fleet/decomposer-deep-constants.d.ts +4 -0
  119. package/dist/fleet/decomposer-deep-constants.d.ts.map +1 -0
  120. package/dist/fleet/decomposer-deep-constants.js +14 -0
  121. package/dist/fleet/decomposer-deep-constants.js.map +1 -0
  122. package/dist/fleet/decomposer-deep-types.d.ts +8 -0
  123. package/dist/fleet/decomposer-deep-types.d.ts.map +1 -0
  124. package/dist/fleet/decomposer-deep-types.js +9 -0
  125. package/dist/fleet/decomposer-deep-types.js.map +1 -0
  126. package/dist/fleet/decomposer-deep.d.ts +51 -0
  127. package/dist/fleet/decomposer-deep.d.ts.map +1 -0
  128. package/dist/fleet/decomposer-deep.js +261 -0
  129. package/dist/fleet/decomposer-deep.js.map +1 -0
  130. package/dist/fleet/decomposer.d.ts +22 -0
  131. package/dist/fleet/decomposer.d.ts.map +1 -0
  132. package/dist/fleet/decomposer.js +144 -0
  133. package/dist/fleet/decomposer.js.map +1 -0
  134. package/dist/fleet/index.d.ts +93 -0
  135. package/dist/fleet/index.d.ts.map +1 -1
  136. package/dist/fleet/index.js +230 -2
  137. package/dist/fleet/index.js.map +1 -1
  138. package/dist/fleet/manifest-write.d.ts +30 -0
  139. package/dist/fleet/manifest-write.d.ts.map +1 -0
  140. package/dist/fleet/manifest-write.js +90 -0
  141. package/dist/fleet/manifest-write.js.map +1 -0
  142. package/dist/fleet/manifest.d.ts +6 -6
  143. package/dist/fleet/shared-blackboard.d.ts +51 -0
  144. package/dist/fleet/shared-blackboard.d.ts.map +1 -0
  145. package/dist/fleet/shared-blackboard.js +79 -0
  146. package/dist/fleet/shared-blackboard.js.map +1 -0
  147. package/dist/fleet/synthesis.d.ts +16 -0
  148. package/dist/fleet/synthesis.d.ts.map +1 -0
  149. package/dist/fleet/synthesis.js +22 -0
  150. package/dist/fleet/synthesis.js.map +1 -0
  151. package/dist/fleet/tier-policy.d.ts +18 -0
  152. package/dist/fleet/tier-policy.d.ts.map +1 -0
  153. package/dist/fleet/tier-policy.js +53 -0
  154. package/dist/fleet/tier-policy.js.map +1 -0
  155. package/dist/fleet/tool-role-guard.d.ts +24 -0
  156. package/dist/fleet/tool-role-guard.d.ts.map +1 -0
  157. package/dist/fleet/tool-role-guard.js +55 -0
  158. package/dist/fleet/tool-role-guard.js.map +1 -0
  159. package/dist/incident/resolution-verify.d.ts +4 -4
  160. package/dist/incident/types.d.ts +8 -8
  161. package/dist/mcp/run-manager.d.ts +9 -1
  162. package/dist/mcp/run-manager.d.ts.map +1 -1
  163. package/dist/mcp/run-manager.js.map +1 -1
  164. package/dist/medical/adapters/apple-health.d.ts +32 -0
  165. package/dist/medical/adapters/apple-health.d.ts.map +1 -0
  166. package/dist/medical/adapters/apple-health.js +100 -0
  167. package/dist/medical/adapters/apple-health.js.map +1 -0
  168. package/dist/medical/audit.d.ts +31 -0
  169. package/dist/medical/audit.d.ts.map +1 -0
  170. package/dist/medical/audit.js +56 -0
  171. package/dist/medical/audit.js.map +1 -0
  172. package/dist/medical/consent.d.ts +37 -0
  173. package/dist/medical/consent.d.ts.map +1 -0
  174. package/dist/medical/consent.js +73 -0
  175. package/dist/medical/consent.js.map +1 -0
  176. package/dist/medical/disclaimer.d.ts +11 -0
  177. package/dist/medical/disclaimer.d.ts.map +1 -0
  178. package/dist/medical/disclaimer.js +21 -0
  179. package/dist/medical/disclaimer.js.map +1 -0
  180. package/dist/medical/egress.d.ts +30 -0
  181. package/dist/medical/egress.d.ts.map +1 -0
  182. package/dist/medical/egress.js +50 -0
  183. package/dist/medical/egress.js.map +1 -0
  184. package/dist/medical/engine.d.ts +73 -0
  185. package/dist/medical/engine.d.ts.map +1 -0
  186. package/dist/medical/engine.js +333 -0
  187. package/dist/medical/engine.js.map +1 -0
  188. package/dist/medical/guardrails.d.ts +47 -0
  189. package/dist/medical/guardrails.d.ts.map +1 -0
  190. package/dist/medical/guardrails.js +92 -0
  191. package/dist/medical/guardrails.js.map +1 -0
  192. package/dist/medical/health-store.d.ts +80 -0
  193. package/dist/medical/health-store.d.ts.map +1 -0
  194. package/dist/medical/health-store.js +204 -0
  195. package/dist/medical/health-store.js.map +1 -0
  196. package/dist/medical/inference.d.ts +22 -0
  197. package/dist/medical/inference.d.ts.map +1 -0
  198. package/dist/medical/inference.js +39 -0
  199. package/dist/medical/inference.js.map +1 -0
  200. package/dist/medical/ingestion.d.ts +33 -0
  201. package/dist/medical/ingestion.d.ts.map +1 -0
  202. package/dist/medical/ingestion.js +52 -0
  203. package/dist/medical/ingestion.js.map +1 -0
  204. package/dist/medical/numerics.d.ts +66 -0
  205. package/dist/medical/numerics.d.ts.map +1 -0
  206. package/dist/medical/numerics.js +216 -0
  207. package/dist/medical/numerics.js.map +1 -0
  208. package/dist/medical/red-flag.d.ts +42 -0
  209. package/dist/medical/red-flag.d.ts.map +1 -0
  210. package/dist/medical/red-flag.js +161 -0
  211. package/dist/medical/red-flag.js.map +1 -0
  212. package/dist/medical/refusal.d.ts +46 -0
  213. package/dist/medical/refusal.d.ts.map +1 -0
  214. package/dist/medical/refusal.js +131 -0
  215. package/dist/medical/refusal.js.map +1 -0
  216. package/dist/medical/retrieval/grounding-critic.d.ts +34 -0
  217. package/dist/medical/retrieval/grounding-critic.d.ts.map +1 -0
  218. package/dist/medical/retrieval/grounding-critic.js +153 -0
  219. package/dist/medical/retrieval/grounding-critic.js.map +1 -0
  220. package/dist/medical/retrieval/literature.d.ts +78 -0
  221. package/dist/medical/retrieval/literature.d.ts.map +1 -0
  222. package/dist/medical/retrieval/literature.js +292 -0
  223. package/dist/medical/retrieval/literature.js.map +1 -0
  224. package/dist/medical/retrieval/medline-source.d.ts +72 -0
  225. package/dist/medical/retrieval/medline-source.d.ts.map +1 -0
  226. package/dist/medical/retrieval/medline-source.js +120 -0
  227. package/dist/medical/retrieval/medline-source.js.map +1 -0
  228. package/dist/medical/team.d.ts +15 -0
  229. package/dist/medical/team.d.ts.map +1 -0
  230. package/dist/medical/team.js +44 -0
  231. package/dist/medical/team.js.map +1 -0
  232. package/dist/medical/types.d.ts +175 -0
  233. package/dist/medical/types.d.ts.map +1 -0
  234. package/dist/medical/types.js +8 -0
  235. package/dist/medical/types.js.map +1 -0
  236. package/dist/medical/whoop/whoop-client.d.ts +114 -0
  237. package/dist/medical/whoop/whoop-client.d.ts.map +1 -0
  238. package/dist/medical/whoop/whoop-client.js +226 -0
  239. package/dist/medical/whoop/whoop-client.js.map +1 -0
  240. package/dist/medical/whoop/whoop-sync.d.ts +25 -0
  241. package/dist/medical/whoop/whoop-sync.d.ts.map +1 -0
  242. package/dist/medical/whoop/whoop-sync.js +97 -0
  243. package/dist/medical/whoop/whoop-sync.js.map +1 -0
  244. package/dist/medical/whoop/whoop-token.d.ts +44 -0
  245. package/dist/medical/whoop/whoop-token.d.ts.map +1 -0
  246. package/dist/medical/whoop/whoop-token.js +66 -0
  247. package/dist/medical/whoop/whoop-token.js.map +1 -0
  248. package/dist/orchestrator/context-handoff.d.ts +60 -60
  249. package/dist/orchestrator/contract-materialization.d.ts +33 -0
  250. package/dist/orchestrator/contract-materialization.d.ts.map +1 -0
  251. package/dist/orchestrator/contract-materialization.js +113 -0
  252. package/dist/orchestrator/contract-materialization.js.map +1 -0
  253. package/dist/orchestrator/memory/distill.d.ts +4 -1
  254. package/dist/orchestrator/memory/distill.d.ts.map +1 -1
  255. package/dist/orchestrator/memory/distill.js +41 -1
  256. package/dist/orchestrator/memory/distill.js.map +1 -1
  257. package/dist/orchestrator/memory/fact-detector.d.ts +61 -0
  258. package/dist/orchestrator/memory/fact-detector.d.ts.map +1 -0
  259. package/dist/orchestrator/memory/fact-detector.js +153 -0
  260. package/dist/orchestrator/memory/fact-detector.js.map +1 -0
  261. package/dist/orchestrator/memory/fact-judge.d.ts +19 -0
  262. package/dist/orchestrator/memory/fact-judge.d.ts.map +1 -0
  263. package/dist/orchestrator/memory/fact-judge.js +121 -0
  264. package/dist/orchestrator/memory/fact-judge.js.map +1 -0
  265. package/dist/orchestrator/memory/fact-retrieve.d.ts +52 -0
  266. package/dist/orchestrator/memory/fact-retrieve.d.ts.map +1 -0
  267. package/dist/orchestrator/memory/fact-retrieve.js +117 -0
  268. package/dist/orchestrator/memory/fact-retrieve.js.map +1 -0
  269. package/dist/orchestrator/memory/hygiene.d.ts +66 -0
  270. package/dist/orchestrator/memory/hygiene.d.ts.map +1 -0
  271. package/dist/orchestrator/memory/hygiene.js +153 -0
  272. package/dist/orchestrator/memory/hygiene.js.map +1 -0
  273. package/dist/orchestrator/memory/reconcile.d.ts +47 -0
  274. package/dist/orchestrator/memory/reconcile.d.ts.map +1 -0
  275. package/dist/orchestrator/memory/reconcile.js +116 -0
  276. package/dist/orchestrator/memory/reconcile.js.map +1 -0
  277. package/dist/orchestrator/memory/retrieve.d.ts +5 -3
  278. package/dist/orchestrator/memory/retrieve.d.ts.map +1 -1
  279. package/dist/orchestrator/memory/retrieve.js +9 -6
  280. package/dist/orchestrator/memory/retrieve.js.map +1 -1
  281. package/dist/orchestrator/pipeline.d.ts +19 -3
  282. package/dist/orchestrator/pipeline.d.ts.map +1 -1
  283. package/dist/orchestrator/pipeline.js +63 -52
  284. package/dist/orchestrator/pipeline.js.map +1 -1
  285. package/dist/orchestrator/planner-agent.d.ts.map +1 -1
  286. package/dist/orchestrator/planner-agent.js +16 -1
  287. package/dist/orchestrator/planner-agent.js.map +1 -1
  288. package/dist/orchestrator/selfimprove/eval-guards.d.ts +51 -0
  289. package/dist/orchestrator/selfimprove/eval-guards.d.ts.map +1 -0
  290. package/dist/orchestrator/selfimprove/eval-guards.js +120 -0
  291. package/dist/orchestrator/selfimprove/eval-guards.js.map +1 -0
  292. package/dist/orchestrator/selfimprove/gepa.d.ts +74 -0
  293. package/dist/orchestrator/selfimprove/gepa.d.ts.map +1 -0
  294. package/dist/orchestrator/selfimprove/gepa.js +227 -0
  295. package/dist/orchestrator/selfimprove/gepa.js.map +1 -0
  296. package/dist/orchestrator/selfimprove/replay-harness.d.ts +65 -0
  297. package/dist/orchestrator/selfimprove/replay-harness.d.ts.map +1 -0
  298. package/dist/orchestrator/selfimprove/replay-harness.js +134 -0
  299. package/dist/orchestrator/selfimprove/replay-harness.js.map +1 -0
  300. package/dist/orchestrator/selfimprove/replay-store.d.ts +45 -0
  301. package/dist/orchestrator/selfimprove/replay-store.d.ts.map +1 -0
  302. package/dist/orchestrator/selfimprove/replay-store.js +119 -0
  303. package/dist/orchestrator/selfimprove/replay-store.js.map +1 -0
  304. package/dist/orchestrator/selfimprove/replay-types.d.ts +32 -0
  305. package/dist/orchestrator/selfimprove/replay-types.d.ts.map +1 -0
  306. package/dist/orchestrator/selfimprove/replay-types.js +15 -0
  307. package/dist/orchestrator/selfimprove/replay-types.js.map +1 -0
  308. package/dist/orchestrator/workflow/engine.d.ts +3 -1
  309. package/dist/orchestrator/workflow/engine.d.ts.map +1 -1
  310. package/dist/orchestrator/workflow/selector.d.ts +18 -0
  311. package/dist/orchestrator/workflow/selector.d.ts.map +1 -1
  312. package/dist/orchestrator/workflow/selector.js +41 -0
  313. package/dist/orchestrator/workflow/selector.js.map +1 -1
  314. package/dist/orchestrator/workflow/ts-engine.d.ts +3 -1
  315. package/dist/orchestrator/workflow/ts-engine.d.ts.map +1 -1
  316. package/dist/orchestrator/workflow/ts-engine.js +2 -2
  317. package/dist/orchestrator/workflow/ts-engine.js.map +1 -1
  318. package/dist/orchestrator/workflow/workflow-engine.d.ts +3 -1
  319. package/dist/orchestrator/workflow/workflow-engine.d.ts.map +1 -1
  320. package/dist/orchestrator/workflow/workflow-engine.js +3 -3
  321. package/dist/orchestrator/workflow/workflow-engine.js.map +1 -1
  322. package/dist/state/facts.d.ts +110 -0
  323. package/dist/state/facts.d.ts.map +1 -0
  324. package/dist/state/facts.js +208 -0
  325. package/dist/state/facts.js.map +1 -0
  326. package/dist/state/guidance.d.ts +37 -0
  327. package/dist/state/guidance.d.ts.map +1 -0
  328. package/dist/state/guidance.js +144 -0
  329. package/dist/state/guidance.js.map +1 -0
  330. package/dist/state/history.d.ts +2 -2
  331. package/dist/state/index.d.ts +1 -1
  332. package/dist/state/index.d.ts.map +1 -1
  333. package/dist/state/index.js +1 -1
  334. package/dist/state/index.js.map +1 -1
  335. package/dist/state/memory.d.ts +43 -11
  336. package/dist/state/memory.d.ts.map +1 -1
  337. package/dist/state/memory.js +93 -15
  338. package/dist/state/memory.js.map +1 -1
  339. package/dist/state/pause.d.ts +47 -0
  340. package/dist/state/pause.d.ts.map +1 -0
  341. package/dist/state/pause.js +146 -0
  342. package/dist/state/pause.js.map +1 -0
  343. package/dist/state/sprint-state.d.ts +8 -0
  344. package/dist/state/sprint-state.d.ts.map +1 -1
  345. package/dist/state/sprint-state.js +32 -1
  346. package/dist/state/sprint-state.js.map +1 -1
  347. package/dist/teams/registry.d.ts +16 -0
  348. package/dist/teams/registry.d.ts.map +1 -0
  349. package/dist/teams/registry.js +53 -0
  350. package/dist/teams/registry.js.map +1 -0
  351. package/dist/teams/types.d.ts +25 -0
  352. package/dist/teams/types.d.ts.map +1 -0
  353. package/dist/teams/types.js +2 -0
  354. package/dist/teams/types.js.map +1 -0
  355. package/package.json +3 -1
@@ -0,0 +1,175 @@
1
+ /**
2
+ * Medical team shared data types (Phase 6, Sprint 1).
3
+ *
4
+ * Defines the type surface for the medical-sop pipeline.
5
+ * No execution logic lives here — real enforcement lands in S2/S3/S6.
6
+ */
7
+ /** Discriminated union returned by GuardrailSet.evaluate. */
8
+ export type GuardrailVerdict = {
9
+ kind: "allow";
10
+ } | {
11
+ kind: "short-circuit";
12
+ rule: string;
13
+ cannedResponse: string;
14
+ } | {
15
+ kind: "refuse";
16
+ rule: string;
17
+ reason: string;
18
+ };
19
+ /** Context passed to GuardrailSet.evaluate. Real fields land in S3. */
20
+ export interface GuardrailContext {
21
+ }
22
+ /** Interface for a rule set that guards medical prompts before LLM call. */
23
+ export interface GuardrailSet {
24
+ evaluate(prompt: string, ctx: GuardrailContext): GuardrailVerdict;
25
+ readonly rulesetVersion: string;
26
+ }
27
+ /** Literature citation shape — source URL + title for a retrieved MedlinePlus passage (S7). */
28
+ export interface Citation {
29
+ title: string;
30
+ url: string;
31
+ source: "medlineplus";
32
+ }
33
+ /** Shape of the medical engine answer. Real population lands in S2/S6. */
34
+ export interface MedicalAnswer {
35
+ body: string;
36
+ abstained: boolean;
37
+ citations: Citation[];
38
+ disclaimerFooter: string;
39
+ shortCircuit: boolean;
40
+ }
41
+ /**
42
+ * Persisted consent record stored at .bober/medical/consent.json.
43
+ * All timestamps are injected ISO 8601 strings — never wall-clock reads.
44
+ */
45
+ export interface ConsentRecord {
46
+ consentVersion: string;
47
+ /** ISO 8601; INJECTED parameter — never Date.now(). */
48
+ acceptedAtIso: string;
49
+ rulesetVersion: string;
50
+ disclaimerVersion: string;
51
+ }
52
+ /** Discriminated audit event type. IDs/enums only — no prompt text or health values. */
53
+ export type AuditEvent = "consent" | "short-circuit" | "refuse" | "answer" | "abstain" | "ingest";
54
+ /** Critic gate outcome on the grounded synthesis path. IDs/enums only — NEVER text. Sprint 3. */
55
+ export type CriticVerdict = "approve" | "reject-abstained" | "error-abstained";
56
+ /**
57
+ * Audit entry appended to .bober/medical/audit-<date>.jsonl.
58
+ * ONLY IDs/enums allowed — NEVER prompt text or health values.
59
+ */
60
+ export interface AuditEntry {
61
+ /** ISO 8601; INJECTED parameter — never Date.now(). */
62
+ tIso: string;
63
+ event: AuditEvent;
64
+ /** Optional ruleset version (populated when a guardrail runs). */
65
+ rulesetVersion?: string;
66
+ /** Optional patternset version (populated in S3 by RedFlagDetector). */
67
+ patternsetVersion?: string;
68
+ /** Optional rule ID triggering the event (IDs only — never text). */
69
+ ruleId?: string;
70
+ /** Critic gate outcome on the grounded path. IDs/enums only — NEVER text. Sprint 3. */
71
+ criticVerdict?: CriticVerdict;
72
+ }
73
+ /**
74
+ * A single time-stamped health metric observation.
75
+ * All timestamps are ISO 8601 INJECTED parameters — the store never reads the clock.
76
+ * id is derived deterministically from (metric|tStart|source|value) via SHA-256.
77
+ */
78
+ export interface HealthObservation {
79
+ /** Deterministic SHA-256 of metric|tStart|source|value; derivable, so optional on input. */
80
+ id?: string;
81
+ metric: string;
82
+ value: number;
83
+ unit: string;
84
+ /** ISO 8601; INJECTED parameter — never Date.now(). */
85
+ tStart: string;
86
+ tEnd?: string;
87
+ /** e.g. "apple-health" | "whoop" */
88
+ source: string;
89
+ }
90
+ /**
91
+ * A laboratory test result with reference range.
92
+ * id is optional on input (derived from biomarker|collectedAtIso|value via SHA-256).
93
+ */
94
+ export interface LabResult {
95
+ id?: string;
96
+ biomarker: string;
97
+ value: number;
98
+ unit: string;
99
+ /** ISO 8601; INJECTED parameter — never Date.now(). */
100
+ collectedAtIso: string;
101
+ referenceLow?: number;
102
+ referenceHigh?: number;
103
+ }
104
+ /** A stored baseline value for a metric used to compute delta/trend. */
105
+ export interface Baseline {
106
+ metric: string;
107
+ value: number;
108
+ unit: string;
109
+ }
110
+ /**
111
+ * Closed whitelist of numeric primitives (ADR-3).
112
+ * Adding a computation requires extending this union (a code review event), not a model decision.
113
+ */
114
+ export type NumericPrimitive = "mean" | "min" | "max" | "latest" | "delta" | "slope" | "percentile" | "zscore";
115
+ /**
116
+ * Result of a NumericsQueryLayer.getMetric() call.
117
+ * value is null when sampleCount === 0 (empty window), cross-unit refusal, or zscore n<2.
118
+ * sampleCount === 0 signals upstream abstention.
119
+ */
120
+ export interface NumericResult {
121
+ primitive: NumericPrimitive;
122
+ /** null when sampleCount === 0 OR cross-unit refusal OR zscore n<2 */
123
+ value: number | null;
124
+ unit: string;
125
+ /** 0 => upstream abstention */
126
+ sampleCount: number;
127
+ }
128
+ /** Time window for querying a metric. */
129
+ export interface MetricWindow {
130
+ metric: string;
131
+ fromIso: string;
132
+ toIso: string;
133
+ /** Expected unit (used for abstain result labelling when window is empty). */
134
+ unit?: string;
135
+ }
136
+ /** Trend summary for a lab biomarker series. */
137
+ export interface LabTrend {
138
+ biomarker: string;
139
+ sampleCount: number;
140
+ /** Latest value; null when sampleCount === 0 (abstain). */
141
+ latestValue: number | null;
142
+ /** Latest unit; empty string when sampleCount === 0. */
143
+ latestUnit: string;
144
+ /** ISO 8601 timestamp of the most recent result; null when sampleCount === 0. */
145
+ latestCollectedAt: string | null;
146
+ /** Simple least-squares slope over (t,value); null when sampleCount < 2. */
147
+ slope: number | null;
148
+ }
149
+ /**
150
+ * Result returned by IngestionAdapter.ingest / IngestionNormalizer.importFile.
151
+ * recordsParsed: total numeric <Record> elements seen.
152
+ * newRows: NEW rows actually inserted (dedup-aware via INSERT OR IGNORE).
153
+ */
154
+ export interface IngestionResult {
155
+ recordsParsed: number;
156
+ newRows: number;
157
+ }
158
+ /**
159
+ * Async sink that receives bounded observation batches from an adapter.
160
+ * writeBatch is awaited by the adapter to apply backpressure before the next batch.
161
+ */
162
+ export interface ObservationSink {
163
+ writeBatch(obs: HealthObservation[], labs: LabResult[]): Promise<void>;
164
+ }
165
+ /**
166
+ * Interface for a streaming health data import adapter.
167
+ * canHandle selects the adapter; ingest streams the file into the sink.
168
+ * Adding a new adapter (Whoop, CSV, …) only requires a new class — ADR-4 registry.
169
+ */
170
+ export interface IngestionAdapter {
171
+ readonly kind: string;
172
+ canHandle(filePath: string): boolean;
173
+ ingest(filePath: string, sink: ObservationSink): Promise<IngestionResult>;
174
+ }
175
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/medical/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Medical team shared data types (Phase 6, Sprint 1).
3
+ *
4
+ * Defines the type surface for the medical-sop pipeline.
5
+ * No execution logic lives here — real enforcement lands in S2/S3/S6.
6
+ */
7
+ export {};
8
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/medical/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}
@@ -0,0 +1,114 @@
1
+ /**
2
+ * WhoopClient — the SECOND ESLint-excepted network file (ADR-1, sibling to medline-source.ts).
3
+ * All WHOOP HTTP access lives here and NOWHERE ELSE under src/medical.
4
+ *
5
+ * assertAllowed("device-connection") is called BEFORE any fetch attempt — runtime
6
+ * defense-in-depth backing the static ESLint boundary (mirrors medline-source.ts).
7
+ *
8
+ * bober: single OAuth2 refresh_token grant + v2 paginated GET; add webhook/push support
9
+ * only if WHOOP public API exposes a server-push option (currently pull-only, ADR-1).
10
+ */
11
+ import type { EgressGuard } from "../egress.js";
12
+ import type { WhoopTokenStore } from "./whoop-token.js";
13
+ /** The four WHOOP data collections available via v2 endpoints. */
14
+ export type WhoopCollection = "recovery" | "sleep" | "cycle" | "workout";
15
+ /** ISO-8601 time window for a paginated fetch request. */
16
+ export type SyncWindow = {
17
+ startIso: string;
18
+ endIso: string;
19
+ };
20
+ /** A single WHOOP record as normalised for downstream processing. */
21
+ export type WhoopRecord = {
22
+ id: string;
23
+ tStartIso: string;
24
+ tEndIso?: string;
25
+ metrics: Record<string, number>;
26
+ };
27
+ /** One page of WHOOP records; nextCursor undefined means this is the last page. */
28
+ export type WhoopPage = {
29
+ records: WhoopRecord[];
30
+ nextCursor?: string;
31
+ };
32
+ /**
33
+ * Injectable transport type — extended vs. the MedlineSource FetchLike to add:
34
+ * - `init` arg: method, headers, body for POST refresh grant + authorised GET
35
+ * - `headers.get(name)`: needed for 429 X-RateLimit-Reset header reading
36
+ *
37
+ * Tests pass a duck-typed fake returning fixture data; production defaults to global fetch.
38
+ */
39
+ export type FetchLike = (url: string, init?: {
40
+ method?: string;
41
+ headers?: Record<string, string>;
42
+ body?: string;
43
+ }) => Promise<{
44
+ ok: boolean;
45
+ status: number;
46
+ headers: {
47
+ get(name: string): string | null;
48
+ };
49
+ json(): Promise<unknown>;
50
+ }>;
51
+ /**
52
+ * Authenticated WHOOP v2 API client.
53
+ *
54
+ * This is the SECOND ESLint-excepted network file (ADR-1). All WHOOP HTTP
55
+ * access — the OAuth2 refresh_token grant and all v2 paginated GETs — lives
56
+ * HERE and nowhere else under src/medical.
57
+ *
58
+ * Design invariants:
59
+ * - ensureAccessToken and fetchPage BOTH start with assertAllowed("device-connection").
60
+ * With the axis off, both methods throw before any fetchImpl call (sc-2-4).
61
+ * - 401 triggers exactly ONE refresh+retry; a second 401 throws (no infinite loop).
62
+ * - 429 reads the X-RateLimit-Reset header (seconds), awaits the INJECTED waiter,
63
+ * then retries; the waiter is injectable so tests never actually sleep (sc-2-7).
64
+ * - Timestamps are never derived from Date.now(); nowIso is injected for testability.
65
+ *
66
+ * bober: single-process access token cache (in-memory); swap for a shared cache
67
+ * (Redis / SQLite) if multi-process token sharing is needed.
68
+ */
69
+ export declare class WhoopClient {
70
+ private readonly egress;
71
+ private readonly tokenStore;
72
+ private readonly fetchImpl;
73
+ private readonly waiter;
74
+ private readonly nowIso;
75
+ private cached?;
76
+ constructor(egress: EgressGuard, tokenStore: WhoopTokenStore, fetchImpl?: FetchLike, waiter?: (ms: number) => Promise<void>, nowIso?: () => string);
77
+ /**
78
+ * Returns a valid WHOOP access token, refreshing if the cached token is expired.
79
+ *
80
+ * Order:
81
+ * 1. assertAllowed("device-connection") — throws if axis is off (sc-2-4).
82
+ * 2. If cached and unexpired, return cached access token.
83
+ * 3. Read refresh token from tokenStore; throw "authorize-first" if absent.
84
+ * 4. POST refresh_token grant to WHOOP_TOKEN_URL via fetchImpl.
85
+ * 5. Persist rotated tokens via tokenStore.writeTokens.
86
+ * 6. Cache and return the new access token.
87
+ *
88
+ * On 401/invalid_grant from the token endpoint, throws a clear "re-authorize" error.
89
+ */
90
+ ensureAccessToken(): Promise<string>;
91
+ /** Performs the OAuth2 refresh_token grant and caches the new tokens. */
92
+ private _doRefresh;
93
+ /**
94
+ * Fetch one page of WHOOP v2 data for the given collection and time window.
95
+ *
96
+ * Order:
97
+ * 1. assertAllowed("device-connection") — throws if axis is off (sc-2-4).
98
+ * 2. ensureAccessToken — gets/refreshes the Bearer token.
99
+ * 3. GET WHOOP_API_BASE + COLLECTION_PATHS[collection] with query params.
100
+ * 4. On 401: call ensureAccessToken (force refresh) and retry EXACTLY ONCE.
101
+ * 5. On 429: read X-RateLimit-Reset header (seconds), await waiter, retry.
102
+ * 6. Parse JSON into WhoopPage.
103
+ *
104
+ * Throws on non-recoverable errors (5xx, second 401, network failure, etc.).
105
+ *
106
+ * @param collection - The WHOOP data collection to fetch.
107
+ * @param window - The ISO-8601 time window for filtering records.
108
+ * @param cursor - Pagination cursor from the previous page's nextCursor.
109
+ */
110
+ fetchPage(collection: WhoopCollection, window: SyncWindow, cursor?: string): Promise<WhoopPage>;
111
+ /** Inner GET with single 401-refresh-retry and single 429-Reset-wait. */
112
+ private _doFetch;
113
+ }
114
+ //# sourceMappingURL=whoop-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"whoop-client.d.ts","sourceRoot":"","sources":["../../../src/medical/whoop/whoop-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,KAAK,EAAE,eAAe,EAAe,MAAM,kBAAkB,CAAC;AAIrE,kEAAkE;AAClE,MAAM,MAAM,eAAe,GAAG,UAAU,GAAG,OAAO,GAAG,OAAO,GAAG,SAAS,CAAC;AAEzE,0DAA0D;AAC1D,MAAM,MAAM,UAAU,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAE9D,qEAAqE;AACrE,MAAM,MAAM,WAAW,GAAG;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjC,CAAC;AAEF,mFAAmF;AACnF,MAAM,MAAM,SAAS,GAAG;IAAE,OAAO,EAAE,WAAW,EAAE,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAExE;;;;;;GAMG;AACH,MAAM,MAAM,SAAS,GAAG,CACtB,GAAG,EAAE,MAAM,EACX,IAAI,CAAC,EAAE;IACL,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,KACE,OAAO,CAAC;IACX,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE;QAAE,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;IAC9C,IAAI,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;CAC1B,CAAC,CAAC;AA8EH;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,WAAW;IAIpB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,UAAU;IAG3B,OAAO,CAAC,QAAQ,CAAC,SAAS;IAG1B,OAAO,CAAC,QAAQ,CAAC,MAAM;IAIvB,OAAO,CAAC,QAAQ,CAAC,MAAM;IAdzB,OAAO,CAAC,MAAM,CAAC,CAAgD;gBAG5C,MAAM,EAAE,WAAW,EACnB,UAAU,EAAE,eAAe,EAG3B,SAAS,GAAE,SAA8B,EAGzC,MAAM,GAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CACF,EAGlC,MAAM,GAAE,MAAM,MAAuC;IAKxE;;;;;;;;;;;;OAYG;IACG,iBAAiB,IAAI,OAAO,CAAC,MAAM,CAAC;IAW1C,yEAAyE;YAC3D,UAAU;IAwDxB;;;;;;;;;;;;;;;;OAgBG;IACG,SAAS,CACb,UAAU,EAAE,eAAe,EAC3B,MAAM,EAAE,UAAU,EAClB,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,SAAS,CAAC;IAOrB,yEAAyE;YAC3D,QAAQ;CAiDvB"}
@@ -0,0 +1,226 @@
1
+ // ── Module constants ─────────────────────────────────────────────────
2
+ const WHOOP_API_BASE = "https://api.prod.whoop.com";
3
+ const WHOOP_TOKEN_URL = "https://api.prod.whoop.com/oauth/oauth2/token";
4
+ /** Maps each WhoopCollection to its WHOOP v2 API path. */
5
+ const COLLECTION_PATHS = {
6
+ recovery: "/v2/recovery",
7
+ sleep: "/v2/activity/sleep",
8
+ cycle: "/v2/cycle",
9
+ workout: "/v2/activity/workout",
10
+ };
11
+ // ── Response parsers ─────────────────────────────────────────────────
12
+ /**
13
+ * Parse a WHOOP v2 paginated response body into a WhoopPage.
14
+ * Returns empty records on any structural mismatch (fail-safe read).
15
+ */
16
+ function parseWhoopPage(raw) {
17
+ if (!raw || typeof raw !== "object")
18
+ return { records: [] };
19
+ const body = raw;
20
+ const records = [];
21
+ const dataArr = body["records"];
22
+ if (Array.isArray(dataArr)) {
23
+ for (const item of dataArr) {
24
+ if (!item || typeof item !== "object")
25
+ continue;
26
+ const r = item;
27
+ const id = typeof r["id"] === "number" ? String(r["id"]) : (typeof r["id"] === "string" ? r["id"] : "");
28
+ const tStartIso = typeof r["start"] === "string" ? r["start"] : "";
29
+ const tEndIso = typeof r["end"] === "string" ? r["end"] : undefined;
30
+ const score = r["score"];
31
+ const metrics = {};
32
+ if (score && typeof score === "object") {
33
+ for (const [k, v] of Object.entries(score)) {
34
+ if (typeof v === "number")
35
+ metrics[k] = v;
36
+ }
37
+ }
38
+ if (id)
39
+ records.push({ id, tStartIso, tEndIso, metrics });
40
+ }
41
+ }
42
+ const nextCursor = typeof body["next_token"] === "string" ? body["next_token"] : undefined;
43
+ return { records, nextCursor };
44
+ }
45
+ /**
46
+ * Parse a WHOOP OAuth token response body.
47
+ * Returns null on any structural mismatch.
48
+ */
49
+ function parseTokenResponse(raw) {
50
+ if (!raw || typeof raw !== "object")
51
+ return null;
52
+ const r = raw;
53
+ const accessToken = r["access_token"];
54
+ const refreshToken = r["refresh_token"];
55
+ const expiresIn = r["expires_in"];
56
+ if (typeof accessToken !== "string" ||
57
+ typeof refreshToken !== "string" ||
58
+ typeof expiresIn !== "number") {
59
+ return null;
60
+ }
61
+ return { accessToken, refreshToken, expiresIn };
62
+ }
63
+ // ── WhoopClient ──────────────────────────────────────────────────────
64
+ /**
65
+ * Authenticated WHOOP v2 API client.
66
+ *
67
+ * This is the SECOND ESLint-excepted network file (ADR-1). All WHOOP HTTP
68
+ * access — the OAuth2 refresh_token grant and all v2 paginated GETs — lives
69
+ * HERE and nowhere else under src/medical.
70
+ *
71
+ * Design invariants:
72
+ * - ensureAccessToken and fetchPage BOTH start with assertAllowed("device-connection").
73
+ * With the axis off, both methods throw before any fetchImpl call (sc-2-4).
74
+ * - 401 triggers exactly ONE refresh+retry; a second 401 throws (no infinite loop).
75
+ * - 429 reads the X-RateLimit-Reset header (seconds), awaits the INJECTED waiter,
76
+ * then retries; the waiter is injectable so tests never actually sleep (sc-2-7).
77
+ * - Timestamps are never derived from Date.now(); nowIso is injected for testability.
78
+ *
79
+ * bober: single-process access token cache (in-memory); swap for a shared cache
80
+ * (Redis / SQLite) if multi-process token sharing is needed.
81
+ */
82
+ export class WhoopClient {
83
+ egress;
84
+ tokenStore;
85
+ fetchImpl;
86
+ waiter;
87
+ nowIso;
88
+ cached;
89
+ constructor(egress, tokenStore,
90
+ // bober: global fetch is the default ONLY in this file (ESLint exception);
91
+ // tests inject a FetchLike returning fixture data so CI stays offline.
92
+ fetchImpl = fetch,
93
+ // bober: waiter defaults to real setTimeout; tests inject a no-wait recording waiter
94
+ // so 429-Reset handling is assertable without sleeping (Pattern 3).
95
+ waiter = (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
96
+ // bober: nowIso is injected for token expiry comparisons; tests pass a fixed string;
97
+ // production callers pass new Date().toISOString() — never Date.now() internally.
98
+ nowIso = () => new Date().toISOString()) {
99
+ this.egress = egress;
100
+ this.tokenStore = tokenStore;
101
+ this.fetchImpl = fetchImpl;
102
+ this.waiter = waiter;
103
+ this.nowIso = nowIso;
104
+ }
105
+ // ── ensureAccessToken ─────────────────────────────────────────────
106
+ /**
107
+ * Returns a valid WHOOP access token, refreshing if the cached token is expired.
108
+ *
109
+ * Order:
110
+ * 1. assertAllowed("device-connection") — throws if axis is off (sc-2-4).
111
+ * 2. If cached and unexpired, return cached access token.
112
+ * 3. Read refresh token from tokenStore; throw "authorize-first" if absent.
113
+ * 4. POST refresh_token grant to WHOOP_TOKEN_URL via fetchImpl.
114
+ * 5. Persist rotated tokens via tokenStore.writeTokens.
115
+ * 6. Cache and return the new access token.
116
+ *
117
+ * On 401/invalid_grant from the token endpoint, throws a clear "re-authorize" error.
118
+ */
119
+ async ensureAccessToken() {
120
+ this.egress.assertAllowed("device-connection"); // MUST be first (sc-2-4)
121
+ // Return cached token if still valid
122
+ if (this.cached && this.cached.expiresAtIso > this.nowIso()) {
123
+ return this.cached.accessToken;
124
+ }
125
+ return this._doRefresh();
126
+ }
127
+ /** Performs the OAuth2 refresh_token grant and caches the new tokens. */
128
+ async _doRefresh() {
129
+ const refreshToken = await this.tokenStore.readRefreshToken();
130
+ if (!refreshToken) {
131
+ throw new Error("WHOOP not yet authorised — run `bober medical whoop authorize` first.");
132
+ }
133
+ const { clientId, clientSecret } = this.tokenStore.clientCredentials();
134
+ // Build form body manually — URLSearchParams is not declared in the ESLint globals config.
135
+ const body = [
136
+ `grant_type=refresh_token`,
137
+ `refresh_token=${encodeURIComponent(refreshToken)}`,
138
+ `client_id=${encodeURIComponent(clientId)}`,
139
+ `client_secret=${encodeURIComponent(clientSecret)}`,
140
+ `scope=offline`,
141
+ ].join("&");
142
+ const res = await this.fetchImpl(WHOOP_TOKEN_URL, {
143
+ method: "POST",
144
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
145
+ body,
146
+ });
147
+ if (!res.ok || res.status === 401) {
148
+ throw new Error("WHOOP token refresh failed (401/invalid_grant) — re-authorize with " +
149
+ "`bober medical whoop authorize`.");
150
+ }
151
+ const raw = await res.json();
152
+ const parsed = parseTokenResponse(raw);
153
+ if (!parsed) {
154
+ throw new Error("WHOOP token endpoint returned an unexpected response shape.");
155
+ }
156
+ // Compute expiry ISO from expires_in (seconds); use injected nowIso as baseline
157
+ const expiresAtMs = new Date(this.nowIso()).getTime() + parsed.expiresIn * 1000;
158
+ const expiresAtIso = new Date(expiresAtMs).toISOString();
159
+ const tokens = {
160
+ accessToken: parsed.accessToken,
161
+ refreshToken: parsed.refreshToken,
162
+ expiresAtIso,
163
+ };
164
+ await this.tokenStore.writeTokens(tokens);
165
+ this.cached = { accessToken: parsed.accessToken, expiresAtIso };
166
+ return parsed.accessToken;
167
+ }
168
+ // ── fetchPage ─────────────────────────────────────────────────────
169
+ /**
170
+ * Fetch one page of WHOOP v2 data for the given collection and time window.
171
+ *
172
+ * Order:
173
+ * 1. assertAllowed("device-connection") — throws if axis is off (sc-2-4).
174
+ * 2. ensureAccessToken — gets/refreshes the Bearer token.
175
+ * 3. GET WHOOP_API_BASE + COLLECTION_PATHS[collection] with query params.
176
+ * 4. On 401: call ensureAccessToken (force refresh) and retry EXACTLY ONCE.
177
+ * 5. On 429: read X-RateLimit-Reset header (seconds), await waiter, retry.
178
+ * 6. Parse JSON into WhoopPage.
179
+ *
180
+ * Throws on non-recoverable errors (5xx, second 401, network failure, etc.).
181
+ *
182
+ * @param collection - The WHOOP data collection to fetch.
183
+ * @param window - The ISO-8601 time window for filtering records.
184
+ * @param cursor - Pagination cursor from the previous page's nextCursor.
185
+ */
186
+ async fetchPage(collection, window, cursor) {
187
+ this.egress.assertAllowed("device-connection"); // MUST be first (sc-2-4)
188
+ const accessToken = await this.ensureAccessToken();
189
+ return this._doFetch(collection, window, cursor, accessToken, false);
190
+ }
191
+ /** Inner GET with single 401-refresh-retry and single 429-Reset-wait. */
192
+ async _doFetch(collection, window, cursor, accessToken, isRetry) {
193
+ const path = COLLECTION_PATHS[collection];
194
+ // Build query string manually — URLSearchParams is not declared in the ESLint globals config.
195
+ const qs = `start=${encodeURIComponent(window.startIso)}&end=${encodeURIComponent(window.endIso)}` +
196
+ (cursor !== undefined ? `&nextToken=${encodeURIComponent(cursor)}` : "");
197
+ const url = `${WHOOP_API_BASE}${path}?${qs}`;
198
+ const res = await this.fetchImpl(url, {
199
+ method: "GET",
200
+ headers: { Authorization: `Bearer ${accessToken}` },
201
+ });
202
+ if (res.status === 401) {
203
+ if (isRetry) {
204
+ throw new Error("WHOOP returned 401 after token refresh — re-authorize with " +
205
+ "`bober medical whoop authorize`.");
206
+ }
207
+ // Force a fresh token and retry exactly once
208
+ this.cached = undefined;
209
+ const freshToken = await this._doRefresh();
210
+ return this._doFetch(collection, window, cursor, freshToken, true);
211
+ }
212
+ if (res.status === 429) {
213
+ const resetHeader = res.headers.get("X-RateLimit-Reset");
214
+ const resetSeconds = resetHeader !== null ? Number(resetHeader) : 60;
215
+ await this.waiter(resetSeconds * 1000);
216
+ // Retry after waiting — use same access token (rate limit, not auth failure)
217
+ return this._doFetch(collection, window, cursor, accessToken, isRetry);
218
+ }
219
+ if (!res.ok) {
220
+ throw new Error(`WHOOP API error: HTTP ${res.status} fetching ${collection}`);
221
+ }
222
+ const raw = await res.json();
223
+ return parseWhoopPage(raw);
224
+ }
225
+ }
226
+ //# sourceMappingURL=whoop-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"whoop-client.js","sourceRoot":"","sources":["../../../src/medical/whoop/whoop-client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ /** WhoopSyncAdapter — network sync of WHOOP v2 records into the ObservationSink (ADR-1). NO network import. */
2
+ import type { ObservationSink, IngestionResult } from "../types.js";
3
+ import type { WhoopClient, SyncWindow } from "./whoop-client.js";
4
+ /**
5
+ * Adapts WhoopClient pagination into the ObservationSink interface (ADR-1).
6
+ *
7
+ * NOT an IngestionAdapter — its entry point is sync(window, sink), not ingest(filePath, sink).
8
+ * All HTTP is confined to WhoopClient (injected dependency).
9
+ *
10
+ * On any fetchPage throw, the error PROPAGATES (fail-closed).
11
+ * Prior batches are already committed via per-batch better-sqlite3 transactions (ADR-4).
12
+ * A failed sync is recovered by re-running — idempotent dedup via INSERT OR IGNORE.
13
+ */
14
+ export declare class WhoopSyncAdapter {
15
+ private readonly client;
16
+ readonly source = "whoop";
17
+ constructor(client: WhoopClient);
18
+ /**
19
+ * Sync all four WHOOP collections over the given window into the sink.
20
+ * Pages each collection following nextCursor until the last page (undefined cursor).
21
+ * Returns IngestionResult { recordsParsed, newRows }.
22
+ */
23
+ sync(window: SyncWindow, sink: ObservationSink): Promise<IngestionResult>;
24
+ }
25
+ //# sourceMappingURL=whoop-sync.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"whoop-sync.d.ts","sourceRoot":"","sources":["../../../src/medical/whoop/whoop-sync.ts"],"names":[],"mappings":"AAAA,+GAA+G;AAC/G,OAAO,KAAK,EAAE,eAAe,EAAqB,eAAe,EAAE,MAAM,aAAa,CAAC;AACvF,OAAO,KAAK,EAAE,WAAW,EAAgC,UAAU,EAAE,MAAM,mBAAmB,CAAC;AA6D/F;;;;;;;;;GASG;AACH,qBAAa,gBAAgB;IAGf,OAAO,CAAC,QAAQ,CAAC,MAAM;IAFnC,QAAQ,CAAC,MAAM,WAAW;gBAEG,MAAM,EAAE,WAAW;IAEhD;;;;OAIG;IACG,IAAI,CAAC,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC,eAAe,CAAC;CAqBhF"}