heor-agent-mcp 1.9.2 → 1.10.2

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 (65) hide show
  1. package/README.md +2 -2
  2. package/dist/analytics.d.ts +18 -4
  3. package/dist/analytics.d.ts.map +1 -1
  4. package/dist/analytics.js +22 -6
  5. package/dist/analytics.js.map +1 -1
  6. package/dist/providers/regulatory/ageRangeParser.d.ts +13 -0
  7. package/dist/providers/regulatory/ageRangeParser.d.ts.map +1 -0
  8. package/dist/providers/regulatory/ageRangeParser.js +78 -0
  9. package/dist/providers/regulatory/ageRangeParser.js.map +1 -0
  10. package/dist/providers/regulatory/autoCheck.d.ts +31 -0
  11. package/dist/providers/regulatory/autoCheck.d.ts.map +1 -0
  12. package/dist/providers/regulatory/autoCheck.js +93 -0
  13. package/dist/providers/regulatory/autoCheck.js.map +1 -0
  14. package/dist/providers/regulatory/cache.d.ts +37 -0
  15. package/dist/providers/regulatory/cache.d.ts.map +1 -0
  16. package/dist/providers/regulatory/cache.js +51 -0
  17. package/dist/providers/regulatory/cache.js.map +1 -0
  18. package/dist/providers/regulatory/dailymed.d.ts +34 -0
  19. package/dist/providers/regulatory/dailymed.d.ts.map +1 -0
  20. package/dist/providers/regulatory/dailymed.js +60 -0
  21. package/dist/providers/regulatory/dailymed.js.map +1 -0
  22. package/dist/providers/regulatory/drugNameNormaliser.d.ts +29 -0
  23. package/dist/providers/regulatory/drugNameNormaliser.d.ts.map +1 -0
  24. package/dist/providers/regulatory/drugNameNormaliser.js +186 -0
  25. package/dist/providers/regulatory/drugNameNormaliser.js.map +1 -0
  26. package/dist/providers/regulatory/emaEpi.d.ts +58 -0
  27. package/dist/providers/regulatory/emaEpi.d.ts.map +1 -0
  28. package/dist/providers/regulatory/emaEpi.js +105 -0
  29. package/dist/providers/regulatory/emaEpi.js.map +1 -0
  30. package/dist/providers/regulatory/index.d.ts +12 -0
  31. package/dist/providers/regulatory/index.d.ts.map +1 -0
  32. package/dist/providers/regulatory/index.js +12 -0
  33. package/dist/providers/regulatory/index.js.map +1 -0
  34. package/dist/providers/regulatory/openfda.d.ts +54 -0
  35. package/dist/providers/regulatory/openfda.d.ts.map +1 -0
  36. package/dist/providers/regulatory/openfda.js +216 -0
  37. package/dist/providers/regulatory/openfda.js.map +1 -0
  38. package/dist/providers/regulatory/types.d.ts +65 -0
  39. package/dist/providers/regulatory/types.d.ts.map +1 -0
  40. package/dist/providers/regulatory/types.js +8 -0
  41. package/dist/providers/regulatory/types.js.map +1 -0
  42. package/dist/providers/types.d.ts +2 -0
  43. package/dist/providers/types.d.ts.map +1 -1
  44. package/dist/server.js +23 -4
  45. package/dist/server.js.map +1 -1
  46. package/dist/tools/costEffectivenessModel.d.ts +30 -0
  47. package/dist/tools/costEffectivenessModel.d.ts.map +1 -1
  48. package/dist/tools/costEffectivenessModel.js +43 -1
  49. package/dist/tools/costEffectivenessModel.js.map +1 -1
  50. package/dist/tools/evidenceUnmetNeed.d.ts +40 -2
  51. package/dist/tools/evidenceUnmetNeed.d.ts.map +1 -1
  52. package/dist/tools/evidenceUnmetNeed.js +186 -12
  53. package/dist/tools/evidenceUnmetNeed.js.map +1 -1
  54. package/dist/tools/htaDossierPrep.d.ts.map +1 -1
  55. package/dist/tools/htaDossierPrep.js +97 -0
  56. package/dist/tools/htaDossierPrep.js.map +1 -1
  57. package/dist/tools/htaWorkflow.d.ts +44 -0
  58. package/dist/tools/htaWorkflow.d.ts.map +1 -1
  59. package/dist/tools/htaWorkflow.js +113 -1
  60. package/dist/tools/htaWorkflow.js.map +1 -1
  61. package/dist/tools/regulatoryStatusCheck.d.ts +60 -0
  62. package/dist/tools/regulatoryStatusCheck.d.ts.map +1 -0
  63. package/dist/tools/regulatoryStatusCheck.js +418 -0
  64. package/dist/tools/regulatoryStatusCheck.js.map +1 -0
  65. package/package.json +1 -1
package/README.md CHANGED
@@ -45,7 +45,7 @@ curl -s http://localhost:8080/health
45
45
  Expected output:
46
46
 
