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,66 @@
1
+ /**
2
+ * NumericsQueryLayer — deterministic closed-whitelist numeric primitives (Phase 6, Sprint 4).
3
+ *
4
+ * ADR-3: The LLM NEVER performs arithmetic. All numeric computations are pure TypeScript
5
+ * over a CLOSED whitelist: mean | min | max | latest | delta | slope | percentile | zscore.
6
+ * Adding a computation requires extending NumericPrimitive (a code review event), not a
7
+ * model decision. `sampleCount: 0` signals upstream abstention.
8
+ *
9
+ * NO async. NO fs. NO network. NO LLM import. NO dynamic execution. NO subprocess.
10
+ * Identical input => identical output.
11
+ *
12
+ * Primitive formulas documented below (also satisfies contract assumptions line 75):
13
+ *
14
+ * mean — sum(values) / n
15
+ * min — Math.min(...values)
16
+ * max — Math.max(...values)
17
+ * latest — value at the maximum t_start (last element of ASC-ordered array)
18
+ * delta — latest − earliest = values[n-1] − values[0]
19
+ * slope — least-squares slope over (t, value) where t = Date.parse(r.tStart) epoch-ms.
20
+ * formula: (n·Σ(t·v) − Σt·Σv) / (n·Σ(t²) − (Σt)²)
21
+ * degenerate: denominator === 0 (all same timestamp) → value: null, sampleCount: n
22
+ * percentile — linear interpolation between closest ranks (sorted ascending):
23
+ * rank = (p/100) · (n−1); lo = floor(rank); hi = ceil(rank);
24
+ * result = v[lo] + (rank − lo) · (v[hi] − v[lo])
25
+ * (for p50, [10,20,30,40] → rank=1.5 → 20 + 0.5·10 = 25)
26
+ * zscore — (latest − mean) / populationStddev
27
+ * populationStddev = sqrt(Σ(v − mean)² / n)
28
+ * sampleCount < 2 → abstain (stddev undefined for n=1, zero for n=0)
29
+ * ([10,20,30,40]: mean=25, popStd=√125≈11.1803, z=(40-25)/11.1803≈1.3416)
30
+ */
31
+ import type { HealthDataStore } from "./health-store.js";
32
+ import type { NumericPrimitive, NumericResult, MetricWindow, LabTrend } from "./types.js";
33
+ /**
34
+ * Provides deterministic, LLM-free numeric aggregations over HealthDataStore.
35
+ *
36
+ * All methods are SYNCHRONOUS. No async, no fs, no network, no LLM import.
37
+ * The LLM never performs arithmetic — this layer does.
38
+ */
39
+ export declare class NumericsQueryLayer {
40
+ private readonly store;
41
+ constructor(store: HealthDataStore);
42
+ /**
43
+ * Compute a numeric primitive over the observations in the given time window.
44
+ *
45
+ * Abstain conditions (return { value: null, sampleCount: 0 } without throwing):
46
+ * - Empty window (no rows in the time range) — sc-4-6
47
+ * - Heterogeneous units across rows — sc-4-7 (cross-unit refusal)
48
+ *
49
+ * Partial abstain (value: null, sampleCount: n):
50
+ * - zscore with sampleCount < 2 (stddev undefined)
51
+ * - slope with degenerate denominator (all same timestamp)
52
+ *
53
+ * @param window — metric + ISO-8601 time range
54
+ * @param primitive — one of the 8 whitelisted NumericPrimitive values
55
+ * @param percentile — percentile p ∈ [0,100], used only for "percentile" primitive (default 50)
56
+ */
57
+ getMetric(window: MetricWindow, primitive: NumericPrimitive, percentile?: number): NumericResult;
58
+ /**
59
+ * Build a LabTrend summary for a biomarker series.
60
+ *
61
+ * Abstains (latestValue: null, slope: null) when sampleCount === 0 (sc-4-6).
62
+ * slope is null when sampleCount < 2 (not enough data for least-squares).
63
+ */
64
+ getLabTrend(biomarker: string): LabTrend;
65
+ }
66
+ //# sourceMappingURL=numerics.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"numerics.d.ts","sourceRoot":"","sources":["../../src/medical/numerics.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,KAAK,EACV,gBAAgB,EAChB,aAAa,EACb,YAAY,EACZ,QAAQ,EAGT,MAAM,YAAY,CAAC;AAsHpB;;;;;GAKG;AACH,qBAAa,kBAAkB;IACjB,OAAO,CAAC,QAAQ,CAAC,KAAK;gBAAL,KAAK,EAAE,eAAe;IAEnD;;;;;;;;;;;;;;OAcG;IACH,SAAS,CAAC,MAAM,EAAE,YAAY,EAAE,SAAS,EAAE,gBAAgB,EAAE,UAAU,SAAK,GAAG,aAAa;IAwB5F;;;;;OAKG;IACH,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,QAAQ;CAuCzC"}
@@ -0,0 +1,216 @@
1
+ /**
2
+ * NumericsQueryLayer — deterministic closed-whitelist numeric primitives (Phase 6, Sprint 4).
3
+ *
4
+ * ADR-3: The LLM NEVER performs arithmetic. All numeric computations are pure TypeScript
5
+ * over a CLOSED whitelist: mean | min | max | latest | delta | slope | percentile | zscore.
6
+ * Adding a computation requires extending NumericPrimitive (a code review event), not a
7
+ * model decision. `sampleCount: 0` signals upstream abstention.
8
+ *
9
+ * NO async. NO fs. NO network. NO LLM import. NO dynamic execution. NO subprocess.
10
+ * Identical input => identical output.
11
+ *
12
+ * Primitive formulas documented below (also satisfies contract assumptions line 75):
13
+ *
14
+ * mean — sum(values) / n
15
+ * min — Math.min(...values)
16
+ * max — Math.max(...values)
17
+ * latest — value at the maximum t_start (last element of ASC-ordered array)
18
+ * delta — latest − earliest = values[n-1] − values[0]
19
+ * slope — least-squares slope over (t, value) where t = Date.parse(r.tStart) epoch-ms.
20
+ * formula: (n·Σ(t·v) − Σt·Σv) / (n·Σ(t²) − (Σt)²)
21
+ * degenerate: denominator === 0 (all same timestamp) → value: null, sampleCount: n
22
+ * percentile — linear interpolation between closest ranks (sorted ascending):
23
+ * rank = (p/100) · (n−1); lo = floor(rank); hi = ceil(rank);
24
+ * result = v[lo] + (rank − lo) · (v[hi] − v[lo])
25
+ * (for p50, [10,20,30,40] → rank=1.5 → 20 + 0.5·10 = 25)
26
+ * zscore — (latest − mean) / populationStddev
27
+ * populationStddev = sqrt(Σ(v − mean)² / n)
28
+ * sampleCount < 2 → abstain (stddev undefined for n=1, zero for n=0)
29
+ * ([10,20,30,40]: mean=25, popStd=√125≈11.1803, z=(40-25)/11.1803≈1.3416)
30
+ */
31
+ // ── Internal computation helpers ──────────────────────────────────────
32
+ /**
33
+ * Compute the least-squares slope over (epoch-ms timestamp, value) pairs.
34
+ * Returns null when the denominator is zero (all observations at the same timestamp).
35
+ */
36
+ function leastSquaresSlope(rows) {
37
+ const n = rows.length;
38
+ const ts = rows.map((r) => Date.parse(r.tStart));
39
+ const vs = rows.map((r) => r.value);
40
+ let sumT = 0;
41
+ let sumV = 0;
42
+ let sumTV = 0;
43
+ let sumT2 = 0;
44
+ for (let i = 0; i < n; i++) {
45
+ sumT += ts[i];
46
+ sumV += vs[i];
47
+ sumTV += ts[i] * vs[i];
48
+ sumT2 += ts[i] * ts[i];
49
+ }
50
+ const denom = n * sumT2 - sumT * sumT;
51
+ if (denom === 0)
52
+ return null; // all same timestamp — slope undefined
53
+ return (n * sumTV - sumT * sumV) / denom;
54
+ }
55
+ /**
56
+ * Linear interpolation percentile on the sorted-ascending values.
57
+ * rank = (p/100) · (n−1); result = v[lo] + (rank − lo) · (v[hi] − v[lo]).
58
+ * p must be in [0, 100].
59
+ */
60
+ function linearPercentile(sortedValues, p) {
61
+ const n = sortedValues.length;
62
+ if (n === 1)
63
+ return sortedValues[0];
64
+ const rank = (p / 100) * (n - 1);
65
+ const lo = Math.floor(rank);
66
+ const hi = Math.ceil(rank);
67
+ if (lo === hi)
68
+ return sortedValues[lo];
69
+ return sortedValues[lo] + (rank - lo) * (sortedValues[hi] - sortedValues[lo]);
70
+ }
71
+ /**
72
+ * Population standard deviation: sqrt(Σ(v − mean)² / n).
73
+ */
74
+ function populationStddev(values, mean) {
75
+ const variance = values.reduce((acc, v) => acc + (v - mean) ** 2, 0) / values.length;
76
+ return Math.sqrt(variance);
77
+ }
78
+ /**
79
+ * Dispatch the computation for a single NumericPrimitive over the given rows/values.
80
+ * rows are ordered by t_start ASC from getObservations.
81
+ * values[i] === rows[i].value.
82
+ * Returns null only for zscore (n<2) and slope (degenerate denominator).
83
+ *
84
+ * The exhaustive switch + never guard ensures every new member of the union
85
+ * causes a compile-time error if not handled (ADR-3 code-review gate).
86
+ */
87
+ function computePrimitive(primitive, rows, values, percentile) {
88
+ const n = values.length;
89
+ switch (primitive) {
90
+ case "mean": {
91
+ const sum = values.reduce((acc, v) => acc + v, 0);
92
+ return sum / n;
93
+ }
94
+ case "min":
95
+ return Math.min(...values);
96
+ case "max":
97
+ return Math.max(...values);
98
+ case "latest":
99
+ // rows are ordered t_start ASC; last element is the most recent.
100
+ return values[n - 1];
101
+ case "delta":
102
+ // latest − earliest (ASC order: first is earliest, last is latest).
103
+ return values[n - 1] - values[0];
104
+ case "slope":
105
+ return leastSquaresSlope(rows);
106
+ case "percentile": {
107
+ const sorted = [...values].sort((a, b) => a - b);
108
+ return linearPercentile(sorted, percentile);
109
+ }
110
+ case "zscore": {
111
+ if (n < 2)
112
+ return null; // population stddev undefined for n<2
113
+ const mean = values.reduce((acc, v) => acc + v, 0) / n;
114
+ const std = populationStddev(values, mean);
115
+ if (std === 0)
116
+ return 0; // all values identical → z-score is 0 by definition
117
+ const latest = values[n - 1];
118
+ return (latest - mean) / std;
119
+ }
120
+ default: {
121
+ // Exhaustive never guard: if a new primitive is added to the union without
122
+ // a case here, TypeScript will raise a compile error (ADR-3).
123
+ const _exhaustive = primitive;
124
+ throw new Error(`Unhandled NumericPrimitive: ${String(_exhaustive)}`);
125
+ }
126
+ }
127
+ }
128
+ // ── NumericsQueryLayer ────────────────────────────────────────────────
129
+ /**
130
+ * Provides deterministic, LLM-free numeric aggregations over HealthDataStore.
131
+ *
132
+ * All methods are SYNCHRONOUS. No async, no fs, no network, no LLM import.
133
+ * The LLM never performs arithmetic — this layer does.
134
+ */
135
+ export class NumericsQueryLayer {
136
+ store;
137
+ constructor(store) {
138
+ this.store = store;
139
+ }
140
+ /**
141
+ * Compute a numeric primitive over the observations in the given time window.
142
+ *
143
+ * Abstain conditions (return { value: null, sampleCount: 0 } without throwing):
144
+ * - Empty window (no rows in the time range) — sc-4-6
145
+ * - Heterogeneous units across rows — sc-4-7 (cross-unit refusal)
146
+ *
147
+ * Partial abstain (value: null, sampleCount: n):
148
+ * - zscore with sampleCount < 2 (stddev undefined)
149
+ * - slope with degenerate denominator (all same timestamp)
150
+ *
151
+ * @param window — metric + ISO-8601 time range
152
+ * @param primitive — one of the 8 whitelisted NumericPrimitive values
153
+ * @param percentile — percentile p ∈ [0,100], used only for "percentile" primitive (default 50)
154
+ */
155
+ getMetric(window, primitive, percentile = 50) {
156
+ const rows = this.store.getObservations(window.metric, window.fromIso, window.toIso);
157
+ // ABSTAIN: empty window (sc-4-6)
158
+ if (rows.length === 0) {
159
+ return { primitive, value: null, unit: window.unit ?? "", sampleCount: 0 };
160
+ }
161
+ // UNIT GUARD (sc-4-7): refuse to aggregate across heterogeneous units (ADR-4).
162
+ // Architecture risk table line 328: "NumericsQueryLayer reads unit per row;
163
+ // refuses cross-unit aggregation."
164
+ const units = new Set(rows.map((r) => r.unit));
165
+ if (units.size > 1) {
166
+ // Abstain — no throw (no-throw contract from architecture API table line 251).
167
+ return { primitive, value: null, unit: "", sampleCount: rows.length };
168
+ }
169
+ const unit = rows[0].unit;
170
+ const values = rows.map((r) => r.value);
171
+ const value = computePrimitive(primitive, rows, values, percentile);
172
+ return { primitive, value, unit, sampleCount: rows.length };
173
+ }
174
+ /**
175
+ * Build a LabTrend summary for a biomarker series.
176
+ *
177
+ * Abstains (latestValue: null, slope: null) when sampleCount === 0 (sc-4-6).
178
+ * slope is null when sampleCount < 2 (not enough data for least-squares).
179
+ */
180
+ getLabTrend(biomarker) {
181
+ const series = this.store.getLabSeries(biomarker);
182
+ if (series.length === 0) {
183
+ return {
184
+ biomarker,
185
+ sampleCount: 0,
186
+ latestValue: null,
187
+ latestUnit: "",
188
+ latestCollectedAt: null,
189
+ slope: null,
190
+ };
191
+ }
192
+ // series is ordered collected_at ASC from getLabSeries.
193
+ const latest = series[series.length - 1];
194
+ let slope = null;
195
+ if (series.length >= 2) {
196
+ // Convert LabResult to pseudo-HealthObservation for slope helper reuse.
197
+ const pseudoRows = series.map((r) => ({
198
+ metric: r.biomarker,
199
+ value: r.value,
200
+ unit: r.unit,
201
+ tStart: r.collectedAtIso,
202
+ source: "lab",
203
+ }));
204
+ slope = leastSquaresSlope(pseudoRows);
205
+ }
206
+ return {
207
+ biomarker,
208
+ sampleCount: series.length,
209
+ latestValue: latest.value,
210
+ latestUnit: latest.unit,
211
+ latestCollectedAt: latest.collectedAtIso,
212
+ slope,
213
+ };
214
+ }
215
+ }
216
+ //# sourceMappingURL=numerics.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"numerics.js","sourceRoot":"","sources":["../../src/medical/numerics.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * RedFlagDetector — pure, synchronous, 0-LLM emergency detection (Phase 6, Sprint 3).
3
+ *
4
+ * ADR-2: Detection is deterministic and local. A conservative keyword/phrase match
5
+ * favors escalation reliability over paraphrase coverage. Novel phrasing may return
6
+ * 'none' and proceed to the normal (still-guardrailed) path — this is the accepted risk
7
+ * documented in ADR-2. Pattern set is versioned for auditability.
8
+ *
9
+ * NO async. NO fs. NO network. NO LLM import. Identical input => identical output.
10
+ */
11
+ /** Six mutually-exclusive categories; 'none' means no emergency pattern matched. */
12
+ export type RedFlagCategory = "cardiac" | "stroke" | "anaphylaxis" | "self-harm" | "overdose" | "none";
13
+ /** Result of a single RedFlagDetector.detect() call. */
14
+ export interface RedFlagMatch {
15
+ category: RedFlagCategory;
16
+ /**
17
+ * Rule ID that fired (IDs only — never prompt text).
18
+ * Undefined when category === 'none'.
19
+ */
20
+ ruleId?: string;
21
+ }
22
+ /** Versioned pattern-set identifier recorded in the audit log (ADR-2). */
23
+ export declare const PATTERNSET_VERSION = "redflag-2026.06.16";
24
+ /**
25
+ * Detects emergency red-flag phrases in a medical prompt.
26
+ *
27
+ * PURE + SYNCHRONOUS. No async, no fs, no network, no LLM import.
28
+ * Identical input => identical RedFlagMatch (asserted by sc-3-5).
29
+ *
30
+ * bober: conservative phrase matching; novel or indirect phrasing may return
31
+ * 'none' and fall through to normal SOP handling (ADR-2 accepted risk).
32
+ */
33
+ export declare class RedFlagDetector {
34
+ readonly patternsetVersion = "redflag-2026.06.16";
35
+ /**
36
+ * Returns the first matching RedFlagMatch, or { category: 'none' } if no
37
+ * rule fires. Evaluation order is self-harm → overdose → cardiac → stroke →
38
+ * anaphylaxis so the correct emergency hotline (988 vs 911) is assigned.
39
+ */
40
+ detect(prompt: string): RedFlagMatch;
41
+ }
42
+ //# sourceMappingURL=red-flag.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"red-flag.d.ts","sourceRoot":"","sources":["../../src/medical/red-flag.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,oFAAoF;AACpF,MAAM,MAAM,eAAe,GACvB,SAAS,GACT,QAAQ,GACR,aAAa,GACb,WAAW,GACX,UAAU,GACV,MAAM,CAAC;AAEX,wDAAwD;AACxD,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,eAAe,CAAC;IAC1B;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAID,0EAA0E;AAC1E,eAAO,MAAM,kBAAkB,uBAAuB,CAAC;AAsJvD;;;;;;;;GAQG;AACH,qBAAa,eAAe;IAC1B,QAAQ,CAAC,iBAAiB,wBAAsB;IAEhD;;;;OAIG;IACH,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY;CASrC"}
@@ -0,0 +1,161 @@
1
+ /**
2
+ * RedFlagDetector — pure, synchronous, 0-LLM emergency detection (Phase 6, Sprint 3).
3
+ *
4
+ * ADR-2: Detection is deterministic and local. A conservative keyword/phrase match
5
+ * favors escalation reliability over paraphrase coverage. Novel phrasing may return
6
+ * 'none' and proceed to the normal (still-guardrailed) path — this is the accepted risk
7
+ * documented in ADR-2. Pattern set is versioned for auditability.
8
+ *
9
+ * NO async. NO fs. NO network. NO LLM import. Identical input => identical output.
10
+ */
11
+ // ── Version constant ─────────────────────────────────────────────────
12
+ /** Versioned pattern-set identifier recorded in the audit log (ADR-2). */
13
+ export const PATTERNSET_VERSION = "redflag-2026.06.16";
14
+ /**
15
+ * Conservative rule list. Order matters:
16
+ * self-harm and overdose are checked so the correct hotline (988) wins over
17
+ * any incidental cardiac/anaphylaxis phrase in the same message.
18
+ *
19
+ * bober: phrase-include matching for now; upgrade to word-boundary regex or
20
+ * an embedded trie if the pattern count grows to > 50 rules.
21
+ */
22
+ const RULES = [
23
+ // ── self-harm (988) ──────────────────────────────────────────────
24
+ {
25
+ ruleId: "self-harm-suicidal",
26
+ category: "self-harm",
27
+ test: (n) => n.includes("suicidal") || n.includes("suicid"),
28
+ },
29
+ {
30
+ ruleId: "self-harm-kill-self",
31
+ category: "self-harm",
32
+ test: (n) => n.includes("kill myself") ||
33
+ n.includes("end my life") ||
34
+ n.includes("take my life") ||
35
+ n.includes("want to die"),
36
+ },
37
+ {
38
+ ruleId: "self-harm-self-harm",
39
+ category: "self-harm",
40
+ test: (n) => n.includes("hurt myself") || n.includes("harm myself"),
41
+ },
42
+ // ── overdose (988) ───────────────────────────────────────────────
43
+ {
44
+ ruleId: "overdose-explicit",
45
+ category: "overdose",
46
+ test: (n) => n.includes("overdose") || n.includes("over dose"),
47
+ },
48
+ {
49
+ ruleId: "overdose-too-many",
50
+ category: "overdose",
51
+ test: (n) => n.includes("took too many") || n.includes("taken too many"),
52
+ },
53
+ // ── cardiac (911) ────────────────────────────────────────────────
54
+ {
55
+ ruleId: "cardiac-chest-pain-radiating",
56
+ category: "cardiac",
57
+ test: (n) => n.includes("chest pain") && (n.includes("radiating") || n.includes("left arm")),
58
+ },
59
+ {
60
+ ruleId: "cardiac-chest-pain-breath",
61
+ category: "cardiac",
62
+ test: (n) => n.includes("chest pain") && (n.includes("short of breath") || n.includes("shortness of breath")),
63
+ },
64
+ {
65
+ ruleId: "cardiac-chest-crushing",
66
+ category: "cardiac",
67
+ test: (n) => (n.includes("crushing chest") || n.includes("chest pressure") || n.includes("chest tightness")) &&
68
+ (n.includes("arm") || n.includes("jaw") || n.includes("breath")),
69
+ },
70
+ {
71
+ ruleId: "cardiac-heart-attack",
72
+ category: "cardiac",
73
+ test: (n) => n.includes("heart attack"),
74
+ },
75
+ // ── stroke (911) — FAST: Face, Arms, Speech, Time ───────────────
76
+ {
77
+ ruleId: "stroke-face-droop",
78
+ category: "stroke",
79
+ test: (n) => n.includes("face droop") ||
80
+ n.includes("facial droop") ||
81
+ n.includes("drooping face"),
82
+ },
83
+ {
84
+ ruleId: "stroke-slurred-speech",
85
+ category: "stroke",
86
+ test: (n) => n.includes("slurred speech") ||
87
+ n.includes("slurring words") ||
88
+ n.includes("can't speak") ||
89
+ n.includes("cannot speak"),
90
+ },
91
+ {
92
+ ruleId: "stroke-arm-weakness",
93
+ category: "stroke",
94
+ test: (n) => (n.includes("arm weakness") || n.includes("arm numb")) &&
95
+ (n.includes("sudden") || n.includes("both arms")),
96
+ },
97
+ {
98
+ ruleId: "stroke-sudden-numbness",
99
+ category: "stroke",
100
+ test: (n) => n.includes("sudden numbness") || n.includes("sudden weakness"),
101
+ },
102
+ {
103
+ ruleId: "stroke-sudden-severe-headache",
104
+ category: "stroke",
105
+ test: (n) => n.includes("worst headache") ||
106
+ (n.includes("sudden") && n.includes("severe headache")),
107
+ },
108
+ // ── anaphylaxis (911) ────────────────────────────────────────────
109
+ {
110
+ ruleId: "anaphylaxis-throat-closing",
111
+ category: "anaphylaxis",
112
+ test: (n) => n.includes("throat closing") ||
113
+ n.includes("throat is closing") ||
114
+ n.includes("throat swelling"),
115
+ },
116
+ {
117
+ ruleId: "anaphylaxis-trouble-breathing-allergic",
118
+ category: "anaphylaxis",
119
+ test: (n) => n.includes("trouble breathing") &&
120
+ (n.includes("allergic") || n.includes("allergy") || n.includes("sting") || n.includes("peanut")),
121
+ },
122
+ {
123
+ ruleId: "anaphylaxis-cant-breathe-allergic",
124
+ category: "anaphylaxis",
125
+ test: (n) => (n.includes("can't breathe") || n.includes("cannot breathe")) &&
126
+ (n.includes("allergic") || n.includes("allergy") || n.includes("sting") || n.includes("peanut")),
127
+ },
128
+ {
129
+ ruleId: "anaphylaxis-anaphylaxis",
130
+ category: "anaphylaxis",
131
+ test: (n) => n.includes("anaphylaxis") || n.includes("anaphylactic"),
132
+ },
133
+ ];
134
+ // ── RedFlagDetector ──────────────────────────────────────────────────
135
+ /**
136
+ * Detects emergency red-flag phrases in a medical prompt.
137
+ *
138
+ * PURE + SYNCHRONOUS. No async, no fs, no network, no LLM import.
139
+ * Identical input => identical RedFlagMatch (asserted by sc-3-5).
140
+ *
141
+ * bober: conservative phrase matching; novel or indirect phrasing may return
142
+ * 'none' and fall through to normal SOP handling (ADR-2 accepted risk).
143
+ */
144
+ export class RedFlagDetector {
145
+ patternsetVersion = PATTERNSET_VERSION;
146
+ /**
147
+ * Returns the first matching RedFlagMatch, or { category: 'none' } if no
148
+ * rule fires. Evaluation order is self-harm → overdose → cardiac → stroke →
149
+ * anaphylaxis so the correct emergency hotline (988 vs 911) is assigned.
150
+ */
151
+ detect(prompt) {
152
+ const norm = prompt.toLowerCase().trim();
153
+ for (const rule of RULES) {
154
+ if (rule.test(norm)) {
155
+ return { category: rule.category, ruleId: rule.ruleId };
156
+ }
157
+ }
158
+ return { category: "none" };
159
+ }
160
+ }
161
+ //# sourceMappingURL=red-flag.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"red-flag.js","sourceRoot":"","sources":["../../src/medical/red-flag.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * RefusalDetector — pure, synchronous, 0-LLM non-emergency content-policy refusal.
3
+ *
4
+ * Classifies prescription / specific-dosing / individualized-treatment-plan requests.
5
+ * Conservative phrase matching: accept FALSE-NEGATIVES (fall through to 'none'),
6
+ * NEVER false-positive into giving medical advice. Pattern set is versioned for audit.
7
+ *
8
+ * NO async. NO fs. NO network. NO LLM import. Identical input => identical output.
9
+ */
10
+ /** Four mutually-exclusive categories; 'none' means no refusal pattern matched. */
11
+ export type RefusalCategory = "prescription" | "specific-dosing" | "individualized-treatment-plan" | "none";
12
+ /** Result of a single RefusalDetector.detect() call. */
13
+ export interface RefusalMatch {
14
+ category: RefusalCategory;
15
+ /**
16
+ * Rule ID that fired (IDs only — never prompt text).
17
+ * Undefined when category === 'none'.
18
+ */
19
+ ruleId?: string;
20
+ }
21
+ /** Versioned pattern-set identifier recorded in the audit log. */
22
+ export declare const REFUSAL_PATTERNSET_VERSION = "refusal-2026.06.17";
23
+ /**
24
+ * Fixed canned reason strings per refusal category.
25
+ * Exported so callers can assert byte-equality (proving non-model-generation).
26
+ */
27
+ export declare const REFUSAL_REASONS: Record<Exclude<RefusalCategory, "none">, string>;
28
+ /**
29
+ * Detects non-emergency content-policy refusal phrases in a medical prompt.
30
+ *
31
+ * PURE + SYNCHRONOUS. No async, no fs, no network, no LLM import.
32
+ * Identical input => identical RefusalMatch output.
33
+ *
34
+ * bober: conservative phrase matching; novel or indirect phrasing may return
35
+ * 'none' and fall through to normal SOP handling (accepted risk; false-negative
36
+ * is safer than false-positive into refused advice).
37
+ */
38
+ export declare class RefusalDetector {
39
+ readonly patternsetVersion = "refusal-2026.06.17";
40
+ /**
41
+ * Returns the first matching RefusalMatch, or { category: 'none' } if no
42
+ * rule fires. Evaluation order is prescription → specific-dosing → treatment-plan.
43
+ */
44
+ detect(prompt: string): RefusalMatch;
45
+ }
46
+ //# sourceMappingURL=refusal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"refusal.d.ts","sourceRoot":"","sources":["../../src/medical/refusal.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,mFAAmF;AACnF,MAAM,MAAM,eAAe,GACvB,cAAc,GACd,iBAAiB,GACjB,+BAA+B,GAC/B,MAAM,CAAC;AAEX,wDAAwD;AACxD,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,eAAe,CAAC;IAC1B;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAID,kEAAkE;AAClE,eAAO,MAAM,0BAA0B,uBAAuB,CAAC;AAkB/D;;;GAGG;AACH,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,OAAO,CAAC,eAAe,EAAE,MAAM,CAAC,EAAE,MAAM,CAI5E,CAAC;AA6FF;;;;;;;;;GASG;AACH,qBAAa,eAAe;IAC1B,QAAQ,CAAC,iBAAiB,wBAA8B;IAExD;;;OAGG;IACH,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY;CASrC"}