heor-agent-mcp 1.2.2 → 1.3.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.
- package/README.md +2 -2
- package/dist/data/niceTaPrecedents.d.ts +72 -0
- package/dist/data/niceTaPrecedents.d.ts.map +1 -0
- package/dist/data/niceTaPrecedents.js +152 -0
- package/dist/data/niceTaPrecedents.js.map +1 -0
- package/dist/jca/scopeEligibility.d.ts +55 -0
- package/dist/jca/scopeEligibility.d.ts.map +1 -0
- package/dist/jca/scopeEligibility.js +56 -0
- package/dist/jca/scopeEligibility.js.map +1 -0
- package/dist/pv/decisionTree.d.ts +2 -0
- package/dist/pv/decisionTree.d.ts.map +1 -1
- package/dist/pv/decisionTree.js +92 -14
- package/dist/pv/decisionTree.js.map +1 -1
- package/dist/pv/gvpModules.d.ts +2 -1
- package/dist/pv/gvpModules.d.ts.map +1 -1
- package/dist/pv/gvpModules.js +7 -7
- package/dist/pv/gvpModules.js.map +1 -1
- package/dist/pv/signalDecision.d.ts +54 -0
- package/dist/pv/signalDecision.d.ts.map +1 -0
- package/dist/pv/signalDecision.js +115 -0
- package/dist/pv/signalDecision.js.map +1 -0
- package/dist/pv/signalStatistics.d.ts +67 -0
- package/dist/pv/signalStatistics.d.ts.map +1 -0
- package/dist/pv/signalStatistics.js +186 -0
- package/dist/pv/signalStatistics.js.map +1 -0
- package/dist/pv/types.d.ts +8 -1
- package/dist/pv/types.d.ts.map +1 -1
- package/dist/server.js +5 -0
- package/dist/server.js.map +1 -1
- package/dist/tools/htaDossierPrep.d.ts.map +1 -1
- package/dist/tools/htaDossierPrep.js +37 -3
- package/dist/tools/htaDossierPrep.js.map +1 -1
- package/dist/tools/jcaPicoScope.d.ts.map +1 -1
- package/dist/tools/jcaPicoScope.js +58 -0
- package/dist/tools/jcaPicoScope.js.map +1 -1
- package/dist/tools/pvClassify.d.ts +1 -1
- package/dist/tools/pvClassify.d.ts.map +1 -1
- package/dist/tools/pvClassify.js +20 -7
- package/dist/tools/pvClassify.js.map +1 -1
- package/dist/tools/pvSignalWorkflow.d.ts +82 -0
- package/dist/tools/pvSignalWorkflow.d.ts.map +1 -0
- package/dist/tools/pvSignalWorkflow.js +315 -0
- package/dist/tools/pvSignalWorkflow.js.map +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -70,7 +70,7 @@ Add to your MCP configuration:
|
|
|
70
70
|
|
|
71
71
|
---
|
|
72
72
|
|
|
73
|
-
## Tools (
|
|
73
|
+
## Tools (22)
|
|
74
74
|
|
|
75
75
|
| Tool | Purpose |
|
|
76
76
|
|------|---------|
|
|
@@ -368,7 +368,7 @@ A companion chat interface is available at:
|
|
|
368
368
|
|
|
369
369
|
**https://web-michael-ns-projects.vercel.app**
|
|
370
370
|
|
|
371
|
-
- Chat with Claude Sonnet 4.6 + all
|
|
371
|
+
- Chat with Claude Sonnet 4.6 + all 22 HEOR tools
|
|
372
372
|
- **BYOK (Bring Your Own Key)** — paste your Anthropic API key in the settings; it stays in your browser's localStorage and is never stored on our servers
|
|
373
373
|
- Markdown rendering with styled tables, tool call cards with live progress timers, and theme-aware mermaid network diagrams
|
|
374
374
|
- 12 example prompts covering literature search, CEA, BIA, NMA, ITC feasibility, RoB, EQ-5D 5L, EU JCA dossiers
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NICE Technology Appraisal precedent lookup.
|
|
3
|
+
*
|
|
4
|
+
* Maps drug-indication pairs to the relevant NICE TA number(s).
|
|
5
|
+
* Used by `hta_dossier` (NICE branch) to surface known precedents
|
|
6
|
+
* upfront and to catch user prompt errors of the form
|
|
7
|
+
* "cite TA902 for HFrEF" (TA902 is HFpEF/HFmrEF; TA679 is HFrEF).
|
|
8
|
+
*
|
|
9
|
+
* Verification policy:
|
|
10
|
+
* - Each entry is the canonical TA at time of writing (2026-05).
|
|
11
|
+
* - Indications use lowercase free-form keywords; matching is
|
|
12
|
+
* substring-based to tolerate user phrasing variation.
|
|
13
|
+
* - When the user passes a different TA number in prose, the
|
|
14
|
+
* tool surfaces both the user's number and the canonical one
|
|
15
|
+
* without auto-overriding (we annotate the mismatch but
|
|
16
|
+
* defer to user judgement).
|
|
17
|
+
*
|
|
18
|
+
* To add an entry: append to PRECEDENTS, keep keywords lowercase,
|
|
19
|
+
* keep `confidence: "high"` only for personally-verified entries.
|
|
20
|
+
*
|
|
21
|
+
* See design log #16.
|
|
22
|
+
*/
|
|
23
|
+
export interface NicePrecedent {
|
|
24
|
+
ta_number: string;
|
|
25
|
+
drug: string;
|
|
26
|
+
indication_keywords: string[];
|
|
27
|
+
title: string;
|
|
28
|
+
date: string;
|
|
29
|
+
url: string;
|
|
30
|
+
notes?: string;
|
|
31
|
+
confidence: "high" | "medium";
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* v1 list — top NICE TAs likely to be cited in HEOR submissions
|
|
35
|
+
* for the indications HEORAgent is most often used for. Expand
|
|
36
|
+
* over time. ~50 entries is enough to catch the common confusions
|
|
37
|
+
* (HFrEF vs HFpEF, indication-specific TAs, biosimilar splits).
|
|
38
|
+
*/
|
|
39
|
+
export declare const PRECEDENTS: ReadonlyArray<NicePrecedent>;
|
|
40
|
+
/**
|
|
41
|
+
* Look up NICE TA precedents that match the given drug and indication
|
|
42
|
+
* substrings. Returns 0..n matches, ranked by drug match first then
|
|
43
|
+
* by number of indication keyword matches.
|
|
44
|
+
*
|
|
45
|
+
* Matching is intentionally loose: we want to catch "HFrEF",
|
|
46
|
+
* "heart failure with reduced ejection fraction", and "hf with
|
|
47
|
+
* reduced EF" all matching TA679. Both drug name and indication
|
|
48
|
+
* are lowercased before substring comparison.
|
|
49
|
+
*/
|
|
50
|
+
export declare function findPrecedents(drug: string, indication: string): NicePrecedent[];
|
|
51
|
+
/**
|
|
52
|
+
* Detect when a user-supplied TA number is mismatched against the
|
|
53
|
+
* canonical TA for the indication. Returns a structured warning
|
|
54
|
+
* payload the caller can surface in the dossier output and audit.
|
|
55
|
+
*
|
|
56
|
+
* Example: user prompt "cite TA902 for dapagliflozin HFrEF"
|
|
57
|
+
* → detects TA902 in prose, looks up canonical TA for
|
|
58
|
+
* dapagliflozin+HFrEF (TA679), and returns a warning recommending
|
|
59
|
+
* correction.
|
|
60
|
+
*/
|
|
61
|
+
export interface PrecedentMismatch {
|
|
62
|
+
user_supplied: string;
|
|
63
|
+
canonical: string;
|
|
64
|
+
user_supplied_url?: string;
|
|
65
|
+
canonical_url: string;
|
|
66
|
+
drug: string;
|
|
67
|
+
indication: string;
|
|
68
|
+
explanation: string;
|
|
69
|
+
}
|
|
70
|
+
export declare function detectMismatch(drug: string, indication: string, userSuppliedTaNumber: string | null | undefined): PrecedentMismatch | null;
|
|
71
|
+
export declare function extractTaNumber(text: string): string | null;
|
|
72
|
+
//# sourceMappingURL=niceTaPrecedents.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"niceTaPrecedents.d.ts","sourceRoot":"","sources":["../../src/data/niceTaPrecedents.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,mBAAmB,EAAE,MAAM,EAAE,CAAC;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,GAAG,QAAQ,CAAC;CAC/B;AAED;;;;;GAKG;AACH,eAAO,MAAM,UAAU,EAAE,aAAa,CAAC,aAAa,CAuEnD,CAAC;AAEF;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,MAAM,GACjB,aAAa,EAAE,CAkBjB;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,iBAAiB;IAChC,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,aAAa,EAAE,MAAM,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,wBAAgB,cAAc,CAC5B,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,MAAM,EAClB,oBAAoB,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAC9C,iBAAiB,GAAG,IAAI,CAiC1B;AAQD,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAG3D"}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* v1 list — top NICE TAs likely to be cited in HEOR submissions
|
|
3
|
+
* for the indications HEORAgent is most often used for. Expand
|
|
4
|
+
* over time. ~50 entries is enough to catch the common confusions
|
|
5
|
+
* (HFrEF vs HFpEF, indication-specific TAs, biosimilar splits).
|
|
6
|
+
*/
|
|
7
|
+
export const PRECEDENTS = [
|
|
8
|
+
// SGLT2 inhibitors in heart failure — the TA679 / TA902 confusion
|
|
9
|
+
// that the management benchmark exposed.
|
|
10
|
+
{
|
|
11
|
+
ta_number: "TA679",
|
|
12
|
+
drug: "dapagliflozin",
|
|
13
|
+
indication_keywords: ["hfref", "heart failure with reduced ejection fraction", "reduced ejection fraction"],
|
|
14
|
+
title: "Dapagliflozin for treating chronic heart failure with reduced ejection fraction",
|
|
15
|
+
date: "2021-02",
|
|
16
|
+
url: "https://www.nice.org.uk/guidance/ta679",
|
|
17
|
+
notes: "DO NOT confuse with TA902 — TA902 covers HFpEF/HFmrEF, not HFrEF.",
|
|
18
|
+
confidence: "high",
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
ta_number: "TA902",
|
|
22
|
+
drug: "dapagliflozin",
|
|
23
|
+
indication_keywords: ["hfpef", "hfmref", "preserved ejection fraction", "mildly reduced ejection fraction"],
|
|
24
|
+
title: "Dapagliflozin for treating chronic heart failure with preserved or mildly reduced ejection fraction",
|
|
25
|
+
date: "2023-06",
|
|
26
|
+
url: "https://www.nice.org.uk/guidance/ta902",
|
|
27
|
+
notes: "DO NOT confuse with TA679 — TA679 covers HFrEF.",
|
|
28
|
+
confidence: "high",
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
ta_number: "TA773",
|
|
32
|
+
drug: "empagliflozin",
|
|
33
|
+
indication_keywords: ["hfref", "heart failure with reduced ejection fraction"],
|
|
34
|
+
title: "Empagliflozin for treating chronic heart failure with reduced ejection fraction",
|
|
35
|
+
date: "2022-03",
|
|
36
|
+
url: "https://www.nice.org.uk/guidance/ta773",
|
|
37
|
+
confidence: "high",
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
ta_number: "TA929",
|
|
41
|
+
drug: "empagliflozin",
|
|
42
|
+
indication_keywords: ["hfpef", "hfmref", "preserved ejection fraction", "mildly reduced ejection fraction"],
|
|
43
|
+
title: "Empagliflozin for treating chronic heart failure with preserved or mildly reduced ejection fraction",
|
|
44
|
+
date: "2023-09",
|
|
45
|
+
url: "https://www.nice.org.uk/guidance/ta929",
|
|
46
|
+
confidence: "high",
|
|
47
|
+
},
|
|
48
|
+
// SGLT2i in CKD
|
|
49
|
+
{
|
|
50
|
+
ta_number: "TA775",
|
|
51
|
+
drug: "dapagliflozin",
|
|
52
|
+
indication_keywords: ["chronic kidney disease", "ckd"],
|
|
53
|
+
title: "Dapagliflozin for treating chronic kidney disease",
|
|
54
|
+
date: "2022-03",
|
|
55
|
+
url: "https://www.nice.org.uk/guidance/ta775",
|
|
56
|
+
confidence: "high",
|
|
57
|
+
},
|
|
58
|
+
// SGLT2i in T2D
|
|
59
|
+
{
|
|
60
|
+
ta_number: "TA288",
|
|
61
|
+
drug: "dapagliflozin",
|
|
62
|
+
indication_keywords: ["type 2 diabetes", "t2dm", "type-2 diabetes"],
|
|
63
|
+
title: "Dapagliflozin in combination therapy for treating type 2 diabetes",
|
|
64
|
+
date: "2013-06",
|
|
65
|
+
url: "https://www.nice.org.uk/guidance/ta288",
|
|
66
|
+
confidence: "high",
|
|
67
|
+
},
|
|
68
|
+
// ARNI in HF
|
|
69
|
+
{
|
|
70
|
+
ta_number: "TA388",
|
|
71
|
+
drug: "sacubitril valsartan",
|
|
72
|
+
indication_keywords: ["heart failure", "hfref", "reduced ejection fraction"],
|
|
73
|
+
title: "Sacubitril valsartan for treating symptomatic chronic heart failure with reduced ejection fraction",
|
|
74
|
+
date: "2016-04",
|
|
75
|
+
url: "https://www.nice.org.uk/guidance/ta388",
|
|
76
|
+
confidence: "high",
|
|
77
|
+
},
|
|
78
|
+
];
|
|
79
|
+
/**
|
|
80
|
+
* Look up NICE TA precedents that match the given drug and indication
|
|
81
|
+
* substrings. Returns 0..n matches, ranked by drug match first then
|
|
82
|
+
* by number of indication keyword matches.
|
|
83
|
+
*
|
|
84
|
+
* Matching is intentionally loose: we want to catch "HFrEF",
|
|
85
|
+
* "heart failure with reduced ejection fraction", and "hf with
|
|
86
|
+
* reduced EF" all matching TA679. Both drug name and indication
|
|
87
|
+
* are lowercased before substring comparison.
|
|
88
|
+
*/
|
|
89
|
+
export function findPrecedents(drug, indication) {
|
|
90
|
+
const drugLower = drug.toLowerCase().trim();
|
|
91
|
+
const indicationLower = indication.toLowerCase().trim();
|
|
92
|
+
if (!drugLower || !indicationLower)
|
|
93
|
+
return [];
|
|
94
|
+
const scored = PRECEDENTS.map((p) => {
|
|
95
|
+
const drugMatch = drugLower.includes(p.drug) || p.drug.includes(drugLower);
|
|
96
|
+
if (!drugMatch)
|
|
97
|
+
return { p, score: 0 };
|
|
98
|
+
const matchedKeywords = p.indication_keywords.filter((kw) => indicationLower.includes(kw));
|
|
99
|
+
return { p, score: matchedKeywords.length };
|
|
100
|
+
});
|
|
101
|
+
return scored
|
|
102
|
+
.filter((s) => s.score > 0)
|
|
103
|
+
.sort((a, b) => b.score - a.score)
|
|
104
|
+
.map((s) => s.p);
|
|
105
|
+
}
|
|
106
|
+
export function detectMismatch(drug, indication, userSuppliedTaNumber) {
|
|
107
|
+
if (!userSuppliedTaNumber)
|
|
108
|
+
return null;
|
|
109
|
+
const userTa = userSuppliedTaNumber.trim().toUpperCase();
|
|
110
|
+
const canonical = findPrecedents(drug, indication);
|
|
111
|
+
if (canonical.length === 0)
|
|
112
|
+
return null;
|
|
113
|
+
const top = canonical[0];
|
|
114
|
+
if (top.ta_number.toUpperCase() === userTa)
|
|
115
|
+
return null;
|
|
116
|
+
// Check whether the user-supplied TA exists in our table for any
|
|
117
|
+
// OTHER indication of the same drug — if so, we can give a much
|
|
118
|
+
// more informative explanation.
|
|
119
|
+
const userTaEntry = PRECEDENTS.find((p) => p.ta_number.toUpperCase() === userTa);
|
|
120
|
+
let explanation;
|
|
121
|
+
if (userTaEntry) {
|
|
122
|
+
explanation =
|
|
123
|
+
`User cited ${userTa} (${userTaEntry.title}) but the canonical NICE TA for ` +
|
|
124
|
+
`"${drug} ${indication}" is ${top.ta_number} (${top.title}). ` +
|
|
125
|
+
`${userTaEntry.notes ?? ""} ${top.notes ?? ""}`.trim();
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
explanation =
|
|
129
|
+
`User cited ${userTa} but the canonical NICE TA for ` +
|
|
130
|
+
`"${drug} ${indication}" is ${top.ta_number} (${top.title}).`;
|
|
131
|
+
}
|
|
132
|
+
return {
|
|
133
|
+
user_supplied: userTa,
|
|
134
|
+
canonical: top.ta_number,
|
|
135
|
+
user_supplied_url: userTaEntry?.url,
|
|
136
|
+
canonical_url: top.url,
|
|
137
|
+
drug,
|
|
138
|
+
indication,
|
|
139
|
+
explanation,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Extract a TA number (e.g. "TA902") from arbitrary prose. Returns
|
|
144
|
+
* the first match or null. Used to scan e.g. evidence_summary for
|
|
145
|
+
* a TA number the user may have referenced informally.
|
|
146
|
+
*/
|
|
147
|
+
const TA_NUMBER_REGEX = /\b(TA\d{2,4})\b/i;
|
|
148
|
+
export function extractTaNumber(text) {
|
|
149
|
+
const m = text.match(TA_NUMBER_REGEX);
|
|
150
|
+
return m ? m[1].toUpperCase() : null;
|
|
151
|
+
}
|
|
152
|
+
//# sourceMappingURL=niceTaPrecedents.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"niceTaPrecedents.js","sourceRoot":"","sources":["../../src/data/niceTaPrecedents.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EU JCA scope eligibility checker.
|
|
3
|
+
*
|
|
4
|
+
* Per Regulation (EU) 2021/2282, JCA scope is phased:
|
|
5
|
+
* • From 12 January 2025: oncology medicinal products + ATMPs with
|
|
6
|
+
* new active substances.
|
|
7
|
+
* • From 13 January 2028: orphan medicinal products added.
|
|
8
|
+
* • From 13 January 2030: all new medicinal products.
|
|
9
|
+
*
|
|
10
|
+
* Therefore, a request to produce a JCA PICO matrix for, e.g.,
|
|
11
|
+
* dapagliflozin in HFrEF (cardiovascular, small molecule) is asking
|
|
12
|
+
* for something that does not exist today and won't until 2030.
|
|
13
|
+
*
|
|
14
|
+
* This module exposes a pure function the `jca_pico_scope` tool calls
|
|
15
|
+
* before producing any output, so the tool can:
|
|
16
|
+
* (a) refuse to produce a fabricated JCA matrix when the indication
|
|
17
|
+
* is not in scope, AND
|
|
18
|
+
* (b) explain the scope rule, the canonical Regulation reference,
|
|
19
|
+
* and the year the indication WILL be in scope.
|
|
20
|
+
*
|
|
21
|
+
* This closes the gap that Claude.ai surfaced during the management
|
|
22
|
+
* benchmark — Claude.ai dynamically discovered the JCA scope reality
|
|
23
|
+
* via web search; HEORAgent now has it baked in.
|
|
24
|
+
*
|
|
25
|
+
* See design log #16.
|
|
26
|
+
*/
|
|
27
|
+
import type { IndicationCategory } from "./types.js";
|
|
28
|
+
export type DrugClassForScope = "monoclonal_antibody" | "small_molecule" | "atmp_cell" | "atmp_gene" | "atmp_tissue" | "biosimilar" | "vaccine" | "radiopharmaceutical" | "other";
|
|
29
|
+
export interface ScopeEligibility {
|
|
30
|
+
/** True if the indication-drug-class combo is currently in JCA scope. */
|
|
31
|
+
in_scope: boolean;
|
|
32
|
+
/** Year (Jan 13) the combo enters JCA scope per Reg 2021/2282. */
|
|
33
|
+
in_scope_from_year: 2025 | 2028 | 2030;
|
|
34
|
+
/** Phase that gates scope entry. */
|
|
35
|
+
phase: "oncology_atmp" | "orphan" | "all_medicinal";
|
|
36
|
+
/** Human-readable explanation suitable for surfacing in tool output. */
|
|
37
|
+
explanation: string;
|
|
38
|
+
/** Canonical Regulation references. */
|
|
39
|
+
references: string[];
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Determine whether a drug-indication combo is currently in JCA scope.
|
|
43
|
+
*
|
|
44
|
+
* Inputs are the same shape `jca_pico_scope` already collects, so the
|
|
45
|
+
* tool can call this with no schema change. The optional `is_orphan`
|
|
46
|
+
* flag tells us whether the product is orphan-designated; when omitted,
|
|
47
|
+
* we conservatively assume not orphan so the function never falsely
|
|
48
|
+
* places a combo in 2028 scope.
|
|
49
|
+
*/
|
|
50
|
+
export declare function checkScopeEligibility(args: {
|
|
51
|
+
indication_category: IndicationCategory;
|
|
52
|
+
drug_class: DrugClassForScope;
|
|
53
|
+
is_orphan?: boolean;
|
|
54
|
+
}): ScopeEligibility;
|
|
55
|
+
//# sourceMappingURL=scopeEligibility.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scopeEligibility.d.ts","sourceRoot":"","sources":["../../src/jca/scopeEligibility.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAErD,MAAM,MAAM,iBAAiB,GACzB,qBAAqB,GACrB,gBAAgB,GAChB,WAAW,GACX,WAAW,GACX,aAAa,GACb,YAAY,GACZ,SAAS,GACT,qBAAqB,GACrB,OAAO,CAAC;AAEZ,MAAM,WAAW,gBAAgB;IAC/B,yEAAyE;IACzE,QAAQ,EAAE,OAAO,CAAC;IAClB,kEAAkE;IAClE,kBAAkB,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;IACvC,oCAAoC;IACpC,KAAK,EAAE,eAAe,GAAG,QAAQ,GAAG,eAAe,CAAC;IACpD,wEAAwE;IACxE,WAAW,EAAE,MAAM,CAAC;IACpB,uCAAuC;IACvC,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB;AAED;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE;IAC1C,mBAAmB,EAAE,kBAAkB,CAAC;IACxC,UAAU,EAAE,iBAAiB,CAAC;IAC9B,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,GAAG,gBAAgB,CAqDnB"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Determine whether a drug-indication combo is currently in JCA scope.
|
|
3
|
+
*
|
|
4
|
+
* Inputs are the same shape `jca_pico_scope` already collects, so the
|
|
5
|
+
* tool can call this with no schema change. The optional `is_orphan`
|
|
6
|
+
* flag tells us whether the product is orphan-designated; when omitted,
|
|
7
|
+
* we conservatively assume not orphan so the function never falsely
|
|
8
|
+
* places a combo in 2028 scope.
|
|
9
|
+
*/
|
|
10
|
+
export function checkScopeEligibility(args) {
|
|
11
|
+
const refs = [
|
|
12
|
+
"Regulation (EU) 2021/2282 (HTA Regulation), Article 7 — phased scope.",
|
|
13
|
+
"EU Implementing Regulation 2024/1381 — JCA procedural rules.",
|
|
14
|
+
"European Commission factsheet, JCA for Medicinal Products (Jan 2025): https://health.ec.europa.eu/document/download/ced91156-ffe1-472d-85eb-aa6a91dd707e_en?filename=hta_htar_factsheet-jca_en.pdf",
|
|
15
|
+
];
|
|
16
|
+
// Phase 1 (in scope NOW, since 12 Jan 2025): oncology medicinal
|
|
17
|
+
// products + ATMP-class drugs (cell, gene, tissue) with new
|
|
18
|
+
// active substances.
|
|
19
|
+
const isOncology = args.indication_category === "oncology_nsclc" ||
|
|
20
|
+
args.indication_category === "oncology_other";
|
|
21
|
+
const isAtmp = args.drug_class === "atmp_cell" ||
|
|
22
|
+
args.drug_class === "atmp_gene" ||
|
|
23
|
+
args.drug_class === "atmp_tissue";
|
|
24
|
+
if (isOncology || isAtmp) {
|
|
25
|
+
return {
|
|
26
|
+
in_scope: true,
|
|
27
|
+
in_scope_from_year: 2025,
|
|
28
|
+
phase: "oncology_atmp",
|
|
29
|
+
explanation: "In JCA scope since 12 January 2025 (Phase 1: oncology medicinal products + ATMPs with new active substances).",
|
|
30
|
+
references: refs,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
// Phase 2 (in scope from 13 Jan 2028): orphan-designated medicinal
|
|
34
|
+
// products. We treat this conservatively — only flag in-scope=true
|
|
35
|
+
// for orphan when the user explicitly sets is_orphan AND we're
|
|
36
|
+
// already past 2028. Since we can't predict the future, we always
|
|
37
|
+
// return out-of-scope today but with the right "in_scope_from_year".
|
|
38
|
+
if (args.is_orphan === true) {
|
|
39
|
+
return {
|
|
40
|
+
in_scope: false,
|
|
41
|
+
in_scope_from_year: 2028,
|
|
42
|
+
phase: "orphan",
|
|
43
|
+
explanation: "Orphan-designated medicinal products enter JCA scope from 13 January 2028 (Phase 2 of Reg 2021/2282). Until then, this indication is assessed at the national level only — produce a consolidated national-HTA PICO matrix instead.",
|
|
44
|
+
references: refs,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
// Phase 3 (in scope from 13 Jan 2030): all new medicinal products.
|
|
48
|
+
return {
|
|
49
|
+
in_scope: false,
|
|
50
|
+
in_scope_from_year: 2030,
|
|
51
|
+
phase: "all_medicinal",
|
|
52
|
+
explanation: "Cardiovascular, metabolic, respiratory, neurology, infectious disease, rheumatology, IBD, and other non-oncology / non-ATMP indications enter JCA scope from 13 January 2030 (Phase 3 of Reg 2021/2282). Until then, this indication is assessed at the national level only — produce a consolidated national-HTA PICO matrix instead.",
|
|
53
|
+
references: refs,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=scopeEligibility.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scopeEligibility.js","sourceRoot":"","sources":["../../src/jca/scopeEligibility.ts"],"names":[],"mappings":"AAoDA;;;;;;;;GAQG;AACH,MAAM,UAAU,qBAAqB,CAAC,IAIrC;IACC,MAAM,IAAI,GAAG;QACX,uEAAuE;QACvE,8DAA8D;QAC9D,oMAAoM;KACrM,CAAC;IAEF,gEAAgE;IAChE,4DAA4D;IAC5D,qBAAqB;IACrB,MAAM,UAAU,GACd,IAAI,CAAC,mBAAmB,KAAK,gBAAgB;QAC7C,IAAI,CAAC,mBAAmB,KAAK,gBAAgB,CAAC;IAChD,MAAM,MAAM,GACV,IAAI,CAAC,UAAU,KAAK,WAAW;QAC/B,IAAI,CAAC,UAAU,KAAK,WAAW;QAC/B,IAAI,CAAC,UAAU,KAAK,aAAa,CAAC;IACpC,IAAI,UAAU,IAAI,MAAM,EAAE,CAAC;QACzB,OAAO;YACL,QAAQ,EAAE,IAAI;YACd,kBAAkB,EAAE,IAAI;YACxB,KAAK,EAAE,eAAe;YACtB,WAAW,EACT,+GAA+G;YACjH,UAAU,EAAE,IAAI;SACjB,CAAC;IACJ,CAAC;IAED,mEAAmE;IACnE,mEAAmE;IACnE,+DAA+D;IAC/D,kEAAkE;IAClE,qEAAqE;IACrE,IAAI,IAAI,CAAC,SAAS,KAAK,IAAI,EAAE,CAAC;QAC5B,OAAO;YACL,QAAQ,EAAE,KAAK;YACf,kBAAkB,EAAE,IAAI;YACxB,KAAK,EAAE,QAAQ;YACf,WAAW,EACT,qOAAqO;YACvO,UAAU,EAAE,IAAI;SACjB,CAAC;IACJ,CAAC;IAED,mEAAmE;IACnE,OAAO;QACL,QAAQ,EAAE,KAAK;QACf,kBAAkB,EAAE,IAAI;QACxB,KAAK,EAAE,eAAe;QACtB,WAAW,EACT,wUAAwU;QAC1U,UAAU,EAAE,IAAI;KACjB,CAAC;AACJ,CAAC"}
|
|
@@ -17,6 +17,8 @@ interface Decision {
|
|
|
17
17
|
primary_category: PvCategory;
|
|
18
18
|
alternatives: PvCategory[];
|
|
19
19
|
rationale: string;
|
|
20
|
+
/** Optional advisory warnings surfaced from edge cases — handler attaches to audit. */
|
|
21
|
+
advisory_warnings?: string[];
|
|
20
22
|
}
|
|
21
23
|
export declare function classifyPv(input: PvClassifyInput): Decision;
|
|
22
24
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"decisionTree.d.ts","sourceRoot":"","sources":["../../src/pv/decisionTree.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAE9D,UAAU,QAAQ;IAChB,gBAAgB,EAAE,UAAU,CAAC;IAC7B,YAAY,EAAE,UAAU,EAAE,CAAC;IAC3B,SAAS,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"decisionTree.d.ts","sourceRoot":"","sources":["../../src/pv/decisionTree.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAE9D,UAAU,QAAQ;IAChB,gBAAgB,EAAE,UAAU,CAAC;IAC7B,YAAY,EAAE,UAAU,EAAE,CAAC;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,uFAAuF;IACvF,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC9B;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,eAAe,GAAG,QAAQ,CAmI3D"}
|
package/dist/pv/decisionTree.js
CHANGED
|
@@ -1,39 +1,75 @@
|
|
|
1
1
|
export function classifyPv(input) {
|
|
2
|
-
// 1. Pre-authorisation: never PASS — characterise via ICH E2E plan
|
|
2
|
+
// 1. Pre-authorisation: never PASS — characterise via ICH E2E plan
|
|
3
|
+
// (which is an ICH guideline, NOT a GVP module). The plan informs
|
|
4
|
+
// the initial RMP that's submitted at MA grant under GVP Module V.
|
|
3
5
|
if (input.regulatory_context === "pre_authorisation") {
|
|
4
6
|
return withPregnancyOverride(input, {
|
|
5
7
|
primary_category: "ICH_E2E_pharmacovigilance_plan",
|
|
6
8
|
alternatives: [],
|
|
7
|
-
rationale: "Study is pre-authorisation. PASS designations require an existing marketing authorisation (Article 107a). Pharmacovigilance planning at this stage falls under ICH E2E
|
|
9
|
+
rationale: "Study is pre-authorisation. PASS designations require an existing marketing authorisation (Article 107a). Pharmacovigilance planning at this stage falls under ICH E2E — note that ICH E2E is a standalone ICH guideline, not a GVP module; the GVP Module V reference reflects the downstream RMP that the E2E plan informs at MAA submission, not a direct mapping.",
|
|
8
10
|
});
|
|
9
11
|
}
|
|
10
12
|
// 2. Spontaneous reports stand alone — Module VI baseline obligation.
|
|
13
|
+
// imposed_by_authority is contradictory here (spontaneous reporting
|
|
14
|
+
// is an inherent obligation, not something an authority can impose
|
|
15
|
+
// as a study) — surface a warning rather than silently dropping the
|
|
16
|
+
// flag.
|
|
11
17
|
if (input.study_design === "spontaneous_reports") {
|
|
18
|
+
const warnings = input.imposed_by_authority
|
|
19
|
+
? [
|
|
20
|
+
"Contradictory inputs: study_design='spontaneous_reports' AND imposed_by_authority=true. Spontaneous adverse-event reporting is an inherent PV obligation under GVP Module VI, not a designed study an authority can impose. The imposed_by_authority flag has been ignored.",
|
|
21
|
+
]
|
|
22
|
+
: undefined;
|
|
12
23
|
return withPregnancyOverride(input, {
|
|
13
24
|
primary_category: "spontaneous_reporting_only",
|
|
14
25
|
alternatives: [],
|
|
15
26
|
rationale: "Spontaneous adverse-event reporting is the baseline PV obligation under GVP Module VI, not a designed study. Categorised as spontaneous_reporting_only.",
|
|
27
|
+
advisory_warnings: warnings,
|
|
16
28
|
});
|
|
17
29
|
}
|
|
18
|
-
// 3.
|
|
30
|
+
// 3. Imposed PASS takes precedence over plain RMP_Annex_4_study —
|
|
31
|
+
// Article 107n imposition outranks Annex 4 listing. The Annex 4
|
|
32
|
+
// listing is then a downstream consequence of the imposition.
|
|
33
|
+
if (input.imposed_by_authority) {
|
|
34
|
+
const warnings = [];
|
|
35
|
+
if ((input.regulatory_context === "conditional_approval" ||
|
|
36
|
+
input.regulatory_context === "accelerated_approval") &&
|
|
37
|
+
!input.imposed_by_authority) {
|
|
38
|
+
// dead branch (imposed=true above) — guarded for clarity if reordered
|
|
39
|
+
}
|
|
40
|
+
return withPregnancyOverride(input, {
|
|
41
|
+
primary_category: "PASS_imposed",
|
|
42
|
+
alternatives: input.regulatory_context === "rmp_commitment"
|
|
43
|
+
? ["RMP_Annex_4_study"]
|
|
44
|
+
: ["RMP_Annex_4_study"],
|
|
45
|
+
rationale: input.regulatory_context === "rmp_commitment"
|
|
46
|
+
? "Authority-imposed obligation that is also tracked as an RMP Annex 4 commitment. Article 107n imposition is the primary classification — the Annex 4 listing is a consequence, not the primary route. Falls under GVP Module VIII (PASS imposed); PRAC pre-review of the protocol is required."
|
|
47
|
+
: "Authority-imposed obligation in a post-authorisation context (Article 107n). Falls under GVP Module VIII as a Post-Authorisation Safety Study (imposed). PRAC pre-review of the protocol is required.",
|
|
48
|
+
advisory_warnings: warnings.length > 0 ? warnings : undefined,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
// 4. RMP commitment context → Annex 4 listed study (when not imposed).
|
|
19
52
|
if (input.regulatory_context === "rmp_commitment") {
|
|
20
|
-
const alts = input.imposed_by_authority
|
|
21
|
-
? ["PASS_imposed"]
|
|
22
|
-
: ["PASS_voluntary"];
|
|
23
53
|
return withPregnancyOverride(input, {
|
|
24
54
|
primary_category: "RMP_Annex_4_study",
|
|
25
|
-
alternatives:
|
|
55
|
+
alternatives: ["PASS_voluntary"],
|
|
26
56
|
rationale: "Study is an explicit RMP commitment, listed in RMP Annex 4 with milestone tracking. Per EMA GVP Module V.",
|
|
27
57
|
});
|
|
28
58
|
}
|
|
29
|
-
//
|
|
30
|
-
//
|
|
31
|
-
//
|
|
32
|
-
|
|
59
|
+
// 4a. Conditional / accelerated approval WITHOUT imposed_by_authority —
|
|
60
|
+
// warn about Specific Obligations (CMA SOBs) per Article 14-a.
|
|
61
|
+
// Falling through to objective-based classification but the user
|
|
62
|
+
// should confirm whether the obligation is actually imposed.
|
|
63
|
+
if ((input.regulatory_context === "conditional_approval" ||
|
|
64
|
+
input.regulatory_context === "accelerated_approval") &&
|
|
65
|
+
!input.imposed_by_authority) {
|
|
66
|
+
const out = classifyByObjective(input);
|
|
33
67
|
return withPregnancyOverride(input, {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
68
|
+
...out,
|
|
69
|
+
advisory_warnings: [
|
|
70
|
+
...(out.advisory_warnings ?? []),
|
|
71
|
+
`regulatory_context='${input.regulatory_context}' typically carries Specific Obligations (CMA Article 14-a / accelerated-approval SOBs) that function as an imposed PASS even without imposed_by_authority=true. Confirm SOB status with regulatory affairs; if confirmed, re-run with imposed_by_authority=true to get the correct PASS_imposed classification + Module VIII PRAC pre-review obligations.`,
|
|
72
|
+
],
|
|
37
73
|
});
|
|
38
74
|
}
|
|
39
75
|
// 5. Otherwise classify by primary objective + study design.
|
|
@@ -82,6 +118,7 @@ function withPregnancyOverride(input, decision) {
|
|
|
82
118
|
alternatives: ["pregnancy_registry"],
|
|
83
119
|
rationale: decision.rationale +
|
|
84
120
|
" Pregnancy population: a pregnancy registry will be required as a post-authorisation commitment per ICH E2E and EMA pregnancy-registry guidance.",
|
|
121
|
+
advisory_warnings: decision.advisory_warnings,
|
|
85
122
|
};
|
|
86
123
|
}
|
|
87
124
|
if (decision.primary_category === "pregnancy_registry")
|
|
@@ -94,6 +131,47 @@ function withPregnancyOverride(input, decision) {
|
|
|
94
131
|
rationale: "Pregnancy population in scope → pregnancy registry takes priority over the primary classification. " +
|
|
95
132
|
decision.rationale +
|
|
96
133
|
" The original classification is retained as an alternative for protocol-design context.",
|
|
134
|
+
advisory_warnings: decision.advisory_warnings,
|
|
97
135
|
};
|
|
98
136
|
}
|
|
137
|
+
/**
|
|
138
|
+
* Pure classification by primary_objective. Extracted so the
|
|
139
|
+
* conditional_approval / accelerated_approval branch can reuse it without
|
|
140
|
+
* duplicating the switch.
|
|
141
|
+
*/
|
|
142
|
+
function classifyByObjective(input) {
|
|
143
|
+
switch (input.primary_objective) {
|
|
144
|
+
case "safety":
|
|
145
|
+
return {
|
|
146
|
+
primary_category: "PASS_voluntary",
|
|
147
|
+
alternatives: ["active_surveillance_registry"],
|
|
148
|
+
rationale: "Safety-objective post-authorisation study without an imposed obligation → voluntary PASS under GVP Module VIII.",
|
|
149
|
+
};
|
|
150
|
+
case "efficacy":
|
|
151
|
+
case "effectiveness":
|
|
152
|
+
return {
|
|
153
|
+
primary_category: "PAES",
|
|
154
|
+
alternatives: ["PASS_voluntary"],
|
|
155
|
+
rationale: "Effectiveness/efficacy-objective post-authorisation study → Post-Authorisation Efficacy Study (PAES).",
|
|
156
|
+
};
|
|
157
|
+
case "drug_utilization":
|
|
158
|
+
return {
|
|
159
|
+
primary_category: "DUS",
|
|
160
|
+
alternatives: ["PASS_voluntary"],
|
|
161
|
+
rationale: "Drug utilisation objective → DUS under GVP Module VIII Addendum I.",
|
|
162
|
+
};
|
|
163
|
+
case "natural_history":
|
|
164
|
+
return {
|
|
165
|
+
primary_category: "active_surveillance_registry",
|
|
166
|
+
alternatives: ["PASS_voluntary"],
|
|
167
|
+
rationale: "Natural history / disease epidemiology objective → active surveillance registry.",
|
|
168
|
+
};
|
|
169
|
+
case "risk_minimisation_evaluation":
|
|
170
|
+
return {
|
|
171
|
+
primary_category: "PASS_voluntary",
|
|
172
|
+
alternatives: ["DUS"],
|
|
173
|
+
rationale: "Evaluation of risk-minimisation measure effectiveness → categorised as PASS (voluntary unless imposed).",
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
}
|
|
99
177
|
//# sourceMappingURL=decisionTree.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"decisionTree.js","sourceRoot":"","sources":["../../src/pv/decisionTree.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"decisionTree.js","sourceRoot":"","sources":["../../src/pv/decisionTree.ts"],"names":[],"mappings":"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"}
|
package/dist/pv/gvpModules.d.ts
CHANGED
|
@@ -9,7 +9,8 @@ import type { GvpModule, PvCategory } from "./types.js";
|
|
|
9
9
|
export declare const GVP_REVISION: "rev_4";
|
|
10
10
|
interface CategoryMapping {
|
|
11
11
|
gvp_module: GvpModule;
|
|
12
|
-
|
|
12
|
+
/** Plain-language ENCePP category label — NOT a registered protocol ID. */
|
|
13
|
+
encepp_study_category?: string;
|
|
13
14
|
rmp_implications: string[];
|
|
14
15
|
fda_analogue?: string;
|
|
15
16
|
submission_obligations: string[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gvpModules.d.ts","sourceRoot":"","sources":["../../src/pv/gvpModules.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAExD,eAAO,MAAM,YAAY,EAAG,OAAgB,CAAC;AAE7C,UAAU,eAAe;IACvB,UAAU,EAAE,SAAS,CAAC;IACtB,
|
|
1
|
+
{"version":3,"file":"gvpModules.d.ts","sourceRoot":"","sources":["../../src/pv/gvpModules.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAExD,eAAO,MAAM,YAAY,EAAG,OAAgB,CAAC;AAE7C,UAAU,eAAe;IACvB,UAAU,EAAE,SAAS,CAAC;IACtB,2EAA2E;IAC3E,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,sBAAsB,EAAE,MAAM,EAAE,CAAC;CAClC;AAED,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,UAAU,EAAE,eAAe,CA2HhE,CAAC;AAEF,wBAAgB,MAAM,CAAC,QAAQ,EAAE,UAAU,GAAG,eAAe,CAE5D"}
|
package/dist/pv/gvpModules.js
CHANGED
|
@@ -2,7 +2,7 @@ export const GVP_REVISION = "rev_4";
|
|
|
2
2
|
export const CATEGORY_MAPPING = {
|
|
3
3
|
PASS_imposed: {
|
|
4
4
|
gvp_module: "VIII",
|
|
5
|
-
|
|
5
|
+
encepp_study_category: "PASS — post-authorisation safety study (imposed, GVP Module VIII). Use the ENCePP Code of Conduct checklist for protocol structure",
|
|
6
6
|
rmp_implications: [
|
|
7
7
|
"Update RMP Part III (PV Plan) to list this study as an imposed PASS.",
|
|
8
8
|
"Annex 4 of the RMP must reference the protocol and timelines.",
|
|
@@ -16,7 +16,7 @@ export const CATEGORY_MAPPING = {
|
|
|
16
16
|
},
|
|
17
17
|
PASS_voluntary: {
|
|
18
18
|
gvp_module: "VIII",
|
|
19
|
-
|
|
19
|
+
encepp_study_category: "PASS — post-authorisation safety study (voluntary, GVP Module VIII). ENCePP registration in EUPAS encouraged",
|
|
20
20
|
rmp_implications: [
|
|
21
21
|
"Document study as a voluntary PASS in RMP Part III.",
|
|
22
22
|
"No PRAC pre-approval required; encouraged ENCePP registration.",
|
|
@@ -29,7 +29,7 @@ export const CATEGORY_MAPPING = {
|
|
|
29
29
|
},
|
|
30
30
|
PAES: {
|
|
31
31
|
gvp_module: "VIII",
|
|
32
|
-
|
|
32
|
+
encepp_study_category: "PAES — post-authorisation efficacy study (GVP Module VIII)",
|
|
33
33
|
rmp_implications: [
|
|
34
34
|
"PAES results may trigger MA variation if effectiveness diverges from MA assumptions.",
|
|
35
35
|
],
|
|
@@ -41,7 +41,7 @@ export const CATEGORY_MAPPING = {
|
|
|
41
41
|
},
|
|
42
42
|
RMP_Annex_4_study: {
|
|
43
43
|
gvp_module: "V",
|
|
44
|
-
|
|
44
|
+
encepp_study_category: "RMP Annex 4 study (GVP Module V)",
|
|
45
45
|
rmp_implications: [
|
|
46
46
|
"Listed in RMP Annex 4 with study ID, status, milestone dates.",
|
|
47
47
|
"Study completion is a tracked RMP commitment.",
|
|
@@ -54,7 +54,7 @@ export const CATEGORY_MAPPING = {
|
|
|
54
54
|
},
|
|
55
55
|
DUS: {
|
|
56
56
|
gvp_module: "VIII_Addendum_I",
|
|
57
|
-
|
|
57
|
+
encepp_study_category: "DUS — drug utilisation study (GVP Module VIII Addendum I)",
|
|
58
58
|
rmp_implications: [
|
|
59
59
|
"DUS may be referenced in RMP Part III for risk minimisation effectiveness.",
|
|
60
60
|
],
|
|
@@ -66,7 +66,7 @@ export const CATEGORY_MAPPING = {
|
|
|
66
66
|
},
|
|
67
67
|
active_surveillance_registry: {
|
|
68
68
|
gvp_module: "VIII",
|
|
69
|
-
|
|
69
|
+
encepp_study_category: "Active surveillance registry (GVP Module VIII)",
|
|
70
70
|
rmp_implications: [
|
|
71
71
|
"Registry serves as data source for ongoing PV signal detection in RMP Part III.",
|
|
72
72
|
],
|
|
@@ -78,7 +78,7 @@ export const CATEGORY_MAPPING = {
|
|
|
78
78
|
},
|
|
79
79
|
pregnancy_registry: {
|
|
80
80
|
gvp_module: "VIII",
|
|
81
|
-
|
|
81
|
+
encepp_study_category: "Pregnancy registry (GVP Module VIII; refer to GVP Considerations P.III)",
|
|
82
82
|
rmp_implications: [
|
|
83
83
|
"Pregnancy registry is a standard RMP commitment for teratogenic-risk drugs (Annex 4).",
|
|
84
84
|
"Outcomes feed into the Pregnancy section of every PSUR.",
|