47
47
  ```json
48
- {"status":"ok","server":"heor-agent-mcp","version":"1.9.2"}
48
+ {"status":"ok","server":"heor-agent-mcp","version":"1.10.2"}
49
49
  ```
50
50
 
51
51
  ✅ If you see the JSON above, the npm package works on your machine. Any further issues are in your MCP client config (Claude Desktop / Cursor / Continue), not the server.
@@ -126,7 +126,7 @@ The first prompt exercises `literature_search` + `validate_links` (free, no API
126
126
 
127
127
  ## What's new
128
128
 
129
- See [CHANGELOG.md](./CHANGELOG.md) for full version history. Current: **v1.9.2** (27 tools, 44 data sources).
129
+ See [CHANGELOG.md](./CHANGELOG.md) for full version history. Current: **v1.10.2** (28 tools, 44 data sources).
130
130
 
131
131
  ### v1.0.4 highlights (still in v1.6.3)
132
132
 
@@ -9,17 +9,31 @@ export type ClientSurface = "claude_anthropic_web" | "chatgpt_adapter" | "claude
9
9
  export declare function inferSurface(clientName: string | undefined): ClientSurface;
10
10
  export declare function trackEvent(event: string, properties?: Record<string, unknown>, sessionId?: string): void;
11
11
  /**
12
- * Extracts structured analytics properties from a thrown value. Always
13
- * returns the same two property names so PostHog dashboards can query
14
- * `properties.error_class` and `properties.error_message` reliably.
12
+ * Extracts structured analytics properties from a thrown value.
15
13
  *
16
- * - Error subclasses constructor name + message (truncated to 500 chars)
14
+ * Returns THREE fields so callers can pick the right one for each surface:
15
+ * - `error_class` — constructor/category name, stable for dashboards
16
+ * - `error_message` — FULL message, no truncation. Use this for the
17
+ * client-facing response so callers (including the
18
+ * ChatGPT Custom GPT) see the complete validation
19
+ * feedback and can recover in-conversation.
20
+ * - `telemetry_message` — same message capped at 500 chars. Use this for
21
+ * PostHog `properties.error_message` so we don't
22
+ * blow up event payload size on a 50KB ZodError.
23
+ *
24
+ * Why we split these (added v1.10.2): pre-v1.10.2 the capped field doubled
25
+ * as the client response (server.ts:475), so ChatGPT received a JSON
26
+ * ZodError truncated mid-key and could not recover. The 500-char cap was
27
+ * a telemetry-hygiene decision that quietly broke the response surface.
28
+ *
29
+ * - Error subclasses → constructor name + full message
17
30
  * - Strings / numbers / null → class="unknown", message=stringified
18
31
  * - Circular objects / weird values → class="unknown", message=safe stringify
19
32
  */
20
33
  export declare function classifyToolError(err: unknown): {
21
34
  error_class: string;
22
35
  error_message: string;
36
+ telemetry_message: string;
23
37
  };
24
38
  export declare function trackToolCall(toolName: string, durationMs: number, status: "ok" | "error", sessionId?: string, properties?: Record<string, unknown>): void;
25
39
  export declare function trackSession(event: "session_start" | "session_end", sessionId: string, properties?: Record<string, unknown>): void;
@@ -1 +1 @@
1
- {"version":3,"file":"analytics.d.ts","sourceRoot":"","sources":["../src/analytics.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AACH,MAAM,MAAM,aAAa,GACrB,sBAAsB,GACtB,iBAAiB,GACjB,gBAAgB,GAChB,UAAU,GACV,OAAO,GACP,UAAU,GACV,YAAY,CAAC;AAEjB,wBAAgB,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,GAAG,aAAa,CAe1E;AAgBD,wBAAgB,UAAU,CACxB,KAAK,EAAE,MAAM,EACb,UAAU,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,EACxC,SAAS,CAAC,EAAE,MAAM,QAanB;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,OAAO,GAAG;IAC/C,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;CACvB,CAiBA;AAED,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,IAAI,GAAG,OAAO,EACtB,SAAS,CAAC,EAAE,MAAM,EAClB,UAAU,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,QAYzC;AAED,wBAAgB,YAAY,CAC1B,KAAK,EAAE,eAAe,GAAG,aAAa,EACtC,SAAS,EAAE,MAAM,EACjB,UAAU,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,QAGzC;AAED,wBAAsB,iBAAiB,kBAKtC"}
1
+ {"version":3,"file":"analytics.d.ts","sourceRoot":"","sources":["../src/analytics.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AACH,MAAM,MAAM,aAAa,GACrB,sBAAsB,GACtB,iBAAiB,GACjB,gBAAgB,GAChB,UAAU,GACV,OAAO,GACP,UAAU,GACV,YAAY,CAAC;AAEjB,wBAAgB,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,GAAG,aAAa,CAe1E;AAgBD,wBAAgB,UAAU,CACxB,KAAK,EAAE,MAAM,EACb,UAAU,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,EACxC,SAAS,CAAC,EAAE,MAAM,QAanB;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,OAAO,GAAG;IAC/C,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,iBAAiB,EAAE,MAAM,CAAC;CAC3B,CAoBA;AAED,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,IAAI,GAAG,OAAO,EACtB,SAAS,CAAC,EAAE,MAAM,EAClB,UAAU,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,QAYzC;AAED,wBAAgB,YAAY,CAC1B,KAAK,EAAE,eAAe,GAAG,aAAa,EACtC,SAAS,EAAE,MAAM,EACjB,UAAU,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,QAGzC;AAED,wBAAsB,iBAAiB,kBAKtC"}
package/dist/analytics.js CHANGED
@@ -47,19 +47,34 @@ export function trackEvent(event, properties = {}, sessionId) {
47
47
  });
48
48
  }
49
49
  /**
50
- * Extracts structured analytics properties from a thrown value. Always
51
- * returns the same two property names so PostHog dashboards can query
52
- * `properties.error_class` and `properties.error_message` reliably.
50
+ * Extracts structured analytics properties from a thrown value.
53
51
  *
54
- * - Error subclasses constructor name + message (truncated to 500 chars)
52
+ * Returns THREE fields so callers can pick the right one for each surface:
53
+ * - `error_class` — constructor/category name, stable for dashboards
54
+ * - `error_message` — FULL message, no truncation. Use this for the
55
+ * client-facing response so callers (including the
56
+ * ChatGPT Custom GPT) see the complete validation
57
+ * feedback and can recover in-conversation.
58
+ * - `telemetry_message` — same message capped at 500 chars. Use this for
59
+ * PostHog `properties.error_message` so we don't
60
+ * blow up event payload size on a 50KB ZodError.
61
+ *
62
+ * Why we split these (added v1.10.2): pre-v1.10.2 the capped field doubled
63
+ * as the client response (server.ts:475), so ChatGPT received a JSON
64
+ * ZodError truncated mid-key and could not recover. The 500-char cap was
65
+ * a telemetry-hygiene decision that quietly broke the response surface.
66
+ *
67
+ * - Error subclasses → constructor name + full message
55
68
  * - Strings / numbers / null → class="unknown", message=stringified
56
69
  * - Circular objects / weird values → class="unknown", message=safe stringify
57
70
  */
58
71
  export function classifyToolError(err) {
59
72
  if (err instanceof Error) {
73
+ const full = err.message ?? "";
60
74
  return {
61
75
  error_class: err.constructor?.name ?? "Error",
62
- error_message: (err.message ?? "").slice(0, 500),
76
+ error_message: full,
77
+ telemetry_message: full.slice(0, 500),
63
78
  };
64
79
  }
65
80
  let message;
@@ -71,7 +86,8 @@ export function classifyToolError(err) {
71
86
  }
72
87
  return {
73
88
  error_class: "unknown",
74
- error_message: message.slice(0, 500),
89
+ error_message: message,
90
+ telemetry_message: message.slice(0, 500),
75
91
  };
76
92
  }
77
93
  export function trackToolCall(toolName, durationMs, status, sessionId, properties = {}) {
@@ -1 +1 @@
1
- {"version":3,"file":"analytics.js","sourceRoot":"","sources":["../src/analytics.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"analytics.js","sourceRoot":"","sources":["../src/analytics.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Best-effort parser for label population text.
3
+ *
4
+ * Extracts { age_min_years, age_max_years, weight_constraint_kg, sex_constraint }
5
+ * from verbatim label text like:
6
+ * "pediatric patients aged 6 to 17 years weighing 45 kg or more"
7
+ *
8
+ * All fields are nullable — unparseable text returns all nulls.
9
+ * The caller always retains the verbatim population text.
10
+ */
11
+ import type { PopulationParsed } from "./types.js";
12
+ export declare function parseAgeRange(text: string): PopulationParsed;
13
+ //# sourceMappingURL=ageRangeParser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ageRangeParser.d.ts","sourceRoot":"","sources":["../../../src/providers/regulatory/ageRangeParser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEnD,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,gBAAgB,CAqD5D"}
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Best-effort parser for label population text.
3
+ *
4
+ * Extracts { age_min_years, age_max_years, weight_constraint_kg, sex_constraint }
5
+ * from verbatim label text like:
6
+ * "pediatric patients aged 6 to 17 years weighing 45 kg or more"
7
+ *
8
+ * All fields are nullable — unparseable text returns all nulls.
9
+ * The caller always retains the verbatim population text.
10
+ */
11
+ export function parseAgeRange(text) {
12
+ const result = {
13
+ age_min_years: null,
14
+ age_max_years: null,
15
+ weight_constraint_kg: null,
16
+ sex_constraint: null,
17
+ };
18
+ if (!text || typeof text !== "string")
19
+ return result;
20
+ const lower = text.toLowerCase();
21
+ // Sex constraint detection
22
+ if (/\b(females?|women|woman|premenopausal|postmenopausal)\b/.test(lower)) {
23
+ result.sex_constraint = "female_only";
24
+ }
25
+ else if (/\b(males?|men|man)\b/.test(lower)) {
26
+ result.sex_constraint = "male_only";
27
+ }
28
+ // Age range: "aged X to Y years" or "X to Y years"
29
+ const rangeMatch = text.match(/(?:aged?\s+)?(\d+)\s+to\s+(\d+)\s+years?/i);
30
+ if (rangeMatch) {
31
+ result.age_min_years = parseInt(rangeMatch[1], 10);
32
+ result.age_max_years = parseInt(rangeMatch[2], 10);
33
+ return _extractWeight(result, text);
34
+ }
35
+ // Minimum age patterns:
36
+ // "18 years of age or older"
37
+ // "6 years of age and older"
38
+ // "≥12 years" or ">= 12 years"
39
+ // "at least 12 years"
40
+ // "12 years and older"
41
+ const minAgePatterns = [
42
+ /(?:aged?\s+)?(\d+)\s+years?\s+(?:of\s+age\s+)?(?:and|or)\s+older/i,
43
+ /(?:aged?\s+)?(\d+)\s+years?\s+(?:of\s+age\s+)?(?:and|or)\s+above/i,
44
+ /(?:at\s+least\s+)?[≥>=]+\s*(\d+)\s+years?/i,
45
+ /patients?\s+(\d+)\s+years?\s+of\s+age\s+or\s+older/i,
46
+ /(?:adults?\s+)?\((\d+)\s+years?\s+and\s+older\)/i,
47
+ /(?:at\s+least\s+)(\d+)\s+years?\s+of\s+age/i,
48
+ /(?:aged?\s+)(\d+)\s+years?\s+(?:and\s+)?(?:and\s+)?older/i,
49
+ /\b(\d+)\s+years?\s+of\s+age\s+(?:and|or)\s+older/i,
50
+ ];
51
+ for (const pattern of minAgePatterns) {
52
+ const m = text.match(pattern);
53
+ if (m) {
54
+ result.age_min_years = parseInt(m[1], 10);
55
+ return _extractWeight(result, text);
56
+ }
57
+ }
58
+ return _extractWeight(result, text);
59
+ }
60
+ function _extractWeight(result, text) {
61
+ // Weight patterns: "weighing 45 kg or more", "at least 30 kg body weight",
62
+ // "body weight ≥ 45 kg"
63
+ const weightPatterns = [
64
+ /weighing\s+(\d+(?:\.\d+)?)\s*kg/i,
65
+ /at\s+least\s+(\d+(?:\.\d+)?)\s*kg/i,
66
+ /[≥>=]+\s*(\d+(?:\.\d+)?)\s*kg/i,
67
+ /(\d+(?:\.\d+)?)\s*kg\s+or\s+(?:more|greater)/i,
68
+ ];
69
+ for (const pattern of weightPatterns) {
70
+ const m = text.match(pattern);
71
+ if (m) {
72
+ result.weight_constraint_kg = parseFloat(m[1]);
73
+ break;
74
+ }
75
+ }
76
+ return result;
77
+ }
78
+ //# sourceMappingURL=ageRangeParser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ageRangeParser.js","sourceRoot":"","sources":["../../../src/providers/regulatory/ageRangeParser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,MAAM,MAAM,GAAqB;QAC/B,aAAa,EAAE,IAAI;QACnB,aAAa,EAAE,IAAI;QACnB,oBAAoB,EAAE,IAAI;QAC1B,cAAc,EAAE,IAAI;KACrB,CAAC;IAEF,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ;QAAE,OAAO,MAAM,CAAC;IAErD,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;IAEjC,2BAA2B;IAC3B,IAAI,yDAAyD,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QAC1E,MAAM,CAAC,cAAc,GAAG,aAAa,CAAC;IACxC,CAAC;SAAM,IAAI,sBAAsB,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QAC9C,MAAM,CAAC,cAAc,GAAG,WAAW,CAAC;IACtC,CAAC;IAED,mDAAmD;IACnD,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,2CAA2C,CAAC,CAAC;IAC3E,IAAI,UAAU,EAAE,CAAC;QACf,MAAM,CAAC,aAAa,GAAG,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACnD,MAAM,CAAC,aAAa,GAAG,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACnD,OAAO,cAAc,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IACtC,CAAC;IAED,wBAAwB;IACxB,6BAA6B;IAC7B,6BAA6B;IAC7B,+BAA+B;IAC/B,sBAAsB;IACtB,uBAAuB;IACvB,MAAM,cAAc,GAAG;QACrB,mEAAmE;QACnE,mEAAmE;QACnE,4CAA4C;QAC5C,qDAAqD;QACrD,kDAAkD;QAClD,6CAA6C;QAC7C,2DAA2D;QAC3D,mDAAmD;KACpD,CAAC;IAEF,KAAK,MAAM,OAAO,IAAI,cAAc,EAAE,CAAC;QACrC,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAC9B,IAAI,CAAC,EAAE,CAAC;YACN,MAAM,CAAC,aAAa,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YAC1C,OAAO,cAAc,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QACtC,CAAC;IACH,CAAC;IAED,OAAO,cAAc,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;AACtC,CAAC;AAED,SAAS,cAAc,CACrB,MAAwB,EACxB,IAAY;IAEZ,2EAA2E;IAC3E,0BAA0B;IAC1B,MAAM,cAAc,GAAG;QACrB,kCAAkC;QAClC,oCAAoC;QACpC,gCAAgC;QAChC,+CAA+C;KAChD,CAAC;IAEF,KAAK,MAAM,OAAO,IAAI,cAAc,EAAE,CAAC;QACrC,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAC9B,IAAI,CAAC,EAAE,CAAC;YACN,MAAM,CAAC,oBAAoB,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YAC/C,MAAM;QACR,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Fan-out helper for auto-wiring regulatory.status_check into other tools.
3
+ *
4
+ * Design log #26. Used by evidence.unmet_need and hta_workflow Phase 3.6.
5
+ *
6
+ * Concurrency: up to 8 parallel calls (OpenFDA rate-limit guard).
7
+ * Cache: uses the shared 24h module-level cache inside regulatoryStatusCheck.ts.
8
+ * Never throws: each call is wrapped — one failure never aborts the batch.
9
+ *
10
+ * CRITICAL CYCLE-SAFETY: This module MUST NOT import from
11
+ * tools/evidenceUnmetNeed.ts or tools/htaWorkflow.ts (would create a cycle).
12
+ * Only imports from tools/regulatoryStatusCheck.ts (which owns no upstream deps).
13
+ */
14
+ import type { RegulatoryStatusResult } from "./types.js";
15
+ export interface AutoCheckRequest {
16
+ drug: string;
17
+ region: "us" | "eu" | "uk" | "global";
18
+ indication?: string;
19
+ }
20
+ export interface AutoCheckResult {
21
+ drug: string;
22
+ region: string;
23
+ result: RegulatoryStatusResult;
24
+ }
25
+ /**
26
+ * Fan-out helper for auto-wiring regulatory.status_check into other tools.
27
+ * Concurrency 8 to avoid OpenFDA rate-limit. Cache hits are free.
28
+ * Never throws — wraps each call so one failure doesn't kill the batch.
29
+ */
30
+ export declare function autoCheckRegulatory(requests: AutoCheckRequest[]): Promise<AutoCheckResult[]>;
31
+ //# sourceMappingURL=autoCheck.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"autoCheck.d.ts","sourceRoot":"","sources":["../../../src/providers/regulatory/autoCheck.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAIzD,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,QAAQ,CAAC;IACtC,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,sBAAsB,CAAC;CAChC;AAwCD;;;;GAIG;AACH,wBAAsB,mBAAmB,CACvC,QAAQ,EAAE,gBAAgB,EAAE,GAC3B,OAAO,CAAC,eAAe,EAAE,CAAC,CA6C5B"}
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Fan-out helper for auto-wiring regulatory.status_check into other tools.
3
+ *
4
+ * Design log #26. Used by evidence.unmet_need and hta_workflow Phase 3.6.
5
+ *
6
+ * Concurrency: up to 8 parallel calls (OpenFDA rate-limit guard).
7
+ * Cache: uses the shared 24h module-level cache inside regulatoryStatusCheck.ts.
8
+ * Never throws: each call is wrapped — one failure never aborts the batch.
9
+ *
10
+ * CRITICAL CYCLE-SAFETY: This module MUST NOT import from
11
+ * tools/evidenceUnmetNeed.ts or tools/htaWorkflow.ts (would create a cycle).
12
+ * Only imports from tools/regulatoryStatusCheck.ts (which owns no upstream deps).
13
+ */
14
+ import { handleRegulatoryStatusCheck } from "../../tools/regulatoryStatusCheck.js";
15
+ // ── Constants ──────────────────────────────────────────────────────────────
16
+ const MAX_AUTO_REGULATORY_CALLS_PER_REQUEST = 8;
17
+ const CONCURRENCY_LIMIT = 8;
18
+ // ── Helper: build a graceful api_error result on exception ────────────────
19
+ function makeErrorResult(drug, region, message) {
20
+ return {
21
+ schema_version: "1.0",
22
+ drug,
23
+ drug_normalised_inn: drug.toLowerCase(),
24
+ drug_brand_names: [],
25
+ region,
26
+ current_status: "api_error",
27
+ approved_indications: [],
28
+ black_box_warnings: [],
29
+ rems_required: false,
30
+ contraindications: [],
31
+ recent_label_changes: {
32
+ count_12_months: 0,
33
+ last_revision_date: null,
34
+ last_revision_summary: null,
35
+ },
36
+ source_urls: [],
37
+ data_fetched_at: new Date().toISOString(),
38
+ data_age_hours: 0,
39
+ cache_hit: false,
40
+ api_error: {
41
+ source: "autoCheck",
42
+ message,
43
+ retry_after_seconds: 60,
44
+ },
45
+ };
46
+ }
47
+ // ── Fan-out with concurrency limit ─────────────────────────────────────────
48
+ /**
49
+ * Fan-out helper for auto-wiring regulatory.status_check into other tools.
50
+ * Concurrency 8 to avoid OpenFDA rate-limit. Cache hits are free.
51
+ * Never throws — wraps each call so one failure doesn't kill the batch.
52
+ */
53
+ export async function autoCheckRegulatory(requests) {
54
+ if (requests.length === 0)
55
+ return [];
56
+ // Cap at MAX to prevent fan-out explosion
57
+ const capped = requests.slice(0, MAX_AUTO_REGULATORY_CALLS_PER_REQUEST);
58
+ // Process in batches of CONCURRENCY_LIMIT
59
+ const results = [];
60
+ for (let i = 0; i < capped.length; i += CONCURRENCY_LIMIT) {
61
+ const batch = capped.slice(i, i + CONCURRENCY_LIMIT);
62
+ const batchResults = await Promise.all(batch.map(async (req) => {
63
+ try {
64
+ const toolResult = await handleRegulatoryStatusCheck({
65
+ drug: req.drug,
66
+ region: req.region,
67
+ indication: req.indication,
68
+ });
69
+ // handleRegulatoryStatusCheck returns { content: string, audit }
70
+ const parsed = JSON.parse(typeof toolResult.content === "string"
71
+ ? toolResult.content
72
+ : JSON.stringify(toolResult.content));
73
+ return {
74
+ drug: req.drug,
75
+ region: req.region,
76
+ result: parsed,
77
+ };
78
+ }
79
+ catch (e) {
80
+ // Never throw — return graceful api_error
81
+ const message = e instanceof Error ? e.message : String(e);
82
+ return {
83
+ drug: req.drug,
84
+ region: req.region,
85
+ result: makeErrorResult(req.drug, req.region, message),
86
+ };
87
+ }
88
+ }));
89
+ results.push(...batchResults);
90
+ }
91
+ return results;
92
+ }
93
+ //# sourceMappingURL=autoCheck.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"autoCheck.js","sourceRoot":"","sources":["../../../src/providers/regulatory/autoCheck.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * 24h in-memory TTL cache for regulatory.status_check results.
3
+ *
4
+ * Design contract:
5
+ * - get() returns null on miss (expired or never set)
6
+ * - get() returns { value, cache_hit: true, data_age_hours } on hit
7
+ * - set() stores value with current timestamp
8
+ * - delete() removes key (used for force_refresh)
9
+ * - TTL configurable; default 24h
10
+ */
11
+ interface CacheHit<T> {
12
+ value: T;
13
+ cache_hit: true;
14
+ data_age_hours: number;
15
+ }
16
+ export declare class RegulatoryCache<T = unknown> {
17
+ private readonly store;
18
+ private readonly ttlMs;
19
+ constructor(options?: {
20
+ ttlMs?: number;
21
+ });
22
+ /**
23
+ * Returns null on cache miss (expired or never set).
24
+ * Returns { value, cache_hit: true, data_age_hours } on hit.
25
+ */
26
+ get(key: string): CacheHit<T> | null;
27
+ /**
28
+ * Stores value with current timestamp.
29
+ */
30
+ set(key: string, value: T): void;
31
+ /**
32
+ * Removes key from cache (used by force_refresh logic).
33
+ */
34
+ delete(key: string): void;
35
+ }
36
+ export {};
37
+ //# sourceMappingURL=cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../../src/providers/regulatory/cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAOH,UAAU,QAAQ,CAAC,CAAC;IAClB,KAAK,EAAE,CAAC,CAAC;IACT,SAAS,EAAE,IAAI,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,qBAAa,eAAe,CAAC,CAAC,GAAG,OAAO;IACtC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAoC;IAC1D,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;gBAEnB,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE;IAKxC;;;OAGG;IACH,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,QAAQ,CAAC,CAAC,CAAC,GAAG,IAAI;IAkBpC;;OAEG;IACH,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,IAAI;IAIhC;;OAEG;IACH,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;CAG1B"}
@@ -0,0 +1,51 @@
1
+ /**
2
+ * 24h in-memory TTL cache for regulatory.status_check results.
3
+ *
4
+ * Design contract:
5
+ * - get() returns null on miss (expired or never set)
6
+ * - get() returns { value, cache_hit: true, data_age_hours } on hit
7
+ * - set() stores value with current timestamp
8
+ * - delete() removes key (used for force_refresh)
9
+ * - TTL configurable; default 24h
10
+ */
11
+ export class RegulatoryCache {
12
+ store = new Map();
13
+ ttlMs;
14
+ constructor(options) {
15
+ // Default 24h TTL
16
+ this.ttlMs = options?.ttlMs ?? 24 * 60 * 60 * 1000;
17
+ }
18
+ /**
19
+ * Returns null on cache miss (expired or never set).
20
+ * Returns { value, cache_hit: true, data_age_hours } on hit.
21
+ */
22
+ get(key) {
23
+ const entry = this.store.get(key);
24
+ if (!entry)
25
+ return null;
26
+ const ageMs = Date.now() - entry.storedAt;
27
+ if (ageMs > this.ttlMs) {
28
+ // Expired — remove and return null
29
+ this.store.delete(key);
30
+ return null;
31
+ }
32
+ return {
33
+ value: entry.value,
34
+ cache_hit: true,
35
+ data_age_hours: ageMs / (1000 * 60 * 60),
36
+ };
37
+ }
38
+ /**
39
+ * Stores value with current timestamp.
40
+ */
41
+ set(key, value) {
42
+ this.store.set(key, { value, storedAt: Date.now() });
43
+ }
44
+ /**
45
+ * Removes key from cache (used by force_refresh logic).
46
+ */
47
+ delete(key) {
48
+ this.store.delete(key);
49
+ }
50
+ }
51
+ //# sourceMappingURL=cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache.js","sourceRoot":"","sources":["../../../src/providers/regulatory/cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAaH,MAAM,OAAO,eAAe;IACT,KAAK,GAAG,IAAI,GAAG,EAAyB,CAAC;IACzC,KAAK,CAAS;IAE/B,YAAY,OAA4B;QACtC,kBAAkB;QAClB,IAAI,CAAC,KAAK,GAAG,OAAO,EAAE,KAAK,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;IACrD,CAAC;IAED;;;OAGG;IACH,GAAG,CAAC,GAAW;QACb,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAClC,IAAI,CAAC,KAAK;YAAE,OAAO,IAAI,CAAC;QAExB,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,QAAQ,CAAC;QAC1C,IAAI,KAAK,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC;YACvB,mCAAmC;YACnC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACvB,OAAO,IAAI,CAAC;QACd,CAAC;QAED,OAAO;YACL,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,SAAS,EAAE,IAAI;YACf,cAAc,EAAE,KAAK,GAAG,CAAC,IAAI,GAAG,EAAE,GAAG,EAAE,CAAC;SACzC,CAAC;IACJ,CAAC;IAED;;OAEG;IACH,GAAG,CAAC,GAAW,EAAE,KAAQ;QACvB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;IACvD,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,GAAW;QAChB,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IACzB,CAAC;CACF"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * DailyMed v2 client — CROSS-CHECK / FALLBACK for US regulatory status.
3
+ *
4
+ * Phase 0 verified 2026-05-07:
5
+ * Search: GET https://dailymed.nlm.nih.gov/dailymed/services/v2/spls.json?drug_name={X}
6
+ * Returns: { data: [{spl_version, published_date, title, setid}], metadata }
7
+ *
8
+ * Used in v1 only to cross-check OpenFDA result freshness (compare
9
+ * published_date against OpenFDA's effective_time). Does NOT replace
10
+ * OpenFDA as the primary source.
11
+ *
12
+ * Design log #25.
13
+ */
14
+ export interface DailyMedEntry {
15
+ setid: string;
16
+ spl_version: number;
17
+ published_date: string;
18
+ title: string;
19
+ }
20
+ /**
21
+ * Build the DailyMed search URL.
22
+ */
23
+ export declare function buildDailyMedSearchUrl(drugName: string): string;
24
+ /**
25
+ * Parse DailyMed search response into structured entries.
26
+ */
27
+ export declare function parseDailyMedSearchResponse(response: unknown): DailyMedEntry[];
28
+ /**
29
+ * Fetch DailyMed cross-check for a drug name.
30
+ * Returns null on any error (404, network failure, etc.) — DailyMed is
31
+ * non-critical; OpenFDA is the primary source.
32
+ */
33
+ export declare function fetchDailyMedCrossCheck(drugName: string): Promise<DailyMedEntry[] | null>;
34
+ //# sourceMappingURL=dailymed.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dailymed.d.ts","sourceRoot":"","sources":["../../../src/providers/regulatory/dailymed.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAIH,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;CACf;AAgBD;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAG/D;AAED;;GAEG;AACH,wBAAgB,2BAA2B,CACzC,QAAQ,EAAE,OAAO,GAChB,aAAa,EAAE,CAYjB;AAED;;;;GAIG;AACH,wBAAsB,uBAAuB,CAC3C,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,aAAa,EAAE,GAAG,IAAI,CAAC,CAcjC"}
@@ -0,0 +1,60 @@
1
+ /**
2
+ * DailyMed v2 client — CROSS-CHECK / FALLBACK for US regulatory status.
3
+ *
4
+ * Phase 0 verified 2026-05-07:
5
+ * Search: GET https://dailymed.nlm.nih.gov/dailymed/services/v2/spls.json?drug_name={X}
6
+ * Returns: { data: [{spl_version, published_date, title, setid}], metadata }
7
+ *
8
+ * Used in v1 only to cross-check OpenFDA result freshness (compare
9
+ * published_date against OpenFDA's effective_time). Does NOT replace
10
+ * OpenFDA as the primary source.
11
+ *
12
+ * Design log #25.
13
+ */
14
+ const BASE_URL = "https://dailymed.nlm.nih.gov/dailymed/services/v2";
15
+ /**
16
+ * Build the DailyMed search URL.
17
+ */
18
+ export function buildDailyMedSearchUrl(drugName) {
19
+ const encoded = encodeURIComponent(drugName);
20
+ return `${BASE_URL}/spls.json?drug_name=${encoded}`;
21
+ }
22
+ /**
23
+ * Parse DailyMed search response into structured entries.
24
+ */
25
+ export function parseDailyMedSearchResponse(response) {
26
+ if (!response || typeof response !== "object")
27
+ return [];
28
+ const resp = response;
29
+ const data = resp.data;
30
+ if (!Array.isArray(data))
31
+ return [];
32
+ return data.map((entry) => ({
33
+ setid: entry.setid ?? "",
34
+ spl_version: entry.spl_version ?? 0,
35
+ published_date: entry.published_date ?? "",
36
+ title: entry.title ?? "",
37
+ }));
38
+ }
39
+ /**
40
+ * Fetch DailyMed cross-check for a drug name.
41
+ * Returns null on any error (404, network failure, etc.) — DailyMed is
42
+ * non-critical; OpenFDA is the primary source.
43
+ */
44
+ export async function fetchDailyMedCrossCheck(drugName) {
45
+ try {
46
+ const url = buildDailyMedSearchUrl(drugName);
47
+ const res = await fetch(url, { signal: AbortSignal.timeout(15_000) });
48
+ if (!res.ok)
49
+ return null;
50
+ const data = (await res.json());
51
+ const entries = parseDailyMedSearchResponse(data);
52
+ if (entries.length === 0)
53
+ return null;
54
+ return entries;
55
+ }
56
+ catch {
57
+ return null;
58
+ }
59
+ }
60
+ //# sourceMappingURL=dailymed.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dailymed.js","sourceRoot":"","sources":["../../../src/providers/regulatory/dailymed.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,MAAM,QAAQ,GAAG,mDAAmD,CAAC;AAuBrE;;GAEG;AACH,MAAM,UAAU,sBAAsB,CAAC,QAAgB;IACrD,MAAM,OAAO,GAAG,kBAAkB,CAAC,QAAQ,CAAC,CAAC;IAC7C,OAAO,GAAG,QAAQ,wBAAwB,OAAO,EAAE,CAAC;AACtD,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,2BAA2B,CACzC,QAAiB;IAEjB,IAAI,CAAC,QAAQ,IAAI,OAAO,QAAQ,KAAK,QAAQ;QAAE,OAAO,EAAE,CAAC;IACzD,MAAM,IAAI,GAAG,QAAkC,CAAC;IAChD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;IACvB,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC;QAAE,OAAO,EAAE,CAAC;IAEpC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QAC1B,KAAK,EAAE,KAAK,CAAC,KAAK,IAAI,EAAE;QACxB,WAAW,EAAE,KAAK,CAAC,WAAW,IAAI,CAAC;QACnC,cAAc,EAAE,KAAK,CAAC,cAAc,IAAI,EAAE;QAC1C,KAAK,EAAE,KAAK,CAAC,KAAK,IAAI,EAAE;KACzB,CAAC,CAAC,CAAC;AACN,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAC3C,QAAgB;IAEhB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,sBAAsB,CAAC,QAAQ,CAAC,CAAC;QAC7C,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACtE,IAAI,CAAC,GAAG,CAAC,EAAE;YAAE,OAAO,IAAI,CAAC;QAEzB,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAY,CAAC;QAC3C,MAAM,OAAO,GAAG,2BAA2B,CAAC,IAAI,CAAC,CAAC;QAClD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC;QAEtC,OAAO,OAAO,CAAC;IACjB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Drug name normaliser — INN / brand / biosimilar-suffix → canonical INN.
3
+ *
4
+ * Design log #25. Covers ~70 most-common HEOR-relevant molecules.
5
+ * Returns did_you_mean[] (Levenshtein-based top-3) when no match.
6
+ *
7
+ * Biosimilar suffix stripping: FDA 4-letter suffixes (e.g., -vfrm, -aooe, -adaz)
8
+ * are stripped to recover the INN stem.
9
+ */
10
+ interface NormaliseResult {
11
+ inn: string | null;
12
+ did_you_mean?: string[];
13
+ }
14
+ /**
15
+ * Normalises a drug name to canonical INN.
16
+ *
17
+ * Lookup priority:
18
+ * 1. INN exact match (case-insensitive)
19
+ * 2. Brand name exact match
20
+ * 3. Strip biosimilar suffix → INN match
21
+ * 4. Not found → null + did_you_mean
22
+ */
23
+ export declare function normaliseDrugName(drug: string): NormaliseResult;
24
+ /**
25
+ * Returns all known brand names for a given INN.
26
+ */
27
+ export declare function getBrandNames(inn: string): string[];
28
+ export {};
29
+ //# sourceMappingURL=drugNameNormaliser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"drugNameNormaliser.d.ts","sourceRoot":"","sources":["../../../src/providers/regulatory/drugNameNormaliser.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,UAAU,eAAe;IACvB,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;CACzB;AAuID;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe,CAoC/D;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAKnD"}